feat: add call management features with useCall and useRoomCallMembers hooks
- Implemented useCall hook for managing call state and actions. - Created useRoomCallMembers hook to track active call members in a room. - Added useRtcConfig for fetching RTC configurations and LiveKit JWT. - Developed Audio settings component for managing audio devices and settings. - Introduced device selection and screen sharing options in the Audio settings. - Persisted audio settings in localStorage for user preferences.
This commit is contained in:
@@ -69,6 +69,8 @@ import { useRoomNavigate } from '../../hooks/useRoomNavigate';
|
||||
import { useRoomCreators } from '../../hooks/useRoomCreators';
|
||||
import { useRoomPermissions } from '../../hooks/useRoomPermissions';
|
||||
import { InviteUserPrompt } from '../../components/invite-user-prompt';
|
||||
import { useRoomCall, useRoomCallMembers } from '../call';
|
||||
import { CallState, CallType } from '../call/types';
|
||||
|
||||
type RoomMenuProps = {
|
||||
room: Room;
|
||||
@@ -254,6 +256,170 @@ const RoomMenu = forwardRef<HTMLDivElement, RoomMenuProps>(({ room, requestClose
|
||||
);
|
||||
});
|
||||
|
||||
type CallIndicatorProps = {
|
||||
roomId: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Shows when others are in a call in this room
|
||||
*/
|
||||
function CallIndicator({ roomId }: CallIndicatorProps) {
|
||||
const { othersInCall, isCallActive } = useRoomCallMembers(roomId);
|
||||
|
||||
if (!isCallActive || othersInCall.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const userCount = othersInCall.length;
|
||||
const tooltipText = userCount === 1
|
||||
? `${othersInCall[0].userId.split(':')[0].slice(1)} is in a call`
|
||||
: `${userCount} people are in a call`;
|
||||
|
||||
return (
|
||||
<TooltipProvider
|
||||
position="Bottom"
|
||||
offset={4}
|
||||
tooltip={
|
||||
<Tooltip>
|
||||
<Text>{tooltipText}</Text>
|
||||
</Tooltip>
|
||||
}
|
||||
>
|
||||
{(triggerRef) => (
|
||||
<Box
|
||||
ref={triggerRef}
|
||||
alignItems="Center"
|
||||
gap="100"
|
||||
style={{
|
||||
padding: `${toRem(4)} ${toRem(8)}`,
|
||||
borderRadius: toRem(12),
|
||||
backgroundColor: 'var(--mx-positive-container)',
|
||||
cursor: 'default',
|
||||
}}
|
||||
>
|
||||
<Icon
|
||||
size="100"
|
||||
src={Icons.Phone}
|
||||
style={{ color: 'var(--mx-positive)' }}
|
||||
/>
|
||||
<Text size="T200" style={{ color: 'var(--mx-positive)' }}>
|
||||
{userCount} in call
|
||||
</Text>
|
||||
</Box>
|
||||
)}
|
||||
</TooltipProvider>
|
||||
);
|
||||
}
|
||||
|
||||
type RoomCallButtonsProps = {
|
||||
roomId: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Component for voice/video call buttons in the room header
|
||||
*/
|
||||
function RoomCallButtons({ roomId }: RoomCallButtonsProps) {
|
||||
const {
|
||||
isInCall,
|
||||
callState,
|
||||
activeCall,
|
||||
startVoiceCall,
|
||||
startVideoCall,
|
||||
endCall,
|
||||
callSupported,
|
||||
callSupportLoading,
|
||||
} = useRoomCall(roomId);
|
||||
|
||||
// Debug logging
|
||||
React.useEffect(() => {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('RoomCallButtons render:', { callSupportLoading, callSupported, roomId });
|
||||
}, [callSupportLoading, callSupported, roomId]);
|
||||
|
||||
if (callSupportLoading || !callSupported) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const isConnecting = callState === CallState.Connecting;
|
||||
const isConnected = callState === CallState.Connected;
|
||||
const isActive = isConnecting || isConnected;
|
||||
|
||||
const handleVoiceCall = async () => {
|
||||
if (isInCall) {
|
||||
await endCall();
|
||||
} else {
|
||||
try {
|
||||
await startVoiceCall();
|
||||
} catch (error) {
|
||||
console.error('Failed to start voice call:', error);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const handleVideoCall = async () => {
|
||||
if (isInCall) {
|
||||
await endCall();
|
||||
} else {
|
||||
try {
|
||||
await startVideoCall();
|
||||
} catch (error) {
|
||||
console.error('Failed to start video call:', error);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<TooltipProvider
|
||||
position="Bottom"
|
||||
offset={4}
|
||||
tooltip={
|
||||
<Tooltip>
|
||||
<Text>{isInCall ? 'End Call' : 'Voice Call'}</Text>
|
||||
</Tooltip>
|
||||
}
|
||||
>
|
||||
{(triggerRef) => (
|
||||
<IconButton
|
||||
ref={triggerRef}
|
||||
onClick={handleVoiceCall}
|
||||
variant={isActive && activeCall?.callType === CallType.Voice ? 'Critical' : 'Background'}
|
||||
>
|
||||
{isConnecting ? (
|
||||
<Spinner size="100" variant="Secondary" />
|
||||
) : (
|
||||
<Icon size="400" src={Icons.Phone} filled={isActive && activeCall?.callType === CallType.Voice} />
|
||||
)}
|
||||
</IconButton>
|
||||
)}
|
||||
</TooltipProvider>
|
||||
<TooltipProvider
|
||||
position="Bottom"
|
||||
offset={4}
|
||||
tooltip={
|
||||
<Tooltip>
|
||||
<Text>{isInCall ? 'End Call' : 'Video Call'}</Text>
|
||||
</Tooltip>
|
||||
}
|
||||
>
|
||||
{(triggerRef) => (
|
||||
<IconButton
|
||||
ref={triggerRef}
|
||||
onClick={handleVideoCall}
|
||||
variant={isActive && activeCall?.callType === CallType.Video ? 'Critical' : 'Background'}
|
||||
>
|
||||
{isConnecting ? (
|
||||
<Spinner size="100" variant="Secondary" />
|
||||
) : (
|
||||
<Icon size="400" src={Icons.VideoCamera} filled={isActive && activeCall?.callType === CallType.Video} />
|
||||
)}
|
||||
</IconButton>
|
||||
)}
|
||||
</TooltipProvider>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export function RoomViewHeader() {
|
||||
const navigate = useNavigate();
|
||||
const mx = useMatrixClient();
|
||||
@@ -369,7 +535,9 @@ export function RoomViewHeader() {
|
||||
)}
|
||||
</Box>
|
||||
</Box>
|
||||
<Box shrink="No">
|
||||
<Box shrink="No" alignItems="Center" gap="100">
|
||||
<CallIndicator roomId={room.roomId} />
|
||||
<RoomCallButtons roomId={room.roomId} />
|
||||
{!ecryptedRoom && (
|
||||
<TooltipProvider
|
||||
position="Bottom"
|
||||
|
||||
Reference in New Issue
Block a user