consolidated event subscription listeners into one

This commit is contained in:
2026-03-08 11:47:32 +08:00
parent 3a3ea3498b
commit 291e0311e8
7 changed files with 64 additions and 108 deletions

View File

@@ -1,10 +1,10 @@
import { writable } from "svelte/store"; import { writable } from "svelte/store";
import { commands, events, type UserData } from "$lib/bindings"; import { commands, events, type UserData } from "$lib/bindings";
import { createListenerSubscription, setupHmrCleanup } from "./listener-utils"; import { createListenersSubscription, setupHmrCleanup } from "./listener-utils";
export const appData = writable<UserData | null>(null); export const appData = writable<UserData | null>(null);
const subscription = createListenerSubscription(); const subscription = createListenersSubscription();
/** /**
* Starts listening for app data refresh events. * Starts listening for app data refresh events.
@@ -17,7 +17,7 @@ export async function startAppData() {
const unlisten = await events.appDataRefreshed.listen((event) => { const unlisten = await events.appDataRefreshed.listen((event) => {
appData.set(event.payload); appData.set(event.payload);
}); });
subscription.setUnlisten(unlisten); subscription.addUnlisten(unlisten);
subscription.setListening(true); subscription.setListening(true);
} catch (error) { } catch (error) {
console.error(error); console.error(error);

View File

@@ -1,13 +1,13 @@
import { writable } from "svelte/store"; import { writable } from "svelte/store";
import { events, type CursorPositions } from "$lib/bindings"; import { events, type CursorPositions } from "$lib/bindings";
import { createListenerSubscription, setupHmrCleanup } from "./listener-utils"; import { createListenersSubscription, setupHmrCleanup } from "./listener-utils";
export const cursorPositionOnScreen = writable<CursorPositions>({ export const cursorPositionOnScreen = writable<CursorPositions>({
raw: { x: 0, y: 0 }, raw: { x: 0, y: 0 },
mapped: { x: 0, y: 0 }, mapped: { x: 0, y: 0 },
}); });
const subscription = createListenerSubscription(); const subscription = createListenersSubscription();
/** /**
* Starts tracking the local cursor position. * Starts tracking the local cursor position.
@@ -20,7 +20,7 @@ export async function startCursorTracking() {
const unlisten = await events.cursorMoved.listen((event) => { const unlisten = await events.cursorMoved.listen((event) => {
cursorPositionOnScreen.set(event.payload); cursorPositionOnScreen.set(event.payload);
}); });
subscription.setUnlisten(unlisten); subscription.addUnlisten(unlisten);
subscription.setListening(true); subscription.setListening(true);
} catch (err) { } catch (err) {
console.error("Failed to initialize cursor tracking:", err); console.error("Failed to initialize cursor tracking:", err);

View File

@@ -3,12 +3,10 @@ import {
events, events,
type CursorPositions, type CursorPositions,
type DollDto, type DollDto,
type FriendActiveDollChangedPayload,
type FriendDisconnectedPayload,
type OutgoingFriendCursorPayload, type OutgoingFriendCursorPayload,
} from "$lib/bindings"; } from "$lib/bindings";
import { import {
createMultiListenerSubscription, createListenersSubscription,
removeFromStore, removeFromStore,
setupHmrCleanup, setupHmrCleanup,
} from "./listener-utils"; } from "./listener-utils";
@@ -23,7 +21,7 @@ export const friendsCursorPositions = writable<Record<string, CursorPositions>>(
); );
export const friendsActiveDolls = writable<Record<string, DollDto | null>>({}); export const friendsActiveDolls = writable<Record<string, DollDto | null>>({});
const subscription = createMultiListenerSubscription(); const subscription = createListenersSubscription();
let friendCursorState: Record<string, FriendCursorData> = {}; let friendCursorState: Record<string, FriendCursorData> = {};
@@ -37,8 +35,8 @@ export async function startFriendCursorTracking() {
try { try {
// TODO: Add initial sync for existing friends' cursors and dolls if needed // TODO: Add initial sync for existing friends' cursors and dolls if needed
const unlistenFriendCursor = await events.friendCursorPositionUpdated.listen( const unlistenFriendCursor =
(event) => { await events.friendCursorPositionUpdated.listen((event) => {
const data: OutgoingFriendCursorPayload = event.payload; const data: OutgoingFriendCursorPayload = event.payload;
friendCursorState[data.userId] = { friendCursorState[data.userId] = {
@@ -52,8 +50,7 @@ export async function startFriendCursorTracking() {
[data.userId]: data.position, [data.userId]: data.position,
}; };
}); });
}, });
);
subscription.addUnlisten(unlistenFriendCursor); subscription.addUnlisten(unlistenFriendCursor);
const unlistenFriendDisconnected = await events.friendDisconnected.listen( const unlistenFriendDisconnected = await events.friendDisconnected.listen(
@@ -72,30 +69,28 @@ export async function startFriendCursorTracking() {
subscription.addUnlisten(unlistenFriendDisconnected); subscription.addUnlisten(unlistenFriendDisconnected);
const unlistenFriendActiveDollChanged = const unlistenFriendActiveDollChanged =
await events.friendActiveDollChanged.listen( await events.friendActiveDollChanged.listen((event) => {
(event) => { const payload = event.payload;
const payload = event.payload;
if (!payload.doll) { if (!payload.doll) {
friendsActiveDolls.update((current) => { friendsActiveDolls.update((current) => {
const next = { ...current }; const next = { ...current };
next[payload.friendId] = null; next[payload.friendId] = null;
return next; return next;
}); });
friendsCursorPositions.update((current) => friendsCursorPositions.update((current) =>
removeFromStore(current, payload.friendId), removeFromStore(current, payload.friendId),
); );
} else { } else {
friendsActiveDolls.update((current) => { friendsActiveDolls.update((current) => {
return { return {
...current, ...current,
[payload.friendId]: payload.doll, [payload.friendId]: payload.doll,
}; };
}); });
} }
}, });
);
subscription.addUnlisten(unlistenFriendActiveDollChanged); subscription.addUnlisten(unlistenFriendActiveDollChanged);
subscription.setListening(true); subscription.setListening(true);

View File

@@ -1,17 +1,10 @@
import { writable } from "svelte/store"; import { writable } from "svelte/store";
import { import { events, type InteractionPayloadDto } from "$lib/bindings";
events, import { createListenersSubscription, setupHmrCleanup } from "./listener-utils";
type InteractionDeliveryFailedDto,
type InteractionPayloadDto,
} from "$lib/bindings";
import {
createMultiListenerSubscription,
setupHmrCleanup,
} from "./listener-utils";
export const receivedInteractions = writable<Map<string, InteractionPayloadDto>>( export const receivedInteractions = writable<
new Map(), Map<string, InteractionPayloadDto>
); >(new Map());
export function addInteraction(interaction: InteractionPayloadDto) { export function addInteraction(interaction: InteractionPayloadDto) {
receivedInteractions.update((map) => { receivedInteractions.update((map) => {
@@ -29,7 +22,7 @@ export function clearInteraction(userId: string) {
}); });
} }
const subscription = createMultiListenerSubscription(); const subscription = createListenersSubscription();
/** /**
* Starts listening for interaction events (received and delivery failed). * Starts listening for interaction events (received and delivery failed).
@@ -38,9 +31,11 @@ export async function startInteraction() {
if (subscription.isListening()) return; if (subscription.isListening()) return;
try { try {
const unlistenReceived = await events.interactionReceived.listen((event) => { const unlistenReceived = await events.interactionReceived.listen(
addInteraction(event.payload); (event) => {
}); addInteraction(event.payload);
},
);
subscription.addUnlisten(unlistenReceived); subscription.addUnlisten(unlistenReceived);
const unlistenFailed = await events.interactionDeliveryFailed.listen( const unlistenFailed = await events.interactionDeliveryFailed.listen(

View File

@@ -1,47 +1,15 @@
import type { UnlistenFn } from "@tauri-apps/api/event"; import type { UnlistenFn } from "@tauri-apps/api/event";
export type ListenerSubscription = { export type ListenerSubscription = {
stop: () => void;
isListening: () => boolean;
setListening: (value: boolean) => void;
setUnlisten: (unlisten: UnlistenFn | null) => void;
};
export type MultiListenerSubscription = {
stop: () => void; stop: () => void;
isListening: () => boolean; isListening: () => boolean;
setListening: (value: boolean) => void; setListening: (value: boolean) => void;
addUnlisten: (unlisten: UnlistenFn | null) => void; addUnlisten: (unlisten: UnlistenFn | null) => void;
}; };
export function createListenerSubscription( export function createListenersSubscription(
stopFn: () => void = () => {}, stopFn: () => void = () => {},
): ListenerSubscription { ): ListenerSubscription {
let unlisten: UnlistenFn | null = null;
let listening = false;
return {
stop: () => {
if (unlisten) {
unlisten();
unlisten = null;
}
listening = false;
stopFn();
},
isListening: () => listening,
setListening: (value) => {
listening = value;
},
setUnlisten: (next) => {
unlisten = next;
},
};
}
export function createMultiListenerSubscription(
stopFn: () => void = () => {},
): MultiListenerSubscription {
let unlistens: UnlistenFn[] = []; let unlistens: UnlistenFn[] = [];
let listening = false; let listening = false;

View File

@@ -1,10 +1,10 @@
import { writable } from "svelte/store"; import { writable } from "svelte/store";
import { commands, events } from "$lib/bindings"; import { commands, events } from "$lib/bindings";
import { createListenerSubscription, setupHmrCleanup } from "./listener-utils"; import { createListenersSubscription, setupHmrCleanup } from "./listener-utils";
export const sceneInteractive = writable<boolean>(false); export const sceneInteractive = writable<boolean>(false);
const subscription = createListenerSubscription(); const subscription = createListenersSubscription();
/** /**
* Starts listening for scene interactive state changes. * Starts listening for scene interactive state changes.
@@ -18,7 +18,7 @@ export async function startSceneInteractive() {
const unlisten = await events.sceneInteractiveChanged.listen((event) => { const unlisten = await events.sceneInteractiveChanged.listen((event) => {
sceneInteractive.set(Boolean(event.payload)); sceneInteractive.set(Boolean(event.payload));
}); });
subscription.setUnlisten(unlisten); subscription.addUnlisten(unlisten);
subscription.setListening(true); subscription.setListening(true);
} catch (error) { } catch (error) {
console.error("Failed to initialize scene interactive listener:", error); console.error("Failed to initialize scene interactive listener:", error);

View File

@@ -1,11 +1,7 @@
import { writable } from "svelte/store"; import { writable } from "svelte/store";
import { events, type UserStatusPayload } from "$lib/bindings";
import { import {
events, createListenersSubscription,
type FriendDisconnectedPayload,
type UserStatusPayload,
} from "$lib/bindings";
import {
createMultiListenerSubscription,
removeFromStore, removeFromStore,
setupHmrCleanup, setupHmrCleanup,
} from "./listener-utils"; } from "./listener-utils";
@@ -15,7 +11,7 @@ export const friendsPresenceStates = writable<
>({}); >({});
export const currentPresenceState = writable<UserStatusPayload | null>(null); export const currentPresenceState = writable<UserStatusPayload | null>(null);
const subscription = createMultiListenerSubscription(); const subscription = createListenersSubscription();
/** /**
* Starts listening for user status changes and friend status updates. * Starts listening for user status changes and friend status updates.
@@ -24,21 +20,23 @@ export async function startUserStatus() {
if (subscription.isListening()) return; if (subscription.isListening()) return;
try { try {
const unlistenStatus = await events.friendUserStatusChanged.listen((event) => { const unlistenStatus = await events.friendUserStatusChanged.listen(
const { userId, status } = event.payload; (event) => {
const { userId, status } = event.payload;
const hasValidName = const hasValidName =
(typeof status.presenceStatus.title === "string" && (typeof status.presenceStatus.title === "string" &&
status.presenceStatus.title.trim() !== "") || status.presenceStatus.title.trim() !== "") ||
(typeof status.presenceStatus.subtitle === "string" && (typeof status.presenceStatus.subtitle === "string" &&
status.presenceStatus.subtitle.trim() !== ""); status.presenceStatus.subtitle.trim() !== "");
if (!hasValidName) return; if (!hasValidName) return;
friendsPresenceStates.update((current) => ({ friendsPresenceStates.update((current) => ({
...current, ...current,
[userId]: status, [userId]: status,
})); }));
}); },
);
subscription.addUnlisten(unlistenStatus); subscription.addUnlisten(unlistenStatus);
const unlistenUserStatusChanged = await events.userStatusChanged.listen( const unlistenUserStatusChanged = await events.userStatusChanged.listen(