|
|
@ -1,13 +1,19 @@ |
|
|
|
import { MediaStream, RTCPeerConnection } from 'react-native-webrtc' |
|
|
|
import { MediaStream, RTCPeerConnection } from 'react-native-webrtc' |
|
|
|
import { create } from 'zustand' |
|
|
|
import { create } from 'zustand' |
|
|
|
import { iceServers } from '../configs' |
|
|
|
import { iceServers } from '../configs' |
|
|
|
import { iceCandidateReq } from '@/api/calls/requests' |
|
|
|
import { |
|
|
|
|
|
|
|
cancelCallReq, |
|
|
|
|
|
|
|
finishCallReq, |
|
|
|
|
|
|
|
iceCandidateReq, |
|
|
|
|
|
|
|
} from '@/api/calls/requests' |
|
|
|
import { AcceptCall, StartCall } from '../core' |
|
|
|
import { AcceptCall, StartCall } from '../core' |
|
|
|
import { useCallFromStore } from './use-call-from.hook' |
|
|
|
import { useCallFromStore } from './use-call-from.hook' |
|
|
|
import { NavigationService } from '@/services/system' |
|
|
|
import { NavigationService } from '@/services/system' |
|
|
|
import { RouteKey } from '@/shared' |
|
|
|
import { RouteKey } from '@/shared' |
|
|
|
import InCallManager from 'react-native-incall-manager' |
|
|
|
// import InCallManager from 'react-native-incall-manager'
|
|
|
|
import { StopCall } from '../core/stop-call' |
|
|
|
import { StopCall } from '../core/stop-call' |
|
|
|
|
|
|
|
import { callsStreamsCtr, initCallsMediaDevices } from './use-call-streams.hook' |
|
|
|
|
|
|
|
import { Alert } from 'react-native' |
|
|
|
|
|
|
|
|
|
|
|
export enum CallMod { |
|
|
|
export enum CallMod { |
|
|
|
Outgoing, |
|
|
|
Outgoing, |
|
|
@ -22,21 +28,21 @@ export interface CallDataStore { |
|
|
|
targetUserId?: number |
|
|
|
targetUserId?: number |
|
|
|
callId?: number |
|
|
|
callId?: number |
|
|
|
remoteRTCMessage?: any |
|
|
|
remoteRTCMessage?: any |
|
|
|
remoteStream?: any |
|
|
|
|
|
|
|
icecandidates: any[] |
|
|
|
icecandidates: any[] |
|
|
|
connectedStatus: RTCIceConnectionState |
|
|
|
connectedStatus: RTCIceConnectionState |
|
|
|
|
|
|
|
|
|
|
|
changeMod: (mod: CallMod) => void |
|
|
|
changeMod: (mod: CallMod) => void |
|
|
|
startCall: (targetUserId: number) => void |
|
|
|
startCall: (targetUserId: number, peerConnection: RTCPeerConnection) => void |
|
|
|
incomeCall: ( |
|
|
|
incomeCall: ( |
|
|
|
targetUserId: number, |
|
|
|
targetUserId: number, |
|
|
|
remoteRTCMessage: any, |
|
|
|
remoteRTCMessage: any, |
|
|
|
callId: number, |
|
|
|
callId: number, |
|
|
|
|
|
|
|
peerConnection: RTCPeerConnection, |
|
|
|
) => void |
|
|
|
) => void |
|
|
|
setRemoteStream: (stream: any) => void |
|
|
|
|
|
|
|
addIcecanidate: (item: any) => void |
|
|
|
addIcecanidate: (item: any) => void |
|
|
|
cleanIcecandidates: () => void |
|
|
|
cleanIcecandidates: () => void |
|
|
|
setConnectedStatus: (connectedStatus: RTCIceConnectionState) => void |
|
|
|
setConnectedStatus: (connectedStatus: RTCIceConnectionState) => void |
|
|
|
|
|
|
|
setCallId: (callId: number) => void |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
export const useCallDataStore = create<CallDataStore>()(set => ({ |
|
|
|
export const useCallDataStore = create<CallDataStore>()(set => ({ |
|
|
@ -53,26 +59,28 @@ export const useCallDataStore = create<CallDataStore>()(set => ({ |
|
|
|
set({ mod }) |
|
|
|
set({ mod }) |
|
|
|
}, |
|
|
|
}, |
|
|
|
|
|
|
|
|
|
|
|
startCall(targetUserId) { |
|
|
|
setCallId(callId: number) { |
|
|
|
|
|
|
|
set({ callId }) |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
startCall(targetUserId, peerConnection) { |
|
|
|
set({ |
|
|
|
set({ |
|
|
|
targetUserId, |
|
|
|
targetUserId, |
|
|
|
peerConnection: createPeerConnection(), |
|
|
|
peerConnection, |
|
|
|
mod: CallMod.Outgoing, |
|
|
|
mod: CallMod.Outgoing, |
|
|
|
}) |
|
|
|
}) |
|
|
|
}, |
|
|
|
}, |
|
|
|
|
|
|
|
|
|
|
|
incomeCall(targetUserId, remoteRTCMessage, callId) { |
|
|
|
incomeCall(targetUserId, remoteRTCMessage, callId, peerConnection) { |
|
|
|
set({ |
|
|
|
set({ |
|
|
|
targetUserId, |
|
|
|
targetUserId, |
|
|
|
callId, |
|
|
|
callId, |
|
|
|
remoteRTCMessage, |
|
|
|
remoteRTCMessage, |
|
|
|
mod: CallMod.Incoming, |
|
|
|
mod: CallMod.Incoming, |
|
|
|
peerConnection: createPeerConnection(), |
|
|
|
peerConnection, |
|
|
|
}) |
|
|
|
}) |
|
|
|
}, |
|
|
|
}, |
|
|
|
setRemoteStream(stream) { |
|
|
|
|
|
|
|
set({ remoteStream: stream }) |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
addIcecanidate(item) { |
|
|
|
addIcecanidate(item) { |
|
|
|
set(prev => ({ icecandidates: [...prev.icecandidates, item] })) |
|
|
|
set(prev => ({ icecandidates: [...prev.icecandidates, item] })) |
|
|
|
}, |
|
|
|
}, |
|
|
@ -87,90 +95,101 @@ export const useCallDataStore = create<CallDataStore>()(set => ({ |
|
|
|
|
|
|
|
|
|
|
|
export const callDataStoreHelper = { |
|
|
|
export const callDataStoreHelper = { |
|
|
|
peerConnection: () => useCallDataStore.getState().peerConnection, |
|
|
|
peerConnection: () => useCallDataStore.getState().peerConnection, |
|
|
|
|
|
|
|
proccesIncome: async (data: any) => { |
|
|
|
|
|
|
|
const peerConnection = await createPeerConnection() |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
useCallDataStore |
|
|
|
|
|
|
|
.getState() |
|
|
|
|
|
|
|
.incomeCall( |
|
|
|
|
|
|
|
data.callerId, |
|
|
|
|
|
|
|
data.rtcMessage, |
|
|
|
|
|
|
|
data.callId, |
|
|
|
|
|
|
|
peerConnection, |
|
|
|
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
peerConnection.setRemoteDescription( |
|
|
|
|
|
|
|
new RTCSessionDescription(data.rtcMessage), |
|
|
|
|
|
|
|
) |
|
|
|
|
|
|
|
}, |
|
|
|
processAccept: async () => { |
|
|
|
processAccept: async () => { |
|
|
|
new AcceptCall(useCallDataStore.getState).accept() |
|
|
|
new AcceptCall(useCallDataStore.getState).accept() |
|
|
|
}, |
|
|
|
}, |
|
|
|
processStart: async (targetUserId: number) => { |
|
|
|
processStart: async (targetUserId: number) => { |
|
|
|
InCallManager.start({ media: 'audio', ringback: '_BUNDLE_' }) |
|
|
|
// InCallManager.start({ media: 'audio', ringback: '_BUNDLE_' })
|
|
|
|
useCallDataStore.getState().startCall(targetUserId) |
|
|
|
try { |
|
|
|
NavigationService.navigate(RouteKey.Call, {}) |
|
|
|
const peerConnection = await createPeerConnection() |
|
|
|
|
|
|
|
useCallDataStore.getState().startCall(targetUserId, peerConnection) |
|
|
|
setTimeout(() => { |
|
|
|
NavigationService.navigate(RouteKey.Call, {}) |
|
|
|
new StartCall( |
|
|
|
new StartCall( |
|
|
|
useCallDataStore.getState, |
|
|
|
useCallDataStore.getState, |
|
|
|
useCallFromStore.getState, |
|
|
|
useCallFromStore.getState, |
|
|
|
).start() |
|
|
|
).start() |
|
|
|
}, 999) |
|
|
|
} catch (e) { |
|
|
|
|
|
|
|
console.log(e) |
|
|
|
|
|
|
|
} |
|
|
|
}, |
|
|
|
}, |
|
|
|
stop: () => { |
|
|
|
stop: () => { |
|
|
|
new StopCall(useCallDataStore.getState).stop() |
|
|
|
new StopCall(useCallDataStore.getState, callsStreamsCtr).stop() |
|
|
|
}, |
|
|
|
}, |
|
|
|
finishCall: () => { |
|
|
|
finishCall: async () => { |
|
|
|
const state = useCallDataStore.getState() |
|
|
|
useCallDataStore.setState(useCallDataStore.getInitialState()) |
|
|
|
if ( |
|
|
|
NavigationService.goBack() |
|
|
|
state.peerConnection.iceConnectionState === 'disconnected' || |
|
|
|
}, |
|
|
|
state.peerConnection.iceConnectionState === 'failed' |
|
|
|
cancel: async () => { |
|
|
|
) { |
|
|
|
await cancelCallReq({ |
|
|
|
new StopCall(useCallDataStore.getState).stop() |
|
|
|
callId: useCallDataStore.getState().callId, |
|
|
|
} |
|
|
|
}).catch(console.log) |
|
|
|
|
|
|
|
new StopCall(useCallDataStore.getState, callsStreamsCtr).stop() |
|
|
|
useCallDataStore.setState(useCallDataStore.getInitialState()) |
|
|
|
useCallDataStore.setState(useCallDataStore.getInitialState()) |
|
|
|
NavigationService.goBack() |
|
|
|
NavigationService.goBack() |
|
|
|
}, |
|
|
|
}, |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
function createPeerConnection() { |
|
|
|
async function createPeerConnection() { |
|
|
|
const peerConnection = new RTCPeerConnection({ |
|
|
|
const peerConnection = new RTCPeerConnection({ |
|
|
|
iceServers: iceServers, |
|
|
|
iceServers: iceServers, |
|
|
|
}) |
|
|
|
}) |
|
|
|
|
|
|
|
await initCallsMediaDevices(peerConnection) |
|
|
|
|
|
|
|
|
|
|
|
peerConnection.addEventListener('track', event => { |
|
|
|
peerConnection.addEventListener('track', event => { |
|
|
|
try { |
|
|
|
try { |
|
|
|
const existremoteStream = useCallDataStore.getState().remoteStream |
|
|
|
const existremoteStream = callsStreamsCtr().remoteStream |
|
|
|
const remoteMediaStream = existremoteStream || new MediaStream() |
|
|
|
const remoteMediaStream = existremoteStream || new MediaStream() |
|
|
|
remoteMediaStream.addTrack(event.track, remoteMediaStream) |
|
|
|
remoteMediaStream.addTrack(event.track) |
|
|
|
useCallDataStore.getState().setRemoteStream(remoteMediaStream) |
|
|
|
callsStreamsCtr().setRemoteStream(remoteMediaStream) |
|
|
|
} catch (e) { |
|
|
|
} catch (e) { |
|
|
|
console.log(e) |
|
|
|
console.log(e) |
|
|
|
} |
|
|
|
} |
|
|
|
}) |
|
|
|
}) |
|
|
|
|
|
|
|
|
|
|
|
peerConnection.addEventListener('icecandidate', event => { |
|
|
|
peerConnection.addEventListener('icecandidate', event => { |
|
|
|
if (!event.candidate) { |
|
|
|
if (event.candidate !== null) { |
|
|
|
return |
|
|
|
iceCandidateReq({ |
|
|
|
|
|
|
|
targetUserId: useCallDataStore.getState().targetUserId, |
|
|
|
|
|
|
|
candidates: [event.candidate], |
|
|
|
|
|
|
|
}) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
iceCandidateReq({ |
|
|
|
|
|
|
|
targetUserId: useCallDataStore.getState().targetUserId, |
|
|
|
|
|
|
|
rtcMessage: { |
|
|
|
|
|
|
|
label: event.candidate.sdpMLineIndex, |
|
|
|
|
|
|
|
id: event.candidate.sdpMid, |
|
|
|
|
|
|
|
candidate: event.candidate.candidate, |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
}) |
|
|
|
|
|
|
|
}) |
|
|
|
}) |
|
|
|
|
|
|
|
|
|
|
|
peerConnection.addEventListener('iceconnectionstatechange', event => { |
|
|
|
peerConnection.addEventListener('iceconnectionstatechange', event => { |
|
|
|
console.log( |
|
|
|
console.log(peerConnection.iceConnectionState) |
|
|
|
'iceconnectionstatechange', |
|
|
|
|
|
|
|
peerConnection.iceConnectionState, |
|
|
|
|
|
|
|
) |
|
|
|
|
|
|
|
useCallDataStore |
|
|
|
useCallDataStore |
|
|
|
.getState() |
|
|
|
.getState() |
|
|
|
.setConnectedStatus(peerConnection.iceConnectionState) |
|
|
|
.setConnectedStatus(peerConnection.iceConnectionState) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (peerConnection.iceConnectionState === 'closed') { |
|
|
|
|
|
|
|
useCallDataStore.getState().changeMod(CallMod.Finished) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if ( |
|
|
|
if ( |
|
|
|
peerConnection.iceConnectionState === 'closed' || |
|
|
|
peerConnection.iceConnectionState === 'disconnected' || |
|
|
|
peerConnection.iceConnectionState === 'disconnected' |
|
|
|
peerConnection.iceConnectionState === 'failed' |
|
|
|
) { |
|
|
|
) { |
|
|
|
useCallDataStore.getState().changeMod(CallMod.Finished) |
|
|
|
new StopCall(useCallDataStore.getState, callsStreamsCtr).stop() |
|
|
|
} |
|
|
|
} |
|
|
|
}) |
|
|
|
}) |
|
|
|
|
|
|
|
|
|
|
|
peerConnection.addEventListener('icecandidateerror', event => { |
|
|
|
peerConnection.addEventListener('onicegatheringstatechange', event => {}) |
|
|
|
// You can ignore some candidate errors.
|
|
|
|
|
|
|
|
// Connections can still be made even when errors occur.
|
|
|
|
|
|
|
|
console.log(event) |
|
|
|
|
|
|
|
}) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return peerConnection |
|
|
|
return peerConnection |
|
|
|
} |
|
|
|
} |
|
|
|