cleanupses

This commit is contained in:
legop3
2026-10-08 13:05:41 -04:00
parent 9d42cb5581
commit 9d3b8df4bf
2 changed files with 38 additions and 84 deletions
+38 -4
View File
@@ -4,9 +4,20 @@ import { useSessionSelector } from '../context/SessionContext.jsx';
import useUserIdentitySync from '../hooks/useUserIdentitySync.js';
import { RESTART_DELAY_MS } from '../lib/whepPlayback.js';
import { waitForIceGatheringComplete } from '../components/vip/VipAudioUploadCard/whipTransport.js';
import useMicPtt from './useMicPtt.js';
import { ControlSystemProvider, KeyboardInputManager, GamepadInputManager, useControlSelector } from '../controls/index.js';
import ControlHint from '../components/ControlHint/index.jsx';
export default function MicApp() {
return (
<ControlSystemProvider>
<KeyboardInputManager />
<GamepadInputManager />
<MicPage />
</ControlSystemProvider>
);
}
function MicPage() {
const socket = useSocket();
const connected = useSessionSelector((state) => state.connected);
const local = useSessionSelector((state) => state.session?.isLocalNetwork);
@@ -14,7 +25,10 @@ export default function MicApp() {
const [status, setStatus] = useState('Connecting…');
const [pttMode, setPttMode] = useState(false);
const available = Boolean(connected && local && enabled);
const { held, setPointerHeld, setButtonKeyHeld, keyLabel } = useMicPtt(available && pttMode);
const pttActive = useControlSelector((control) => Boolean(control.state.mic?.pttActive));
const [pointerHeld, setPointerHeld] = useState(false);
const [buttonKeyHeld, setButtonKeyHeld] = useState(false);
const held = available && pttMode && (pttActive || pointerHeld || buttonKeyHeld);
const transmitting = available && (!pttMode || held);
const mediaRef = useRef(null);
const transmittingRef = useRef(transmitting);
@@ -22,6 +36,22 @@ export default function MicApp() {
transmittingRef.current = transmitting;
mediaRef.current?.getAudioTracks().forEach((track) => { track.enabled = transmitting; });
}, [transmitting]);
useEffect(() => {
// Pointer capture can outlive focus; release the page button when leaving the page.
function releaseButton() {
setPointerHeld(false);
setButtonKeyHeld(false);
}
function visibilityChange() {
if (document.hidden) releaseButton();
}
window.addEventListener('blur', releaseButton);
document.addEventListener('visibilitychange', visibilityChange);
return () => {
window.removeEventListener('blur', releaseButton);
document.removeEventListener('visibilitychange', visibilityChange);
};
}, []);
useUserIdentitySync();
useEffect(() => {
if (!connected || !local || !enabled) return undefined;
@@ -131,7 +161,11 @@ export default function MicApp() {
{[false, true].map((ptt) => (
<button key={String(ptt)} type="button" aria-pressed={pttMode === ptt}
className={`button-dark w-full ${pttMode === ptt ? 'bg-emerald-500 text-white hover:bg-emerald-500' : ''}`}
onClick={() => setPttMode(ptt)}>
onClick={() => {
setPointerHeld(false);
setButtonKeyHeld(false);
setPttMode(ptt);
}}>
{ptt ? 'Push to talk' : 'Open mic'}
</button>
))}
@@ -162,7 +196,7 @@ export default function MicApp() {
onBlur={() => setButtonKeyHeld(false)}>
{held ? 'Talking…' : 'Hold to talk'}
</button>
<p className="mt-2 text-center text-sm text-slate-400">Hold the button, {keyLabel}, or your configured controller PTT control.</p>
<p className="mt-2 text-center text-sm text-slate-400">Hold the button or <ControlHint actionId="micPtt" /> to talk.</p>
</>
)}
</div>
-80
View File
@@ -1,80 +0,0 @@
import { useEffect, useMemo, useState } from 'react';
import { useSettingsNamespace } from '../settings/index.js';
import { DEFAULT_KEYMAP } from '../controls/constants.js';
import { formatKeyLabel, normalizeKeymapEntries, tokensForEvent } from '../controls/keymapUtils.js';
import { GAMEPAD_PROFILE_DEFAULT, GAMEPAD_SETTINGS_DEFAULTS } from '../settings/namespaces.js';
import { computeGamepadOutputs, resolveGamepadProfile } from '../controls/inputs/gamepadBindings.js';
import { subscribeGamepadHub } from '../controls/inputs/gamepadHub.js';
import { isTextInputElement, isTextEntryActive } from '../controls/inputs/inputFocusUtils.js';
import { isKeyboardCaptureLocked } from '../controls/inputs/keyboardCaptureLock.js';
import { isControllerControlLocked } from '../controls/inputs/controllerRuntime.js';
export default function useMicPtt(enabled) {
const { value: controls } = useSettingsNamespace('controls', { keymap: DEFAULT_KEYMAP });
const { value: gamepad } = useSettingsNamespace('gamepad', GAMEPAD_SETTINGS_DEFAULTS);
const bindings = controls?.keymap?.micPtt ?? DEFAULT_KEYMAP.micPtt;
const tokens = useMemo(() => normalizeKeymapEntries({ micPtt: bindings }).micPtt, [bindings]);
const [keyboardHeld, setKeyboardHeld] = useState(false);
const [controllerHeld, setControllerHeld] = useState(false);
const [pointerHeld, setPointerHeld] = useState(false);
const [buttonKeyHeld, setButtonKeyHeld] = useState(false);
useEffect(() => {
if (!enabled) return undefined;
const pressed = new Set();
function reset() {
pressed.clear();
setKeyboardHeld(false);
setControllerHeld(false);
setPointerHeld(false);
setButtonKeyHeld(false);
}
function keyDown(event) {
if (isKeyboardCaptureLocked() || isTextInputElement(event.target)) return;
const matching = tokensForEvent(event).filter((token) => tokens.has(token));
if (!matching.length) return;
event.preventDefault();
matching.forEach((token) => pressed.add(token));
setKeyboardHeld(true);
}
function keyUp(event) {
tokensForEvent(event).forEach((token) => pressed.delete(token));
setKeyboardHeld(pressed.size > 0);
}
function visibilityChange() {
if (document.hidden) reset();
}
// Reuse the configured bindings and shared poller without mounting rover controls on /mic.
const unsubscribe = subscribeGamepadHub(({ pads }) => {
if (document.hidden || !document.hasFocus() || isTextEntryActive() || isControllerControlLocked()) {
setControllerHeld(false);
return;
}
const pad = pads.find((entry) => entry.instanceKey === gamepad?.activeInstanceKey) ?? pads[0];
const profile = resolveGamepadProfile(
gamepad?.profiles?.[pad?.signature] ?? gamepad?.defaults?.profile,
GAMEPAD_PROFILE_DEFAULT,
);
setControllerHeld(Boolean(pad && computeGamepadOutputs(pad, profile).buttons.micPtt));
});
window.addEventListener('keydown', keyDown);
window.addEventListener('keyup', keyUp);
window.addEventListener('blur', reset);
document.addEventListener('visibilitychange', visibilityChange);
return () => {
unsubscribe();
window.removeEventListener('keydown', keyDown);
window.removeEventListener('keyup', keyUp);
window.removeEventListener('blur', reset);
document.removeEventListener('visibilitychange', visibilityChange);
reset();
};
}, [enabled, tokens, gamepad]);
return {
held: enabled && (keyboardHeld || controllerHeld || pointerHeld || buttonKeyHeld),
setPointerHeld,
setButtonKeyHeld,
keyLabel: (Array.isArray(bindings) ? bindings : [bindings]).map(formatKeyLabel).join(' / '),
};
}