From 48a08e78955c55dc12b9d6d847bfa4bba5dc5b1b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=90=95=E6=9F=8F=E9=9D=92?= Date: Mon, 15 Jun 2026 22:12:46 +0800 Subject: [PATCH 1/2] =?UTF-8?q?perf(onboarding,settings):=20=E6=9D=83?= =?UTF-8?q?=E9=99=90/=E7=83=AD=E9=94=AE=E7=8A=B6=E6=80=81=E7=BA=AF?= =?UTF-8?q?=E4=BA=8B=E4=BB=B6=E9=A9=B1=E5=8A=A8=EF=BC=8C=E5=8E=BB=E6=8E=89?= =?UTF-8?q?=E6=89=80=E6=9C=89=E9=AB=98=E9=A2=91=E8=BD=AE=E8=AF=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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) 保留不动。 --- openless-all/app/src/components/Onboarding.tsx | 16 ++++++++++++---- .../src/pages/settings/PermissionsSection.tsx | 11 ++++++----- 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/openless-all/app/src/components/Onboarding.tsx b/openless-all/app/src/components/Onboarding.tsx index b1466f4c..4d3fedca 100644 --- a/openless-all/app/src/components/Onboarding.tsx +++ b/openless-all/app/src/components/Onboarding.tsx @@ -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); }; }, []); @@ -300,12 +302,14 @@ function DesktopOnboarding({ useEffect(() => { refresh(); - const id = window.setInterval(refresh, 1000); + // issue #470:纯事件驱动,去掉每秒轮询。授权必经系统设置 App,切回 OpenLess 必触发 focus/visibilitychange。 const onFocus = () => refresh(); + const onVisibility = () => { if (document.visibilityState === 'visible') 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 () => { diff --git a/openless-all/app/src/pages/settings/PermissionsSection.tsx b/openless-all/app/src/pages/settings/PermissionsSection.tsx index d59e2e14..dfc0a463 100644 --- a/openless-all/app/src/pages/settings/PermissionsSection.tsx +++ b/openless-all/app/src/pages/settings/PermissionsSection.tsx @@ -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]); From d221ee2db03ad735225d0cec55502fdef836b5c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=90=95=E6=9F=8F=E9=9D=92?= Date: Tue, 16 Jun 2026 10:58:05 +0800 Subject: [PATCH 2/2] =?UTF-8?q?fix(onboarding):=20DesktopOnboarding=20effe?= =?UTF-8?q?ct=20=E5=86=85=20refresh()=20=E7=BB=9F=E4=B8=80=E5=8A=A0=20void?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit @claude 审查 #673 指出:DesktopOnboarding 的 useEffect 里 refresh()/onFocus/onVisibility 三处 refresh() 是 floating promise(与上方 Android 段的 void refresh() 不一致,IPC 失败会 静默)。统一加 void 明确 fire-and-forget。issue #470。 --- openless-all/app/src/components/Onboarding.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/openless-all/app/src/components/Onboarding.tsx b/openless-all/app/src/components/Onboarding.tsx index 4d3fedca..249f1ba6 100644 --- a/openless-all/app/src/components/Onboarding.tsx +++ b/openless-all/app/src/components/Onboarding.tsx @@ -301,10 +301,10 @@ function DesktopOnboarding({ }; useEffect(() => { - refresh(); + void refresh(); // issue #470:纯事件驱动,去掉每秒轮询。授权必经系统设置 App,切回 OpenLess 必触发 focus/visibilitychange。 - const onFocus = () => refresh(); - const onVisibility = () => { if (document.visibilityState === 'visible') refresh(); }; + const onFocus = () => { void refresh(); }; + const onVisibility = () => { if (document.visibilityState === 'visible') void refresh(); }; window.addEventListener('focus', onFocus); document.addEventListener('visibilitychange', onVisibility); return () => {