83 lines
2.9 KiB
TypeScript
83 lines
2.9 KiB
TypeScript
import {
|
|
type ChatService,
|
|
type SyncCompletion,
|
|
type SyncProgress,
|
|
} from './chat/index.js';
|
|
|
|
/** Events emitted by one account-owned chat service into the shared UI/push hub. */
|
|
export interface AccountRuntimeEventReporter {
|
|
conversation(input: { id: string; state: 'replied'; unread: number }): unknown;
|
|
reply(input: { conversationId: string; id: string; username: string; body?: string }): unknown;
|
|
mediaReceived(input: { conversationId: string; username: string; count: number }): unknown;
|
|
sync(input:
|
|
| { phase: 'started'; id: string; kind: 'delta' | 'full' }
|
|
| { phase: 'progress'; id: string; kind: 'delta' | 'full'; current: number; total?: number; label?: string }
|
|
| { phase: 'completed'; id: string; kind: 'delta' | 'full'; processed: number }
|
|
| { phase: 'failed'; id: string; kind: 'delta' | 'full'; error: string }
|
|
): unknown;
|
|
}
|
|
|
|
type AccountChatEventSource = Pick<
|
|
ChatService,
|
|
'setProgressListener' | 'setNewReplyListener' | 'setMediaReceivedListener'
|
|
>;
|
|
|
|
/** Connects one account-owned ChatService to the process-wide event stream. */
|
|
export function wireAccountChatEvents(
|
|
chatService: AccountChatEventSource,
|
|
events: AccountRuntimeEventReporter,
|
|
accountId: string,
|
|
): void {
|
|
let activeSync: SyncProgress | null = null;
|
|
|
|
chatService.setProgressListener((progress, completion) => {
|
|
if (progress) {
|
|
const id = accountSyncId(accountId, progress.startedAt);
|
|
if (progress.phase === 'starting') {
|
|
events.sync({ phase: 'started', id, kind: progress.kind });
|
|
} else {
|
|
events.sync({
|
|
phase: 'progress',
|
|
id,
|
|
kind: progress.kind,
|
|
current: progress.current,
|
|
total: progress.total,
|
|
label: progress.label ?? progress.phase,
|
|
});
|
|
}
|
|
activeSync = progress;
|
|
return;
|
|
}
|
|
|
|
if (!activeSync) return;
|
|
reportSyncCompletion(events, accountId, activeSync, completion);
|
|
activeSync = null;
|
|
});
|
|
|
|
chatService.setMediaReceivedListener(({ conversationId, username, count }) => {
|
|
events.mediaReceived({ conversationId, username, count });
|
|
});
|
|
chatService.setNewReplyListener(({ conversationId, replyId, username, replySnippet }) => {
|
|
events.conversation({ id: conversationId, state: 'replied', unread: 1 });
|
|
events.reply({ conversationId, id: replyId, username, body: replySnippet });
|
|
});
|
|
}
|
|
|
|
function reportSyncCompletion(
|
|
events: AccountRuntimeEventReporter,
|
|
accountId: string,
|
|
progress: SyncProgress,
|
|
completion?: SyncCompletion,
|
|
): void {
|
|
const id = accountSyncId(accountId, progress.startedAt);
|
|
if (completion?.status === 'failed') {
|
|
events.sync({ phase: 'failed', id, kind: progress.kind, error: completion.error });
|
|
return;
|
|
}
|
|
events.sync({ phase: 'completed', id, kind: progress.kind, processed: progress.current });
|
|
}
|
|
|
|
function accountSyncId(accountId: string, startedAt: number): string {
|
|
return `chat-${accountId}-${startedAt}`;
|
|
}
|