/** * Per-account dedupe of inbound QR strings from Baileys. Pure logic so the * pair-handler stays thin and the dedupe is unit-testable. * * Invariant: for a given accountId, identical QR payloads are dropped. * A different payload (or the first ever for that account) returns true, * meaning "yes, emit this one to the web". */ export function makeQrDedupe() { const last = new Map(); return { /** Returns true if this payload should be forwarded; false if it's a dup. */ shouldEmit(accountId: string, payload: string): boolean { if (last.get(accountId) === payload) return false; last.set(accountId, payload); return true; }, /** Forget the last payload — call when the session ends or pairing restarts. */ reset(accountId: string): void { last.delete(accountId); }, /** Test helper — current cache size. */ size(): number { return last.size; }, }; } /** * Pure logic for the pair page's countdown bar. Given a remaining-seconds * value and the total window, return the rendering primitives. Extracted * so the visual behaviour is unit-testable. */ export interface CountdownRender { /** Width % for the progress bar [0..100]. */ pct: number; /** True when the remaining time crosses the danger threshold — UI flips * to destructive colours. Threshold scales with `total`: 10 s for short * per-QR timers, 30 s for the multi-minute pairing window. */ danger: boolean; /** True when the QR has expired (≤ 0). */ expired: boolean; } export function countdownRender(seconds: number, total: number): CountdownRender { const safeTotal = total > 0 ? total : 1; const clamped = Math.max(0, Math.min(seconds, safeTotal)); const pct = Math.round((clamped / safeTotal) * 100); // Short windows (≤60 s) use a 10 s warning; longer ones (the 5-minute // pairing window) use 30 s so it shows up while still actionable. const dangerThreshold = safeTotal <= 60 ? 10 : 30; return { pct, danger: clamped > 0 && clamped <= dangerThreshold, expired: clamped <= 0, }; }