Browse Source

FEATURE | Calls

master
Vitalik 9 months ago
parent
commit
0df2652ce2
  1. 2
      src/api/calls/requests.ts
  2. 28
      src/modules/calls/components/call-background.component.tsx
  3. 64
      src/modules/calls/core/accept-call.ts
  4. 29
      src/modules/calls/core/call-events-listener.ts
  5. 2
      src/modules/calls/core/index.ts
  6. 54
      src/modules/calls/core/start-call.ts
  7. 19
      src/modules/calls/core/stop-call.ts
  8. 109
      src/modules/calls/hooks/use-call-data.hook.ts
  9. 2
      src/modules/calls/hooks/use-call-from.hook.ts
  10. 10
      src/modules/calls/screens/call/atoms/calling.atom.tsx
  11. 32
      src/modules/calls/screens/call/atoms/income-call.atom.tsx
  12. 50
      src/modules/calls/screens/call/atoms/outgoing-call.atom.tsx
  13. 1
      src/modules/calls/screens/call/hooks/use-call-sockets.hook.ts
  14. 153
      src/modules/calls/screens/call/index.tsx
  15. 9
      src/modules/contacts/screens/contacts.screen.tsx
  16. 1
      src/modules/root/index.tsx
  17. 3
      src/modules/root/navigation-groups/tab-bar.group.tsx
  18. 4
      src/services/system/navigation.service.ts

2
src/api/calls/requests.ts

@ -6,7 +6,7 @@ import { @@ -6,7 +6,7 @@ import {
} from './requests.interfaces'
export const startCallReq = (payload: IStartCallPayload) => {
return api.post('calls/start', payload, {}, '')
return api.post<any>('calls/start', payload, {}, '')
}
export const answerCallReq = (payload: IAnswerCallPayload) => {

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

@ -8,6 +8,10 @@ interface IProps { @@ -8,6 +8,10 @@ interface IProps {
avatarImageUrl: string
title: string
subtitle?: string
fullComponent?: JSX.Element
showAvatar?: boolean
needOverlay?: boolean
}
export const CallBackground: FC<PropsWithChildren<IProps>> = ({
@ -15,11 +19,16 @@ export const CallBackground: FC<PropsWithChildren<IProps>> = ({ @@ -15,11 +19,16 @@ export const CallBackground: FC<PropsWithChildren<IProps>> = ({
title,
subtitle,
children,
fullComponent,
showAvatar = true,
needOverlay = true,
}) => {
const { styles } = useTheme(createStyles)
const renderImg = () => {
if (avatarImageUrl) {
if (fullComponent) {
return <View style={styles.fullCimponent}>{fullComponent}</View>
} else if (avatarImageUrl) {
return (
<Image
source={{
@ -31,18 +40,22 @@ export const CallBackground: FC<PropsWithChildren<IProps>> = ({ @@ -31,18 +40,22 @@ export const CallBackground: FC<PropsWithChildren<IProps>> = ({
)
}
}
console.log('avatarImageUrl', avatarImageUrl)
return (
<View style={styles.container}>
{renderImg()}
<View style={styles.txtPreviewContent}>
{avatarImageUrl ? (
{showAvatar && !avatarImageUrl ? (
<View style={styles.txtPreviewBlock}>
<Txt style={styles.txtPreview}>{title[0]}</Txt>
</View>
) : null}
</View>
<View style={styles.content}>
<View
style={[
styles.content,
{ backgroundColor: needOverlay ? 'rgba(0,0,0,.3)' : null },
]}>
<Txt style={styles.title}>{title}</Txt>
{subtitle ? (
<Txt style={styles.subtitle}>{subtitle}</Txt>
@ -71,6 +84,13 @@ const createStyles = (theme: PartialTheme) => @@ -71,6 +84,13 @@ const createStyles = (theme: PartialTheme) =>
width: '100%',
left: 0,
},
fullCimponent: {
position: 'absolute',
top: 0,
height: '100%',
width: '100%',
left: 0,
},
content: {
paddingBottom: 100,
justifyContent: 'flex-start',

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

@ -0,0 +1,64 @@ @@ -0,0 +1,64 @@
import inCallManager from 'react-native-incall-manager'
import { CallDataStore, CallMod } from '../hooks'
import { answerCallReq } from '@/api/calls/requests'
import { RTCSessionDescription } from 'react-native-webrtc'
export class AcceptCall {
constructor(private readonly getCallDataStore: () => CallDataStore) {}
private get peerConnection() {
return this.getCallDataStore().peerConnection
}
private get callDataStore() {
return this.getCallDataStore()
}
public async accept() {
try {
this.prepare()
this.setRemoteDescription()
await this.sendAnswer()
this.proccessIceCandidates()
inCallManager.start({ media: 'video' })
} catch (e) {
console.log(e)
}
}
private prepare() {
this.callDataStore.changeMod(CallMod.Speaking)
inCallManager.stopRingtone()
}
private setRemoteDescription() {
this.peerConnection.setRemoteDescription(
new RTCSessionDescription(this.callDataStore.remoteRTCMessage),
)
}
private async sendAnswer() {
const sessionDescription = await this.peerConnection.createAnswer()
await this.peerConnection.setLocalDescription(sessionDescription)
await answerCallReq({
callId: this.callDataStore.callId,
rtcMessage: sessionDescription,
})
}
private proccessIceCandidates() {
const existIcecandidates = this.callDataStore.icecandidates
if (existIcecandidates.length) {
existIcecandidates.map(candidate =>
this.peerConnection.addIceCandidate(candidate),
)
this.callDataStore.cleanIcecandidates()
}
}
}

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

@ -0,0 +1,29 @@ @@ -0,0 +1,29 @@
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)
inCallManager.stopRingback()
})
}
}
export const callEventsListener = new CallEventsListenter()

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

@ -0,0 +1,2 @@ @@ -0,0 +1,2 @@
export * from './accept-call'
export * from './start-call'

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

@ -0,0 +1,54 @@ @@ -0,0 +1,54 @@
import { startCallReq } from '@/api/calls/requests'
import { CallDataStore, CallFromStore } from '../hooks'
export class StartCall {
constructor(
private readonly getCallDataStore: () => CallDataStore,
private readonly getCallFromStore: () => CallFromStore,
) {}
private get callDataStore() {
return this.getCallDataStore()
}
private get peerConnection() {
return this.callDataStore.peerConnection
}
public async start() {
const sessionDescription = await this.createOffer()
await this.peerConnection.setLocalDescription(sessionDescription)
const data = await this.sendStartCallReq(sessionDescription)
this.proccessResponse(data)
}
private async createOffer() {
const sessionDescription = await this.peerConnection.createOffer({
mandatory: {
OfferToReceiveAudio: true,
OfferToReceiveVideo: true,
VoiceActivityDetection: true,
},
})
return sessionDescription
}
private async sendStartCallReq(sessionDescription: any) {
const result = await startCallReq({
targetUserId: this.callDataStore.targetUserId,
rtcMessage: sessionDescription,
})
return result.data
}
private async proccessResponse(data: any) {
const from = data.from
? data.from
: {
title: 'Невідомий користувач',
}
this.getCallFromStore().put(from.title, from.avatarImageUrl)
}
}

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

@ -0,0 +1,19 @@ @@ -0,0 +1,19 @@
import inCallManager from 'react-native-incall-manager'
import { CallDataStore } from '../hooks'
export class StopCall {
constructor(private readonly getCallDataStore: () => CallDataStore) {}
private get peerConnection() {
return this.getCallDataStore().peerConnection
}
private get callDataStore() {
return this.getCallDataStore()
}
public stop() {
this.peerConnection.close()
inCallManager.stop()
}
}

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

@ -1,22 +1,22 @@ @@ -1,22 +1,22 @@
import {
MediaStream,
RTCPeerConnection,
RTCSessionDescription,
} from 'react-native-webrtc'
import { MediaStream, RTCPeerConnection } from 'react-native-webrtc'
import { create } from 'zustand'
import { iceServers } from '../configs'
import inCallManager from 'react-native-incall-manager'
import { answerCallReq, iceCandidateReq } from '@/api/calls/requests'
import { Alert } from 'react-native'
import { 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'
export enum CallMod {
Temporary,
Incoming,
Outgoing,
Speaking,
Finished,
Incoming,
}
interface CallDataStore {
export interface CallDataStore {
peerConnection: RTCPeerConnection
mod: CallMod
targetUserId?: number
@ -24,6 +24,7 @@ interface CallDataStore { @@ -24,6 +24,7 @@ interface CallDataStore {
remoteRTCMessage?: any
remoteStream?: any
icecandidates: any[]
connectedStatus: RTCIceConnectionState
changeMod: (mod: CallMod) => void
startCall: (targetUserId: number) => void
@ -35,6 +36,7 @@ interface CallDataStore { @@ -35,6 +36,7 @@ interface CallDataStore {
setRemoteStream: (stream: any) => void
addIcecanidate: (item: any) => void
cleanIcecandidates: () => void
setConnectedStatus: (connectedStatus: RTCIceConnectionState) => void
}
export const useCallDataStore = create<CallDataStore>()(set => ({
@ -45,6 +47,8 @@ export const useCallDataStore = create<CallDataStore>()(set => ({ @@ -45,6 +47,8 @@ export const useCallDataStore = create<CallDataStore>()(set => ({
remoteRTCMessage: null,
icecandidates: [],
connectedStatus: null,
changeMod: mod => {
set({ mod })
},
@ -75,48 +79,42 @@ export const useCallDataStore = create<CallDataStore>()(set => ({ @@ -75,48 +79,42 @@ export const useCallDataStore = create<CallDataStore>()(set => ({
cleanIcecandidates() {
set({ icecandidates: [] })
},
setConnectedStatus(connectedStatus) {
set({ connectedStatus })
},
}))
export const callDataStoreHelper = {
peerConnection: () => useCallDataStore.getState().peerConnection,
processAccept: async () => {
try {
useCallDataStore.getState().changeMod(CallMod.Speaking)
inCallManager.stopRingtone()
callDataStoreHelper
.peerConnection()
.setRemoteDescription(
new RTCSessionDescription(
useCallDataStore.getState().remoteRTCMessage,
),
)
const sessionDescription = await callDataStoreHelper
.peerConnection()
.createAnswer()
await callDataStoreHelper
.peerConnection()
.setLocalDescription(sessionDescription)
await answerCallReq({
callId: useCallDataStore.getState().callId,
rtcMessage: sessionDescription,
})
const existIcecandidates = useCallDataStore.getState().icecandidates
if (existIcecandidates.length) {
existIcecandidates.map(candidate =>
callDataStoreHelper
.peerConnection()
.addIceCandidate(candidate),
)
useCallDataStore.getState().cleanIcecandidates()
}
} catch (e) {
console.log(e)
new AcceptCall(useCallDataStore.getState).accept()
},
processStart: async (targetUserId: number) => {
InCallManager.start({ media: 'audio', ringback: '_BUNDLE_' })
useCallDataStore.getState().startCall(targetUserId)
NavigationService.navigate(RouteKey.Call, {})
setTimeout(() => {
new StartCall(
useCallDataStore.getState,
useCallFromStore.getState,
).start()
}, 999)
},
stop: () => {
new StopCall(useCallDataStore.getState).stop()
},
finishCall: () => {
const state = useCallDataStore.getState()
if (
state.peerConnection.iceConnectionState === 'disconnected' ||
state.peerConnection.iceConnectionState === 'failed'
) {
new StopCall(useCallDataStore.getState).stop()
}
useCallDataStore.setState(useCallDataStore.getInitialState())
NavigationService.goBack()
},
}
@ -127,12 +125,10 @@ function createPeerConnection() { @@ -127,12 +125,10 @@ function createPeerConnection() {
peerConnection.addEventListener('track', event => {
try {
console.log('accepted track', event.track.id)
const existremoteStream = useCallDataStore.getState().remoteStream
const remoteMediaStream = existremoteStream || new MediaStream()
remoteMediaStream.addTrack(event.track, remoteMediaStream)
useCallDataStore.getState().setRemoteStream(remoteMediaStream)
Alert.alert('track cyka')
} catch (e) {
console.log(e)
}
@ -154,7 +150,20 @@ function createPeerConnection() { @@ -154,7 +150,20 @@ function createPeerConnection() {
})
peerConnection.addEventListener('iceconnectionstatechange', event => {
console.log(peerConnection.iceConnectionState)
console.log(
'iceconnectionstatechange',
peerConnection.iceConnectionState,
)
useCallDataStore
.getState()
.setConnectedStatus(peerConnection.iceConnectionState)
if (
peerConnection.iceConnectionState === 'closed' ||
peerConnection.iceConnectionState === 'disconnected'
) {
useCallDataStore.getState().changeMod(CallMod.Finished)
}
})
peerConnection.addEventListener('icecandidateerror', event => {

2
src/modules/calls/hooks/use-call-from.hook.ts

@ -1,6 +1,6 @@ @@ -1,6 +1,6 @@
import { create } from 'zustand'
interface CallFromStore {
export interface CallFromStore {
title: string
avatarImageUrl?: string

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

@ -12,11 +12,9 @@ export const CallingAtom: FC<IProps> = ({ localStream, remoteStream }) => { @@ -12,11 +12,9 @@ export const CallingAtom: FC<IProps> = ({ localStream, remoteStream }) => {
<View
style={{
flex: 1,
backgroundColor: '#050A0E',
paddingHorizontal: 12,
paddingVertical: 12,
backgroundColor: '#000',
}}>
{localStream ? (
{/* {localStream ? (
<RTCView
objectFit={'cover'}
style={{
@ -26,14 +24,12 @@ export const CallingAtom: FC<IProps> = ({ localStream, remoteStream }) => { @@ -26,14 +24,12 @@ export const CallingAtom: FC<IProps> = ({ localStream, remoteStream }) => {
}}
streamURL={localStream.toURL()}
/>
) : null}
) : null} */}
{remoteStream ? (
<RTCView
objectFit={'cover'}
style={{
flex: 1,
backgroundColor: 'blue',
marginTop: 8,
}}
streamURL={remoteStream.toURL()}
/>

32
src/modules/calls/screens/call/atoms/income-call.atom.tsx

@ -1,17 +1,31 @@ @@ -1,17 +1,31 @@
import { CallBackground, CallBtn } from '@/modules/calls/components'
import { Button, Txt } from '@/shared'
import React, { FC } from 'react'
import { StyleSheet, View } from 'react-native'
interface IProps {
onPressAnswer: () => void
title: string
avatarImageUrl: string
}
export const IncomingCallAtom: FC<IProps> = ({ onPressAnswer }) => {
export const IncomingCallAtom: FC<IProps> = ({
onPressAnswer,
title,
avatarImageUrl,
}) => {
return (
<View style={styles.container}>
<Txt>Incoming call</Txt>
<Button title="Answer" onPress={onPressAnswer} />
</View>
<CallBackground
title={title}
avatarImageUrl={avatarImageUrl}
subtitle="Телефоннє">
<View style={styles.row}>
<CallBtn
iconName="phone-2"
bgColor="#38B362"
onPress={onPressAnswer}
/>
</View>
</CallBackground>
)
}
@ -23,4 +37,10 @@ const styles = StyleSheet.create({ @@ -23,4 +37,10 @@ const styles = StyleSheet.create({
paddingTop: 100,
paddingHorizontal: 20,
},
row: {
flexDirection: 'row',
justifyContent: 'space-around',
alignItems: 'center',
width: '100%',
},
})

50
src/modules/calls/screens/call/atoms/outgoing-call.atom.tsx

@ -1,11 +1,47 @@ @@ -1,11 +1,47 @@
import { Txt } from '@/shared'
import React from 'react'
import { View } from 'react-native'
import { CallBackground, CallBtn } from '@/modules/calls/components'
import { Button, Txt } from '@/shared'
import React, { FC } from 'react'
import { StyleSheet, View } from 'react-native'
export const OutgoingCallAtom = () => {
interface IProps {
onPressCancel: () => void
title: string
avatarImageUrl: string
}
export const OutgoingcallAtom: FC<IProps> = ({
onPressCancel,
title,
avatarImageUrl,
}) => {
return (
<View>
<Txt>Outgoing call</Txt>
</View>
<CallBackground
title={title}
avatarImageUrl={avatarImageUrl}
subtitle="Виклик">
<View style={styles.row}>
<CallBtn
iconName="phone-2"
bgColor="#DE253B"
onPress={onPressCancel}
isAnimated={false}
/>
</View>
</CallBackground>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
paddingTop: 100,
paddingHorizontal: 20,
},
row: {
flexDirection: 'row',
justifyContent: 'space-around',
alignItems: 'center',
width: '100%',
},
})

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

@ -1 +0,0 @@ @@ -1 +0,0 @@
export const useCallSockets = () => {}

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

@ -1,31 +1,23 @@ @@ -1,31 +1,23 @@
import React, { useEffect, useRef, useState } from 'react'
import { CallingAtom, OutgoingCallAtom } from './atoms'
import { useSocketListener } from '@/shared'
import { CallingAtom, OutgoingcallAtom } from './atoms'
import { Button, useSocketListener } from '@/shared'
import { mediaDevices, RTCSessionDescription } from 'react-native-webrtc'
import { startCallReq } from '@/api/calls/requests'
import { CallMod, callDataStoreHelper, useCallDataStore } from '../../hooks'
import {
CallMod,
callDataStoreHelper,
useCallDataStore,
useCallFromStore,
} from '../../hooks'
import { CallBackground, CallBtn } from '../../components'
import { Dimensions, Modal, StatusBar, StyleSheet, View } from 'react-native'
export const CallScreen = () => {
const mod = useCallDataStore(s => s.mod)
const changeMod = useCallDataStore(s => s.changeMod)
const remoteStream = useCallDataStore(s => s.remoteStream)
const [localStream, setlocalStream] = useState(null)
console.log('remoteStream', remoteStream)
useSocketListener(
'call/answered',
data => {
console.log('ansewerd', data.rtcMessage)
callDataStoreHelper
.peerConnection()
.setRemoteDescription(
new RTCSessionDescription(data.rtcMessage),
)
changeMod(CallMod.Speaking)
},
[],
)
const { title, avatarImageUrl } = useCallFromStore()
const connectedStatus = useCallDataStore(s => s.connectedStatus)
const initMediaDevices = async () => {
const stream = await mediaDevices.getUserMedia({
@ -37,66 +29,99 @@ export const CallScreen = () => { @@ -37,66 +29,99 @@ export const CallScreen = () => {
})
setlocalStream(stream)
stream.getTracks().forEach(track => {
console.log('sendedn track', track.id)
callDataStoreHelper
.peerConnection()
.addTrack(track as any, stream as any)
console.log('Track was sent')
})
}
useEffect(() => {
initMediaDevices()
if (!mod) {
useCallDataStore.getState().startCall(40)
setTimeout(() => {
processCall()
}, 1000)
}
}, [])
// useEffect(() => {
// if (mod === CallMod.Speaking) {
// initMediaDevices()
// setTimeout(() => {
// initMediaDevices()
// }, 1000)
// }
// }, [mod])
async function processCall() {
const sessionDescription = await callDataStoreHelper
.peerConnection()
.createOffer({
mandatory: {
OfferToReceiveAudio: true,
OfferToReceiveVideo: true,
VoiceActivityDetection: true,
},
})
await callDataStoreHelper
.peerConnection()
.setLocalDescription(sessionDescription)
await startCallReq({
targetUserId: useCallDataStore.getState().targetUserId,
rtcMessage: sessionDescription,
})
const cancelCall = () => {
callDataStoreHelper.stop()
}
const templates = {
[CallMod.Speaking]: (
<CallingAtom
localStream={localStream}
remoteStream={remoteStream}
<CallBackground
title={title}
avatarImageUrl={avatarImageUrl}
showAvatar={false}
needOverlay={false}
subtitle={connectedStatus}
fullComponent={
<CallingAtom
localStream={localStream}
remoteStream={remoteStream}
/>
}>
<View style={styles.row}>
<CallBtn
iconName="phone-2"
bgColor="#DE253B"
isAnimated={false}
onPress={() => callDataStoreHelper.stop()}
/>
</View>
</CallBackground>
),
[CallMod.Outgoing]: (
<OutgoingcallAtom
title={title}
onPressCancel={cancelCall}
avatarImageUrl={avatarImageUrl}
/>
),
[CallMod.Outgoing]: <OutgoingCallAtom />,
[CallMod.Temporary]: null,
[CallMod.Finished]: (
<CallBackground
title={'Звінок завершенно'}
avatarImageUrl={avatarImageUrl}
needOverlay={false}
showAvatar={false}>
<View style={styles.row}>
<Button
title="Вийти"
onPress={() => callDataStoreHelper.finishCall()}
/>
</View>
</CallBackground>
),
}
return templates[mod]
return (
<>
<Modal
presentationStyle="overFullScreen"
visible={true}
statusBarTranslucent={true}
animationType="none"
style={{
backgroundColor: 'black',
margin: 0,
height: Dimensions.get('screen').height,
}}>
{templates[mod]}
</Modal>
<StatusBar hidden />
</>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
paddingTop: 100,
paddingHorizontal: 20,
},
row: {
flexDirection: 'row',
justifyContent: 'space-around',
alignItems: 'center',
width: '100%',
paddingHorizontal: 30,
},
})

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

@ -1,11 +1,12 @@ @@ -1,11 +1,12 @@
import { $size, IRouteParams, ScreenLayout, SwitchButtons } from '@/shared'
import React, { FC, useState } from 'react'
import React, { FC, useEffect, 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 {}
@ -28,6 +29,12 @@ export const ContactScreen: FC<IProps> = () => { @@ -28,6 +29,12 @@ export const ContactScreen: FC<IProps> = () => {
calls: () => <CallSmartList />,
})
useEffect(() => {
setTimeout(() => {
callDataStoreHelper.processStart(40)
}, 1000)
}, [])
const renderTabBar = () => (
<SwitchButtons
style={styles.switchContainer}

1
src/modules/root/index.tsx

@ -40,6 +40,7 @@ import { PartialTheme } from '@/shared/themes/interfaces' @@ -40,6 +40,7 @@ import { PartialTheme } from '@/shared/themes/interfaces'
import { useAppBadge, useAppSocketListener, useNetConnect } from './hooks'
import { PreviewFileModal, LoaderModal } from '@/shared/components/modals'
import { IncomingCallWidget } from '../calls/widgets'
import '@/modules/calls/core/call-events-listener'
export const Navigation: FC = () => {
const activeModule = useSelector(selectActiveNavigationModule)

3
src/modules/root/navigation-groups/tab-bar.group.tsx

@ -11,7 +11,6 @@ import { SettingsScreen } from '@/modules/settings' @@ -11,7 +11,6 @@ import { SettingsScreen } from '@/modules/settings'
import { AddUpdateTaskScreen } from '@/modules/tasks'
import { Comments } from '@/modules/comments/screens/comments.screen'
import { HomeGroup } from './home.group'
import { CallScreen } from '@/modules/calls/screens/call'
const Tab = createBottomTabNavigator()
@ -40,7 +39,7 @@ export const TabNavigator: FC = () => { @@ -40,7 +39,7 @@ export const TabNavigator: FC = () => {
name={RouteKey.AddTask}
component={AddUpdateTaskScreen}
/>
<Tab.Screen name={RouteKey.Contacts} component={CallScreen} />
<Tab.Screen name={RouteKey.Contacts} component={ContactScreen} />
<Tab.Screen name={RouteKey.Settings} component={SettingsScreen} />
</Tab.Navigator>
)

4
src/services/system/navigation.service.ts

@ -12,8 +12,12 @@ export const navigationRef = React.createRef<any>() @@ -12,8 +12,12 @@ export const navigationRef = React.createRef<any>()
export function navigate(name: string, params: any) {
navigationRef.current?.navigate(name, params)
}
export function goBack() {
navigationRef.current?.goBack()
}
export const NavigationService = {
setModule,
goBack,
navigate,
}

Loading…
Cancel
Save