refactor(team): extract refresh timestamps

This commit is contained in:
777genius 2026-05-22 10:00:54 +03:00
parent 7d08a10d6f
commit e718ccf39a
3 changed files with 93 additions and 10 deletions

View file

@ -29,6 +29,12 @@ import {
isTeamTaskNeedsFixActionable,
} from '@shared/utils/teamTaskState';
import {
clearAllLastResolvedTeamDataRefreshes,
clearLastResolvedTeamDataRefreshAt,
hasLastResolvedTeamDataRefreshAt,
recordLastResolvedTeamDataRefresh,
} from '../team/teamDataRefreshTimestamps';
import {
getFullTeamDataRequestKey,
getTeamDataRequestKey,
@ -116,6 +122,7 @@ import type {
} from '@shared/types';
import type { StateCreator } from 'zustand';
export { getLastResolvedTeamDataRefreshAt } from '../team/teamDataRefreshTimestamps';
export {
selectTeamDataForName,
selectTeamIsAliveForName,
@ -164,7 +171,6 @@ const pendingFreshTeamMessagesHeadRefreshes = new Set<string>();
const inFlightTeamMemberActivityMetaRequests = new Map<string, Promise<void>>();
const pendingFreshTeamMemberActivityMetaRefreshes = new Set<string>();
const pendingTeamPendingReplyRefreshTimers = new Map<string, ReturnType<typeof setTimeout>>();
const lastResolvedTeamDataRefreshAtByTeam = new Map<string, number>();
let inFlightGlobalTasksRefresh: Promise<void> | null = null;
let pendingFreshGlobalTasksRefresh = false;
const memberSpawnStatusesIpcBackoffUntilByTeam = new Map<string, number>();
@ -213,10 +219,6 @@ export function isTeamDataRefreshPending(teamName: string): boolean {
);
}
export function getLastResolvedTeamDataRefreshAt(teamName: string): number | undefined {
return lastResolvedTeamDataRefreshAtByTeam.get(teamName);
}
export function __resetTeamSliceModuleStateForTests(): void {
inFlightTeamDataRequests.clear();
inFlightRefreshTeamDataCalls.clear();
@ -237,7 +239,7 @@ export function __resetTeamSliceModuleStateForTests(): void {
}
pendingTeamPendingReplyRefreshTimers.clear();
clearAllPendingReplyRefreshWaits();
lastResolvedTeamDataRefreshAtByTeam.clear();
clearAllLastResolvedTeamDataRefreshes();
clearAllTeamLocalStateEpochs();
memberSpawnStatusesIpcBackoffUntilByTeam.clear();
teamRefreshBurstDiagnostics.clear();
@ -271,7 +273,7 @@ function clearTeamScopedTransientState(teamName: string): void {
pendingFreshTeamMessagesHeadRefreshes.delete(teamName);
inFlightTeamMemberActivityMetaRequests.delete(teamName);
pendingFreshTeamMemberActivityMetaRefreshes.delete(teamName);
lastResolvedTeamDataRefreshAtByTeam.delete(teamName);
clearLastResolvedTeamDataRefreshAt(teamName);
memberSpawnStatusesIpcBackoffUntilByTeam.delete(teamName);
teamRefreshBurstDiagnostics.delete(teamName);
memberSpawnUiEqualLastWarnAtByTeam.delete(teamName);
@ -656,7 +658,7 @@ export function __getTeamScopedTransientStateForTests(teamName: string): {
hasPendingFreshMessagesHeadRefresh: pendingFreshTeamMessagesHeadRefreshes.has(teamName),
hasPendingFreshMemberActivityMetaRefresh:
pendingFreshTeamMemberActivityMetaRefreshes.has(teamName),
hasLastResolvedTeamDataRefresh: lastResolvedTeamDataRefreshAtByTeam.has(teamName),
hasLastResolvedTeamDataRefresh: hasLastResolvedTeamDataRefreshAt(teamName),
hasCurrentLocalStateEpoch: hasTeamLocalStateEpoch(teamName),
hasMemberSpawnStatusesIpcBackoff: memberSpawnStatusesIpcBackoffUntilByTeam.has(teamName),
hasTeamRefreshBurstDiagnostics: teamRefreshBurstDiagnostics.has(teamName),
@ -4014,7 +4016,7 @@ export const createTeamSlice: StateCreator<AppState, [], [], TeamSlice> = (set,
selectedTeamError: null,
};
});
lastResolvedTeamDataRefreshAtByTeam.set(teamName, Date.now());
recordLastResolvedTeamDataRefresh(teamName);
try {
const invalidationState = previousData
@ -4238,7 +4240,7 @@ export const createTeamSlice: StateCreator<AppState, [], [], TeamSlice> = (set,
...selectedState,
};
});
lastResolvedTeamDataRefreshAtByTeam.set(teamName, Date.now());
recordLastResolvedTeamDataRefresh(teamName);
const invalidationState = previousData
? collectTaskChangeInvalidationState(teamName, previousData.tasks, data.tasks)
: { cacheKeys: [], taskIds: [] };

View file

@ -0,0 +1,21 @@
const lastResolvedTeamDataRefreshAtByTeam = new Map<string, number>();
export function getLastResolvedTeamDataRefreshAt(teamName: string): number | undefined {
return lastResolvedTeamDataRefreshAtByTeam.get(teamName);
}
export function recordLastResolvedTeamDataRefresh(teamName: string, resolvedAt = Date.now()): void {
lastResolvedTeamDataRefreshAtByTeam.set(teamName, resolvedAt);
}
export function hasLastResolvedTeamDataRefreshAt(teamName: string): boolean {
return lastResolvedTeamDataRefreshAtByTeam.has(teamName);
}
export function clearLastResolvedTeamDataRefreshAt(teamName: string): void {
lastResolvedTeamDataRefreshAtByTeam.delete(teamName);
}
export function clearAllLastResolvedTeamDataRefreshes(): void {
lastResolvedTeamDataRefreshAtByTeam.clear();
}

View file

@ -0,0 +1,60 @@
import { afterEach, describe, expect, it, vi } from 'vitest';
import {
clearAllLastResolvedTeamDataRefreshes,
clearLastResolvedTeamDataRefreshAt,
getLastResolvedTeamDataRefreshAt,
hasLastResolvedTeamDataRefreshAt,
recordLastResolvedTeamDataRefresh,
} from '../../../src/renderer/store/team/teamDataRefreshTimestamps';
afterEach(() => {
vi.useRealTimers();
clearAllLastResolvedTeamDataRefreshes();
});
describe('teamDataRefreshTimestamps', () => {
it('returns undefined for teams without a recorded refresh', () => {
expect(getLastResolvedTeamDataRefreshAt('my-team')).toBeUndefined();
expect(hasLastResolvedTeamDataRefreshAt('my-team')).toBe(false);
});
it('records explicit refresh timestamps by team', () => {
recordLastResolvedTeamDataRefresh('my-team', 100);
recordLastResolvedTeamDataRefresh('other-team', 200);
expect(getLastResolvedTeamDataRefreshAt('my-team')).toBe(100);
expect(getLastResolvedTeamDataRefreshAt('other-team')).toBe(200);
expect(hasLastResolvedTeamDataRefreshAt('my-team')).toBe(true);
});
it('uses Date.now by default to preserve current call-site behavior', () => {
vi.setSystemTime(new Date('2026-05-22T06:30:00.000Z'));
recordLastResolvedTeamDataRefresh('my-team');
expect(getLastResolvedTeamDataRefreshAt('my-team')).toBe(
new Date('2026-05-22T06:30:00.000Z').getTime()
);
});
it('clears one team timestamp without touching other teams', () => {
recordLastResolvedTeamDataRefresh('my-team', 100);
recordLastResolvedTeamDataRefresh('other-team', 200);
clearLastResolvedTeamDataRefreshAt('my-team');
expect(getLastResolvedTeamDataRefreshAt('my-team')).toBeUndefined();
expect(getLastResolvedTeamDataRefreshAt('other-team')).toBe(200);
});
it('clears all recorded timestamps', () => {
recordLastResolvedTeamDataRefresh('my-team', 100);
recordLastResolvedTeamDataRefresh('other-team', 200);
clearAllLastResolvedTeamDataRefreshes();
expect(hasLastResolvedTeamDataRefreshAt('my-team')).toBe(false);
expect(hasLastResolvedTeamDataRefreshAt('other-team')).toBe(false);
});
});