fix: VideoButton disabled state, PTT listener leak, TS prop errors
- VideoButton now accepts disabled prop with tooltip and visual feedback; PrescreenControls passes disabled=true when cameraOnJoin=false - PTT key listener in settings tracked via ref, cleaned up on unmount, guarded against stacking on double-click; useCallback + useRef - CallControls screenshare cancel button: variant Surface -> Secondary - General.tsx Box align prop: align -> alignItems (TS2322 fix)
This commit is contained in:
@@ -235,7 +235,7 @@ export function CallControls({ callEmbed }: CallControlsProps) {
|
|||||||
<Text size="B300">Share</Text>
|
<Text size="B300">Share</Text>
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
size="300" variant="Surface" fill="Soft" radii="300" outlined
|
size="300" variant="Secondary" fill="Soft" radii="300" outlined
|
||||||
onClick={() => setShareConfirm(false)}
|
onClick={() => setShareConfirm(false)}
|
||||||
>
|
>
|
||||||
<Text size="B300">Cancel</Text>
|
<Text size="B300">Cancel</Text>
|
||||||
|
|||||||
@@ -81,15 +81,18 @@ export function SoundButton({ enabled, onToggle }: SoundButtonProps) {
|
|||||||
type VideoButtonProps = {
|
type VideoButtonProps = {
|
||||||
enabled: boolean;
|
enabled: boolean;
|
||||||
onToggle: () => void;
|
onToggle: () => void;
|
||||||
|
disabled?: boolean;
|
||||||
};
|
};
|
||||||
export function VideoButton({ enabled, onToggle }: VideoButtonProps) {
|
export function VideoButton({ enabled, onToggle, disabled }: VideoButtonProps) {
|
||||||
return (
|
return (
|
||||||
<TooltipProvider
|
<TooltipProvider
|
||||||
position="Top"
|
position="Top"
|
||||||
delay={500}
|
delay={500}
|
||||||
tooltip={
|
tooltip={
|
||||||
<Tooltip>
|
<Tooltip>
|
||||||
<Text size="T200">{enabled ? 'Stop Camera' : 'Start Camera'}</Text>
|
<Text size="T200">
|
||||||
|
{disabled ? 'Camera disabled in settings' : enabled ? 'Stop Camera' : 'Start Camera'}
|
||||||
|
</Text>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
@@ -102,6 +105,8 @@ export function VideoButton({ enabled, onToggle }: VideoButtonProps) {
|
|||||||
size="400"
|
size="400"
|
||||||
onClick={() => onToggle()}
|
onClick={() => onToggle()}
|
||||||
outlined
|
outlined
|
||||||
|
disabled={disabled}
|
||||||
|
style={disabled ? { opacity: 0.4, cursor: 'not-allowed' } : undefined}
|
||||||
>
|
>
|
||||||
<Icon
|
<Icon
|
||||||
size="400"
|
size="400"
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ import { ChatButton, ControlDivider, MicrophoneButton, SoundButton, VideoButton
|
|||||||
import { useIsDirectRoom, useRoom } from '../../hooks/useRoom';
|
import { useIsDirectRoom, useRoom } from '../../hooks/useRoom';
|
||||||
import { useCallEmbed, useCallJoined, useCallStart } from '../../hooks/useCallEmbed';
|
import { useCallEmbed, useCallJoined, useCallStart } from '../../hooks/useCallEmbed';
|
||||||
import { useCallPreferences } from '../../state/hooks/callPreferences';
|
import { useCallPreferences } from '../../state/hooks/callPreferences';
|
||||||
|
import { useSetting } from '../../state/hooks/settings';
|
||||||
|
import { settingsAtom } from '../../state/settings';
|
||||||
|
|
||||||
type MediaPermState = 'granted' | 'denied' | 'prompt' | 'unknown';
|
type MediaPermState = 'granted' | 'denied' | 'prompt' | 'unknown';
|
||||||
|
|
||||||
@@ -50,6 +52,7 @@ export function PrescreenControls({ canJoin }: PrescreenControlsProps) {
|
|||||||
|
|
||||||
const { microphone, video, sound, toggleMicrophone, toggleVideo, toggleSound } =
|
const { microphone, video, sound, toggleMicrophone, toggleVideo, toggleSound } =
|
||||||
useCallPreferences();
|
useCallPreferences();
|
||||||
|
const [cameraOnJoin] = useSetting(settingsAtom, 'cameraOnJoin');
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SequenceCard
|
<SequenceCard
|
||||||
@@ -67,7 +70,7 @@ export function PrescreenControls({ canJoin }: PrescreenControlsProps) {
|
|||||||
</Box>
|
</Box>
|
||||||
<ControlDivider />
|
<ControlDivider />
|
||||||
<Box shrink="No" alignItems="Inherit" justifyContent="SpaceBetween" gap="200">
|
<Box shrink="No" alignItems="Inherit" justifyContent="SpaceBetween" gap="200">
|
||||||
<VideoButton enabled={video} onToggle={toggleVideo} />
|
<VideoButton enabled={video} onToggle={toggleVideo} disabled={!cameraOnJoin} />
|
||||||
<ChatButton />
|
<ChatButton />
|
||||||
</Box>
|
</Box>
|
||||||
<Box grow="Yes" direction="Column" gap="200">
|
<Box grow="Yes" direction="Column" gap="200">
|
||||||
|
|||||||
@@ -3,7 +3,9 @@ import React, {
|
|||||||
FormEventHandler,
|
FormEventHandler,
|
||||||
KeyboardEventHandler,
|
KeyboardEventHandler,
|
||||||
MouseEventHandler,
|
MouseEventHandler,
|
||||||
|
useCallback,
|
||||||
useEffect,
|
useEffect,
|
||||||
|
useRef,
|
||||||
useState,
|
useState,
|
||||||
} from 'react';
|
} from 'react';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
@@ -378,7 +380,7 @@ function Appearance() {
|
|||||||
title="Lotus Terminal Mode"
|
title="Lotus Terminal Mode"
|
||||||
description="LotusGuild Terminal Design System: Anduril Orange + Ice Cyan + Matrix Green, dot-grid background, CRT scanlines, and boot sequence animation."
|
description="LotusGuild Terminal Design System: Anduril Orange + Ice Cyan + Matrix Green, dot-grid background, CRT scanlines, and boot sequence animation."
|
||||||
after={
|
after={
|
||||||
<Box direction="Row" gap="200" align="Center">
|
<Box direction="Row" gap="200" alignItems="Center">
|
||||||
{lotusTerminal && (
|
{lotusTerminal && (
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
@@ -811,8 +813,18 @@ function Calls() {
|
|||||||
const [pttMode, setPttMode] = useSetting(settingsAtom, 'pttMode');
|
const [pttMode, setPttMode] = useSetting(settingsAtom, 'pttMode');
|
||||||
const [pttKey, setPttKey] = useSetting(settingsAtom, 'pttKey');
|
const [pttKey, setPttKey] = useSetting(settingsAtom, 'pttKey');
|
||||||
const [listeningForKey, setListeningForKey] = useState(false);
|
const [listeningForKey, setListeningForKey] = useState(false);
|
||||||
|
const keyListenerRef = useRef<((e: KeyboardEvent) => void) | null>(null);
|
||||||
|
|
||||||
const handleKeyBind = () => {
|
useEffect(
|
||||||
|
() => () => {
|
||||||
|
if (keyListenerRef.current)
|
||||||
|
window.removeEventListener('keydown', keyListenerRef.current, true);
|
||||||
|
},
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleKeyBind = useCallback(() => {
|
||||||
|
if (listeningForKey) return;
|
||||||
setListeningForKey(true);
|
setListeningForKey(true);
|
||||||
const onKey = (e: KeyboardEvent) => {
|
const onKey = (e: KeyboardEvent) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
@@ -823,9 +835,11 @@ function Calls() {
|
|||||||
setListeningForKey(false);
|
setListeningForKey(false);
|
||||||
}
|
}
|
||||||
window.removeEventListener('keydown', onKey, true);
|
window.removeEventListener('keydown', onKey, true);
|
||||||
|
keyListenerRef.current = null;
|
||||||
};
|
};
|
||||||
|
keyListenerRef.current = onKey;
|
||||||
window.addEventListener('keydown', onKey, true);
|
window.addEventListener('keydown', onKey, true);
|
||||||
};
|
}, [listeningForKey, setPttKey]);
|
||||||
|
|
||||||
const keyLabel = (code: string) => code === 'Space' ? 'Space' : code.replace('Key', '').replace('Digit', '');
|
const keyLabel = (code: string) => code === 'Space' ? 'Space' : code.replace('Key', '').replace('Digit', '');
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user