Browse Source

FEATURE | Calls history

master
Vitalik 8 months ago
parent
commit
9d87502a41
  1. 8
      .env.stage
  2. 2
      android/app/build.gradle
  3. 1
      ios/Podfile
  4. 8
      ios/Podfile.lock
  5. 21
      ios/taskme2.xcodeproj/project.pbxproj
  6. 15
      package-lock.json
  7. 1
      package.json
  8. 5
      src/App.tsx
  9. 18
      src/api/calls/requests.interfaces.ts
  10. 17
      src/api/calls/requests.ts
  11. 13
      src/modules/calls/atoms/call-card-info.atom.tsx
  12. 2
      src/modules/calls/components/call-background.component.tsx
  13. 14
      src/modules/calls/components/call-row-card.component.tsx
  14. 5
      src/modules/calls/configs/ice-servers.config.ts
  15. 6
      src/modules/calls/core/accept-call.ts
  16. 19
      src/modules/calls/core/call-events-listener.ts
  17. 1
      src/modules/calls/core/start-call.ts
  18. 37
      src/modules/calls/core/stop-call.ts
  19. 1
      src/modules/calls/enums/call-types.enum.ts
  20. 2
      src/modules/calls/hooks/index.ts
  21. 127
      src/modules/calls/hooks/use-call-data.hook.ts
  22. 47
      src/modules/calls/hooks/use-call-streams.hook.ts
  23. 67
      src/modules/calls/hooks/use-calls-history.hook.ts
  24. 4
      src/modules/calls/screens/call/atoms/calling.atom.tsx
  25. 46
      src/modules/calls/screens/call/index.tsx
  26. 37
      src/modules/calls/smart-components/call-swipable-row-card.smart-component.tsx
  27. 113
      src/modules/calls/smart-components/calls-list.smart-component.tsx
  28. 55
      src/modules/calls/widgets/incoming-call.widget.tsx
  29. 5
      src/modules/contacts/screens/contact-detail.screen.tsx
  30. 17
      src/modules/contacts/screens/contacts.screen.tsx
  31. 1
      src/modules/root/index.tsx
  32. 1
      src/services/system/real-time.service.ts
  33. 6
      src/shared/enums/call-status.enum.ts
  34. 27
      src/shared/enums/index.ts
  35. 5
      src/shared/events/index.ts
  36. 17
      src/shared/interfaces/call.inteface.ts
  37. 27
      src/shared/interfaces/index.ts
  38. 3
      tsconfig.json

8
.env.stage

@ -1,7 +1,3 @@ @@ -1,7 +1,3 @@
# API_URL=https://taskme-api.work-jetup.site
# SOCKET_URL=https://taskme-api.work-jetup.site
# ONE_SIGNAL_KEY=8b9066f5-8c3f-49f7-bef4-c5ab621f9d27
API_URL=http://localhost:3000
SOCKET_URL=http://localhost:3000
API_URL=https://taskme-api.work-jetup.site
SOCKET_URL=https://taskme-api.work-jetup.site
ONE_SIGNAL_KEY=8b9066f5-8c3f-49f7-bef4-c5ab621f9d27

2
android/app/build.gradle

