Implement dual-surface financials and db bootstrap
This commit is contained in:
@@ -104,6 +104,13 @@ export function useTaskNotificationsCenter(): UseTaskNotificationsCenterResult {
|
||||
const invalidatedTerminalRef = useRef(new Set<string>());
|
||||
const activeSnapshotRef = useRef<Task[]>([]);
|
||||
const finishedSnapshotRef = useRef<Task[]>([]);
|
||||
const [isDocumentVisible, setIsDocumentVisible] = useState(() => {
|
||||
if (typeof document === 'undefined') {
|
||||
return true;
|
||||
}
|
||||
|
||||
return document.visibilityState === 'visible';
|
||||
});
|
||||
|
||||
const applyTaskLocally = useCallback((task: Task) => {
|
||||
setActiveTasks((prev) => prev.map((entry) => (entry.id === task.id ? task : entry)));
|
||||
@@ -285,10 +292,54 @@ export function useTaskNotificationsCenter(): UseTaskNotificationsCenterResult {
|
||||
}
|
||||
}, [processSnapshots]);
|
||||
|
||||
useEffect(() => {
|
||||
if (typeof document === 'undefined') {
|
||||
return;
|
||||
}
|
||||
|
||||
const onVisibilityChange = () => {
|
||||
setIsDocumentVisible(document.visibilityState === 'visible');
|
||||
};
|
||||
|
||||
document.addEventListener('visibilitychange', onVisibilityChange);
|
||||
return () => document.removeEventListener('visibilitychange', onVisibilityChange);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
let cancelled = false;
|
||||
let activeTimer: ReturnType<typeof setTimeout> | null = null;
|
||||
let terminalTimer: ReturnType<typeof setTimeout> | null = null;
|
||||
let stableTerminalPolls = 0;
|
||||
let previousTerminalSignature = '';
|
||||
|
||||
const nextActiveDelay = () => {
|
||||
if (!isDocumentVisible) {
|
||||
return 30_000;
|
||||
}
|
||||
|
||||
const hasActiveTasks = activeSnapshotRef.current.length > 0;
|
||||
if (isPopoverOpen || isDetailOpen || hasActiveTasks) {
|
||||
return 2_000;
|
||||
}
|
||||
|
||||
return 12_000;
|
||||
};
|
||||
|
||||
const nextTerminalDelay = () => {
|
||||
if (!isDocumentVisible) {
|
||||
return 60_000;
|
||||
}
|
||||
|
||||
if (isPopoverOpen || isDetailOpen) {
|
||||
return 4_000;
|
||||
}
|
||||
|
||||
if (finishedSnapshotRef.current.some((task) => isUnread(task))) {
|
||||
return 15_000;
|
||||
}
|
||||
|
||||
return stableTerminalPolls >= 2 ? 45_000 : 20_000;
|
||||
};
|
||||
|
||||
const runActiveLoop = async () => {
|
||||
if (cancelled) {
|
||||
@@ -314,7 +365,7 @@ export function useTaskNotificationsCenter(): UseTaskNotificationsCenterResult {
|
||||
// ignore transient polling failures
|
||||
}
|
||||
|
||||
activeTimer = setTimeout(runActiveLoop, 2_000);
|
||||
activeTimer = setTimeout(runActiveLoop, nextActiveDelay());
|
||||
};
|
||||
|
||||
const runTerminalLoop = async () => {
|
||||
@@ -337,11 +388,19 @@ export function useTaskNotificationsCenter(): UseTaskNotificationsCenterResult {
|
||||
setHasLoadedFinished(true);
|
||||
setFinishedTasks(response.tasks);
|
||||
processSnapshots();
|
||||
|
||||
const signature = response.tasks.map((task) => taskSignature(task)).join('||');
|
||||
if (signature === previousTerminalSignature) {
|
||||
stableTerminalPolls += 1;
|
||||
} else {
|
||||
stableTerminalPolls = 0;
|
||||
previousTerminalSignature = signature;
|
||||
}
|
||||
} catch {
|
||||
// ignore transient polling failures
|
||||
}
|
||||
|
||||
terminalTimer = setTimeout(runTerminalLoop, 4_000);
|
||||
terminalTimer = setTimeout(runTerminalLoop, nextTerminalDelay());
|
||||
};
|
||||
|
||||
void runActiveLoop();
|
||||
@@ -356,7 +415,7 @@ export function useTaskNotificationsCenter(): UseTaskNotificationsCenterResult {
|
||||
clearTimeout(terminalTimer);
|
||||
}
|
||||
};
|
||||
}, [processSnapshots]);
|
||||
}, [isDetailOpen, isDocumentVisible, isPopoverOpen, processSnapshots]);
|
||||
|
||||
const normalizedActiveTasks = useMemo(() => {
|
||||
return activeTasks.filter((task) => ACTIVE_STATUSES.includes(task.status));
|
||||
|
||||
Reference in New Issue
Block a user