feat(call): add sound effects and incoming call notification UI

- Implemented sound management for call events including mute, unmute, deafen, undeafen, call joined, call depart, and dialing sounds.
- Created a new CallSounds class to handle sound playback and state management.
- Added IncomingCallNotification component to display incoming call alerts with caller information and action buttons.
- Styled the incoming call notification using vanilla-extract CSS for animations and layout.
- Integrated sound playback for incoming calls with a 15-second threshold for dialing sound.
This commit is contained in:
2026-02-05 03:24:18 +11:00
parent cd912025f1
commit 1a452f52ca
27 changed files with 1888 additions and 179 deletions

View File

@@ -12,6 +12,7 @@ interface CallContextValue {
startCall: (roomId: string, callType: CallType) => Promise<void>;
endCall: () => Promise<void>;
toggleMute: () => boolean;
toggleDeafen: () => boolean;
toggleVideo: () => boolean;
toggleScreenShare: () => Promise<boolean>;
}
@@ -113,15 +114,19 @@ export function useCallService(options?: UseCallServiceOptions): CallContextValu
event.type === CallEventType.ScreenShareStarted ||
event.type === CallEventType.ScreenShareStopped ||
event.type === CallEventType.RemoteStreamAdded ||
event.type === CallEventType.RemoteStreamRemoved) {
event.type === CallEventType.RemoteStreamRemoved ||
event.type === CallEventType.ParticipantJoined ||
event.type === CallEventType.ParticipantLeft ||
event.type === CallEventType.SpeakersChanged) {
const newCall = service?.getActiveCall();
if (newCall) {
// Deep copy Maps to ensure React detects changes
// Deep copy Maps and Sets to ensure React detects changes
setActiveCall({
...newCall,
remoteStreams: new Map(newCall.remoteStreams),
remoteScreenTracks: new Map(newCall.remoteScreenTracks),
remoteScreenStreams: new Map(newCall.remoteScreenStreams),
activeSpeakers: new Set(newCall.activeSpeakers),
});
} else {
setActiveCall(null);
@@ -173,6 +178,18 @@ export function useCallService(options?: UseCallServiceOptions): CallContextValu
return newState;
}, [callService]);
const toggleDeafen = useCallback(() => {
if (!callService) {
return false;
}
const newState = callService.toggleDeafen();
const call = callService.getActiveCall();
if (call) {
setActiveCall({ ...call });
}
return newState;
}, [callService]);
const toggleVideo = useCallback(() => {
if (!callService) {
return false;
@@ -206,10 +223,11 @@ export function useCallService(options?: UseCallServiceOptions): CallContextValu
startCall,
endCall,
toggleMute,
toggleDeafen,
toggleVideo,
toggleScreenShare,
}),
[callService, activeCall, callSupported, callSupportLoading, startCall, endCall, toggleMute, toggleVideo, toggleScreenShare]
[callService, activeCall, callSupported, callSupportLoading, startCall, endCall, toggleMute, toggleDeafen, toggleVideo, toggleScreenShare]
);
}