mirror of
https://github.com/balzack/databag.git
synced 2025-04-25 11:05:27 +00:00
adding video call screen
This commit is contained in:
parent
67ea8c9925
commit
dda3eb23ed
20
app/client/mobile/src/call/Call.styled.ts
Normal file
20
app/client/mobile/src/call/Call.styled.ts
Normal file
@ -0,0 +1,20 @@
|
||||
import {StyleSheet} from 'react-native';
|
||||
import { Colors } from '../constants/Colors';
|
||||
|
||||
export const styles = StyleSheet.create({
|
||||
active: {
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
position: 'absolute',
|
||||
},
|
||||
inactive: {
|
||||
display: 'none',
|
||||
},
|
||||
call: {
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
},
|
||||
});
|
85
app/client/mobile/src/call/Call.tsx
Normal file
85
app/client/mobile/src/call/Call.tsx
Normal file
@ -0,0 +1,85 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { View } from 'react-native';
|
||||
import { useCall } from './useCall.hook';
|
||||
import { styles } from './Call.styled'
|
||||
import { Card as Contact } from '../card/Card';
|
||||
import { Text, Surface, IconButton, ActivityIndicator } from 'react-native-paper';
|
||||
import { Confirm } from '../confirm/Confirm';
|
||||
import { Colors } from '../constants/Colors';
|
||||
|
||||
export function Call() {
|
||||
const { state, actions } = useCall();
|
||||
const [alert, setAlert] = useState(false);
|
||||
const [ending, setEnding] = useState(false);
|
||||
const [applyingAudio, setApplyingAudio] = useState(false);
|
||||
const [accepting, setAccepting] = useState(null as null|string);
|
||||
const [ignoring, setIgnoring] = useState(null as null|string);
|
||||
const [declining, setDeclining] = useState(null as null|string);
|
||||
|
||||
const toggleAudio = async () => {
|
||||
if (!applyingAudio) {
|
||||
setApplyingAudio(true);
|
||||
try {
|
||||
if (state.audioEnabled) {
|
||||
await actions.disableAudio();
|
||||
} else if (!state.audioEnabled) {
|
||||
await actions.enableAudio();
|
||||
}
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
setAlert(true);
|
||||
}
|
||||
setApplyingAudio(false);
|
||||
}
|
||||
}
|
||||
|
||||
const toggleVideo = async () => {
|
||||
if (!applyingVideo) {
|
||||
setApplyingVideo(true);
|
||||
try {
|
||||
if (state.videoEnabled) {
|
||||
await actions.disableVideo();
|
||||
} else if (!state.videoEnabled) {
|
||||
await actions.enableVideo();
|
||||
}
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
setAlert(true);
|
||||
}
|
||||
setApplyingVideo(false);
|
||||
}
|
||||
}
|
||||
|
||||
const end = async () => {
|
||||
if (!ending) {
|
||||
setEnding(true);
|
||||
try {
|
||||
await actions.end();
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
setAlert(true);
|
||||
}
|
||||
setEnding(false);
|
||||
}
|
||||
}
|
||||
|
||||
const alertParams = {
|
||||
title: state.strings.operationFailed,
|
||||
prompt: state.strings.tryAgain,
|
||||
cancel: {
|
||||
label: state.strings.close,
|
||||
action: () => {
|
||||
setAlert(false);
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
return (
|
||||
<View style={(state.calling && state.fullscreen) ? styles.active : styles.inactive}>
|
||||
<Surface elevation={4} mode="flat" style={styles.call}>
|
||||
</Surface>
|
||||
<Confirm show={alert} params={alertParams} />
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
54
app/client/mobile/src/call/useCall.hook.ts
Normal file
54
app/client/mobile/src/call/useCall.hook.ts
Normal file
@ -0,0 +1,54 @@
|
||||
import { useState, useContext, useEffect, useRef } from 'react'
|
||||
import { RingContext } from '../context/RingContext'
|
||||
import { DisplayContext } from '../context/DisplayContext'
|
||||
import { ContextType } from '../context/ContextType'
|
||||
import { Card } from 'databag-client-sdk';
|
||||
|
||||
export function useCall() {
|
||||
const ring = useContext(RingContext) as ContextType;
|
||||
const display = useContext(DisplayContext) as ContextType;
|
||||
const offsetTime = useRef(0);
|
||||
const offset = useRef(false);
|
||||
|
||||
const [state, setState] = useState({
|
||||
strings: display.state.strings,
|
||||
calls: [] as { callId: string, card: Card }[],
|
||||
calling: null as null | Card,
|
||||
remoteVideo: false,
|
||||
localVideo: false,
|
||||
audioEnabled: false,
|
||||
videoEnabled: false,
|
||||
connected: false,
|
||||
duration: 0,
|
||||
failed: false,
|
||||
})
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const updateState = (value: any) => {
|
||||
setState((s) => ({ ...s, ...value }))
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
const interval = setInterval(() => {
|
||||
if (offset.current) {
|
||||
const now = new Date();
|
||||
const duration = Math.floor((now.getTime() / 1000) - offsetTime.current);
|
||||
updateState({ duration });
|
||||
}
|
||||
}, 1000);
|
||||
return () => {
|
||||
clearInterval(interval);
|
||||
}
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const { calls, calling, fullscreen, remoteVideo, localVideo, audioEnabled, videoEnabled, connected, connectedTime, failed } = ring.state;
|
||||
offsetTime.current = connectedTime;
|
||||
offset.current = connected;
|
||||
const duration = connected ? Math.floor(((new Date()).getTime() / 1000) - connectedTime) : 0;
|
||||
updateState({ calls, calling, fullscreen, duration, remoteVideo, localVideo, audioEnabled, videoEnabled, connected, failed });
|
||||
}, [ring.state]);
|
||||
|
||||
const actions = ring.actions;
|
||||
return { state, actions };
|
||||
}
|
@ -49,6 +49,7 @@ export function useRingContext() {
|
||||
connected: false,
|
||||
connectedTime: 0,
|
||||
failed: false,
|
||||
fullscreen: false,
|
||||
})
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
|
@ -121,11 +121,11 @@ export function Ring() {
|
||||
)}
|
||||
{ state.calling && (
|
||||
<Surface elevation={4} mode="flat" style={styles.ring}>
|
||||
<IconButton style={styles.circleIcon} iconColor="white" containerColor={Colors.primary} icon={state.audioEnabled ? 'microphone' : 'microphone-off'} compact="true" mode="contained" size={24} onPress={toggleAudio} />
|
||||
<IconButton style={styles.circleIcon} iconColor="white" containerColor={Colors.confirmed} icon="arrow-expand-all" compact="true" mode="contained" size={24} onPress={toggleAudio} />
|
||||
<IconButton style={styles.circleIcon} iconColor="white" disabled={!state.connected} containerColor={Colors.primary} icon={state.audioEnabled ? 'microphone' : 'microphone-off'} compact="true" mode="contained" size={24} onPress={toggleAudio} />
|
||||
<IconButton style={styles.circleIcon} iconColor="white" disabled={!state.connected} containerColor={Colors.confirmed} icon={(state.remoteVideo || state.localVideo) ? 'video-switch-outline' : 'arrow-expand-all'} compact="true" mode="contained" size={24} onPress={()=>actions.setFullscreen(true)} />
|
||||
<View style={styles.name}>
|
||||
{ state.calling.name && (
|
||||
<Text style={styles.nameSet} numberOfLines={1}>asuperlongnamehere asuperlongnamehere</Text>
|
||||
<Text style={styles.nameSet} numberOfLines={1}>{ state.calling.name }</Text>
|
||||
)}
|
||||
{ !state.calling.name && (
|
||||
<Text style={styles.nameUnset} adjustsFontSizeToFit={true} numberOfLines={1}>{ state.strings.name }</Text>
|
||||
|
@ -15,6 +15,7 @@ export function useRing() {
|
||||
calls: [] as { callId: string, card: Card }[],
|
||||
calling: null as null | Card,
|
||||
remoteVideo: false,
|
||||
localVideo: false,
|
||||
audioEnabled: false,
|
||||
connected: false,
|
||||
duration: 0,
|
||||
@ -40,11 +41,11 @@ export function useRing() {
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const { calls, calling, remoteVideo, audioEnabled, connected, connectedTime, failed } = ring.state;
|
||||
const { calls, calling, localVideo, remoteVideo, audioEnabled, connected, connectedTime, failed } = ring.state;
|
||||
offsetTime.current = connectedTime;
|
||||
offset.current = connected;
|
||||
const duration = connected ? Math.floor(((new Date()).getTime() / 1000) - connectedTime) : 0;
|
||||
updateState({ calls, calling, duration, remoteVideo, audioEnabled, connected, failed });
|
||||
updateState({ calls, calling, duration, localVideo, remoteVideo, audioEnabled, connected, failed });
|
||||
}, [ring.state]);
|
||||
|
||||
const actions = ring.actions;
|
||||
|
@ -19,6 +19,7 @@ import {createDrawerNavigator} from '@react-navigation/drawer';
|
||||
import {createNativeStackNavigator} from '@react-navigation/native-stack';
|
||||
import {Colors} from '../constants/Colors';
|
||||
import {Ring} from '../ring/Ring';
|
||||
import {Call} from '../call/Call';
|
||||
|
||||
const SettingsDrawer = createDrawerNavigator();
|
||||
const ContactsDrawer = createDrawerNavigator();
|
||||
@ -198,6 +199,7 @@ export function Session() {
|
||||
</Surface>
|
||||
</View>
|
||||
)}
|
||||
<Call />
|
||||
</View>
|
||||
</RingContextProvider>
|
||||
);
|
||||
|
Loading…
x
Reference in New Issue
Block a user