diff --git a/libs/workspace/dashboard/feature/src/lib/rails/dashboard-source-expiry-clock.spec.ts b/libs/workspace/dashboard/feature/src/lib/rails/dashboard-source-expiry-clock.spec.ts index 1fef554da..8b8c72843 100644 --- a/libs/workspace/dashboard/feature/src/lib/rails/dashboard-source-expiry-clock.spec.ts +++ b/libs/workspace/dashboard/feature/src/lib/rails/dashboard-source-expiry-clock.spec.ts @@ -126,4 +126,21 @@ describe('createSourceExpiryClock', () => { TestBed.tick(); expect(jest.getTimerCount()).toBe(1); }); + + it('reads the clock at once when the sources rail is re-enabled', () => { + active.set(false); + TestBed.tick(); + // Past the expiry while the rail is off: no boundary is left to wait + // for, so only the re-enable can refresh the cached badge. + facts.set(new Map([['xtream', expiringIn(60_000)]])); + TestBed.tick(); + jest.advanceTimersByTime(3 * 60_000); + const before = clock(); + + active.set(true); + TestBed.tick(); + + expect(clock()).toBe(Date.now()); + expect(clock()).toBeGreaterThan(before); + }); }); diff --git a/libs/workspace/dashboard/feature/src/lib/rails/dashboard-source-expiry-clock.ts b/libs/workspace/dashboard/feature/src/lib/rails/dashboard-source-expiry-clock.ts index 5a0c3a19d..b552e2393 100644 --- a/libs/workspace/dashboard/feature/src/lib/rails/dashboard-source-expiry-clock.ts +++ b/libs/workspace/dashboard/feature/src/lib/rails/dashboard-source-expiry-clock.ts @@ -1,4 +1,11 @@ -import { DestroyRef, effect, inject, signal, type Signal } from '@angular/core'; +import { + DestroyRef, + effect, + inject, + signal, + untracked, + type Signal, +} from '@angular/core'; import { DOCUMENT } from '@angular/common'; import { nextSourceExpiryChangeMs, @@ -54,6 +61,16 @@ export function createSourceExpiryClock( onCleanup(() => clearTimeout(timer)); }); + // Re-enabling the rail must not show a badge cached while it was off: a + // boundary may have passed (an expired source has no next boundary to + // wait for), so read the clock at once, as becoming visible does. + let wasActive = untracked(active); + effect(() => { + const isActive = active(); + if (isActive && !wasActive) untracked(() => now.set(Date.now())); + wasActive = isActive; + }); + const onVisibilityChange = () => { visible.set(!document.hidden); if (!document.hidden) now.set(Date.now());