doll active state <-> doll visibility toggle

This commit is contained in:
2025-12-23 15:58:01 +08:00
parent b4234c12f5
commit c8efcfc83c
8 changed files with 228 additions and 34 deletions

View File

@@ -3,6 +3,7 @@ import { listen, type UnlistenFn } from "@tauri-apps/api/event";
import { writable } from "svelte/store";
import type { CursorPositions } from "../types/bindings/CursorPositions";
import type { CursorPosition } from "../types/bindings/CursorPosition";
import type { DollDto } from "../types/bindings/DollDto";
export let cursorPositionOnScreen = writable<CursorPositions>({
raw: { x: 0, y: 0 },
@@ -24,13 +25,13 @@ type FriendCursorData = {
// The exported store will only expose the position part to consumers,
// but internally we manage the full data.
// Actually, it's easier if we just export the positions and manage state internally.
export let friendsCursorPositions = writable<Record<string, CursorPositions>>(
{},
);
export let friendsCursorPositions = writable<Record<string, CursorPositions>>({});
export let friendsActiveDolls = writable<Record<string, DollDto | null>>({});
let unlistenCursor: UnlistenFn | null = null;
let unlistenFriendCursor: UnlistenFn | null = null;
let unlistenFriendDisconnected: UnlistenFn | null = null;
let unlistenFriendActiveDollChanged: UnlistenFn | null = null;
let isListening = false;
// Internal state to track timestamps
@@ -89,16 +90,19 @@ export async function initCursorTracking() {
try {
payload = JSON.parse(payload);
} catch (e) {
console.error("[Cursor] Failed to parse friend disconnected payload:", e);
console.error(
"[Cursor] Failed to parse friend disconnected payload:",
e,
);
return;
}
}
const data = Array.isArray(payload) ? payload[0] : payload;
// Remove from internal state
if (friendCursorState[data.userId]) {
delete friendCursorState[data.userId];
delete friendCursorState[data.userId];
}
// Update svelte store
@@ -107,9 +111,75 @@ export async function initCursorTracking() {
delete next[data.userId];
return next;
});
}
},
);
// Listen to friend active doll changed events
unlistenFriendActiveDollChanged = await listen<
| string
| {
friendId: string;
doll: DollDto | null;
}
>("friend-active-doll-changed", (event) => {
let data = event.payload;
if (typeof data === "string") {
try {
data = JSON.parse(data);
} catch (e) {
console.error(
"[Cursor] Failed to parse friend-active-doll-changed payload:",
e,
);
return;
}
}
// Cast to expected type after parsing
const payload = data as { friendId: string; doll: DollDto | null };
console.log(
"[Cursor] Received friend-active-doll-changed event:",
payload,
);
if (!payload.doll) {
// If doll is null, it means the friend deactivated their doll.
console.log(
`[Cursor] Removing doll for friend ${payload.friendId} due to deactivation`,
);
// Update the active dolls store to explicitly set this friend's doll to null
// We MUST set it to null instead of deleting it, otherwise the UI might
// fall back to the initial appData snapshot which might still have the old doll.
friendsActiveDolls.update((current) => {
const next = { ...current };
next[payload.friendId] = null;
return next;
});
// Also remove from cursor positions so the sprite disappears
friendsCursorPositions.update((current) => {
const next = { ...current };
delete next[payload.friendId];
return next;
});
} else {
// Update or add the new doll configuration
console.log(
`[Cursor] Updating doll for friend ${payload.friendId}:`,
payload.doll,
);
friendsActiveDolls.update((current) => {
return {
...current,
[payload.friendId]: payload.doll!,
};
});
}
});
isListening = true;
} catch (err) {
console.error("[Cursor] Failed to initialize cursor tracking:", err);
@@ -134,6 +204,10 @@ export function stopCursorTracking() {
unlistenFriendDisconnected();
unlistenFriendDisconnected = null;
}
if (unlistenFriendActiveDollChanged) {
unlistenFriendActiveDollChanged();
unlistenFriendActiveDollChanged = null;
}
isListening = false;
}