feat(desktop): Tier A desktop features — web side (P5-46/36/44/43/49/47/55/57)
Web half of the desktop feature wave. A shared bridge (`hooks/useTauri.ts`: invokeTauri/isTauri/useTauriEvent) backs per-feature hooks that no-op in the browser and drive the native Tauri commands (compiled in cinny-desktop): - P5-46 useTauriCallPower — hold system awake while a call is active. - P5-36 useTauriJumpList — Windows jump list of recent rooms → matrix: deep links. - P5-44 useTauriThumbbar — taskbar Mute/Deafen/End; events toggle mic/sound/hangup. - P5-43 useTauriSmtc — SMTC call state + button events. - P5-49 useTauriNetwork — react to native network-change → mx.retryImmediately(). - P5-47 window chrome — opt-in `customWindowChromeAtom` + TDS `TitleBar`; DesktopChrome wrapper in App.tsx (zero layout impact when off) + a desktop-only settings toggle. - P5-55 composer toolbar drag-reorder (settings order[] + pragmatic-drag-and-drop). - P5-57 DraftIndicator — subtle "draft saved" cue in the composer. Client-scoped hooks mount via TauriDesktopFeatures in ClientNonUIFeatures; window chrome mounts at App level. Gates: tsc/eslint/prettier clean, build OK, 556 tests. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,38 @@
|
||||
import { useEffect } from 'react';
|
||||
import { useAtomValue } from 'jotai';
|
||||
import { callEmbedAtom } from '../state/callEmbed';
|
||||
import { useCallControlState } from '../plugins/call';
|
||||
import { invokeTauri, useTauriEvent } from './useTauri';
|
||||
|
||||
/**
|
||||
* P5-43 — expose the active call to the Windows System Media Transport Controls
|
||||
* (the volume-flyout / media overlay). Mirrors the call-embed atom (undefined =
|
||||
* no active call) and the current mic state onto the native
|
||||
* `set_smtc_call_state` command, and translates SMTC button presses back into
|
||||
* call actions:
|
||||
* - Play/Pause (`smtc-action` → `mute`) toggles the microphone.
|
||||
* - Stop (`smtc-action` → `end`) hangs up the call.
|
||||
* No-op in the browser (the native command and events only fire under Tauri).
|
||||
*/
|
||||
type SmtcAction = { action: 'mute' | 'end' };
|
||||
|
||||
export function useTauriSmtc(): void {
|
||||
const callEmbed = useAtomValue(callEmbedAtom);
|
||||
// `microphone` reflects mic-enabled; muted is its inverse while in a call.
|
||||
const { microphone } = useCallControlState(callEmbed?.control);
|
||||
const active = callEmbed !== undefined;
|
||||
const muted = active && !microphone;
|
||||
|
||||
useEffect(() => {
|
||||
invokeTauri('set_smtc_call_state', { active, muted });
|
||||
}, [active, muted]);
|
||||
|
||||
useTauriEvent<SmtcAction>('smtc-action', ({ action }) => {
|
||||
if (!callEmbed) return;
|
||||
if (action === 'mute') {
|
||||
callEmbed.control.toggleMicrophone().catch(() => undefined);
|
||||
} else if (action === 'end') {
|
||||
callEmbed.hangup().catch(() => undefined);
|
||||
}
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user