Skip to content

Commit c9ee696

Browse files
committed
Dedupe same-tick visibility+focus reload bump; use motion tokens (CL-6487)
plugins-page.tsx and connections-section.tsx: a shared refreshWhenVisible handler with a microtask guard collapses the same-tick visibilitychange+focus pair into a single scheduled reload instead of two. app.css: chat-sidebar-row-menu-trigger's transition and onboarding-phase-in's animation now use --duration-micro/--duration-large and --ease-out instead of a bare 120ms/0.28s ease, matching this PR's own Motion section guidance.
1 parent d74500b commit c9ee696

3 files changed

Lines changed: 37 additions & 22 deletions

File tree

‎apps/web/src/app.css‎

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -649,9 +649,9 @@ select:disabled,
649649
color: var(--muted-foreground);
650650
opacity: 0;
651651
transition:
652-
opacity 120ms var(--ease-out, ease),
653-
background 120ms var(--ease-out, ease),
654-
color 120ms var(--ease-out, ease);
652+
opacity var(--duration-micro, 150ms) var(--ease-out, ease),
653+
background var(--duration-micro, 150ms) var(--ease-out, ease),
654+
color var(--duration-micro, 150ms) var(--ease-out, ease);
655655
}
656656

657657
.chat-sidebar-row-menu-trigger::after {
@@ -2321,7 +2321,8 @@ select:disabled,
23212321
attention-seeking animation, fully off under reduced motion.
23222322
DESIGN.md's motion ceiling is 300ms — stays at 280ms. */
23232323
.onboarding-phase {
2324-
animation: onboarding-phase-in 0.28s ease both;
2324+
animation: onboarding-phase-in var(--duration-large, 300ms) var(--ease-out, ease)
2325+
both;
23252326
}
23262327

23272328
@keyframes onboarding-phase-in {

‎apps/web/src/pages/plugins-page.tsx‎

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -153,18 +153,25 @@ export function PluginsRoute({
153153
// use for in-room connect cards.
154154
useEffect(() => {
155155
if (selectedTenantId === null) return;
156-
const refresh = () => {
157-
if (document.visibilityState === "visible") {
156+
// `visibilitychange` and `focus` both fire in the same tick when a tab
157+
// regains focus; the microtask guard collapses that pair into one
158+
// scheduled bump instead of two back-to-back reloads.
159+
let bumpScheduled = false;
160+
const refreshWhenVisible = () => {
161+
if (document.visibilityState !== "visible") return;
162+
if (bumpScheduled) return;
163+
bumpScheduled = true;
164+
queueMicrotask(() => {
165+
bumpScheduled = false;
158166
setPluginsReloadKey((key) => key + 1);
159-
}
167+
});
160168
};
161-
const onFocus = () => setPluginsReloadKey((key) => key + 1);
162-
document.addEventListener("visibilitychange", refresh);
163-
window.addEventListener("focus", onFocus);
164-
const interval = setInterval(refresh, 30_000);
169+
document.addEventListener("visibilitychange", refreshWhenVisible);
170+
window.addEventListener("focus", refreshWhenVisible);
171+
const interval = setInterval(refreshWhenVisible, 30_000);
165172
return () => {
166-
document.removeEventListener("visibilitychange", refresh);
167-
window.removeEventListener("focus", onFocus);
173+
document.removeEventListener("visibilitychange", refreshWhenVisible);
174+
window.removeEventListener("focus", refreshWhenVisible);
168175
clearInterval(interval);
169176
};
170177
}, [selectedTenantId]);

‎packages/settings-ui/src/connections-section.tsx‎

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -289,18 +289,25 @@ export function ConnectionsSection({
289289
// solve via `subscribeConnectState`.
290290
useEffect(() => {
291291
if (tenantId === null) return;
292-
const refresh = () => {
293-
if (document.visibilityState === "visible") {
292+
// `visibilitychange` and `focus` both fire in the same tick when a tab
293+
// regains focus; the microtask guard collapses that pair into one
294+
// scheduled bump instead of two back-to-back reloads.
295+
let bumpScheduled = false;
296+
const refreshWhenVisible = () => {
297+
if (document.visibilityState !== "visible") return;
298+
if (bumpScheduled) return;
299+
bumpScheduled = true;
300+
queueMicrotask(() => {
301+
bumpScheduled = false;
294302
setReloadKey((value) => value + 1);
295-
}
303+
});
296304
};
297-
const onFocus = () => setReloadKey((value) => value + 1);
298-
document.addEventListener("visibilitychange", refresh);
299-
window.addEventListener("focus", onFocus);
300-
const interval = setInterval(refresh, 30_000);
305+
document.addEventListener("visibilitychange", refreshWhenVisible);
306+
window.addEventListener("focus", refreshWhenVisible);
307+
const interval = setInterval(refreshWhenVisible, 30_000);
301308
return () => {
302-
document.removeEventListener("visibilitychange", refresh);
303-
window.removeEventListener("focus", onFocus);
309+
document.removeEventListener("visibilitychange", refreshWhenVisible);
310+
window.removeEventListener("focus", refreshWhenVisible);
304311
clearInterval(interval);
305312
};
306313
}, [tenantId]);

0 commit comments

Comments
 (0)