perf(renderer): defer task project scans
This commit is contained in:
parent
1f46a14a79
commit
7ea57cb012
3 changed files with 57 additions and 9 deletions
|
|
@ -352,6 +352,14 @@ function cloneCached<T>(value: T): T {
|
|||
: (JSON.parse(JSON.stringify(value)) as T);
|
||||
}
|
||||
|
||||
function dateFromFingerprintMs(ms: unknown): Date | null {
|
||||
if (typeof ms !== 'number' || !Number.isFinite(ms) || ms <= 0) {
|
||||
return null;
|
||||
}
|
||||
const date = new Date(ms);
|
||||
return Number.isFinite(date.getTime()) ? date : null;
|
||||
}
|
||||
|
||||
async function statPathFingerprint(filePath: string): Promise<PathFingerprint> {
|
||||
try {
|
||||
const stat = await fs.promises.stat(filePath, { bigint: true });
|
||||
|
|
@ -1460,7 +1468,6 @@ async function readTasksDirForTeam(
|
|||
}
|
||||
taskDiag.cacheMisses++;
|
||||
|
||||
const stat = await fs.promises.stat(taskPath);
|
||||
const raw = await readFileUtf8WithTimeout(taskPath, payload.maxTaskReadMs);
|
||||
const parsed = JSON.parse(raw) as ParsedTask;
|
||||
const metadata = parsed.metadata;
|
||||
|
|
@ -1504,11 +1511,12 @@ async function readTasksDirForTeam(
|
|||
typeof parsed.createdAt === 'string' ? parsed.createdAt : undefined;
|
||||
let updatedAt: string | undefined;
|
||||
try {
|
||||
const birthtime = dateFromFingerprintMs(pathFingerprint.birthtimeMs);
|
||||
const mtime = dateFromFingerprintMs(pathFingerprint.mtimeMs);
|
||||
if (!createdAt) {
|
||||
const bt = stat.birthtime.getTime();
|
||||
createdAt = (bt > 0 ? stat.birthtime : stat.mtime).toISOString();
|
||||
createdAt = (birthtime ?? mtime)?.toISOString();
|
||||
}
|
||||
updatedAt = stat.mtime.toISOString();
|
||||
updatedAt = mtime?.toISOString();
|
||||
} catch {
|
||||
/* ignore */
|
||||
}
|
||||
|
|
@ -1559,7 +1567,7 @@ async function readTasksDirForTeam(
|
|||
status,
|
||||
workIntervals: normalizeWorkIntervals(parsed),
|
||||
reviewIntervals: normalizeReviewIntervals(parsed),
|
||||
historyEvents: normalizeHistoryEvents(parsed),
|
||||
historyEvents,
|
||||
blocks: Array.isArray(parsed.blocks) ? (parsed.blocks as unknown[]) : undefined,
|
||||
blockedBy: Array.isArray(parsed.blockedBy) ? (parsed.blockedBy as unknown[]) : undefined,
|
||||
related: Array.isArray(parsed.related)
|
||||
|
|
|
|||
|
|
@ -462,6 +462,9 @@ export const GlobalTaskList = memo(function GlobalTaskList({
|
|||
}, [fetchAllTasks, globalTasksLoading]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!filtersPopoverOpen) {
|
||||
return;
|
||||
}
|
||||
if (
|
||||
viewMode === 'grouped' &&
|
||||
!repositoryGroupsInitialized &&
|
||||
|
|
@ -475,6 +478,7 @@ export const GlobalTaskList = memo(function GlobalTaskList({
|
|||
}, [
|
||||
fetchProjects,
|
||||
fetchRepositoryGroups,
|
||||
filtersPopoverOpen,
|
||||
projectsError,
|
||||
projectsInitialized,
|
||||
projectsLoading,
|
||||
|
|
|
|||
|
|
@ -248,7 +248,7 @@ describe('GlobalTaskList project grouping', () => {
|
|||
storeListeners.clear();
|
||||
});
|
||||
|
||||
it('fetches repository groups when grouped project filter data is missing', async () => {
|
||||
it('fetches repository groups when grouped project filter data is needed', async () => {
|
||||
vi.stubGlobal('IS_REACT_ACT_ENVIRONMENT', true);
|
||||
storeState.viewMode = 'grouped';
|
||||
|
||||
|
|
@ -261,6 +261,19 @@ describe('GlobalTaskList project grouping', () => {
|
|||
await flushMicrotasks();
|
||||
});
|
||||
|
||||
expect(storeState.fetchRepositoryGroups).not.toHaveBeenCalled();
|
||||
expect(storeState.fetchProjects).not.toHaveBeenCalled();
|
||||
|
||||
await act(async () => {
|
||||
root.render(
|
||||
React.createElement(GlobalTaskList, {
|
||||
filtersPopoverOpen: true,
|
||||
onFiltersPopoverOpenChange: vi.fn(),
|
||||
})
|
||||
);
|
||||
await flushMicrotasks();
|
||||
});
|
||||
|
||||
expect(storeState.fetchRepositoryGroups).toHaveBeenCalledTimes(1);
|
||||
expect(storeState.fetchProjects).not.toHaveBeenCalled();
|
||||
|
||||
|
|
@ -270,7 +283,7 @@ describe('GlobalTaskList project grouping', () => {
|
|||
});
|
||||
});
|
||||
|
||||
it('fetches flat projects when flat project filter data is missing', async () => {
|
||||
it('fetches flat projects when flat project filter data is needed', async () => {
|
||||
vi.stubGlobal('IS_REACT_ACT_ENVIRONMENT', true);
|
||||
|
||||
const host = document.createElement('div');
|
||||
|
|
@ -282,6 +295,19 @@ describe('GlobalTaskList project grouping', () => {
|
|||
await flushMicrotasks();
|
||||
});
|
||||
|
||||
expect(storeState.fetchProjects).not.toHaveBeenCalled();
|
||||
expect(storeState.fetchRepositoryGroups).not.toHaveBeenCalled();
|
||||
|
||||
await act(async () => {
|
||||
root.render(
|
||||
React.createElement(GlobalTaskList, {
|
||||
filtersPopoverOpen: true,
|
||||
onFiltersPopoverOpenChange: vi.fn(),
|
||||
})
|
||||
);
|
||||
await flushMicrotasks();
|
||||
});
|
||||
|
||||
expect(storeState.fetchProjects).toHaveBeenCalledTimes(1);
|
||||
expect(storeState.fetchRepositoryGroups).not.toHaveBeenCalled();
|
||||
|
||||
|
|
@ -301,7 +327,12 @@ describe('GlobalTaskList project grouping', () => {
|
|||
const root = createRoot(host);
|
||||
|
||||
await act(async () => {
|
||||
root.render(React.createElement(GlobalTaskList));
|
||||
root.render(
|
||||
React.createElement(GlobalTaskList, {
|
||||
filtersPopoverOpen: true,
|
||||
onFiltersPopoverOpenChange: vi.fn(),
|
||||
})
|
||||
);
|
||||
await flushMicrotasks();
|
||||
});
|
||||
|
||||
|
|
@ -323,7 +354,12 @@ describe('GlobalTaskList project grouping', () => {
|
|||
const root = createRoot(host);
|
||||
|
||||
await act(async () => {
|
||||
root.render(React.createElement(GlobalTaskList));
|
||||
root.render(
|
||||
React.createElement(GlobalTaskList, {
|
||||
filtersPopoverOpen: true,
|
||||
onFiltersPopoverOpenChange: vi.fn(),
|
||||
})
|
||||
);
|
||||
await flushMicrotasks();
|
||||
});
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue