perf(onboarding,settings): 权限/热键状态纯事件驱动,去掉高频轮询 (#673)

* perf(onboarding,settings): 权限/热键状态纯事件驱动,去掉所有高频轮询

issue #470:引导页桌面(1s)/Android(3s)、设置页热键(1s) 的 setInterval 高频权限/热键轮询
全部改为 window focus / visibilitychange 事件驱动,连低频兜底也去掉(纯事件驱动)。
覆盖论证:麦克风前台原生弹窗授予由申请结果回调 + 800ms 兜底覆盖;辅助功能授予必经系统
设置 App、切回 OpenLess 必触发 focus/visibilitychange。并给 onGrantAccessibility 补对称的
void refresh() + 800ms 兜底(与 onRequestMicrophone 对齐)。PermissionsSection 的
refreshPermissions(10s)/refreshNetwork(30s) 保留不动。

* fix(onboarding): DesktopOnboarding effect 内 refresh() 统一加 void

@claude 审查 #673 指出:DesktopOnboarding 的 useEffect 里 refresh()/onFocus/onVisibility
三处 refresh() 是 floating promise(与上方 Android 段的 void refresh() 不一致,IPC 失败会
静默)。统一加 void 明确 fire-and-forget。issue #470。

---------

Co-authored-by: 吕柏青 <lubaiqing9@gmail.com>
This commit is contained in:
TRIPand吕柏青 authored and GitHub committed 2026-06-16 11:10:53 +08:00
1 parent 001615ebf4
commit ee42d1a2e7
2 files changed
+20 -11

No files matched your search

+14 -6
View File
@@ -220,12 +220,14 @@ function AndroidMicrophoneStep() {
useEffect(() => {
void refresh();
const id = window.setInterval(refresh, 3000);
// issue #470:纯事件驱动,去掉高频轮询。窗口重新聚焦或重新可见时刷新(授权必经系统设置再切回)。
const onFocus = () => { void refresh(); };
const onVisibility = () => { if (document.visibilityState === 'visible') void refresh(); };
window.addEventListener('focus', onFocus);
document.addEventListener('visibilitychange', onVisibility);
return () => {
window.clearInterval(id);
window.removeEventListener('focus', onFocus);
document.removeEventListener('visibilitychange', onVisibility);
};
}, []);
@@ -299,13 +301,15 @@ function DesktopOnboarding({
};
useEffect(() => {
refresh();
const id = window.setInterval(refresh, 1000);
const onFocus = () => refresh();
void refresh();
// issue #470:纯事件驱动,去掉每秒轮询。授权必经系统设置 App,切回 OpenLess 必触发 focus/visibilitychange。
const onFocus = () => { void refresh(); };
const onVisibility = () => { if (document.visibilityState === 'visible') void refresh(); };
window.addEventListener('focus', onFocus);
document.addEventListener('visibilitychange', onVisibility);
return () => {
window.clearInterval(id);
window.removeEventListener('focus', onFocus);
document.removeEventListener('visibilitychange', onVisibility);
if (refreshTimeoutRef.current) clearTimeout(refreshTimeoutRef.current);
};
}, [requiresAccessibility]);
@@ -318,6 +322,10 @@ function DesktopOnboarding({
} finally {
setBusy(false);
}
// issue #470:与麦克风路径对称——授权动作返回后立即刷新,并挂一次 800ms 兜底覆盖 app 内按钮发起的授予。
void refresh();
if (refreshTimeoutRef.current) clearTimeout(refreshTimeoutRef.current);
refreshTimeoutRef.current = window.setTimeout(refresh, 800);
};
const onRequestMicrophone = async () => {
@@ -80,13 +80,11 @@ export function PermissionsSection() {
refreshWindowsIme();
}
refreshNetwork();
const hotkeyId = platformCaps?.supportsDesktopHotkey === true
? window.setInterval(refreshHotkey, 1000)
: undefined;
// issue #470:热键状态改为纯事件驱动,去掉每秒轮询,靠下方 focus/visibilitychange 刷新。
// 麦克风检查会短暂打开输入流,避免每秒探测导致隐私指示器频繁闪烁。
const permissionId = window.setInterval(refreshPermissions, 10000);
const networkId = window.setInterval(refreshNetwork, 30000);
const onFocus = () => {
const refreshAll = () => {
refreshPermissions();
if (platformCaps?.supportsDesktopHotkey === true) {
refreshHotkey();
@@ -96,12 +94,15 @@ export function PermissionsSection() {
}
refreshNetwork();
};
const onFocus = () => refreshAll();
const onVisibility = () => { if (document.visibilityState === 'visible') refreshAll(); };
window.addEventListener('focus', onFocus);
document.addEventListener('visibilitychange', onVisibility);
return () => {
if (hotkeyId !== undefined) window.clearInterval(hotkeyId);
window.clearInterval(permissionId);
window.clearInterval(networkId);
window.removeEventListener('focus', onFocus);
document.removeEventListener('visibilitychange', onVisibility);
};
}, [platformCaps?.platform, platformCaps?.supportsDesktopHotkey]);