perf(meeting): only run process poller while auto-capture is enabled
Previously the 15-second meeting-detection setInterval was started in onMount unconditionally (when Tauri runtime was available). When `settings.meetingAutoCapture` was disabled the callback still fired every 15 s, just to early-return — burning a wakeup that did no useful work and confusing "is this firing? did the toggle take effect?" debugging. Move the timer into a `$effect` whose only tracked dependency is `settings.meetingAutoCapture`. Toggling off now clears the interval; toggling on creates a fresh one. Reads of `meetingAutoCaptureApps` and `globalHotkey` happen inside the interval callback (post-setup) so they don't trigger the effect to tear down on every keystroke in the apps editor. The `meetingCapturePoller` variable and its `onDestroy` cleanup are gone — the effect's own cleanup return takes care of it on unmount. https://claude.ai/code/session_0189xUb6ie6t9qHkzatGZ9Rb
This commit is contained in:
@@ -340,43 +340,52 @@
|
|||||||
invoke("prewarm_default_model_cmd").catch(() => {});
|
invoke("prewarm_default_model_cmd").catch(() => {});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Meeting auto-capture: poll the process list and toast when a match
|
// Meeting auto-capture is wired up below as a `$effect` so the
|
||||||
// appears (edge-triggered — no repeat toasts until the app goes away
|
// 15-second poller only exists while the setting is enabled —
|
||||||
// and comes back). We never start recording from this signal; the
|
// toggling off in Settings stops the wakeups, not just the work.
|
||||||
// user decides whether to hit the hotkey.
|
|
||||||
if (tauriRuntimeAvailable) {
|
|
||||||
let previous: Set<string> = new Set();
|
|
||||||
meetingCapturePoller = window.setInterval(async () => {
|
|
||||||
if (!settings.meetingAutoCapture) { previous = new Set(); return; }
|
|
||||||
const patterns = settings.meetingAutoCaptureApps;
|
|
||||||
if (!Array.isArray(patterns) || patterns.length === 0) return;
|
|
||||||
try {
|
|
||||||
const matches: string[] = await invoke("detect_meeting_processes", { patterns });
|
|
||||||
const current = new Set(matches);
|
|
||||||
for (const match of matches) {
|
|
||||||
if (!previous.has(match)) {
|
|
||||||
toasts.info(
|
|
||||||
`${match[0].toUpperCase()}${match.slice(1)} detected`,
|
|
||||||
`Press ${settings.globalHotkey} to start recording.`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
previous = current;
|
|
||||||
} catch { /* ignore — backend may be mid-restart */ }
|
|
||||||
}, 15000);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
let meetingCapturePoller: number | null = null;
|
// Meeting auto-capture poller. Edge-triggered: the first time a matching
|
||||||
|
// process appears we surface a non-modal toast; subsequent ticks where
|
||||||
|
// the same match is still present stay quiet until the app exits and
|
||||||
|
// returns. We never start recording from this signal — the user decides.
|
||||||
|
//
|
||||||
|
// The effect's only tracked dependencies are `tauriRuntimeAvailable`
|
||||||
|
// (constant after mount) and `settings.meetingAutoCapture`. Reads of
|
||||||
|
// `settings.meetingAutoCaptureApps` and `settings.globalHotkey` happen
|
||||||
|
// inside the interval callback, which runs after the synchronous setup
|
||||||
|
// and therefore is not tracked — editing the apps list doesn't tear
|
||||||
|
// down the poller.
|
||||||
|
$effect(() => {
|
||||||
|
if (!tauriRuntimeAvailable) return;
|
||||||
|
if (!settings.meetingAutoCapture) return;
|
||||||
|
|
||||||
|
let previous: Set<string> = new Set();
|
||||||
|
const id = window.setInterval(async () => {
|
||||||
|
const patterns = settings.meetingAutoCaptureApps;
|
||||||
|
if (!Array.isArray(patterns) || patterns.length === 0) return;
|
||||||
|
try {
|
||||||
|
const matches: string[] = await invoke("detect_meeting_processes", { patterns });
|
||||||
|
const current = new Set(matches);
|
||||||
|
for (const match of matches) {
|
||||||
|
if (!previous.has(match)) {
|
||||||
|
toasts.info(
|
||||||
|
`${match[0].toUpperCase()}${match.slice(1)} detected`,
|
||||||
|
`Press ${settings.globalHotkey} to start recording.`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
previous = current;
|
||||||
|
} catch { /* ignore — backend may be mid-restart */ }
|
||||||
|
}, 15000);
|
||||||
|
|
||||||
|
return () => { window.clearInterval(id); };
|
||||||
|
});
|
||||||
|
|
||||||
onDestroy(() => {
|
onDestroy(() => {
|
||||||
window.removeEventListener("resize", handleResize);
|
window.removeEventListener("resize", handleResize);
|
||||||
if (onWindowError) window.removeEventListener("error", onWindowError);
|
if (onWindowError) window.removeEventListener("error", onWindowError);
|
||||||
if (onUnhandledRejection) window.removeEventListener("unhandledrejection", onUnhandledRejection);
|
if (onUnhandledRejection) window.removeEventListener("unhandledrejection", onUnhandledRejection);
|
||||||
if (meetingCapturePoller !== null) {
|
|
||||||
window.clearInterval(meetingCapturePoller);
|
|
||||||
meetingCapturePoller = null;
|
|
||||||
}
|
|
||||||
if (!tauriRuntimeAvailable) {
|
if (!tauriRuntimeAvailable) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user