PTT fixes, TDS expansions, performance hooks, state event renderers
PTT fixes (BUG-7/8/9): - BUG-7: Fix isEditable to use ownerDocument.body (works in EC iframe context) - BUG-8: Release mic if callEmbed changes during active PTT (cleanup fn) - BUG-9: Wire iframe blur/focus listeners for stuck-mic prevention PiP bug fix (BUG-11): - Track prevPipMode ref so position only resets when first entering pip mode, not on every callVisible change (user drag position preserved) GIF improvements (BUG-15): - Show user-visible error text in toolbar on GIF send failure - Also surface size-limit rejection with a 4-second auto-clearing message Performance: - useInterval: replace useMemo with useEffect for setInterval (React StrictMode safe) - usePan: add unmount cleanup effect to remove document mousemove/mouseup listeners Feature: timeline state event renderers (low effort, high value): - Added renderers for RoomEncryption, RoomJoinRules, RoomGuestAccess, RoomCanonicalAlias - These were silently falling through to the hidden-events fallback TDS (Lotus Terminal Design System): - Fix: correct 8 escaped template literals in GIF picker light-mode block - Add data-emoji-board attribute to EmojiBoardLayout for stable CSS targeting - Add Tooltip panel styles (dark + light mode) - Add Switch toggle styles (dark + light mode) - Add Spinner stroke colors (dark + light mode) - Add EmojiBoard panel styles (dark + light mode) - Add PopOut/Menu/floating panel styles (dark + light mode)
This commit is contained in:
@@ -97,20 +97,21 @@ export function CallControls({ callEmbed }: CallControlsProps) {
|
||||
setCords(undefined);
|
||||
};
|
||||
|
||||
const pttActiveRef = useRef(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (!pttMode) return;
|
||||
const iframeWindow = callEmbed.iframe.contentWindow;
|
||||
|
||||
const onKeyDown = (e: KeyboardEvent) => {
|
||||
if (e.code !== pttKey || e.repeat) return;
|
||||
// Don't intercept keys typed into a text input or editable element
|
||||
const target = e.target as HTMLElement;
|
||||
// Skip PTT if key is pressed inside any text-input or editable surface
|
||||
// BUG-7: use ownerDocument.body so isEditable works inside the EC iframe
|
||||
const isEditable = (el: HTMLElement): boolean => {
|
||||
const tag = el.tagName;
|
||||
if (tag === 'INPUT' || tag === 'TEXTAREA' || tag === 'SELECT') return true;
|
||||
let node: HTMLElement | null = el;
|
||||
while (node && node !== document.body) {
|
||||
while (node && node !== el.ownerDocument.body) {
|
||||
if (node.contentEditable === 'true') return true;
|
||||
if (node.contentEditable === 'false') return false;
|
||||
node = node.parentElement;
|
||||
@@ -120,29 +121,34 @@ export function CallControls({ callEmbed }: CallControlsProps) {
|
||||
if (isEditable(target)) return;
|
||||
e.preventDefault();
|
||||
if (!microphoneRef.current) callEmbed.control.setMicrophone(true);
|
||||
pttActiveRef.current = true;
|
||||
setPttActive(true);
|
||||
};
|
||||
const onKeyUp = (e: KeyboardEvent) => {
|
||||
if (e.code !== pttKey) return;
|
||||
callEmbed.control.setMicrophone(false);
|
||||
pttActiveRef.current = false;
|
||||
setPttActive(false);
|
||||
};
|
||||
// Release PTT when the tab loses focus to prevent stuck-on mic
|
||||
const onBlur = () => {
|
||||
callEmbed.control.setMicrophone(false);
|
||||
pttActiveRef.current = false;
|
||||
setPttActive(false);
|
||||
};
|
||||
// Re-mute on focus restore: EC can re-assert audio_enabled:true on audio-context resume
|
||||
const onFocus = () => {
|
||||
callEmbed.control.setMicrophone(false);
|
||||
pttActiveRef.current = false;
|
||||
setPttActive(false);
|
||||
};
|
||||
window.addEventListener('keydown', onKeyDown);
|
||||
window.addEventListener('keyup', onKeyUp);
|
||||
window.addEventListener('blur', onBlur);
|
||||
window.addEventListener('focus', onFocus);
|
||||
// BUG-9: also wire iframe blur/focus so stuck-mic release works when focus moves to iframe
|
||||
iframeWindow?.addEventListener('keydown', onKeyDown);
|
||||
iframeWindow?.addEventListener('keyup', onKeyUp);
|
||||
iframeWindow?.addEventListener('blur', onBlur);
|
||||
iframeWindow?.addEventListener('focus', onFocus);
|
||||
return () => {
|
||||
window.removeEventListener('keydown', onKeyDown);
|
||||
window.removeEventListener('keyup', onKeyUp);
|
||||
@@ -150,6 +156,14 @@ export function CallControls({ callEmbed }: CallControlsProps) {
|
||||
window.removeEventListener('focus', onFocus);
|
||||
iframeWindow?.removeEventListener('keydown', onKeyDown);
|
||||
iframeWindow?.removeEventListener('keyup', onKeyUp);
|
||||
iframeWindow?.removeEventListener('blur', onBlur);
|
||||
iframeWindow?.removeEventListener('focus', onFocus);
|
||||
// BUG-8: if callEmbed changes while PTT is active, release mic on cleanup
|
||||
if (pttActiveRef.current) {
|
||||
callEmbed.control.setMicrophone(false);
|
||||
pttActiveRef.current = false;
|
||||
setPttActive(false);
|
||||
}
|
||||
};
|
||||
// microphone intentionally read via microphoneRef — excluded from deps to avoid listener churn
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
|
||||
Reference in New Issue
Block a user