mirror of
https://github.com/block/buzz.git
synced 2026-08-18 06:50:31 +02:00
## Summary - defer foreground resume work until the activation task has returned, a frame has painted, and a trailing task gets a turn - centralize app-focus subscribers and remove the broad TanStack `refetchOnWindowFocus` fan-out - coalesce relay recovery and preserve an explicit deferred refresh only for workflow data without a polling/push freshness path - defer the notification permission native check while keeping blur and cheap correctness signals immediate ## Why Buzz Desktop 0.5.10 can spend roughly 1.5 seconds in the WebKit window-focus listener/microtask checkpoint before returning to the run loop. Focus currently fans out into query refetches, React polling updates, relay reconnect/replay, and native work in one activation turn. This patch establishes an interaction-first foreground boundary rather than letting those consumers compete with the activating input and first paint. ## Validation - focused foreground/workflow/relay tests: 18/18 passed before commit - `pnpm --dir desktop typecheck`: passed before commit - pre-commit desktop check and file-size gate: passed - pre-push desktop check, typecheck, and full desktop unit suite: 4,743/4,743 passed at `704e7b4b6618fafce655bb2b07c7a9fe0fc8c643` - Princess Donut independent adversarial review: PASS after two lifecycle/freshness blockers were resolved ## Manual test 1. Install the PR build and use Buzz long enough to populate channels, home, workflows, agents, and other polling surfaces. 2. Switch to another app for 30-60 seconds. 3. Return by clicking Buzz and immediately click a channel or scroll. 4. Confirm the first interaction and paint are prompt, then confirm channels/home/workflows refresh and a degraded relay reconnects after the activation boundary. 5. Repeat while rapidly switching away again to verify no resume work starts after focus has been lost. --------- Signed-off-by: Wes <wesbillman@users.noreply.github.com> Co-authored-by: Carl <c7ebe626f000404285d3686e1dc74cc07cc60a9754a150041ba132e14bd3e2ec@buzz.block.builderlab.xyz>
66 lines
1.5 KiB
JavaScript
66 lines
1.5 KiB
JavaScript
import assert from "node:assert/strict";
|
|
import { afterEach, test } from "node:test";
|
|
|
|
import {
|
|
focusManager,
|
|
QueryClient,
|
|
QueryObserver,
|
|
} from "@tanstack/react-query";
|
|
|
|
import { pulseFocusRefetchPolicy } from "./hooks.ts";
|
|
|
|
afterEach(() => {
|
|
focusManager.setFocused(undefined);
|
|
});
|
|
|
|
async function focusRefetchCount({ ageMs, policy }) {
|
|
focusManager.setFocused(false);
|
|
const queryClient = new QueryClient({
|
|
defaultOptions: { queries: { retry: false } },
|
|
});
|
|
queryClient.mount();
|
|
|
|
const queryKey = ["focus-refetch-policy", policy.staleTime, ageMs];
|
|
queryClient.setQueryData(queryKey, "cached", {
|
|
updatedAt: Date.now() - ageMs,
|
|
});
|
|
let fetchCount = 0;
|
|
const observer = new QueryObserver(queryClient, {
|
|
queryKey,
|
|
queryFn: async () => {
|
|
fetchCount += 1;
|
|
return "refetched";
|
|
},
|
|
refetchOnMount: false,
|
|
...policy,
|
|
});
|
|
const unsubscribe = observer.subscribe(() => {});
|
|
|
|
focusManager.setFocused(true);
|
|
await new Promise((resolve) => setImmediate(resolve));
|
|
|
|
unsubscribe();
|
|
queryClient.unmount();
|
|
return fetchCount;
|
|
}
|
|
|
|
test("pulse: skips fresh focus refetch", async () => {
|
|
assert.equal(
|
|
await focusRefetchCount({
|
|
ageMs: pulseFocusRefetchPolicy.staleTime - 1_000,
|
|
policy: pulseFocusRefetchPolicy,
|
|
}),
|
|
0,
|
|
);
|
|
});
|
|
|
|
test("pulse: does not refetch stale data on focus", async () => {
|
|
assert.equal(
|
|
await focusRefetchCount({
|
|
ageMs: pulseFocusRefetchPolicy.staleTime + 1,
|
|
policy: pulseFocusRefetchPolicy,
|
|
}),
|
|
0,
|
|
);
|
|
});
|