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);
|
: (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> {
|
async function statPathFingerprint(filePath: string): Promise<PathFingerprint> {
|
||||||
try {
|
try {
|
||||||
const stat = await fs.promises.stat(filePath, { bigint: true });
|
const stat = await fs.promises.stat(filePath, { bigint: true });
|
||||||
|
|
@ -1460,7 +1468,6 @@ async function readTasksDirForTeam(
|
||||||
}
|
}
|
||||||
taskDiag.cacheMisses++;
|
taskDiag.cacheMisses++;
|
||||||
|
|
||||||
const stat = await fs.promises.stat(taskPath);
|
|
||||||
const raw = await readFileUtf8WithTimeout(taskPath, payload.maxTaskReadMs);
|
const raw = await readFileUtf8WithTimeout(taskPath, payload.maxTaskReadMs);
|
||||||
const parsed = JSON.parse(raw) as ParsedTask;
|
const parsed = JSON.parse(raw) as ParsedTask;
|
||||||
const metadata = parsed.metadata;
|
const metadata = parsed.metadata;
|
||||||
|
|
@ -1504,11 +1511,12 @@ async function readTasksDirForTeam(
|
||||||
typeof parsed.createdAt === 'string' ? parsed.createdAt : undefined;
|
typeof parsed.createdAt === 'string' ? parsed.createdAt : undefined;
|
||||||
let updatedAt: string | undefined;
|
let updatedAt: string | undefined;
|
||||||
try {
|
try {
|
||||||
|
const birthtime = dateFromFingerprintMs(pathFingerprint.birthtimeMs);
|
||||||
|
const mtime = dateFromFingerprintMs(pathFingerprint.mtimeMs);
|
||||||
if (!createdAt) {
|
if (!createdAt) {
|
||||||
const bt = stat.birthtime.getTime();
|
createdAt = (birthtime ?? mtime)?.toISOString();
|
||||||
createdAt = (bt > 0 ? stat.birthtime : stat.mtime).toISOString();
|
|
||||||
}
|
}
|
||||||
updatedAt = stat.mtime.toISOString();
|
updatedAt = mtime?.toISOString();
|
||||||
} catch {
|
} catch {
|
||||||
/* ignore */
|
/* ignore */
|
||||||
}
|
}
|
||||||
|
|
@ -1559,7 +1567,7 @@ async function readTasksDirForTeam(
|
||||||
status,
|
status,
|
||||||
workIntervals: normalizeWorkIntervals(parsed),
|
workIntervals: normalizeWorkIntervals(parsed),
|
||||||
reviewIntervals: normalizeReviewIntervals(parsed),
|
reviewIntervals: normalizeReviewIntervals(parsed),
|
||||||
historyEvents: normalizeHistoryEvents(parsed),
|
historyEvents,
|
||||||
blocks: Array.isArray(parsed.blocks) ? (parsed.blocks as unknown[]) : undefined,
|
blocks: Array.isArray(parsed.blocks) ? (parsed.blocks as unknown[]) : undefined,
|
||||||
blockedBy: Array.isArray(parsed.blockedBy) ? (parsed.blockedBy as unknown[]) : undefined,
|
blockedBy: Array.isArray(parsed.blockedBy) ? (parsed.blockedBy as unknown[]) : undefined,
|
||||||
related: Array.isArray(parsed.related)
|
related: Array.isArray(parsed.related)
|
||||||
|
|
|
||||||
|
|
@ -462,6 +462,9 @@ export const GlobalTaskList = memo(function GlobalTaskList({
|
||||||
}, [fetchAllTasks, globalTasksLoading]);
|
}, [fetchAllTasks, globalTasksLoading]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
if (!filtersPopoverOpen) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (
|
if (
|
||||||
viewMode === 'grouped' &&
|
viewMode === 'grouped' &&
|
||||||
!repositoryGroupsInitialized &&
|
!repositoryGroupsInitialized &&
|
||||||
|
|
@ -475,6 +478,7 @@ export const GlobalTaskList = memo(function GlobalTaskList({
|
||||||
}, [
|
}, [
|
||||||
fetchProjects,
|
fetchProjects,
|
||||||
fetchRepositoryGroups,
|
fetchRepositoryGroups,
|
||||||
|
filtersPopoverOpen,
|
||||||
projectsError,
|
projectsError,
|
||||||
projectsInitialized,
|
projectsInitialized,
|
||||||
projectsLoading,
|
projectsLoading,
|
||||||
|
|
|
||||||
|
|
@ -248,7 +248,7 @@ describe('GlobalTaskList project grouping', () => {
|
||||||
storeListeners.clear();
|
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);
|
vi.stubGlobal('IS_REACT_ACT_ENVIRONMENT', true);
|
||||||
storeState.viewMode = 'grouped';
|
storeState.viewMode = 'grouped';
|
||||||
|
|
||||||
|
|
@ -261,6 +261,19 @@ describe('GlobalTaskList project grouping', () => {
|
||||||
await flushMicrotasks();
|
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.fetchRepositoryGroups).toHaveBeenCalledTimes(1);
|
||||||
expect(storeState.fetchProjects).not.toHaveBeenCalled();
|
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);
|
vi.stubGlobal('IS_REACT_ACT_ENVIRONMENT', true);
|
||||||
|
|
||||||
const host = document.createElement('div');
|
const host = document.createElement('div');
|
||||||
|
|
@ -282,6 +295,19 @@ describe('GlobalTaskList project grouping', () => {
|
||||||
await flushMicrotasks();
|
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.fetchProjects).toHaveBeenCalledTimes(1);
|
||||||
expect(storeState.fetchRepositoryGroups).not.toHaveBeenCalled();
|
expect(storeState.fetchRepositoryGroups).not.toHaveBeenCalled();
|
||||||
|
|
||||||
|
|
@ -301,7 +327,12 @@ describe('GlobalTaskList project grouping', () => {
|
||||||
const root = createRoot(host);
|
const root = createRoot(host);
|
||||||
|
|
||||||
await act(async () => {
|
await act(async () => {
|
||||||
root.render(React.createElement(GlobalTaskList));
|
root.render(
|
||||||
|
React.createElement(GlobalTaskList, {
|
||||||
|
filtersPopoverOpen: true,
|
||||||
|
onFiltersPopoverOpenChange: vi.fn(),
|
||||||
|
})
|
||||||
|
);
|
||||||
await flushMicrotasks();
|
await flushMicrotasks();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -323,7 +354,12 @@ describe('GlobalTaskList project grouping', () => {
|
||||||
const root = createRoot(host);
|
const root = createRoot(host);
|
||||||
|
|
||||||
await act(async () => {
|
await act(async () => {
|
||||||
root.render(React.createElement(GlobalTaskList));
|
root.render(
|
||||||
|
React.createElement(GlobalTaskList, {
|
||||||
|
filtersPopoverOpen: true,
|
||||||
|
onFiltersPopoverOpenChange: vi.fn(),
|
||||||
|
})
|
||||||
|
);
|
||||||
await flushMicrotasks();
|
await flushMicrotasks();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue