Browse Source

FEATURE | Call ket

master
Vitalik 8 months ago
parent
commit
f45016365c
  1. 1
      index.js
  2. 11
      ios/taskme2.xcodeproj/project.pbxproj
  3. 2
      ios/taskme2.xcodeproj/xcshareddata/xcschemes/taskme2.xcscheme
  4. 45
      ios/taskme2/AppDelegate.mm
  5. 1
      ios/taskme2/Info.plist
  6. 17
      src/App.tsx
  7. 4
      src/api/calls/requests.ts
  8. 2
      src/config/index.ts
  9. 44
      src/modules/calls/core/call-events-listener.ts
  10. 113
      src/modules/calls/hooks/use-call-data.hook.ts
  11. 1
      src/modules/calls/hooks/use-call-streams.hook.ts
  12. 22
      src/modules/calls/screens/call/index.tsx
  13. 22
      src/modules/calls/services/call-root.service.ts
  14. 10
      src/modules/calls/services/call-utility.service.ts
  15. 68
      src/modules/calls/services/call.service.ts
  16. 78
      src/modules/calls/services/callkeep.service.ts
  17. 77
      src/modules/calls/services/calls-events.service.ts
  18. 6
      src/modules/calls/services/index.ts
  19. 66
      src/modules/calls/services/peer-connection.service.ts
  20. 6
      src/modules/calls/smart-components/call-swipable-row-card.smart-component.tsx
  21. 40
      src/modules/calls/widgets/incoming-call.widget.tsx
  22. 17
      src/modules/contacts/screens/contact-detail.screen.tsx
  23. 1
      src/modules/root/hooks/use-app-socket-listener.hook.ts
  24. 1
      src/modules/root/index.tsx
  25. 1
      src/services/domain/actions-queue.service.ts
  26. 27
      src/services/system/callkeep.service.ts
  27. 3
      src/services/system/index.ts
  28. 71
      src/services/system/voip-notification.service.ts
  29. 11
      src/shared/db/index.ts
  30. 2
      src/shared/events/index.ts
  31. 2
      src/shared/helpers/chat-message-preview-text.helper.ts

1
index.js

@ -16,6 +16,7 @@ import '@/services/system/skeleton-data.service' @@ -16,6 +16,7 @@ import '@/services/system/skeleton-data.service'
const errorHandler = (e, isFatal) => {
if (isFatal) {
console.log(e)
Alert.alert(
'Unexpected error occurred',
`

11
ios/taskme2.xcodeproj/project.pbxproj

@ -334,7 +334,7 @@ @@ -334,7 +334,7 @@
CreatedOnToolsVersion = 14.3.1;
};
13B07F861A680F5B00A75B9A = {
LastSwiftMigration = 1120;
LastSwiftMigration = 1520;
};
};
};
@ -734,7 +734,7 @@ @@ -734,7 +734,7 @@
ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = NO;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = taskme2/taskme2Stage.Release.entitlements;
CURRENT_PROJECT_VERSION = 20;
CURRENT_PROJECT_VERSION = 22;
DEVELOPMENT_TEAM = HQ3J3TDPR2;
HEADER_SEARCH_PATHS = (
"$(inherited)",
@ -854,6 +854,7 @@ @@ -854,6 +854,7 @@
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
SUPPORTS_MACCATALYST = NO;
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES;
SWIFT_OBJC_BRIDGING_HEADER = "taskme2-Bridging-Header.h";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
VERSIONING_SYSTEM = "apple-generic";
@ -1013,7 +1014,7 @@ @@ -1013,7 +1014,7 @@
ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = NO;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = taskme2/taskme2.entitlements;
CURRENT_PROJECT_VERSION = 20;
CURRENT_PROJECT_VERSION = 22;
DEVELOPMENT_TEAM = HQ3J3TDPR2;
ENABLE_BITCODE = NO;
HEADER_SEARCH_PATHS = (
@ -1134,6 +1135,7 @@ @@ -1134,6 +1135,7 @@
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
SUPPORTS_MACCATALYST = NO;
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES;
SWIFT_OBJC_BRIDGING_HEADER = "taskme2-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
@ -1150,7 +1152,7 @@ @@ -1150,7 +1152,7 @@
ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = NO;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = taskme2/taskme2.entitlements;
CURRENT_PROJECT_VERSION = 20;
CURRENT_PROJECT_VERSION = 22;
DEVELOPMENT_TEAM = HQ3J3TDPR2;
HEADER_SEARCH_PATHS = (
"$(inherited)",
@ -1270,6 +1272,7 @@ @@ -1270,6 +1272,7 @@
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
SUPPORTS_MACCATALYST = NO;
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES;
SWIFT_OBJC_BRIDGING_HEADER = "taskme2-Bridging-Header.h";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
VERSIONING_SYSTEM = "apple-generic";

2
ios/taskme2.xcodeproj/xcshareddata/xcschemes/taskme2.xcscheme

@ -59,7 +59,7 @@ @@ -59,7 +59,7 @@
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Release"
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"

45
ios/taskme2/AppDelegate.mm

@ -17,7 +17,7 @@ @@ -17,7 +17,7 @@
// They will be passed down to the ViewController used by React Native.
self.initialProps = @{};
NSLog(@"setup testtttt");
return [super application:application didFinishLaunchingWithOptions:launchOptions];
}
@ -43,57 +43,46 @@ NSLog(@"setup testtttt"); @@ -43,57 +43,46 @@ NSLog(@"setup testtttt");
// --- Handle updated push credentials
- (void)pushRegistry:(PKPushRegistry *)registry didUpdatePushCredentials:(PKPushCredentials *)credentials forType:(PKPushType)type {
// Register VoIP push token (a property of PKPushCredentials) with server
NSString *token = [credentials.token description];
token = [token stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"<>"]];
token = [token stringByReplacingOccurrencesOfString:@" " withString:@""];
NSLog(@"VoIP token: %@", token);
NSDictionary *params = @{@"token": token};
NSLog(@"pushRegistry didUpdatePushCredentials");
[RNVoipPushNotificationManager didUpdatePushCredentials:credentials forType:type];
}
[[NSNotificationCenter defaultCenter] postNotificationName:@"VoipTokenReceived" object:nil userInfo:params];
[RNVoipPushNotificationManager didUpdatePushCredentials:credentials forType:(NSString *)type];
}
- (void)pushRegistry:(PKPushRegistry *)registry didInvalidatePushTokenForType:(PKPushType)type
{
// --- The system calls this method when a previously provided push token is no longer valid for use. No action is necessary on your part to reregister the push type. Instead, use this method to notify your server not to send push notifications using the matching push token.
// --- Handle invalidation of push token
- (void)pushRegistry:(PKPushRegistry *)registry didInvalidatePushTokenForType:(PKPushType)type {
NSLog(@"pushRegistry didInvalidatePushTokenForType");
}
// --- Handle incoming pushes
- (void)pushRegistry:(PKPushRegistry *)registry didReceiveIncomingPushWithPayload:(PKPushPayload *)payload forType:(PKPushType)type withCompletionHandler:(void (^)(void))completion {
// --- NOTE: apple forced us to invoke callkit ASAP when we receive voip push
// --- see: react-native-callkeep
// --- Retrieve information from your voip push payload
NSString *uuid = payload.dictionaryPayload[@"uuid"];
NSString *callerName = [NSString stringWithFormat:@"%@ (Connecting...)", payload.dictionaryPayload[@"callerName"]];
NSString *callerName = payload.dictionaryPayload[@"callerName"];
NSString *handle = payload.dictionaryPayload[@"handle"];
// --- this is optional, only required if you want to call `completion()` on the js side
[RNVoipPushNotificationManager addCompletionHandler:uuid completionHandler:completion];
NSLog(@"Payload uuid: %@", uuid);
// --- this is optional, only required if you want to call `completion()` on the js side
// [RNVoipPushNotificationManager addCompletionHandler:uuid completionHandler:completion];
// --- Process the received push
[RNVoipPushNotificationManager didReceiveIncomingPushWithPayload:payload forType:(NSString *)type];
// --- You should make sure to report to callkit BEFORE execute `completion()`
[RNCallKeep reportNewIncomingCall: uuid
handle: handle
handleType: @"generic"
hasVideo: NO
localizedCallerName: callerName
supportsHolding: YES
supportsDTMF: YES
supportsGrouping: YES
supportsUngrouping: YES
supportsHolding: NO
supportsDTMF: NO
supportsGrouping: NO
supportsUngrouping: NO
fromPushKit: YES
payload: nil
withCompletionHandler: completion];
// --- You don't need to call it if you stored `completion()` and will call it on the js side.
completion();
}

1
ios/taskme2/Info.plist

@ -79,6 +79,7 @@ @@ -79,6 +79,7 @@
</array>
<key>UIBackgroundModes</key>
<array>
<string>fetch</string>
<string>remote-notification</string>
<string>voip</string>
</array>

17
src/App.tsx

@ -7,7 +7,6 @@ if (__DEV__) { @@ -7,7 +7,6 @@ if (__DEV__) {
}
import { ThemeProvider } from './shared/themes'
// import Orientation from 'react-native-orientation-locker'
import { AppState, LogBox, Platform } from 'react-native'
import { appService } from './services/app.service'
import 'react-native-gesture-handler'
@ -18,17 +17,17 @@ import { GestureHandlerRootView } from 'react-native-gesture-handler' @@ -18,17 +17,17 @@ import { GestureHandlerRootView } from 'react-native-gesture-handler'
import { appEvents } from './shared'
import Toast from 'react-native-toast-message'
import { toastConfig } from './config/toast.config'
import './services/system/callkeep.service'
import { VoipNotificationsService } from './services/system'
import { callKeepService } from './modules/calls/services/callkeep.service'
import './modules/calls/services/calls-events.service'
LogBox.ignoreLogs(['Warning: ...', 'Require cycle: ...']) // Ignore log notification by message
LogBox.ignoreAllLogs() //Ignore all log notifications
LogBox.ignoreLogs(['Warning: ...', 'Require cycle: ...'])
LogBox.ignoreAllLogs()
const App: FC = () => {
useEffect(() => {
appService.initApp()
// Orientation.lockToPortrait()
}, [])
useEffect(() => {
@ -48,6 +47,14 @@ const App: FC = () => { @@ -48,6 +47,14 @@ const App: FC = () => {
}
}, [])
useEffect(() => {
// console.log(callsEventsService)
callKeepService.register()
return () => {
callKeepService.unregister()
}
}, [])
useEffect(() => {
if (Platform.OS === 'ios') {
VoipNotificationsService.getInstance().initListeners()

4
src/api/calls/requests.ts

@ -33,3 +33,7 @@ export const getCallsListReq = (params: any) => { @@ -33,3 +33,7 @@ export const getCallsListReq = (params: any) => {
export const deleteCallHistoryReq = (callId: number) => {
return api.delete(`calls/history/${callId}`, null, '')
}
export const getIncomeDataReq = (callId: string) => {
return api.post(`calls/income/${callId}`, {}, {}, '')
}

2
src/config/index.ts

@ -9,8 +9,6 @@ export const dynamicConfig = { @@ -9,8 +9,6 @@ export const dynamicConfig = {
googlePlayUrl:
'https://play.google.com/store/apps/details?id=com.app.task_me',
}
console.log('config', Config)
export const config = {
...dynamicConfig,
fonts,

44
src/modules/calls/core/call-events-listener.ts

@ -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()

113
src/modules/calls/hooks/use-call-data.hook.ts

@ -1,19 +1,5 @@ @@ -1,19 +1,5 @@
import { MediaStream, RTCPeerConnection } from 'react-native-webrtc'
import { RTCPeerConnection } from 'react-native-webrtc'
import { create } from 'zustand'
import { iceServers } from '../configs'
import {
cancelCallReq,
finishCallReq,
iceCandidateReq,
} from '@/api/calls/requests'
import { AcceptCall, StartCall } from '../core'
import { useCallFromStore } from './use-call-from.hook'
import { NavigationService } from '@/services/system'
import { RouteKey } from '@/shared'
// import InCallManager from 'react-native-incall-manager'
import { StopCall } from '../core/stop-call'
import { callsStreamsCtr, initCallsMediaDevices } from './use-call-streams.hook'
import { Alert } from 'react-native'
export enum CallMod {
Outgoing,
@ -95,101 +81,4 @@ export const useCallDataStore = create<CallDataStore>()(set => ({ @@ -95,101 +81,4 @@ export const useCallDataStore = create<CallDataStore>()(set => ({
export const callDataStoreHelper = {
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 () => {
new AcceptCall(useCallDataStore.getState).accept()
},
processStart: async (targetUserId: number) => {
// InCallManager.start({ media: 'audio', ringback: '_BUNDLE_' })
try {
const peerConnection = await createPeerConnection()
useCallDataStore.getState().startCall(targetUserId, peerConnection)
NavigationService.navigate(RouteKey.Call, {})
new StartCall(
useCallDataStore.getState,
useCallFromStore.getState,
).start()
} catch (e) {
console.log(e)
}
},
stop: () => {
new StopCall(useCallDataStore.getState, callsStreamsCtr).stop()
},
finishCall: async () => {
useCallDataStore.setState(useCallDataStore.getInitialState())
NavigationService.goBack()
},
cancel: async () => {
await cancelCallReq({
callId: useCallDataStore.getState().callId,
}).catch(console.log)
new StopCall(useCallDataStore.getState, callsStreamsCtr).stop()
useCallDataStore.setState(useCallDataStore.getInitialState())
NavigationService.goBack()
},
}
async function createPeerConnection() {
const peerConnection = new RTCPeerConnection({
iceServers: iceServers,
})
await initCallsMediaDevices(peerConnection)
peerConnection.addEventListener('track', event => {
try {
const existremoteStream = callsStreamsCtr().remoteStream
const remoteMediaStream = existremoteStream || new MediaStream()
remoteMediaStream.addTrack(event.track)
callsStreamsCtr().setRemoteStream(remoteMediaStream)
} catch (e) {
console.log(e)
}
})
peerConnection.addEventListener('icecandidate', event => {
if (event.candidate !== null) {
iceCandidateReq({
targetUserId: useCallDataStore.getState().targetUserId,
candidates: [event.candidate],
})
}
})
peerConnection.addEventListener('iceconnectionstatechange', event => {
console.log(peerConnection.iceConnectionState)
useCallDataStore
.getState()
.setConnectedStatus(peerConnection.iceConnectionState)
if (peerConnection.iceConnectionState === 'closed') {
useCallDataStore.getState().changeMod(CallMod.Finished)
}
if (
peerConnection.iceConnectionState === 'disconnected' ||
peerConnection.iceConnectionState === 'failed'
) {
new StopCall(useCallDataStore.getState, callsStreamsCtr).stop()
}
})
peerConnection.addEventListener('onicegatheringstatechange', event => {})
return peerConnection
}

1
src/modules/calls/hooks/use-call-streams.hook.ts

@ -1,6 +1,5 @@ @@ -1,6 +1,5 @@
import { MediaStream, mediaDevices } from 'react-native-webrtc'
import { create } from 'zustand'
import { callDataStoreHelper } from './use-call-data.hook'
export interface ICallStreamsStore {
localStream: MediaStream

22
src/modules/calls/screens/call/index.tsx

@ -1,22 +1,15 @@ @@ -1,22 +1,15 @@
import React, { useEffect, useRef, useState } from 'react'
import React from 'react'
import { CallingAtom, OutgoingcallAtom } from './atoms'
import { Button } from '@/shared'
import {
CallMod,
callDataStoreHelper,
useCallDataStore,
useCallFromStore,
useCallsStream,
} from '../../hooks'
import { CallBackground, CallBtn } from '../../components'
import {
Alert,
Dimensions,
Modal,
StatusBar,
StyleSheet,
View,
} from 'react-native'
import { Dimensions, Modal, StatusBar, StyleSheet, View } from 'react-native'
import { callService } from '../../services'
export const CallScreen = () => {
const mod = useCallDataStore(s => s.mod)
@ -25,7 +18,7 @@ export const CallScreen = () => { @@ -25,7 +18,7 @@ export const CallScreen = () => {
const connectedStatus = useCallDataStore(s => s.connectedStatus)
const stopCall = () => {
callDataStoreHelper.stop()
callService.stop()
}
const templates = {
@ -55,7 +48,7 @@ export const CallScreen = () => { @@ -55,7 +48,7 @@ export const CallScreen = () => {
[CallMod.Outgoing]: (
<OutgoingcallAtom
title={title}
onPressCancel={callDataStoreHelper.cancel}
onPressCancel={callService.cancel}
avatarImageUrl={avatarImageUrl}
/>
),
@ -66,10 +59,7 @@ export const CallScreen = () => { @@ -66,10 +59,7 @@ export const CallScreen = () => {
needOverlay={false}
showAvatar={false}>
<View style={styles.row}>
<Button
title="Вийти"
onPress={() => callDataStoreHelper.finishCall()}
/>
<Button title="Вийти" onPress={callService.finishCall} />
</View>
</CallBackground>
),

22
src/modules/calls/services/call-root.service.ts

@ -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()
}
}

10
src/modules/calls/services/call-utility.service.ts

@ -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()

68
src/modules/calls/services/call.service.ts

@ -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()

78
src/modules/calls/services/callkeep.service.ts

@ -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()

77
src/modules/calls/services/calls-events.service.ts

@ -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()

6
src/modules/calls/services/index.ts

@ -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'

66
src/modules/calls/services/peer-connection.service.ts

@ -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()

6
src/modules/calls/smart-components/call-swipable-row-card.smart-component.tsx

@ -1,4 +1,4 @@ @@ -1,4 +1,4 @@
import { getTheme, RouteKey, SquareButton, useNav } from '@/shared'
import { RouteKey, SquareButton, useNav } from '@/shared'
import { useTheme } from '@/shared/hooks/use-theme.hook'
import { PartialTheme } from '@/shared/themes/interfaces'
import React, { FC } from 'react'
@ -7,7 +7,7 @@ import { Swipeable } from 'react-native-gesture-handler' @@ -7,7 +7,7 @@ import { Swipeable } from 'react-native-gesture-handler'
import { CallRowCard } from '../components'
import { CallTypesEnum } from '../enums'
import { deleteCallHistoryReq } from '@/api/calls/requests'
import { callDataStoreHelper } from '../hooks'
import { callService } from '../services'
interface IProps {
id: number
@ -36,7 +36,7 @@ export const CallSwipableRowCardSmart: FC<IProps> = props => { @@ -36,7 +36,7 @@ export const CallSwipableRowCardSmart: FC<IProps> = props => {
}
const handlePressCall = () => {
callDataStoreHelper.processStart(props.targetUserId)
callService.processStart(props.targetUserId)
}
const btnToRender = () => (

40
src/modules/calls/widgets/incoming-call.widget.tsx

@ -1,55 +1,25 @@ @@ -1,55 +1,25 @@
import {
$size,
RouteKey,
Txt,
useNav,
useSocketListener,
useTheme,
} from '@/shared'
import { RouteKey, useSocketListener, useTheme } from '@/shared'
import React, { useState } from 'react'
import { Alert, Dimensions, Modal, StyleSheet, View } from 'react-native'
import { Dimensions, Modal, StyleSheet, View } from 'react-native'
import {
callDataStoreHelper,
useCallDataStore,
useCallFromStore,
} from '../hooks'
// import inCallManager from 'react-native-incall-manager'
import { PartialTheme } from '@/shared/themes/interfaces'
import { CallBackground, CallBtn } from '../components'
import { NavigationService } from '@/services/system'
import { RTCIceCandidate } from 'react-native-webrtc'
import { callService } from '../services'
export const IncomingCallWidget = () => {
const [isVisible, setVisible] = useState(false)
const { title, avatarImageUrl } = useCallFromStore()
const { styles } = useTheme(createStyles)
useSocketListener(
'call/new',
data => {
try {
// inCallManager.startRingtone(
// '_DEFAULT_',
// [1000, 400, 300],
// null,
// 30,
// )
setVisible(true)
useCallFromStore
.getState()
.put(data.from.title, data.from.avatarImageUrl)
callDataStoreHelper.proccesIncome(data)
} catch (e) {
console.log(e)
}
},
[setVisible],
)
useSocketListener('call/canceled', data => {
setVisible(false)
callDataStoreHelper.stop()
callService.stop()
})
useSocketListener('call/ICEcandidate', data => {
@ -75,7 +45,7 @@ export const IncomingCallWidget = () => { @@ -75,7 +45,7 @@ export const IncomingCallWidget = () => {
const accept = async () => {
// inCallManager.stopRingtone()
await callDataStoreHelper.processAccept()
await callService.processAccept()
NavigationService.navigate(RouteKey.Call, {})
setVisible(false)
}

17
src/modules/contacts/screens/contact-detail.screen.tsx

@ -21,7 +21,7 @@ import { selectId } from '@/store/account' @@ -21,7 +21,7 @@ import { selectId } from '@/store/account'
import { simpleDispatch } from '@/store/store-helpers'
import { SelectChat } from '@/store/chats'
import { chatManager } from '@/managers'
import { callDataStoreHelper } from '@/modules/calls/hooks'
import { callService } from '@/modules/calls/services'
interface IProps extends IRouteParams {
route: {
@ -59,10 +59,13 @@ export const ContactDetailScreen: FC<IProps> = ({ navigation, route }) => { @@ -59,10 +59,13 @@ export const ContactDetailScreen: FC<IProps> = ({ navigation, route }) => {
const getSubText = () => {
if (contact?.factoryName) {
if (contact.position)
if (contact.position) {
return `${contact.factoryName}, ${contact.position}`
}
return contact.factoryName
} else return contact?.position ? contact.position : ''
} else {
return contact?.position ? contact.position : ''
}
}
const fieldsToRender = useMemo(
@ -73,8 +76,12 @@ export const ContactDetailScreen: FC<IProps> = ({ navigation, route }) => { @@ -73,8 +76,12 @@ export const ContactDetailScreen: FC<IProps> = ({ navigation, route }) => {
key={`${it.value}---${i}`}
disabled={type === ContactDetailFieldType.OTHER}
onPress={() => {
if (contact?.status !== EUserStatus.Deleted && it.value)
if (
contact?.status !== EUserStatus.Deleted &&
it.value
) {
onPress(type, it.value)
}
}}
needCopy
/>
@ -125,7 +132,7 @@ export const ContactDetailScreen: FC<IProps> = ({ navigation, route }) => { @@ -125,7 +132,7 @@ export const ContactDetailScreen: FC<IProps> = ({ navigation, route }) => {
<ContactsSpeakings
contactId={contactId}
onCall={() =>
callDataStoreHelper.processStart(contact.userId)
callService.processStart(contact.userId)
}
onMessage={onPressMessage}
disabled={contact?.userId === accountId}

1
src/modules/root/hooks/use-app-socket-listener.hook.ts

@ -10,7 +10,6 @@ export const useAppSocketListener = () => { @@ -10,7 +10,6 @@ export const useAppSocketListener = () => {
const onStopSession = async () => await authService.stopSession()
const onChangedUserPermissions = async () => {
console.log('permission changed')
await permissionsService.loadPermissionsForUsers()
}

1
src/modules/root/index.tsx

@ -97,7 +97,6 @@ export const Navigation: FC = () => { @@ -97,7 +97,6 @@ export const Navigation: FC = () => {
<SelectTaxonomiesModalSmart />
<RecordAudioModalSmart />
<ChatSendImgModal />
<IncomingCallWidget />
</>
)
}

1
src/services/domain/actions-queue.service.ts

@ -225,7 +225,6 @@ class ActionsQueueService { @@ -225,7 +225,6 @@ class ActionsQueueService {
let hasNext = false
const action = await this.dbAQRepository.getFirst()
console.log('action to execute', action)
try {
if (!action) return

27
src/services/system/callkeep.service.ts

@ -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)
})

3
src/services/system/index.ts

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
export * from './app-info.service'
export * from './callkeep.service'
export * from './camera-roll.service'
export * from './converter.service'
export * from './device-info.service'
@ -9,7 +9,6 @@ export * from './media-permissions.service' @@ -9,7 +9,6 @@ export * from './media-permissions.service'
export * from './media.service'
export * from './navigation.service'
export * from './notification.service'
export * from './reactron.service'
export * from './real-time.service'
export * from './skeleton-data.service'
export * from './storage.service'

71
src/services/system/voip-notification.service.ts

@ -1,10 +1,6 @@ @@ -1,10 +1,6 @@
import { saveUserDeviceReq } from '@/api'
import axios from 'axios'
import { Platform } from 'react-native'
import VoipPushNotification from 'react-native-voip-push-notification'
import { DeviceInfoService } from './device-info.service'
import RNCallKeep from 'react-native-callkeep'
export class VoipNotificationsService {
static instance: VoipNotificationsService
@ -16,70 +12,43 @@ export class VoipNotificationsService { @@ -16,70 +12,43 @@ export class VoipNotificationsService {
return this.instance
}
private oneSignalId: string
private voipToken: string
public initListeners() {
VoipPushNotification.addEventListener(
'register',
this.handleRegister.bind(this),
)
VoipPushNotification.addEventListener(
'notification',
this.handleNotification.bind(this),
)
try {
VoipPushNotification.addEventListener(
'register',
this.handleRegister.bind(this),
)
VoipPushNotification.registerVoipToken()
VoipPushNotification.registerVoipToken()
} catch (e) {
console.log(e)
}
}
public removeListeners() {
VoipPushNotification.removeEventListener('register')
VoipPushNotification.removeEventListener('notification')
}
private async handleRegister(token: string) {
try {
console.log('voip token', token)
this.sendTokenToOneSignal(token)
this.voipToken = token
} catch (e) {
console.log('Handle register', e)
}
}
private async handleNotification(notification) {
console.log('app', notification)
RNCallKeep.displayIncomingCall('sda', 'Vutaku', 'Yatsenkoo', 'number')
VoipPushNotification.onVoipNotificationCompleted(notification.uuid)
}
private async sendTokenToOneSignal(token: string) {
const { data } = await axios.post(
'https://onesignal.com/api/v1/players',
{
app_id: '42e07e75-a735-4351-af0b-912a47831a5a',
identifier: token,
device_type: Platform.OS === 'ios' ? 0 : 1,
test_type: 1,
},
{
headers: {
'Content-Type': 'application/json',
},
},
)
if (data?.id) {
this.oneSignalId = data.id
}
}
public async saveUser() {
const deviceUuid = await DeviceInfoService.getDeviceUniqueId()
saveUserDeviceReq({
deviceUuid,
notificationUserId: this.oneSignalId,
isVoip: true,
})
setTimeout(async () => {
const deviceUuid = await DeviceInfoService.getDeviceUniqueId()
saveUserDeviceReq({
deviceUuid,
notificationUserId: this.voipToken,
isVoip: true,
})
}, 150)
}
}

11
src/shared/db/index.ts

@ -34,7 +34,7 @@ const migrations = [ @@ -34,7 +34,7 @@ const migrations = [
versionMigrations,
taskMigrations,
chatMigrations,
chatMessageMigrations
chatMessageMigrations,
]
const runMigrations = async () => {
@ -43,9 +43,7 @@ const runMigrations = async () => { @@ -43,9 +43,7 @@ const runMigrations = async () => {
db.executeSql(
item.raw,
[],
(tx, results) => {
console.log('Migrations success - ' + item.name)
},
(tx, results) => {},
error => {
Alert.alert('Error on migrations', JSON.stringify(error))
console.log(error)
@ -86,8 +84,3 @@ runMigrations() @@ -86,8 +84,3 @@ runMigrations()
// dropTable(DbTableKey.Tasks)
// dropTable(DbTableKey.Chats)
// dropTable(DbTableKey.ChatMessages)

2
src/shared/events/index.ts

@ -224,7 +224,7 @@ export type SocketEvents = { @@ -224,7 +224,7 @@ export type SocketEvents = {
'call/new': {
callerId: number
rtcMessage: any
callId: number
callId: string
from: {
type: 'personal'
title: string

2
src/shared/helpers/chat-message-preview-text.helper.ts

@ -3,7 +3,7 @@ import { MessageType } from '../enums' @@ -3,7 +3,7 @@ import { MessageType } from '../enums'
const regexToMatch = /@\[([^[]*)]\(([^(^)]*)\)/g
const parseMentionsMessage = (message: string, regex: RegExp): string => {
const subst = ` @$1 `
const result = message.replace(regex, subst)
const result = message?.replace(regex, subst)
return result
}

Loading…
Cancel
Save