import { Injectable } from '@nestjs/common'; /** * In-memory focus tracker (single instance). Keyed on userId (the stable externalId the * gateway has as principal.userId). NOTE: room membership ≠ viewing — the sidebar joins * every thread room for live updates, so presence uses an explicit `focus_thread` signal. * Prod (multi-replica): back this with Redis. */ @Injectable() export class PresenceService { private readonly focus = new Map(); // socketId → focus setFocus(socketId: string, userId: string, threadId: string | null): void { this.focus.set(socketId, { userId, threadId }); } clearSocket(socketId: string): void { this.focus.delete(socketId); } isViewing(userId: string, threadId: string): boolean { for (const f of this.focus.values()) if (f.userId === userId && f.threadId === threadId) return true; return false; } }