refactor(team): extract refresh timestamps
This commit is contained in:
parent
7d08a10d6f
commit
e718ccf39a
3 changed files with 93 additions and 10 deletions
|
|
@ -29,6 +29,12 @@ import {
|
||||||
isTeamTaskNeedsFixActionable,
|
isTeamTaskNeedsFixActionable,
|
||||||
} from '@shared/utils/teamTaskState';
|
} from '@shared/utils/teamTaskState';
|
||||||
|
|
||||||
|
import {
|
||||||
|
clearAllLastResolvedTeamDataRefreshes,
|
||||||
|
clearLastResolvedTeamDataRefreshAt,
|
||||||
|
hasLastResolvedTeamDataRefreshAt,
|
||||||
|
recordLastResolvedTeamDataRefresh,
|
||||||
|
} from '../team/teamDataRefreshTimestamps';
|
||||||
import {
|
import {
|
||||||
getFullTeamDataRequestKey,
|
getFullTeamDataRequestKey,
|
||||||
getTeamDataRequestKey,
|
getTeamDataRequestKey,
|
||||||
|
|
@ -116,6 +122,7 @@ import type {
|
||||||
} from '@shared/types';
|
} from '@shared/types';
|
||||||
import type { StateCreator } from 'zustand';
|
import type { StateCreator } from 'zustand';
|
||||||
|
|
||||||
|
export { getLastResolvedTeamDataRefreshAt } from '../team/teamDataRefreshTimestamps';
|
||||||
export {
|
export {
|
||||||
selectTeamDataForName,
|
selectTeamDataForName,
|
||||||
selectTeamIsAliveForName,
|
selectTeamIsAliveForName,
|
||||||
|
|
@ -164,7 +171,6 @@ const pendingFreshTeamMessagesHeadRefreshes = new Set<string>();
|
||||||
const inFlightTeamMemberActivityMetaRequests = new Map<string, Promise<void>>();
|
const inFlightTeamMemberActivityMetaRequests = new Map<string, Promise<void>>();
|
||||||
const pendingFreshTeamMemberActivityMetaRefreshes = new Set<string>();
|
const pendingFreshTeamMemberActivityMetaRefreshes = new Set<string>();
|
||||||
const pendingTeamPendingReplyRefreshTimers = new Map<string, ReturnType<typeof setTimeout>>();
|
const pendingTeamPendingReplyRefreshTimers = new Map<string, ReturnType<typeof setTimeout>>();
|
||||||
const lastResolvedTeamDataRefreshAtByTeam = new Map<string, number>();
|
|
||||||
let inFlightGlobalTasksRefresh: Promise<void> | null = null;
|
let inFlightGlobalTasksRefresh: Promise<void> | null = null;
|
||||||
let pendingFreshGlobalTasksRefresh = false;
|
let pendingFreshGlobalTasksRefresh = false;
|
||||||
const memberSpawnStatusesIpcBackoffUntilByTeam = new Map<string, number>();
|
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 {
|
export function __resetTeamSliceModuleStateForTests(): void {
|
||||||
inFlightTeamDataRequests.clear();
|
inFlightTeamDataRequests.clear();
|
||||||
inFlightRefreshTeamDataCalls.clear();
|
inFlightRefreshTeamDataCalls.clear();
|
||||||
|
|
@ -237,7 +239,7 @@ export function __resetTeamSliceModuleStateForTests(): void {
|
||||||
}
|
}
|
||||||
pendingTeamPendingReplyRefreshTimers.clear();
|
pendingTeamPendingReplyRefreshTimers.clear();
|
||||||
clearAllPendingReplyRefreshWaits();
|
clearAllPendingReplyRefreshWaits();
|
||||||
lastResolvedTeamDataRefreshAtByTeam.clear();
|
clearAllLastResolvedTeamDataRefreshes();
|
||||||
clearAllTeamLocalStateEpochs();
|
clearAllTeamLocalStateEpochs();
|
||||||
memberSpawnStatusesIpcBackoffUntilByTeam.clear();
|
memberSpawnStatusesIpcBackoffUntilByTeam.clear();
|
||||||
teamRefreshBurstDiagnostics.clear();
|
teamRefreshBurstDiagnostics.clear();
|
||||||
|
|
@ -271,7 +273,7 @@ function clearTeamScopedTransientState(teamName: string): void {
|
||||||
pendingFreshTeamMessagesHeadRefreshes.delete(teamName);
|
pendingFreshTeamMessagesHeadRefreshes.delete(teamName);
|
||||||
inFlightTeamMemberActivityMetaRequests.delete(teamName);
|
inFlightTeamMemberActivityMetaRequests.delete(teamName);
|
||||||
pendingFreshTeamMemberActivityMetaRefreshes.delete(teamName);
|
pendingFreshTeamMemberActivityMetaRefreshes.delete(teamName);
|
||||||
lastResolvedTeamDataRefreshAtByTeam.delete(teamName);
|
clearLastResolvedTeamDataRefreshAt(teamName);
|
||||||
memberSpawnStatusesIpcBackoffUntilByTeam.delete(teamName);
|
memberSpawnStatusesIpcBackoffUntilByTeam.delete(teamName);
|
||||||
teamRefreshBurstDiagnostics.delete(teamName);
|
teamRefreshBurstDiagnostics.delete(teamName);
|
||||||
memberSpawnUiEqualLastWarnAtByTeam.delete(teamName);
|
memberSpawnUiEqualLastWarnAtByTeam.delete(teamName);
|
||||||
|
|
@ -656,7 +658,7 @@ export function __getTeamScopedTransientStateForTests(teamName: string): {
|
||||||
hasPendingFreshMessagesHeadRefresh: pendingFreshTeamMessagesHeadRefreshes.has(teamName),
|
hasPendingFreshMessagesHeadRefresh: pendingFreshTeamMessagesHeadRefreshes.has(teamName),
|
||||||
hasPendingFreshMemberActivityMetaRefresh:
|
hasPendingFreshMemberActivityMetaRefresh:
|
||||||
pendingFreshTeamMemberActivityMetaRefreshes.has(teamName),
|
pendingFreshTeamMemberActivityMetaRefreshes.has(teamName),
|
||||||
hasLastResolvedTeamDataRefresh: lastResolvedTeamDataRefreshAtByTeam.has(teamName),
|
hasLastResolvedTeamDataRefresh: hasLastResolvedTeamDataRefreshAt(teamName),
|
||||||
hasCurrentLocalStateEpoch: hasTeamLocalStateEpoch(teamName),
|
hasCurrentLocalStateEpoch: hasTeamLocalStateEpoch(teamName),
|
||||||
hasMemberSpawnStatusesIpcBackoff: memberSpawnStatusesIpcBackoffUntilByTeam.has(teamName),
|
hasMemberSpawnStatusesIpcBackoff: memberSpawnStatusesIpcBackoffUntilByTeam.has(teamName),
|
||||||
hasTeamRefreshBurstDiagnostics: teamRefreshBurstDiagnostics.has(teamName),
|
hasTeamRefreshBurstDiagnostics: teamRefreshBurstDiagnostics.has(teamName),
|
||||||
|
|
@ -4014,7 +4016,7 @@ export const createTeamSlice: StateCreator<AppState, [], [], TeamSlice> = (set,
|
||||||
selectedTeamError: null,
|
selectedTeamError: null,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
lastResolvedTeamDataRefreshAtByTeam.set(teamName, Date.now());
|
recordLastResolvedTeamDataRefresh(teamName);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const invalidationState = previousData
|
const invalidationState = previousData
|
||||||
|
|
@ -4238,7 +4240,7 @@ export const createTeamSlice: StateCreator<AppState, [], [], TeamSlice> = (set,
|
||||||
...selectedState,
|
...selectedState,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
lastResolvedTeamDataRefreshAtByTeam.set(teamName, Date.now());
|
recordLastResolvedTeamDataRefresh(teamName);
|
||||||
const invalidationState = previousData
|
const invalidationState = previousData
|
||||||
? collectTaskChangeInvalidationState(teamName, previousData.tasks, data.tasks)
|
? collectTaskChangeInvalidationState(teamName, previousData.tasks, data.tasks)
|
||||||
: { cacheKeys: [], taskIds: [] };
|
: { cacheKeys: [], taskIds: [] };
|
||||||
|
|
|
||||||
21
src/renderer/store/team/teamDataRefreshTimestamps.ts
Normal file
21
src/renderer/store/team/teamDataRefreshTimestamps.ts
Normal 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();
|
||||||
|
}
|
||||||
60
test/renderer/store/teamDataRefreshTimestamps.test.ts
Normal file
60
test/renderer/store/teamDataRefreshTimestamps.test.ts
Normal 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);
|
||||||
|
});
|
||||||
|
});
|
||||||
Loading…
Reference in a new issue