@ -25,7 +25,7 @@ android { @@ -25,7 +25,7 @@ android {
applicationId "com.app.task_me"
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 221
versionCode 222
versionName "2.3"
resValue "string", "build_config_package", "com.app.task_me"
}

1
ios/Podfile

@ -31,7 +31,6 @@ target 'taskme2' do @@ -31,7 +31,6 @@ target 'taskme2' do
pod 'react-native-sqlite-storage', :path => '../node_modules/react-native-sqlite-storage'
pod 'react-native-config/Extension', :path => '../node_modules/react-native-config'
pod 'ReactNativeIncallManager', :path => '../node_modules/react-native-incall-manager'
# Flags change depending on the env values.
flags = get_default_flags()

8
ios/Podfile.lock

@ -560,8 +560,6 @@ PODS: @@ -560,8 +560,6 @@ PODS:
- React-perflogger (= 0.72.10)
- ReactNativeExceptionHandler (2.10.10):
- React-Core
- ReactNativeIncallManager (4.2.0):
- React-Core
- rn-fetch-blob (0.12.0):
- React-Core
- RNAudioRecorderPlayer (3.6.6):
@ -718,7 +716,6 @@ DEPENDENCIES: @@ -718,7 +716,6 @@ DEPENDENCIES:
- React-utils (from `../node_modules/react-native/ReactCommon/react/utils`)
- ReactCommon/turbomodule/core (from `../node_modules/react-native/ReactCommon`)
- ReactNativeExceptionHandler (from `../node_modules/react-native-exception-handler`)
- ReactNativeIncallManager (from `../node_modules/react-native-incall-manager`)
- rn-fetch-blob (from `../node_modules/rn-fetch-blob`)
- RNAudioRecorderPlayer (from `../node_modules/react-native-audio-recorder-player`)
- "RNCAsyncStorage (from `../node_modules/@react-native-async-storage/async-storage`)"
@ -883,8 +880,6 @@ EXTERNAL SOURCES: @@ -883,8 +880,6 @@ EXTERNAL SOURCES:
:path: "../node_modules/react-native/ReactCommon"
ReactNativeExceptionHandler:
:path: "../node_modules/react-native-exception-handler"
ReactNativeIncallManager:
:path: "../node_modules/react-native-incall-manager"
rn-fetch-blob:
:path: "../node_modules/rn-fetch-blob"
RNAudioRecorderPlayer:
@ -1005,7 +1000,6 @@ SPEC CHECKSUMS: @@ -1005,7 +1000,6 @@ SPEC CHECKSUMS:
React-utils: 372b83030a74347331636909278bf0a60ec30d59
ReactCommon: 38824bfffaf4c51fbe03a2730b4fd874ef34d67b
ReactNativeExceptionHandler: b11ff67c78802b2f62eed0e10e75cb1ef7947c60
ReactNativeIncallManager: bfc9c67358cd524882a7c4116dcb311ac2293d4b
rn-fetch-blob: f065bb7ab7fb48dd002629f8bdcb0336602d3cba
RNAudioRecorderPlayer: f790fc1afb118552ae6285d60adde52ee6b5d9ef
RNCAsyncStorage: 7deab901e27d1f989a83e8be6ce91b673772c848
@ -1034,6 +1028,6 @@ SPEC CHECKSUMS: @@ -1034,6 +1028,6 @@ SPEC CHECKSUMS:
Yoga: d0003f849d2b5224c072cef6568b540d8bb15cd3
YogaKit: f782866e155069a2cca2517aafea43200b01fd5a
PODFILE CHECKSUM: 12feabb02fdabf7d6322d9ef0cd90fefc37bdafa
PODFILE CHECKSUM: f9f0683c1738b3e88b8940a4c7885412b08fb771
COCOAPODS: 1.15.2

21
ios/taskme2.xcodeproj/project.pbxproj

@ -710,7 +710,10 @@ @@ -710,7 +710,10 @@
"-DFOLLY_MOBILE=1",
"-DFOLLY_USE_LIBCPP=1",
);
OTHER_LDFLAGS = "$(inherited) ";
OTHER_LDFLAGS = (
"$(inherited)",
" ",
);
REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native";
SDKROOT = iphoneos;
VALIDATE_PRODUCT = YES;
@ -726,7 +729,7 @@ @@ -726,7 +729,7 @@
ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = NO;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = taskme2/taskme2Stage.Release.entitlements;
CURRENT_PROJECT_VERSION = 17;
CURRENT_PROJECT_VERSION = 19;
DEVELOPMENT_TEAM = HQ3J3TDPR2;
INFOPLIST_FILE = taskme2/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = "Task me ;)";
@ -905,7 +908,7 @@ @@ -905,7 +908,7 @@
ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = NO;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = taskme2/taskme2.entitlements;
CURRENT_PROJECT_VERSION = 17;
CURRENT_PROJECT_VERSION = 19;
DEVELOPMENT_TEAM = HQ3J3TDPR2;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = taskme2/Info.plist;
@ -942,7 +945,7 @@ @@ -942,7 +945,7 @@
ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = NO;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = taskme2/taskme2.entitlements;
CURRENT_PROJECT_VERSION = 17;
CURRENT_PROJECT_VERSION = 19;
DEVELOPMENT_TEAM = HQ3J3TDPR2;
INFOPLIST_FILE = taskme2/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = "Task me ;)";
@ -1037,7 +1040,10 @@ @@ -1037,7 +1040,10 @@
"-DFOLLY_MOBILE=1",
"-DFOLLY_USE_LIBCPP=1",
);
OTHER_LDFLAGS = "$(inherited) ";
OTHER_LDFLAGS = (
"$(inherited)",
" ",
);
REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native";
SDKROOT = iphoneos;
};
@ -1107,7 +1113,10 @@ @@ -1107,7 +1113,10 @@
"-DFOLLY_MOBILE=1",
"-DFOLLY_USE_LIBCPP=1",
);
OTHER_LDFLAGS = "$(inherited) ";
OTHER_LDFLAGS = (
"$(inherited)",
" ",
);
REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native";
SDKROOT = iphoneos;
VALIDATE_PRODUCT = YES;

15
package-lock.json generated

