diff --git a/ui/src/components/account/flow-viz/utils.ts b/ui/src/components/account/flow-viz/utils.ts index e6249006..fdbd0e35 100644 --- a/ui/src/components/account/flow-viz/utils.ts +++ b/ui/src/components/account/flow-viz/utils.ts @@ -47,10 +47,15 @@ export function generateConnectionEvents(accounts: AccountData[]): ConnectionEve // Use a shared base time so events from all accounts interleave in the timeline. // Without this, accounts with more recent lastUsedAt dominate the sorted output. const now = Date.now(); - const sharedBaseTime = accounts.reduce((latest, a) => { - const t = a.lastUsedAt ? new Date(a.lastUsedAt).getTime() : now; - return Math.max(latest, isNaN(t) ? now : t); - }, now); + const latestLastUsedAt = accounts.reduce((latest, account) => { + if (!account.lastUsedAt) return latest; + + const timestamp = new Date(account.lastUsedAt).getTime(); + if (Number.isNaN(timestamp)) return latest; + + return latest === undefined ? timestamp : Math.max(latest, timestamp); + }, undefined); + const sharedBaseTime = latestLastUsedAt ?? now; accounts.forEach((account) => { const lastUsed = new Date(sharedBaseTime); diff --git a/ui/tests/unit/ui/components/account/flow-viz/utils.test.ts b/ui/tests/unit/ui/components/account/flow-viz/utils.test.ts index 3d70a166..b5bdba4b 100644 --- a/ui/tests/unit/ui/components/account/flow-viz/utils.test.ts +++ b/ui/tests/unit/ui/components/account/flow-viz/utils.test.ts @@ -40,6 +40,7 @@ describe('generateConnectionEvents()', () => { }); afterEach(() => { + vi.useRealTimers(); vi.restoreAllMocks(); }); @@ -78,6 +79,33 @@ describe('generateConnectionEvents()', () => { expect(emails.has('older@example.com')).toBe(true); }); + it('bases shared timelines on the latest actual lastUsedAt instead of current time', () => { + vi.useFakeTimers(); + vi.setSystemTime(new Date('2026-05-29T00:00:00.000Z')); + + const latestActualLastUsedAt = new Date('2021-01-01T00:00:00.000Z').getTime(); + const accounts: AccountData[] = [ + makeAccount({ + id: 'old2020', + email: 'old2020@example.com', + successCount: 1, + lastUsedAt: '2020-01-01T00:00:00.000Z', + }), + makeAccount({ + id: 'old2021', + email: 'old2021@example.com', + successCount: 1, + lastUsedAt: '2021-01-01T00:00:00.000Z', + }), + ]; + + const events = generateConnectionEvents(accounts); + const mostRecentTimestamp = Math.max(...events.map((event) => event.timestamp.getTime())); + + expect(mostRecentTimestamp).toBe(latestActualLastUsedAt); + expect(mostRecentTimestamp).toBeLessThan(Date.now() - 365 * 24 * 60 * 60 * 1000); + }); + it('returns at most MAX_TIMELINE_EVENTS events (cap respected by caller slice)', () => { // generateConnectionEvents returns all events unsorted-by-cap; cap is applied by caller. // But we verify total output does not exceed successCount + failureCount across accounts.