perf: pause background polling and animations when tab is hidden
- Background polling pauses when tab is not visible - Timer-based animations replaced with efficient frame-based updates - Reduced CPU usage when OpenChamber is in background
This commit is contained in:
@@ -405,6 +405,10 @@ export function DesktopHostSwitcherDialog({
|
|||||||
};
|
};
|
||||||
void run();
|
void run();
|
||||||
const interval = window.setInterval(() => {
|
const interval = window.setInterval(() => {
|
||||||
|
// Skip polling when tab is hidden to reduce background work
|
||||||
|
if (typeof document !== 'undefined' && document.visibilityState !== 'visible') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
void run();
|
void run();
|
||||||
}, 1_500);
|
}, 1_500);
|
||||||
|
|
||||||
@@ -1255,6 +1259,10 @@ export function DesktopHostSwitcherButton({ headerIconButtonClass }: DesktopHost
|
|||||||
|
|
||||||
void run();
|
void run();
|
||||||
const interval = window.setInterval(() => {
|
const interval = window.setInterval(() => {
|
||||||
|
// Skip polling when tab is hidden to reduce background work
|
||||||
|
if (typeof document !== 'undefined' && document.visibilityState !== 'visible') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
void run();
|
void run();
|
||||||
}, 10_000);
|
}, 10_000);
|
||||||
return () => {
|
return () => {
|
||||||
|
|||||||
@@ -250,12 +250,41 @@ export const OpenChamberVisualSettings: React.FC<OpenChamberVisualSettingsProps>
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const intervalId = setInterval(() => {
|
// Use requestAnimationFrame for smoother animation without setInterval overhead
|
||||||
setChatRenderPreviewTick((prev) => (prev + 1) % 24);
|
let rafId: number | null = null;
|
||||||
}, 420);
|
let lastTime = Date.now();
|
||||||
|
|
||||||
|
const tick = () => {
|
||||||
|
const now = Date.now();
|
||||||
|
// Update every ~420ms
|
||||||
|
if (now - lastTime >= 420) {
|
||||||
|
setChatRenderPreviewTick((prev) => (prev + 1) % 24);
|
||||||
|
lastTime = now;
|
||||||
|
}
|
||||||
|
rafId = requestAnimationFrame(tick);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Only run when visible
|
||||||
|
if (typeof document === 'undefined' || document.visibilityState === 'visible') {
|
||||||
|
rafId = requestAnimationFrame(tick);
|
||||||
|
}
|
||||||
|
|
||||||
|
const onVisibility = () => {
|
||||||
|
if (document.visibilityState === 'visible' && rafId === null) {
|
||||||
|
rafId = requestAnimationFrame(tick);
|
||||||
|
} else if (document.visibilityState !== 'visible' && rafId !== null) {
|
||||||
|
cancelAnimationFrame(rafId);
|
||||||
|
rafId = null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
document.addEventListener('visibilitychange', onVisibility);
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
clearInterval(intervalId);
|
document.removeEventListener('visibilitychange', onVisibility);
|
||||||
|
if (rafId !== null) {
|
||||||
|
cancelAnimationFrame(rafId);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}, [shouldAnimateChatPreview]);
|
}, [shouldAnimateChatPreview]);
|
||||||
|
|
||||||
|
|||||||
@@ -486,6 +486,9 @@ export const TunnelSettings: React.FC = () => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let rafId: number | null = null;
|
||||||
|
let lastTime = Date.now();
|
||||||
|
|
||||||
const updateRemaining = () => {
|
const updateRemaining = () => {
|
||||||
const remaining = tunnelInfo.bootstrapExpiresAt ? tunnelInfo.bootstrapExpiresAt - Date.now() : 0;
|
const remaining = tunnelInfo.bootstrapExpiresAt ? tunnelInfo.bootstrapExpiresAt - Date.now() : 0;
|
||||||
if (remaining <= 0) {
|
if (remaining <= 0) {
|
||||||
@@ -495,14 +498,79 @@ export const TunnelSettings: React.FC = () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const tick = () => {
|
||||||
|
const now = Date.now();
|
||||||
|
// Update only once per second
|
||||||
|
if (now - lastTime >= 1_000) {
|
||||||
|
updateRemaining();
|
||||||
|
lastTime = now;
|
||||||
|
}
|
||||||
|
rafId = requestAnimationFrame(tick);
|
||||||
|
};
|
||||||
|
|
||||||
updateRemaining();
|
updateRemaining();
|
||||||
const timer = window.setInterval(updateRemaining, 1000);
|
|
||||||
return () => window.clearInterval(timer);
|
// Only run when visible
|
||||||
|
if (typeof document === 'undefined' || document.visibilityState === 'visible') {
|
||||||
|
rafId = requestAnimationFrame(tick);
|
||||||
|
}
|
||||||
|
|
||||||
|
const onVisibility = () => {
|
||||||
|
if (document.visibilityState === 'visible' && rafId === null) {
|
||||||
|
rafId = requestAnimationFrame(tick);
|
||||||
|
} else if (document.visibilityState !== 'visible' && rafId !== null) {
|
||||||
|
cancelAnimationFrame(rafId);
|
||||||
|
rafId = null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
document.addEventListener('visibilitychange', onVisibility);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
document.removeEventListener('visibilitychange', onVisibility);
|
||||||
|
if (rafId !== null) {
|
||||||
|
cancelAnimationFrame(rafId);
|
||||||
|
}
|
||||||
|
};
|
||||||
}, [tunnelInfo?.bootstrapExpiresAt]);
|
}, [tunnelInfo?.bootstrapExpiresAt]);
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
const timer = window.setInterval(() => setNowTs(Date.now()), 1000);
|
// Use requestAnimationFrame for smoother updates without setInterval overhead
|
||||||
return () => window.clearInterval(timer);
|
let rafId: number | null = null;
|
||||||
|
let lastTime = Date.now();
|
||||||
|
|
||||||
|
const tick = () => {
|
||||||
|
const now = Date.now();
|
||||||
|
// Update only once per second
|
||||||
|
if (now - lastTime >= 1_000) {
|
||||||
|
setNowTs(now);
|
||||||
|
lastTime = now;
|
||||||
|
}
|
||||||
|
rafId = requestAnimationFrame(tick);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Only run when visible
|
||||||
|
if (typeof document === 'undefined' || document.visibilityState === 'visible') {
|
||||||
|
rafId = requestAnimationFrame(tick);
|
||||||
|
}
|
||||||
|
|
||||||
|
const onVisibility = () => {
|
||||||
|
if (document.visibilityState === 'visible' && rafId === null) {
|
||||||
|
rafId = requestAnimationFrame(tick);
|
||||||
|
} else if (document.visibilityState !== 'visible' && rafId !== null) {
|
||||||
|
cancelAnimationFrame(rafId);
|
||||||
|
rafId = null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
document.addEventListener('visibilitychange', onVisibility);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
document.removeEventListener('visibilitychange', onVisibility);
|
||||||
|
if (rafId !== null) {
|
||||||
|
cancelAnimationFrame(rafId);
|
||||||
|
}
|
||||||
|
};
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
@@ -530,6 +598,10 @@ export const TunnelSettings: React.FC = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const timer = window.setInterval(() => {
|
const timer = window.setInterval(() => {
|
||||||
|
// Skip polling when tab is hidden
|
||||||
|
if (typeof document !== 'undefined' && document.visibilityState !== 'visible') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
void refreshSessions();
|
void refreshSessions();
|
||||||
}, 5000);
|
}, 5000);
|
||||||
|
|
||||||
|
|||||||
@@ -305,6 +305,10 @@ export const RemoteInstancesPage: React.FC = () => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const interval = window.setInterval(() => {
|
const interval = window.setInterval(() => {
|
||||||
|
// Skip polling when tab is hidden to reduce background work
|
||||||
|
if (typeof document !== 'undefined' && document.visibilityState !== 'visible') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
void refreshStatuses();
|
void refreshStatuses();
|
||||||
}, 2_000);
|
}, 2_000);
|
||||||
return () => {
|
return () => {
|
||||||
@@ -313,11 +317,41 @@ export const RemoteInstancesPage: React.FC = () => {
|
|||||||
}, [refreshStatuses, selectedId]);
|
}, [refreshStatuses, selectedId]);
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
const interval = window.setInterval(() => {
|
// Use requestAnimationFrame for smoother clock updates without setInterval overhead
|
||||||
setClockMs(Date.now());
|
let rafId: number | null = null;
|
||||||
}, 1_000);
|
let lastTime = Date.now();
|
||||||
|
|
||||||
|
const tick = () => {
|
||||||
|
const now = Date.now();
|
||||||
|
// Update only once per second
|
||||||
|
if (now - lastTime >= 1_000) {
|
||||||
|
setClockMs(now);
|
||||||
|
lastTime = now;
|
||||||
|
}
|
||||||
|
rafId = requestAnimationFrame(tick);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Only run when visible
|
||||||
|
if (typeof document === 'undefined' || document.visibilityState === 'visible') {
|
||||||
|
rafId = requestAnimationFrame(tick);
|
||||||
|
}
|
||||||
|
|
||||||
|
const onVisibility = () => {
|
||||||
|
if (document.visibilityState === 'visible' && rafId === null) {
|
||||||
|
rafId = requestAnimationFrame(tick);
|
||||||
|
} else if (document.visibilityState !== 'visible' && rafId !== null) {
|
||||||
|
cancelAnimationFrame(rafId);
|
||||||
|
rafId = null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
document.addEventListener('visibilitychange', onVisibility);
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
window.clearInterval(interval);
|
document.removeEventListener('visibilitychange', onVisibility);
|
||||||
|
if (rafId !== null) {
|
||||||
|
cancelAnimationFrame(rafId);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
@@ -525,6 +559,10 @@ export const RemoteInstancesPage: React.FC = () => {
|
|||||||
|
|
||||||
void run();
|
void run();
|
||||||
const interval = window.setInterval(() => {
|
const interval = window.setInterval(() => {
|
||||||
|
// Skip polling when tab is hidden
|
||||||
|
if (typeof document !== 'undefined' && document.visibilityState !== 'visible') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
void run();
|
void run();
|
||||||
}, 1_000);
|
}, 1_000);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user