Vitalik
8 months ago
31 changed files with 422 additions and 350 deletions
@ -1,44 +0,0 @@
@@ -1,44 +0,0 @@
|
||||
import { socketEvents } from '@/shared' |
||||
import { CallMod, callDataStoreHelper, useCallDataStore } from '../hooks' |
||||
import { RTCSessionDescription } from 'react-native-webrtc' |
||||
// import inCallManager from 'react-native-incall-manager'
|
||||
|
||||
class CallEventsListenter { |
||||
constructor() { |
||||
this.init() |
||||
} |
||||
|
||||
private init() { |
||||
this.initCallAnswered() |
||||
} |
||||
|
||||
private initCallAnswered() { |
||||
socketEvents.on('call/answered', data => { |
||||
console.log('ANSWERED') |
||||
callDataStoreHelper |
||||
.peerConnection() |
||||
.setRemoteDescription( |
||||
new RTCSessionDescription(data.rtcMessage), |
||||
) |
||||
useCallDataStore.getState().changeMod(CallMod.Speaking) |
||||
|
||||
this.proccessIceCandidates() |
||||
// inCallManager.stopRingback()
|
||||
}) |
||||
} |
||||
|
||||
private proccessIceCandidates() { |
||||
const existIcecandidates = useCallDataStore.getState().icecandidates |
||||
|
||||
if (existIcecandidates.length) { |
||||
existIcecandidates.map(candidate => |
||||
useCallDataStore |
||||
.getState() |
||||
.peerConnection.addIceCandidate(candidate), |
||||
) |
||||
useCallDataStore.getState().cleanIcecandidates() |
||||
} |
||||
} |
||||
} |
||||
|
||||
export const callEventsListener = new CallEventsListenter() |
@ -0,0 +1,22 @@
@@ -0,0 +1,22 @@
|
||||
import { |
||||
CallDataStore, |
||||
CallFromStore, |
||||
ICallStreamsStore, |
||||
useCallDataStore, |
||||
useCallFromStore, |
||||
useCallsStream, |
||||
} from '../hooks' |
||||
|
||||
export class CallRoot { |
||||
protected get store(): CallDataStore { |
||||
return useCallDataStore.getState() |
||||
} |
||||
|
||||
protected get storeFrom(): CallFromStore { |
||||
return useCallFromStore.getState() |
||||
} |
||||
|
||||
protected get streamsStore(): ICallStreamsStore { |
||||
return useCallsStream.getState() |
||||
} |
||||
} |
@ -0,0 +1,10 @@
@@ -0,0 +1,10 @@
|
||||
import { CallDataStore, useCallDataStore } from '../hooks' |
||||
import { CallRoot } from './call-root.service' |
||||
|
||||
class CallUtilityService extends CallRoot { |
||||
public peerConnection() { |
||||
return this.store.peerConnection |
||||
} |
||||
} |
||||
|
||||
export const callUtilityService = new CallUtilityService() |
@ -0,0 +1,68 @@
@@ -0,0 +1,68 @@
|
||||
import { NavigationService } from '@/services/system' |
||||
import { AcceptCall, StartCall } from '../core' |
||||
import { callsStreamsCtr, useCallDataStore, useCallFromStore } from '../hooks' |
||||
import { CallRoot } from './call-root.service' |
||||
import { peerConnectionService } from './peer-connection.service' |
||||
import { RouteKey } from '@/shared' |
||||
import { StopCall } from '../core/stop-call' |
||||
import { cancelCallReq } from '@/api/calls/requests' |
||||
import { RTCSessionDescription } from 'react-native-webrtc' |
||||
|
||||
class CallService extends CallRoot { |
||||
public async proccesIncome(data: any) { |
||||
const peerConnection = await peerConnectionService.createIns() |
||||
|
||||
useCallDataStore |
||||
.getState() |
||||
.incomeCall( |
||||
data.callerId, |
||||
data.rtcMessage, |
||||
data.callId, |
||||
peerConnection, |
||||
) |
||||
|
||||
peerConnection.setRemoteDescription( |
||||
new RTCSessionDescription(data.rtcMessage), |
||||
) |
||||
} |
||||
|
||||
public async processAccept() { |
||||
new AcceptCall(useCallDataStore.getState).accept() |
||||
} |
||||
|
||||
public async processStart(targetUserId: number) { |
||||
try { |
||||
const peerConnection = await peerConnectionService.createIns() |
||||
useCallDataStore.getState().startCall(targetUserId, peerConnection) |
||||
NavigationService.navigate(RouteKey.Call, {}) |
||||
|
||||
new StartCall( |
||||
useCallDataStore.getState, |
||||
useCallFromStore.getState, |
||||
).start() |
||||
} catch (e) { |
||||
console.log(e) |
||||
} |
||||
} |
||||
|
||||
public async stop() { |
||||
new StopCall(useCallDataStore.getState, callsStreamsCtr).stop() |
||||
} |
||||
|
||||
public async finishCall() { |
||||
useCallDataStore.setState(useCallDataStore.getInitialState()) |
||||
NavigationService.goBack() |
||||
} |
||||
|
||||
public async cancel() { |
||||
await cancelCallReq({ |
||||
callId: useCallDataStore.getState().callId, |
||||
}).catch(console.log) |
||||
|
||||
new StopCall(useCallDataStore.getState, callsStreamsCtr).stop() |
||||
useCallDataStore.setState(useCallDataStore.getInitialState()) |
||||
NavigationService.goBack() |
||||
} |
||||
} |
||||
|
||||
export const callService = new CallService() |
@ -0,0 +1,78 @@
@@ -0,0 +1,78 @@
|
||||
import { getIncomeDataReq } from '@/api/calls/requests' |
||||
import RNCallKeep from 'react-native-callkeep' |
||||
|
||||
class CallKeepService { |
||||
public register() { |
||||
this.init() |
||||
this.initListeners() |
||||
} |
||||
|
||||
public unregister() { |
||||
this.removeListeners() |
||||
} |
||||
|
||||
private init() { |
||||
RNCallKeep.setup({ |
||||
ios: { |
||||
appName: 'TaskMe', |
||||
includesCallsInRecents: false, |
||||
maximumCallGroups: '1', |
||||
supportsVideo: false, |
||||
}, |
||||
android: { |
||||
alertTitle: 'Permissions required', |
||||
alertDescription: |
||||
'This application needs to access your phone accounts', |
||||
cancelButton: 'Cancel', |
||||
okButton: 'ok', |
||||
imageName: 'phone_account_icon', |
||||
additionalPermissions: [], |
||||
foregroundService: { |
||||
channelId: 'com.company.my', |
||||
channelName: 'Foreground service for my app', |
||||
notificationTitle: 'My app is running on background', |
||||
notificationIcon: |
||||
'Path to the resource icon of the notification', |
||||
}, |
||||
}, |
||||
}) |
||||
} |
||||
|
||||
private initListeners() { |
||||
RNCallKeep.addEventListener('answerCall', this.onAnswer.bind(this)) |
||||
RNCallKeep.addEventListener('endCall', this.onEnd.bind(this)) |
||||
RNCallKeep.addEventListener( |
||||
'didPerformSetMutedCallAction', |
||||
this.changeMuted.bind(this), |
||||
) |
||||
} |
||||
private removeListeners() { |
||||
RNCallKeep.clearInitialEvents() |
||||
RNCallKeep.removeEventListener('answerCall') |
||||
RNCallKeep.removeEventListener('endCall') |
||||
RNCallKeep.removeEventListener('didPerformSetMutedCallAction') |
||||
} |
||||
|
||||
private async onAnswer(data: any) { |
||||
try { |
||||
console.log('ON ANSWER', data) |
||||
getIncomeDataReq(data.callUUID) |
||||
} catch (e) { |
||||
console.log('Error on answer', e) |
||||
} |
||||
} |
||||
|
||||
private async onEnd() { |
||||
console.log('onend') |
||||
} |
||||
|
||||
private async changeMuted() { |
||||
console.log('changemuted') |
||||
} |
||||
|
||||
public async stop() { |
||||
RNCallKeep.endCall('') |
||||
} |
||||
} |
||||
|
||||
export const callKeepService = new CallKeepService() |
@ -0,0 +1,77 @@
@@ -0,0 +1,77 @@
|
||||
import { RouteKey, socketEvents } from '@/shared' |
||||
import { CallRoot } from './call-root.service' |
||||
import { CallMod } from '../hooks' |
||||
import { callService } from './call.service' |
||||
import { NavigationService } from '@/services/system' |
||||
import { Alert } from 'react-native' |
||||
import { RTCIceCandidate, RTCSessionDescription } from 'react-native-webrtc' |
||||
|
||||
class CallsEventsService extends CallRoot { |
||||
constructor() { |
||||
super() |
||||
this.init() |
||||
} |
||||
|
||||
private init() { |
||||
socketEvents.on('call/answered', data => { |
||||
this.store.peerConnection.setRemoteDescription( |
||||
new RTCSessionDescription(data.rtcMessage), |
||||
) |
||||
this.store.changeMod(CallMod.Speaking) |
||||
this.proccessIceCandidates() |
||||
}) |
||||
socketEvents.on('call/new', this.proccessNewCall.bind(this)) |
||||
socketEvents.on( |
||||
'call/ICEcandidate', |
||||
this.proccessIceCandidateEvent.bind(this), |
||||
) |
||||
} |
||||
|
||||
private proccessIceCandidates() { |
||||
const existIcecandidates = this.store.icecandidates |
||||
|
||||
if (existIcecandidates.length) { |
||||
existIcecandidates.map(candidate => |
||||
this.store.peerConnection.addIceCandidate(candidate), |
||||
) |
||||
this.store.cleanIcecandidates() |
||||
} |
||||
} |
||||
|
||||
private async proccessNewCall(data) { |
||||
console.log('procces new call', data) |
||||
try { |
||||
this.storeFrom.put(data.from.title, data.from.avatarImageUrl) |
||||
|
||||
await callService.proccesIncome(data) |
||||
|
||||
setTimeout(() => { |
||||
callService.processAccept() |
||||
NavigationService.navigate(RouteKey.Call, {}) |
||||
}, 100) |
||||
} catch (e) { |
||||
console.log('e', e) |
||||
} |
||||
} |
||||
|
||||
private proccessIceCandidateEvent(data) { |
||||
try { |
||||
const candidates = data.candidates |
||||
const peerConnection = this.store.peerConnection |
||||
|
||||
candidates.map(it => { |
||||
const item = new RTCIceCandidate(it) |
||||
|
||||
if (peerConnection) { |
||||
peerConnection.addIceCandidate(item) |
||||
} else { |
||||
this.store.addIcecanidate(item) |
||||
} |
||||
}) |
||||
} catch (e) { |
||||
console.log(e) |
||||
} |
||||
} |
||||
} |
||||
|
||||
export const callsEventsService = new CallsEventsService() |
@ -0,0 +1,6 @@
@@ -0,0 +1,6 @@
|
||||
export * from './call-root.service' |
||||
export * from './call-utility.service' |
||||
export * from './call.service' |
||||
export * from './callkeep.service' |
||||
export * from './calls-events.service' |
||||
export * from './peer-connection.service' |
@ -0,0 +1,66 @@
@@ -0,0 +1,66 @@
|
||||
import { MediaStream, RTCPeerConnection } from 'react-native-webrtc' |
||||
import { iceServers } from '../configs' |
||||
import { CallRoot } from './call-root.service' |
||||
import { iceCandidateReq } from '@/api/calls/requests' |
||||
import { |
||||
CallMod, |
||||
callsStreamsCtr, |
||||
initCallsMediaDevices, |
||||
useCallDataStore, |
||||
} from '../hooks' |
||||
import { StopCall } from '../core/stop-call' |
||||
|
||||
class PeerConnectionService extends CallRoot { |
||||
public async createIns() { |
||||
const peerConnection = new RTCPeerConnection({ |
||||
iceServers: iceServers, |
||||
}) |
||||
|
||||
await initCallsMediaDevices(peerConnection) |
||||
|
||||
await this.addListeners(peerConnection) |
||||
|
||||
return peerConnection |
||||
} |
||||
|
||||
private async addListeners(peerConnection: RTCPeerConnection) { |
||||
peerConnection.addEventListener('track', event => { |
||||
try { |
||||
const existremoteStream = this.streamsStore.remoteStream |
||||
const remoteMediaStream = existremoteStream || new MediaStream() |
||||
remoteMediaStream.addTrack(event.track) |
||||
this.streamsStore.setRemoteStream(remoteMediaStream) |
||||
} catch (e) { |
||||
console.log(e) |
||||
} |
||||
}) |
||||
|
||||
peerConnection.addEventListener('icecandidate', event => { |
||||
if (event.candidate === null) { |
||||
return |
||||
} |
||||
|
||||
iceCandidateReq({ |
||||
targetUserId: this.store.targetUserId, |
||||
candidates: [event.candidate], |
||||
}) |
||||
}) |
||||
|
||||
peerConnection.addEventListener('iceconnectionstatechange', event => { |
||||
this.store.setConnectedStatus(peerConnection.iceConnectionState) |
||||
|
||||
if (peerConnection.iceConnectionState === 'closed') { |
||||
this.store.changeMod(CallMod.Finished) |
||||
} |
||||
|
||||
if ( |
||||
peerConnection.iceConnectionState === 'disconnected' || |
||||
peerConnection.iceConnectionState === 'failed' |
||||
) { |
||||
new StopCall(useCallDataStore.getState, callsStreamsCtr).stop() |
||||
} |
||||
}) |
||||
} |
||||
} |
||||
|
||||
export const peerConnectionService = new PeerConnectionService() |
@ -1,27 +0,0 @@
@@ -1,27 +0,0 @@
|
||||
import RNCallKeep from 'react-native-callkeep' |
||||
|
||||
const options = { |
||||
ios: { |
||||
appName: 'TaskMe', |
||||
}, |
||||
android: { |
||||
alertTitle: 'Permissions required', |
||||
alertDescription: |
||||
'This application needs to access your phone accounts', |
||||
cancelButton: 'Cancel', |
||||
okButton: 'ok', |
||||
imageName: 'phone_account_icon', |
||||
additionalPermissions: [], |
||||
// Required to get audio in background when using Android 11
|
||||
foregroundService: { |
||||
channelId: 'com.company.my', |
||||
channelName: 'Foreground service for my app', |
||||
notificationTitle: 'My app is running on background', |
||||
notificationIcon: 'Path to the resource icon of the notification', |
||||
}, |
||||
}, |
||||
} |
||||
|
||||
RNCallKeep.setup(options).then(accepted => { |
||||
console.log('accepted', accepted) |
||||
}) |
Loading…
Reference in new issue