@ -62,7 +62,6 @@ @@ -62,7 +62,6 @@
"react-native-html-to-pdf": "^0.12.0",
"react-native-image-crop-picker": "^0.40.0",
"react-native-image-picker": "^5.6.0",
"react-native-incall-manager": "^4.2.0",
"react-native-keyboard-aware-scroll-view": "^0.9.5",
"react-native-masked-text": "^1.13.0",
"react-native-modal": "^13.0.1",
@ -12350,14 +12349,6 @@ @@ -12350,14 +12349,6 @@
"react-native": "*"
}
},
"node_modules/react-native-incall-manager": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/react-native-incall-manager/-/react-native-incall-manager-4.2.0.tgz",
"integrity": "sha512-DC5XRQVAwNgNA6YZ3ILF6ttWXv/MUQ4omzmVDh/uHc0TW0v4f8QIdt6D9GHZhGKb3+qB7XKUxpXVBrLH+9zqfQ==",
"peerDependencies": {
"react-native": ">=0.40.0"
}
},
"node_modules/react-native-iphone-x-helper": {
"version": "1.3.1",
"license": "MIT",
@ -22184,12 +22175,6 @@ @@ -22184,12 +22175,6 @@
"version": "5.7.0",
"requires": {}
},
"react-native-incall-manager": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/react-native-incall-manager/-/react-native-incall-manager-4.2.0.tgz",
"integrity": "sha512-DC5XRQVAwNgNA6YZ3ILF6ttWXv/MUQ4omzmVDh/uHc0TW0v4f8QIdt6D9GHZhGKb3+qB7XKUxpXVBrLH+9zqfQ==",
"requires": {}
},
"react-native-iphone-x-helper": {
"version": "1.3.1",
"requires": {}

1
package.json

@ -72,7 +72,6 @@ @@ -72,7 +72,6 @@
"react-native-html-to-pdf": "^0.12.0",
"react-native-image-crop-picker": "^0.40.0",
"react-native-image-picker": "^5.6.0",
"react-native-incall-manager": "^4.2.0",
"react-native-keyboard-aware-scroll-view": "^0.9.5",
"react-native-masked-text": "^1.13.0",
"react-native-modal": "^13.0.1",

5
src/App.tsx

@ -2,7 +2,10 @@ import React, { FC, useEffect } from 'react' @@ -2,7 +2,10 @@ import React, { FC, useEffect } from 'react'
import { Provider } from 'react-redux'
import { Navigation } from './modules/root'
import store from './store'
import './services/system/reactron.service'
if (__DEV__) {
require('./services/system/reactron.service')
}
import { ThemeProvider } from './shared/themes'
// import Orientation from 'react-native-orientation-locker'
import { AppState, LogBox } from 'react-native'

18
src/api/calls/requests.interfaces.ts

@ -1,3 +1,5 @@ @@ -1,3 +1,5 @@
import { ICall } from '@/shared'
export interface IStartCallPayload {
targetUserId: number
rtcMessage: any
@ -10,5 +12,19 @@ export interface IAnswerCallPayload { @@ -10,5 +12,19 @@ export interface IAnswerCallPayload {
export interface IIceCandidatePayload {
targetUserId: number
rtcMessage: any
candidates: any[]
}
export interface ICancelCallPayload {
callId: number
}
export interface IFinishCallPayload {
callId: number
}
export interface ICallsListResponse {
items: ICall[]
count: number
}

17
src/api/calls/requests.ts

@ -1,6 +1,8 @@ @@ -1,6 +1,8 @@
import api from '../http.service'
import {
IAnswerCallPayload,
ICancelCallPayload,
IFinishCallPayload,
IIceCandidatePayload,
IStartCallPayload,
} from './requests.interfaces'
@ -16,3 +18,18 @@ export const answerCallReq = (payload: IAnswerCallPayload) => { @@ -16,3 +18,18 @@ export const answerCallReq = (payload: IAnswerCallPayload) => {
export const iceCandidateReq = (payload: IIceCandidatePayload) => {
return api.post('calls/iceCandidate', payload, {}, '')
}
export const cancelCallReq = (payload: ICancelCallPayload) => {
return api.post('calls/cancel', payload, {}, '')
}
export const finishCallReq = (payload: IFinishCallPayload) => {
return api.post('calls/finish', payload, {}, '')
}
export const getCallsListReq = (params: any) => {
return api.get('calls', params, '')
}
export const deleteCallHistoryReq = (callId: number) => {
return api.delete(`calls/history/${callId}`, null, '')
}

13
src/modules/calls/atoms/call-card-info.atom.tsx

@ -7,7 +7,7 @@ import { CallTypesEnum } from '../enums' @@ -7,7 +7,7 @@ import { CallTypesEnum } from '../enums'
interface IProps {
callerFullName: string
dateTime: Date
dateTime: string
callStatus: CallTypesEnum
isMissed: boolean
}
@ -23,16 +23,9 @@ export const CallCardInfo: FC<IProps> = ({ @@ -23,16 +23,9 @@ export const CallCardInfo: FC<IProps> = ({
const callStatuses = {
incoming: 'Вихідний',
outgoing: 'Вхідний',
skipped: 'Пропущений',
}
const date = dateTime
.toISOString()
.toString()
.substr(0, 10)
.replace(/\-/g, '.')
const time = dateTime.toISOString().toString().substr(11, 5)
return (
<View style={styles.wrapper}>
<Txt
@ -49,7 +42,7 @@ export const CallCardInfo: FC<IProps> = ({ @@ -49,7 +42,7 @@ export const CallCardInfo: FC<IProps> = ({
<Txt
style={
styles.infoText
}>{`${callStatuses[callStatus]} ${date} ${time}`}</Txt>
}>{`${callStatuses[callStatus]} ${dateTime}`}</Txt>
<IconComponent
style={[

2
src/modules/calls/components/call-background.component.tsx

@ -92,7 +92,7 @@ const createStyles = (theme: PartialTheme) => @@ -92,7 +92,7 @@ const createStyles = (theme: PartialTheme) =>
left: 0,
},
content: {
paddingBottom: 100,
paddingBottom: 60,
justifyContent: 'flex-start',
alignItems: 'center',
backgroundColor: 'rgba(0,0,0,.3)',

14
src/modules/calls/components/call-row-card.component.tsx

@ -7,18 +7,16 @@ import { CallCardInfo } from '../atoms' @@ -7,18 +7,16 @@ import { CallCardInfo } from '../atoms'
import { CallTypesEnum } from '../enums'
interface IProps {
id: string
callerFullName: string
imageUrl?: string
callStatus: CallTypesEnum
dateTime: Date
dateTime: string
isMissed: boolean
onPressCard: (id: string) => void
onPressCall: (id: string) => void
onPressCard: () => void
onPressCall: () => void
}
export const CallRowCard: FC<IProps> = ({
id,
imageUrl,
callerFullName,
callStatus,
@ -32,7 +30,8 @@ export const CallRowCard: FC<IProps> = ({ @@ -32,7 +30,8 @@ export const CallRowCard: FC<IProps> = ({
return (
<TouchableOpacity
style={styles.container}
onPress={() => onPressCard(id)}>
activeOpacity={1}
onPress={() => onPressCard()}>
<View style={styles.mainContent}>
<Avatar
containerStyle={styles.avatar}
@ -52,7 +51,7 @@ export const CallRowCard: FC<IProps> = ({ @@ -52,7 +51,7 @@ export const CallRowCard: FC<IProps> = ({
<TouchableOpacity
style={styles.callBtn}
onPress={() => onPressCall(id)}>
onPress={() => onPressCall()}>
<IconComponent
name={'phone-1'}
size={$size(23, 22)}
@ -72,6 +71,7 @@ const createStyles = (theme: PartialTheme) => @@ -72,6 +71,7 @@ const createStyles = (theme: PartialTheme) =>
borderBottomWidth: 1,
borderBottomColor: theme.$border,
justifyContent: 'space-between',
backgroundColor: theme.$layoutBg,
},
mainContent: {
flexDirection: 'row',

5
src/modules/calls/configs/ice-servers.config.ts

@ -8,4 +8,9 @@ export const iceServers = [ @@ -8,4 +8,9 @@ export const iceServers = [
{
urls: 'stun:stun2.l.google.com:19302',
},
{
urls: 'turn:relay1.expressturn.com:3478',
username: 'efFBS0EV3YVRQY4HFQ',
credential: 'ssU85viEgPpENkG7',
},
]

6
src/modules/calls/core/accept-call.ts

@ -1,4 +1,3 @@ @@ -1,4 +1,3 @@
import inCallManager from 'react-native-incall-manager'
import { CallDataStore, CallMod } from '../hooks'
import { answerCallReq } from '@/api/calls/requests'
import { RTCSessionDescription } from 'react-native-webrtc'
@ -20,10 +19,9 @@ export class AcceptCall { @@ -20,10 +19,9 @@ export class AcceptCall {
this.setRemoteDescription()
await this.sendAnswer()
this.proccessIceCandidates()
inCallManager.start({ media: 'video' })
// inCallManager.start({ media: 'video' })
} catch (e) {
console.log(e)
}
@ -31,7 +29,7 @@ export class AcceptCall { @@ -31,7 +29,7 @@ export class AcceptCall {
private prepare() {
this.callDataStore.changeMod(CallMod.Speaking)
inCallManager.stopRingtone()
// inCallManager.stopRingtone()
}
private setRemoteDescription() {

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

@ -1,7 +1,7 @@ @@ -1,7 +1,7 @@
import { socketEvents } from '@/shared'
import { CallMod, callDataStoreHelper, useCallDataStore } from '../hooks'
import { RTCSessionDescription } from 'react-native-webrtc'
import inCallManager from 'react-native-incall-manager'
// import inCallManager from 'react-native-incall-manager'
class CallEventsListenter {
constructor() {
@ -21,9 +21,24 @@ class CallEventsListenter { @@ -21,9 +21,24 @@ class CallEventsListenter {
new RTCSessionDescription(data.rtcMessage),
)
useCallDataStore.getState().changeMod(CallMod.Speaking)
inCallManager.stopRingback()
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()

1
src/modules/calls/core/start-call.ts

@ -50,5 +50,6 @@ export class StartCall { @@ -50,5 +50,6 @@ export class StartCall {
title: 'Невідомий користувач',
}
this.getCallFromStore().put(from.title, from.avatarImageUrl)
this.callDataStore.setCallId(data.call.id)
}
}

37
src/modules/calls/core/stop-call.ts

@ -1,8 +1,12 @@ @@ -1,8 +1,12 @@
import inCallManager from 'react-native-incall-manager'
import { CallDataStore } from '../hooks'
import { finishCallReq } from '@/api/calls/requests'
import { CallDataStore, ICallStreamsStore } from '../hooks'
import { MediaStream } from 'react-native-webrtc'
export class StopCall {
constructor(private readonly getCallDataStore: () => CallDataStore) {}
constructor(
private readonly getCallDataStore: () => CallDataStore,
private readonly getCallStreamsStore: () => ICallStreamsStore,
) {}
private get peerConnection() {
return this.getCallDataStore().peerConnection
@ -12,8 +16,31 @@ export class StopCall { @@ -12,8 +16,31 @@ export class StopCall {
return this.getCallDataStore()
}
public stop() {
public async stop() {
await finishCallReq({
callId: this.callDataStore.callId,
}).catch(console.log)
// inCallManager.stop()
this.peerConnection.close()
inCallManager.stop()
this.stopStreams()
}
private stopStreams() {
const streamsStore = this.getCallStreamsStore()
this.stopStream(streamsStore.localStream)
this.stopStream(streamsStore.remoteStream)
streamsStore.setLocalStream(null)
streamsStore.setRemoteStream(null)
}
private stopStream(stream: MediaStream) {
try {
if (!stream) {
return
}
stream.getTracks().forEach(track => track.stop())
} catch (e) {}
}
}

1
src/modules/calls/enums/call-types.enum.ts

@ -1,4 +1,5 @@ @@ -1,4 +1,5 @@
export enum CallTypesEnum {
INCOMING = 'incoming',
OUTGOING = 'outgoing',
SKIP = 'skipped',
}

2
src/modules/calls/hooks/index.ts

@ -1,2 +1,4 @@ @@ -1,2 +1,4 @@
export * from './use-call-data.hook'
export * from './use-call-from.hook'
export * from './use-call-streams.hook'
export * from './use-calls-history.hook'

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

@ -1,13 +1,19 @@ @@ -1,13 +1,19 @@
import { MediaStream, RTCPeerConnection } from 'react-native-webrtc'
import { create } from 'zustand'
import { iceServers } from '../configs'
import { iceCandidateReq } from '@/api/calls/requests'
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 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,
@ -22,21 +28,21 @@ export interface CallDataStore { @@ -22,21 +28,21 @@ export interface CallDataStore {
targetUserId?: number
callId?: number
remoteRTCMessage?: any
remoteStream?: any
icecandidates: any[]
connectedStatus: RTCIceConnectionState
changeMod: (mod: CallMod) => void
startCall: (targetUserId: number) => void
startCall: (targetUserId: number, peerConnection: RTCPeerConnection) => void
incomeCall: (
targetUserId: number,
remoteRTCMessage: any,
callId: number,
peerConnection: RTCPeerConnection,
) => void
setRemoteStream: (stream: any) => void
addIcecanidate: (item: any) => void
cleanIcecandidates: () => void
setConnectedStatus: (connectedStatus: RTCIceConnectionState) => void
setCallId: (callId: number) => void
}
export const useCallDataStore = create<CallDataStore>()(set => ({
@ -53,26 +59,28 @@ export const useCallDataStore = create<CallDataStore>()(set => ({ @@ -53,26 +59,28 @@ export const useCallDataStore = create<CallDataStore>()(set => ({
set({ mod })
},
startCall(targetUserId) {
setCallId(callId: number) {
set({ callId })
},
startCall(targetUserId, peerConnection) {
set({
targetUserId,
peerConnection: createPeerConnection(),
peerConnection,
mod: CallMod.Outgoing,
})
},
incomeCall(targetUserId, remoteRTCMessage, callId) {
incomeCall(targetUserId, remoteRTCMessage, callId, peerConnection) {
set({
targetUserId,
callId,
remoteRTCMessage,
mod: CallMod.Incoming,
peerConnection: createPeerConnection(),
peerConnection,
})
},
setRemoteStream(stream) {
set({ remoteStream: stream })
},
addIcecanidate(item) {
set(prev => ({ icecandidates: [...prev.icecandidates, item] }))
},
@ -87,90 +95,101 @@ export const useCallDataStore = create<CallDataStore>()(set => ({ @@ -87,90 +95,101 @@ 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_' })
useCallDataStore.getState().startCall(targetUserId)
NavigationService.navigate(RouteKey.Call, {})
setTimeout(() => {
// 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()
}, 999)
} catch (e) {
console.log(e)
}
},
stop: () => {
new StopCall(useCallDataStore.getState).stop()
new StopCall(useCallDataStore.getState, callsStreamsCtr).stop()
},
finishCall: () => {
const state = useCallDataStore.getState()
if (
state.peerConnection.iceConnectionState === 'disconnected' ||
state.peerConnection.iceConnectionState === 'failed'
) {
new StopCall(useCallDataStore.getState).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()
},
}
function createPeerConnection() {
async function createPeerConnection() {
const peerConnection = new RTCPeerConnection({
iceServers: iceServers,
})
await initCallsMediaDevices(peerConnection)
peerConnection.addEventListener('track', event => {
try {
const existremoteStream = useCallDataStore.getState().remoteStream
const existremoteStream = callsStreamsCtr().remoteStream
const remoteMediaStream = existremoteStream || new MediaStream()
remoteMediaStream.addTrack(event.track, remoteMediaStream)
useCallDataStore.getState().setRemoteStream(remoteMediaStream)
remoteMediaStream.addTrack(event.track)
callsStreamsCtr().setRemoteStream(remoteMediaStream)
} catch (e) {
console.log(e)
}
})
peerConnection.addEventListener('icecandidate', event => {
if (!event.candidate) {
return
if (event.candidate !== null) {
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 => {
console.log(
'iceconnectionstatechange',
peerConnection.iceConnectionState,
)
console.log(peerConnection.iceConnectionState)
useCallDataStore
.getState()
.setConnectedStatus(peerConnection.iceConnectionState)
if (peerConnection.iceConnectionState === 'closed') {
useCallDataStore.getState().changeMod(CallMod.Finished)
}
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 => {
// You can ignore some candidate errors.
// Connections can still be made even when errors occur.
console.log(event)
})
peerConnection.addEventListener('onicegatheringstatechange', event => {})
return peerConnection
}

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

@ -0,0 +1,47 @@ @@ -0,0 +1,47 @@
import { MediaStream, mediaDevices } from 'react-native-webrtc'
import { create } from 'zustand'
import { callDataStoreHelper } from './use-call-data.hook'
export interface ICallStreamsStore {
localStream: MediaStream
remoteStream: MediaStream
setLocalStream: (localStream: MediaStream) => void
setRemoteStream: (remoteStream: MediaStream) => void
}
const callsStreamStore = create<ICallStreamsStore>()(set => ({
localStream: null,
remoteStream: null,
setLocalStream(localStream) {
set({ localStream })
},
setRemoteStream(remoteStream) {
set({ remoteStream })
},
}))
export const useCallsStream = callsStreamStore
export const callsStreamsCtr = () => callsStreamStore.getState()
export const initCallsMediaDevices = async (peerConnection: any) => {
try {
const stream = await mediaDevices.getUserMedia({
audio: true,
video: {
frameRate: 30,
facingMode: 'user',
},
})
callsStreamsCtr().setLocalStream(stream)
stream.getTracks().forEach(track => {
peerConnection.addTrack(track as any, stream as any)
})
} catch (e) {
console.log('eerror')
}
}

67
src/modules/calls/hooks/use-calls-history.hook.ts

@ -0,0 +1,67 @@ @@ -0,0 +1,67 @@
import { getCallsListReq } from '@/api/calls/requests'
import { CallStatus, ICall, IUser, useFlatList } from '@/shared'
import store from '@/store'
import { selectId } from '@/store/account'
import moment from 'moment'
import { CallTypesEnum } from '../enums'
export interface ICallListItem {
callId: number
date: string
title: string
avatarUrl?: string
type: CallTypesEnum
isMissed: boolean
targetUserId: number
}
export const useCallHistory = () => {
const list = useFlatList<ICallListItem>({
fetchItems: getCallsListReq,
needInit: false,
limit: 10,
serrializatorItems: transformItems,
clearWhenReload: false,
})
function transformItems(items: ICall[]) {
const result: ICallListItem[] = []
const userId = selectId(store.getState())
items.map((it, i) => {
result[i] = transformItem(it, userId)
})
return result
}
function transformItem(it: ICall, userId: number): ICallListItem {
const targetUser = it.users.find(it => it.id !== userId)
const info = targetUser.info
return {
callId: it.id,
title: `${info.firstName} ${info.lastName}`,
date: moment(new Date(it.startAt)).format('DD.MM.YY'),
avatarUrl: info.avatarUrl,
isMissed: it.status !== CallStatus.Finished,
targetUserId: targetUser.id,
type: getType(it, userId),
}
}
function getType(it: ICall, userId: number) {
if (it.status !== CallStatus.Finished) {
if (userId !== it.initiatorUserId) {
return CallTypesEnum.SKIP
}
}
return userId === it.initiatorUserId
? CallTypesEnum.INCOMING
: CallTypesEnum.OUTGOING
}
return {
list,
}
}

4
src/modules/calls/screens/call/atoms/calling.atom.tsx

@ -14,7 +14,7 @@ export const CallingAtom: FC<IProps> = ({ localStream, remoteStream }) => { @@ -14,7 +14,7 @@ export const CallingAtom: FC<IProps> = ({ localStream, remoteStream }) => {
flex: 1,
backgroundColor: '#000',
}}>
{/* {localStream ? (
{localStream ? (
<RTCView
objectFit={'cover'}
style={{
@ -24,7 +24,7 @@ export const CallingAtom: FC<IProps> = ({ localStream, remoteStream }) => { @@ -24,7 +24,7 @@ export const CallingAtom: FC<IProps> = ({ localStream, remoteStream }) => {
}}
streamURL={localStream.toURL()}
/>
) : null} */}
) : null}
{remoteStream ? (
<RTCView
objectFit={'cover'}

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

@ -1,45 +1,30 @@ @@ -1,45 +1,30 @@
import React, { useEffect, useRef, useState } from 'react'
import { CallingAtom, OutgoingcallAtom } from './atoms'
import { Button, useSocketListener } from '@/shared'
import { mediaDevices, RTCSessionDescription } from 'react-native-webrtc'
import { startCallReq } from '@/api/calls/requests'
import { Button } from '@/shared'
import {
CallMod,
callDataStoreHelper,
useCallDataStore,
useCallFromStore,
useCallsStream,
} from '../../hooks'
import { CallBackground, CallBtn } from '../../components'
import { Dimensions, Modal, StatusBar, StyleSheet, View } from 'react-native'
import {
Alert,
Dimensions,
Modal,
StatusBar,
StyleSheet,
View,
} from 'react-native'
export const CallScreen = () => {
const mod = useCallDataStore(s => s.mod)
const remoteStream = useCallDataStore(s => s.remoteStream)
const [localStream, setlocalStream] = useState(null)
const { remoteStream, localStream } = useCallsStream()
const { title, avatarImageUrl } = useCallFromStore()
const connectedStatus = useCallDataStore(s => s.connectedStatus)
const initMediaDevices = async () => {
const stream = await mediaDevices.getUserMedia({
audio: true,
video: {
frameRate: 30,
facingMode: 'user',
},
})
setlocalStream(stream)
stream.getTracks().forEach(track => {
callDataStoreHelper
.peerConnection()
.addTrack(track as any, stream as any)
})
}
useEffect(() => {
initMediaDevices()
}, [])
const cancelCall = () => {
const stopCall = () => {
callDataStoreHelper.stop()
}
@ -62,7 +47,7 @@ export const CallScreen = () => { @@ -62,7 +47,7 @@ export const CallScreen = () => {
iconName="phone-2"
bgColor="#DE253B"
isAnimated={false}
onPress={() => callDataStoreHelper.stop()}
onPress={stopCall}
/>
</View>
</CallBackground>
@ -70,7 +55,7 @@ export const CallScreen = () => { @@ -70,7 +55,7 @@ export const CallScreen = () => {
[CallMod.Outgoing]: (
<OutgoingcallAtom
title={title}
onPressCancel={cancelCall}
onPressCancel={callDataStoreHelper.cancel}
avatarImageUrl={avatarImageUrl}
/>
),
@ -103,6 +88,9 @@ export const CallScreen = () => { @@ -103,6 +88,9 @@ export const CallScreen = () => {
height: Dimensions.get('screen').height,
}}>
{templates[mod]}
{/* <Txt style={{ color: 'red', fontSize: 50 }}>
{mod} {connectedStatus}
</Txt> */}
</Modal>
<StatusBar hidden />
</>

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

@ -1,4 +1,4 @@ @@ -1,4 +1,4 @@
import { getTheme, SquareButton } from '@/shared'
import { getTheme, RouteKey, SquareButton, useNav } from '@/shared'
import { useTheme } from '@/shared/hooks/use-theme.hook'
import { PartialTheme } from '@/shared/themes/interfaces'
import React, { FC } from 'react'
@ -6,21 +6,38 @@ import { StyleSheet } from 'react-native' @@ -6,21 +6,38 @@ import { StyleSheet } from 'react-native'
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'
interface IProps {
id: string
id: number
callerFullName: string
imageUrl?: string
callStatus: CallTypesEnum
dateTime: Date
dateTime: string
isMissed: boolean
onDelete: (id: string) => void
onPressCard: (id: string) => void
onPressCall: (id: string) => void
onDeleted?: () => void
targetUserId: number
}
export const CallSwipableRowCardSmart: FC<IProps> = props => {
const { theme } = useTheme(createStyles)
const nav = useNav()
const handlePressDelete = async () => {
await deleteCallHistoryReq(props.id)
if (props.onDeleted) {
props.onDeleted()
}
}
const handlePressCard = () => {
nav.navigate(RouteKey.ContactDetail, { contactId: props.id })
}
const handlePressCall = () => {
callDataStoreHelper.processStart(props.targetUserId)
}
const btnToRender = () => (
<SquareButton
@ -28,13 +45,17 @@ export const CallSwipableRowCardSmart: FC<IProps> = props => { @@ -28,13 +45,17 @@ export const CallSwipableRowCardSmart: FC<IProps> = props => {
bgColor={theme.calls.deleteBtn.$bg}
txtColor={theme.calls.deleteBtn.$text}
iconName={'bin'}
onPress={() => props.onDelete(props.id)}
onPress={handlePressDelete}
/>
)
return (
<Swipeable overshootLeft={false} renderRightActions={btnToRender}>
<CallRowCard {...props} />
<CallRowCard
{...props}
onPressCall={handlePressCall}
onPressCard={handlePressCard}
/>
</Swipeable>
)
}

113
src/modules/calls/smart-components/calls-list.smart-component.tsx

@ -1,64 +1,91 @@ @@ -1,64 +1,91 @@
import { $size, Txt } from '@/shared'
import { $size, Loading, NotFound, Txt } from '@/shared'
import { SearchForm } from '@/shared/components/forms'
import { useTheme } from '@/shared/hooks/use-theme.hook'
import { PartialTheme } from '@/shared/themes/interfaces'
import React, { FC, useState } from 'react'
import { ScrollView, StyleSheet } from 'react-native'
import { FlatList } from 'react-native-gesture-handler'
import React, { FC, useCallback, useEffect, useRef, useState } from 'react'
import { StyleSheet, FlatList } from 'react-native'
import { CallSwipableRowCardSmart } from '.'
import { getCallsConfig } from '../configs/calls.config'
import { ICallListItem, useCallHistory } from '../hooks'
export const CallSmartList: FC = () => {
const { styles, theme } = useTheme(createStyles)
const { styles } = useTheme(createStyles)
const [searchString, setSearchString] = useState<string>()
const { list } = useCallHistory()
const { isLoading, isLoadingNext } = list
const timmer = useRef(null)
useEffect(() => {
if (searchString !== undefined) {
clearTimeout(timmer.current)
const callsData = getCallsConfig()
timmer.current = setTimeout(() => {
list.setLoadParams({ searchString })
}, 400)
const itemToRender = ({ item }) => (
return () => {
clearTimeout(timmer.current)
}
}
}, [searchString])
const itemToRender = ({ item }: { item: ICallListItem }) => (
<CallSwipableRowCardSmart
id={item.id}
callStatus={item.callType}
imageUrl={null}
callerFullName={`${item.firstName} ${item.lastName}`}
dateTime={item.dateTime}
id={item.callId}
callStatus={item.type}
imageUrl={item.avatarUrl}
callerFullName={item.title}
dateTime={item.date}
isMissed={item.isMissed}
onDelete={() => {}}
onPressCard={() => {}}
onPressCall={() => {}}
onDeleted={list.resetFlatList}
targetUserId={item.targetUserId}
/>
)
const keyExtractor = useCallback(
(item: ICallListItem) => String(item.callId),
[],
)
return (
<ScrollView>
<Txt
style={{
color: theme.$textPrimary,
textAlign: 'center',
fontWeight: '500',
alignSelf: 'center',
}}>
Буде реалізовано в наступній версії додатку.
</Txt>
{/* <SearchForm
placeholder="Знайти виклик"
searchValue={searchString}
onChange={setSearchString}
/>
<FlatList
style={styles.listContainer}
data={callsData}
renderItem={itemToRender}
/> */}
</ScrollView>
<FlatList
style={styles.listContainer}
contentContainerStyle={styles.containerInner}
data={list.items}
renderItem={itemToRender}
showsVerticalScrollIndicator={false}
showsHorizontalScrollIndicator={false}
initialNumToRender={10}
onEndReached={list.loadMore}
keyExtractor={keyExtractor}
refreshing={!searchString && list.isLoading}
onRefresh={list.resetFlatList}
ListFooterComponent={() => {
if (!isLoading && isLoadingNext) {
return <Loading />
}
if (isLoading) {
return null
}
if (list.isEmpty) {
return <NotFound text="Виклики не знайдені" />
}
}}
ListHeaderComponent={
<SearchForm
placeholder="Знайти виклик"
searchValue={searchString}
onChange={setSearchString}
/>
}
/>
)
}
const createStyles = (theme: PartialTheme) =>
const createStyles = () =>
StyleSheet.create({
listContainer: {
marginBottom: $size(25, 23),
// marginBottom: $size(25, 23),
flex: 1,
},
containerInner: {
paddingBottom: 20,
},
})

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

@ -13,7 +13,7 @@ import { @@ -13,7 +13,7 @@ import {
useCallDataStore,
useCallFromStore,
} from '../hooks'
import inCallManager from 'react-native-incall-manager'
// import inCallManager from 'react-native-incall-manager'
import { PartialTheme } from '@/shared/themes/interfaces'
import { CallBackground, CallBtn } from '../components'
import { NavigationService } from '@/services/system'
@ -28,19 +28,18 @@ export const IncomingCallWidget = () => { @@ -28,19 +28,18 @@ export const IncomingCallWidget = () => {
'call/new',
data => {
try {
inCallManager.startRingtone(
'_DEFAULT_',
[1000, 400, 300],
null,
30,
)
// inCallManager.startRingtone(
// '_DEFAULT_',
// [1000, 400, 300],
// null,
// 30,
// )
setVisible(true)
useCallFromStore
.getState()
.put(data.from.title, data.from.avatarImageUrl)
useCallDataStore
.getState()
.incomeCall(data.callerId, data.rtcMessage, data.callId)
callDataStoreHelper.proccesIncome(data)
} catch (e) {
console.log(e)
}
@ -48,21 +47,25 @@ export const IncomingCallWidget = () => { @@ -48,21 +47,25 @@ export const IncomingCallWidget = () => {
[setVisible],
)
useSocketListener('call/canceled', data => {
setVisible(false)
callDataStoreHelper.stop()
})
useSocketListener('call/ICEcandidate', data => {
try {
const message = data.rtcMessage
const candidates = data.candidates
const peerConnection = callDataStoreHelper.peerConnection()
const candidate = new RTCIceCandidate({
candidate: message.candidate,
sdpMid: message.id,
sdpMLineIndex: message.label,
candidates.map(it => {
const item = new RTCIceCandidate(it)
if (peerConnection) {
peerConnection.addIceCandidate(item)
} else {
useCallDataStore.getState().addIcecanidate(item)
}
})
if (peerConnection) {
peerConnection.addIceCandidate(candidate)
} else {
useCallDataStore.getState().addIcecanidate(candidate)
}
} catch (e) {
console.log(e)
}
@ -70,12 +73,10 @@ export const IncomingCallWidget = () => { @@ -70,12 +73,10 @@ export const IncomingCallWidget = () => {
const decline = () => {}
const accept = () => {
inCallManager.stopRingtone()
callDataStoreHelper.processAccept()
setTimeout(() => {
NavigationService.navigate(RouteKey.Call, {})
}, 1000)
const accept = async () => {
// inCallManager.stopRingtone()
await callDataStoreHelper.processAccept()
NavigationService.navigate(RouteKey.Call, {})
setVisible(false)
}

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

@ -21,6 +21,7 @@ import { selectId } from '@/store/account' @@ -21,6 +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'
interface IProps extends IRouteParams {
route: {
@ -124,9 +125,7 @@ export const ContactDetailScreen: FC<IProps> = ({ navigation, route }) => { @@ -124,9 +125,7 @@ export const ContactDetailScreen: FC<IProps> = ({ navigation, route }) => {
<ContactsSpeakings
contactId={contactId}
onCall={() =>
Alert.alert(
'ця функція буде реалізована в наступній версії додатку',
)
callDataStoreHelper.processStart(contact.userId)
}
onMessage={onPressMessage}
disabled={contact?.userId === accountId}

17
src/modules/contacts/screens/contacts.screen.tsx

@ -1,21 +1,18 @@ @@ -1,21 +1,18 @@
import { $size, IRouteParams, ScreenLayout, SwitchButtons } from '@/shared'
import React, { FC, useEffect, useState } from 'react'
import { $size, ScreenLayout, SwitchButtons } from '@/shared'
import React, { FC, useState } from 'react'
import { StyleSheet } from 'react-native'
import { ContactsSmartList } from '../smart-component'
import { TabView, SceneMap } from 'react-native-tab-view'
import { CallSmartList } from '@/modules/calls/smart-components/calls-list.smart-component'
import { PartialTheme } from '@/shared/themes/interfaces'
import { useTheme } from '@/shared/hooks/use-theme.hook'
import { callDataStoreHelper } from '@/modules/calls/hooks'
interface IProps extends IRouteParams {}
type TRoutes = {
key: string
title: string
}[]
export const ContactScreen: FC<IProps> = () => {
export const ContactScreen: FC = () => {
const { styles } = useTheme(createStyles)
const [index, setIndex] = useState<number>(0)
@ -26,15 +23,9 @@ export const ContactScreen: FC<IProps> = () => { @@ -26,15 +23,9 @@ export const ContactScreen: FC<IProps> = () => {
const renderScene = SceneMap({
contacts: ContactsSmartList,
calls: () => <CallSmartList />,
calls: CallSmartList,
})
useEffect(() => {
setTimeout(() => {
callDataStoreHelper.processStart(40)
}, 1000)
}, [])
const renderTabBar = () => (
<SwitchButtons
style={styles.switchContainer}

1
src/modules/root/index.tsx

@ -54,7 +54,6 @@ export const Navigation: FC = () => { @@ -54,7 +54,6 @@ export const Navigation: FC = () => {
// const status = 'online'
useSharedFiles()
useAppSocketListener()
useAppBadge()

1
src/services/system/real-time.service.ts

@ -122,6 +122,7 @@ export class SocketIo { @@ -122,6 +122,7 @@ export class SocketIo {
this._onSocketSendEvent('call/answered')
this._onSocketSendEvent('call/new')
this._onSocketSendEvent('call/ICEcandidate')
this._onSocketSendEvent('call/canceled')
this._on('error/join-user', async () => {
await authService.refreshSession()

6
src/shared/enums/call-status.enum.ts

@ -0,0 +1,6 @@ @@ -0,0 +1,6 @@
export enum CallStatus {
New = 'n',
InProccess = 'i',
Canceled = 'c',
Finished = 'f',
}

27
src/shared/enums/index.ts

@ -1,18 +1,19 @@ @@ -1,18 +1,19 @@
export * from './route-key.enum'
export * from './navigation-module-key.enum'
export * from './exception-keys.enum'
export * from './storage-keys.enum'
export * from './user.enum'
export * from './taxonomies.enum'
export * from './task-status.enum'
export * from './task-actions.enum'
export * from './actions-queue-type.enum'
export * from './btns-type.enum'
export * from './call-status.enum'
export * from './chat-bg.enum'
export * from './task-events.enum'
export * from './notification.enum'
export * from './chat.enums'
export * from './btns-type.enum'
export * from './actions-queue-type.enum'
export * from './entity-type.enum'
export * from './permissions.enum'
export * from './error-message.enum'
export * from './exception-keys.enum'
export * from './file-type.enum'
export * from './navigation-module-key.enum'
export * from './notification.enum'
export * from './permissions.enum'
export * from './route-key.enum'
export * from './storage-keys.enum'
export * from './task-actions.enum'
export * from './task-events.enum'
export * from './task-status.enum'
export * from './taxonomies.enum'
export * from './user.enum'

5
src/shared/events/index.ts

@ -235,7 +235,10 @@ export type SocketEvents = { @@ -235,7 +235,10 @@ export type SocketEvents = {
rtcMessage: any
}
'call/ICEcandidate': {
rtcMessage: any
candidates: any[]
}
'call/canceled': {
callId: number
}
}

17
src/shared/interfaces/call.inteface.ts

@ -0,0 +1,17 @@ @@ -0,0 +1,17 @@
import { CallStatus } from '../enums'
import { IUser } from './user.interfaces'
export interface ICall {
id: number
usersIds: number[]
initiatorUserId: number
finishedAt: string
startAt: string
status: CallStatus
createdAt: string
updatedAt: string
users?: IUser[]
}

27
src/shared/interfaces/index.ts

@ -1,15 +1,18 @@ @@ -1,15 +1,18 @@
export * from './routing.interfaces'
export * from './token-pair.interfaces'
export * from './user.interfaces'
export * from './styles.interfaces'
export * from './taxonomy.interfaces'
export * from './contact.interfaces'
export * from './pagination.interfaces'
export * from './tasks.interfaces'
export * from './call.inteface'
export * from './chats.interfaces'
export * from './comments.interfaces'
export * from './configs.interfaces'
export * from './contact.interfaces'
export * from './drawer.interface'
export * from './entities'
export * from './factories.interfaces'
export * from './options.interfaces'
export * from './notification.interfaces'
export * from './chats.interfaces'
export * from './media.interfaces'
export * from './configs.interfaces'
export * from './notification.interfaces'
export * from './options.interfaces'
export * from './pagination.interfaces'
export * from './routing.interfaces'
export * from './styles.interfaces'
export * from './tasks.interfaces'
export * from './taxonomy.interfaces'
export * from './token-pair.interfaces'
export * from './user.interfaces'

3
tsconfig.json

@ -63,7 +63,8 @@ @@ -63,7 +63,8 @@
"node_modules",
"babel.config.js",
"metro.config.js",
"jest.config.js"
"jest.config.js",
"taskme"
]
}

Loading…
Cancel
Save