Событийные звуки звонка (сбросить, мутинг, и прочее...)
This commit is contained in:
@@ -10,7 +10,8 @@ import { PacketWebRTC, WebRTCSignalType } from "../ProtocolProvider/protocol/pac
|
||||
import { PacketIceServers } from "../ProtocolProvider/protocol/packets/packet.ice.servers";
|
||||
import { modals } from "@mantine/modals";
|
||||
import { Button, Flex, Text } from "@mantine/core";
|
||||
|
||||
import { useSound } from "@/app/hooks/useSound";
|
||||
import useWindow from "@/app/hooks/useWindow";
|
||||
|
||||
export interface CallContextValue {
|
||||
call: (callable: string) => void;
|
||||
@@ -59,8 +60,9 @@ export interface CallProviderProps {
|
||||
export function CallProvider(props : CallProviderProps) {
|
||||
const [activeCall, setActiveCall] = useState<string>("");
|
||||
const [callState, setCallState] = useState<CallState>(CallState.ENDED);
|
||||
const [muted, setMuted] = useState<boolean>(false);
|
||||
const [sound, setSound] = useState<boolean>(true);
|
||||
const [muted, setMutedState] = useState<boolean>(false);
|
||||
const [sound, setSoundState] = useState<boolean>(true);
|
||||
const durationIntervalRef = useRef<NodeJS.Timeout | null>(null);
|
||||
const [duration, setDuration] = useState<number>(0);
|
||||
const [showCallView, setShowCallView] = useState<boolean>(callState == CallState.INCOMING);
|
||||
const {info} = useConsoleLogger("CallProvider");
|
||||
@@ -73,8 +75,26 @@ export function CallProvider(props : CallProviderProps) {
|
||||
const roleRef = useRef<CallRole | null>(null);
|
||||
const [sharedSecret, setSharedSecret] = useState<string>("");
|
||||
const iceServersRef = useRef<RTCIceServer[]>([]);
|
||||
const remoteAudioRef = useRef<HTMLAudioElement>(null);
|
||||
const remoteAudioRef = useRef<HTMLAudioElement | null>(null);
|
||||
const iceCandidatesBufferRef = useRef<RTCIceCandidate[]>([]);
|
||||
const mutedRef = useRef<boolean>(false);
|
||||
const soundRef = useRef<boolean>(true);
|
||||
|
||||
const {playSound, stopSound, stopLoopSound} = useSound();
|
||||
const {setWindowPriority} = useWindow();
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
if(callState == CallState.ACTIVE){
|
||||
stopLoopSound();
|
||||
stopSound();
|
||||
playSound("connected.mp3");
|
||||
setWindowPriority(false);
|
||||
durationIntervalRef.current = setInterval(() => {
|
||||
setDuration(prev => prev + 1);
|
||||
}, 1000);
|
||||
}
|
||||
}, [callState]);
|
||||
|
||||
useEffect(() => {
|
||||
/**
|
||||
@@ -83,6 +103,16 @@ export function CallProvider(props : CallProviderProps) {
|
||||
*/
|
||||
let packet = new PacketIceServers();
|
||||
send(packet);
|
||||
|
||||
return () => {
|
||||
stopSound();
|
||||
if (remoteAudioRef.current) {
|
||||
remoteAudioRef.current.pause();
|
||||
remoteAudioRef.current.srcObject = null;
|
||||
}
|
||||
peerConnectionRef.current?.close();
|
||||
peerConnectionRef.current = null;
|
||||
};
|
||||
}, []);
|
||||
|
||||
usePacket(28, async (packet: PacketIceServers) => {
|
||||
@@ -168,43 +198,11 @@ export function CallProvider(props : CallProviderProps) {
|
||||
usePacket(26, async (packet: PacketSignalPeer) => {
|
||||
const signalType = packet.getSignalType();
|
||||
if(signalType == SignalType.END_CALL_BECAUSE_BUSY) {
|
||||
modals.open({
|
||||
title: 'Busy',
|
||||
centered: true,
|
||||
children: (
|
||||
<>
|
||||
<Text size="sm">
|
||||
Line is busy, the user is currently on another call. Please try again later.
|
||||
</Text>
|
||||
<Flex align={'center'} justify={'flex-end'}>
|
||||
<Button color={'red'} variant={'subtle'} onClick={() => modals.closeAll()} mt="md">
|
||||
Close
|
||||
</Button>
|
||||
</Flex>
|
||||
</>
|
||||
),
|
||||
withCloseButton: false
|
||||
});
|
||||
openCallsModal("Line is busy, the user is currently on another call. Please try again later.");
|
||||
end();
|
||||
}
|
||||
if(signalType == SignalType.END_CALL_BECAUSE_PEER_DISCONNECTED) {
|
||||
modals.open({
|
||||
title: 'Connection lost',
|
||||
centered: true,
|
||||
children: (
|
||||
<>
|
||||
<Text size="sm">
|
||||
The connection with the user was lost. The call has ended.
|
||||
</Text>
|
||||
<Flex align={'center'} justify={'flex-end'}>
|
||||
<Button color={'red'} variant={'subtle'} onClick={() => modals.closeAll()} mt="md">
|
||||
Close
|
||||
</Button>
|
||||
</Flex>
|
||||
</>
|
||||
),
|
||||
withCloseButton: false
|
||||
});
|
||||
openCallsModal("The connection with the user was lost. The call has ended.")
|
||||
end();
|
||||
}
|
||||
if(activeCall){
|
||||
@@ -228,6 +226,8 @@ export function CallProvider(props : CallProviderProps) {
|
||||
/**
|
||||
* Нам поступает звонок
|
||||
*/
|
||||
setWindowPriority(true);
|
||||
playSound("ringtone.mp3", true);
|
||||
setActiveCall(packet.getSrc());
|
||||
setCallState(CallState.INCOMING);
|
||||
setShowCallView(true);
|
||||
@@ -325,10 +325,13 @@ export function CallProvider(props : CallProviderProps) {
|
||||
/**
|
||||
* При получении медиа-трека с другой стороны
|
||||
*/
|
||||
console.info("TRACK RECV!!!!!");
|
||||
if(remoteAudioRef.current){
|
||||
if(remoteAudioRef.current && event.streams[0]){
|
||||
console.info(event.streams);
|
||||
remoteAudioRef.current.srcObject = event.streams[0];
|
||||
remoteAudioRef.current.muted = !soundRef.current;
|
||||
void remoteAudioRef.current.play().catch((e) => {
|
||||
console.error("Failed to play remote audio:", e);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -355,6 +358,25 @@ export function CallProvider(props : CallProviderProps) {
|
||||
}
|
||||
}, [activeCall, sessionKeys]);
|
||||
|
||||
const openCallsModal = (text : string) => {
|
||||
modals.open({
|
||||
centered: true,
|
||||
children: (
|
||||
<>
|
||||
<Text size="sm">
|
||||
{text}
|
||||
</Text>
|
||||
<Flex align={'center'} justify={'flex-end'}>
|
||||
<Button color={'red'} variant={'subtle'} onClick={() => modals.closeAll()} mt="md">
|
||||
Close
|
||||
</Button>
|
||||
</Flex>
|
||||
</>
|
||||
),
|
||||
withCloseButton: false
|
||||
});
|
||||
}
|
||||
|
||||
const generateSessionKeys = () => {
|
||||
const sessionKeys = nacl.box.keyPair();
|
||||
info("Generated keys for call session, len: " + sessionKeys.publicKey.length);
|
||||
@@ -363,6 +385,14 @@ export function CallProvider(props : CallProviderProps) {
|
||||
}
|
||||
|
||||
const call = (dialog: string) => {
|
||||
if(callState == CallState.ACTIVE
|
||||
|| callState == CallState.CONNECTING
|
||||
|| callState == CallState.KEY_EXCHANGE
|
||||
|| callState == CallState.WEB_RTC_EXCHANGE){
|
||||
openCallsModal("You are already on a call, please end the current call before starting a new one.");
|
||||
return;
|
||||
}
|
||||
setWindowPriority(false);
|
||||
setActiveCall(dialog);
|
||||
setCallState(CallState.CONNECTING);
|
||||
setShowCallView(true);
|
||||
@@ -372,6 +402,7 @@ export function CallProvider(props : CallProviderProps) {
|
||||
signalPacket.setSignalType(SignalType.CALL);
|
||||
send(signalPacket);
|
||||
roleRef.current = CallRole.CALLER;
|
||||
playSound("calling.mp3", true);
|
||||
}
|
||||
|
||||
const close = () => {
|
||||
@@ -384,14 +415,28 @@ export function CallProvider(props : CallProviderProps) {
|
||||
}
|
||||
|
||||
const end = () => {
|
||||
stopLoopSound();
|
||||
stopSound();
|
||||
if (remoteAudioRef.current) {
|
||||
remoteAudioRef.current.pause();
|
||||
remoteAudioRef.current.srcObject = null;
|
||||
}
|
||||
setDuration(0);
|
||||
durationIntervalRef.current && clearInterval(durationIntervalRef.current);
|
||||
setWindowPriority(false);
|
||||
playSound("end_call.mp3");
|
||||
peerConnectionRef.current?.close();
|
||||
peerConnectionRef.current = null;
|
||||
roomIdRef.current = "";
|
||||
mutedRef.current = false;
|
||||
soundRef.current = true;
|
||||
setActiveCall("");
|
||||
setCallState(CallState.ENDED);
|
||||
setShowCallView(false);
|
||||
setSessionKeys(null);
|
||||
setDuration(0);
|
||||
setMutedState(false);
|
||||
setSoundState(true);
|
||||
roleRef.current = null;
|
||||
}
|
||||
|
||||
@@ -402,6 +447,9 @@ export function CallProvider(props : CallProviderProps) {
|
||||
*/
|
||||
return;
|
||||
}
|
||||
setWindowPriority(false);
|
||||
stopLoopSound();
|
||||
stopSound();
|
||||
/**
|
||||
* Звонок принят, генерируем ключи для сессии и отправляем их другой стороне для установления защищенного канала связи
|
||||
*/
|
||||
@@ -428,6 +476,46 @@ export function CallProvider(props : CallProviderProps) {
|
||||
return sharedSecret;
|
||||
}
|
||||
|
||||
|
||||
const setMuted = (nextMuted: boolean) => {
|
||||
if (mutedRef.current === nextMuted) {
|
||||
return;
|
||||
}
|
||||
|
||||
mutedRef.current = nextMuted;
|
||||
playSound(nextMuted ? "micro_enable.mp3" : "micro_disable.mp3");
|
||||
|
||||
if(peerConnectionRef.current){
|
||||
peerConnectionRef.current.getSenders().forEach(sender => {
|
||||
if(sender.track?.kind == "audio"){
|
||||
sender.track.enabled = !nextMuted;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
setMutedState(nextMuted);
|
||||
}
|
||||
|
||||
const setSound = (nextSound: boolean) => {
|
||||
if (soundRef.current === nextSound) {
|
||||
return;
|
||||
}
|
||||
|
||||
soundRef.current = nextSound;
|
||||
playSound(nextSound ? "sound_enable.mp3" : "sound_disable.mp3");
|
||||
|
||||
if(remoteAudioRef.current){
|
||||
remoteAudioRef.current.muted = !nextSound;
|
||||
if (nextSound) {
|
||||
void remoteAudioRef.current.play().catch((e) => {
|
||||
console.error("Failed to resume remote audio:", e);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
setSoundState(nextSound);
|
||||
}
|
||||
|
||||
const context = {
|
||||
call,
|
||||
close,
|
||||
|
||||
Reference in New Issue
Block a user