feat: live overlay wiring and backend click-through sync hooks
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
48
src/hooks/useClickThroughSync.ts
Normal file
48
src/hooks/useClickThroughSync.ts
Normal file
@@ -0,0 +1,48 @@
|
||||
import { useEffect } from "react";
|
||||
import { listen, type UnlistenFn } from "@tauri-apps/api/event";
|
||||
import { useOverlayStore } from "../lib/store";
|
||||
import { applyOpacity } from "../lib/overlay";
|
||||
import {
|
||||
EVT_CLICK_THROUGH_NO_OVERLAY,
|
||||
EVT_CLICK_THROUGH_TOGGLED,
|
||||
type ClickThroughToggledPayload,
|
||||
} from "../types/overlay";
|
||||
|
||||
/** Subscribes to backend events about click-through state and toast about no-overlay attempts. */
|
||||
export function useClickThroughSync(
|
||||
onNoOverlay: () => void
|
||||
): void {
|
||||
useEffect(() => {
|
||||
let unlistenToggled: UnlistenFn | undefined;
|
||||
let unlistenNoOverlay: UnlistenFn | undefined;
|
||||
let cancelled = false;
|
||||
|
||||
(async () => {
|
||||
unlistenToggled = await listen<ClickThroughToggledPayload>(
|
||||
EVT_CLICK_THROUGH_TOGGLED,
|
||||
(e) => {
|
||||
if (cancelled) return;
|
||||
useOverlayStore.getState()._setClickThroughFromBackend(
|
||||
e.payload.clickThrough
|
||||
);
|
||||
// Restore the user's target opacity. Rust performed the dip-half of
|
||||
// the pulse and emitted this event after sleeping ~180ms; we own
|
||||
// the restore. Reading from the live store guarantees we restore
|
||||
// to whatever the slider says *now*, not whatever it said when the
|
||||
// hotkey was pressed.
|
||||
void applyOpacity(useOverlayStore.getState().opacity);
|
||||
}
|
||||
);
|
||||
unlistenNoOverlay = await listen(EVT_CLICK_THROUGH_NO_OVERLAY, () => {
|
||||
if (cancelled) return;
|
||||
onNoOverlay();
|
||||
});
|
||||
})().catch((err) => console.warn("event listen failed", err));
|
||||
|
||||
return () => {
|
||||
cancelled = true;
|
||||
unlistenToggled?.();
|
||||
unlistenNoOverlay?.();
|
||||
};
|
||||
}, [onNoOverlay]);
|
||||
}
|
||||
34
src/hooks/useLiveOverlayWiring.ts
Normal file
34
src/hooks/useLiveOverlayWiring.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
import { useEffect, useRef } from "react";
|
||||
import { useOverlayStore } from "../lib/store";
|
||||
import {
|
||||
applyAlwaysOnTop,
|
||||
applyClickThrough,
|
||||
applyOpacity,
|
||||
} from "../lib/overlay";
|
||||
|
||||
/** Pushes Zustand changes to the overlay window in real time. No-op when overlay is closed. */
|
||||
export function useLiveOverlayWiring(): void {
|
||||
const opacity = useOverlayStore((s) => s.opacity);
|
||||
const alwaysOnTop = useOverlayStore((s) => s.alwaysOnTop);
|
||||
const clickThrough = useOverlayStore((s) => s.clickThrough);
|
||||
const isOpen = useOverlayStore((s) => s.isOpen);
|
||||
|
||||
const lastAppliedClickThrough = useRef<boolean | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isOpen) return;
|
||||
void applyOpacity(opacity);
|
||||
}, [opacity, isOpen]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isOpen) return;
|
||||
void applyAlwaysOnTop(alwaysOnTop);
|
||||
}, [alwaysOnTop, isOpen]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isOpen) return;
|
||||
if (lastAppliedClickThrough.current === clickThrough) return;
|
||||
lastAppliedClickThrough.current = clickThrough;
|
||||
void applyClickThrough(clickThrough);
|
||||
}, [clickThrough, isOpen]);
|
||||
}
|
||||
Reference in New Issue
Block a user