render video call screen

This commit is contained in:
balzack 2025-02-05 22:16:24 -08:00
parent 38e65a2492
commit 45be19a17f
5 changed files with 96 additions and 29 deletions

View File

@ -38,5 +38,34 @@ export const styles = StyleSheet.create({
right: '10%',
width: '20%',
height: '20%',
}
},
titleView: {
position: 'absolute',
top: 0,
width: '100%',
height: '100%',
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
paddingTop: 92,
},
titleName: {
fontSize: 24,
paddingBottom: 32,
},
titleImage: {
width: '80%',
height: 'auto',
aspectRatio: 1,
borderRadius: 8,
},
duration: {
paddingTop: 16,
fontSize: 20,
},
logoView: {
height: '100%',
width: 'auto',
aspectRatio: 1,
},
});

View File

@ -1,5 +1,5 @@
import React, { useEffect, useState } from 'react';
import { View } from 'react-native';
import { useWindowDimensions, Dimensions, Image, View } from 'react-native';
import { useCall } from './useCall.hook';
import { styles } from './Call.styled'
import { Card as Contact } from '../card/Card';
@ -17,6 +17,7 @@ export function Call() {
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 {height, width} = useWindowDimensions();
const toggleAudio = async () => {
if (!applyingAudio) {
@ -76,36 +77,65 @@ export function Call() {
},
};
const showName = (state.height - (8 * state.width / 10)) > 256;
return (
<View style={(state.calling && state.fullscreen) ? styles.active : styles.inactive}>
<Surface elevation={4} mode="flat" style={styles.call}>
{ state.calling && (
<Surface elevation={4} mode="flat" style={styles.call}>
{ state.remoteVideo && (
<RTCView
style={styles.full}
mirror={true}
objectFit={'contain'}
streamURL={state.remoteStream.toURL()}
/>
)}
{ !state.remoteVideo && !state.localVideo && showName && (
<View style={styles.titleView}>
{ state.calling.name && (
<Text style={styles.titleName} adjustsFontSizeToFit={true} numberOfLines={1}>{ state.calling.name }</Text>
)}
{ !state.calling.name && (
<Text style={styles.titleName} adjustsFontSizeToFit={true} numberOfLines={1}>{ `${state.calling.handle}/${state.calling.node}` }</Text>
)}
<Image
style={styles.titleImage}
resizeMode="contain"
source={{ uri: state.calling.imageUrl }}
/>
<Text style={styles.duration}>{ `${Math.floor(state.duration/60)}:${(state.duration % 60).toString().padStart(2, '0')}` }</Text>
</View>
)}
{ state.localVideo && (
<RTCView
style={state.remoteVideo ? styles.box : styles.full}
mirror={true}
objectFit={'contain'}
streamURL={state.localStream.toURL()}
zOrder={2}
/>
)}
{ !state.remoteVideo && !state.localVideo && !showName && (
<Image
style={styles.logoView}
resizeMode="contain"
source={{ uri: state.calling.imageUrl }}
/>
)}
<Surface elevation={3} mode="flat" style={styles.controls}>
<IconButton style={styles.closeIcon} iconColor="white" disabled={!state.connected} icon="arrow-collapse-all" loading={applyingAudio} compact="true" mode="contained" size={32} onPress={()=>actions.setFullscreen(false)} />
<IconButton style={styles.closeIcon} iconColor="white" disabled={!state.connected} containerColor={Colors.primary} icon={state.audioEnabled ? 'microphone' : 'microphone-off'} loading={applyingAudio} compact="true" mode="contained" size={32} onPress={toggleAudio} />
<IconButton style={styles.closeIcon} iconColor="white" disabled={!state.connected} containerColor={Colors.primary} icon={state.videoEnabled ? 'video-outline' : 'video-off-outline'} loading={applyingVideo} compact="true" mode="contained" size={32} onPress={toggleVideo} />
<IconButton style={styles.closeIcon} iconColor="white" containerColor={Colors.danger} icon="phone-hangup-outline" compact="true" mode="contained" size={32} onPress={end} />
{ state.remoteVideo && (
<RTCView
style={styles.full}
mirror={true}
objectFit={'contain'}
streamURL={state.remoteStream.toURL()}
/>
)}
{ state.localVideo && (
<RTCView
style={state.remoteVideo ? styles.box : styles.full}
mirror={true}
objectFit={'contain'}
streamURL={state.localStream.toURL()}
zOrder={2}
/>
)}
<Surface elevation={3} mode="flat" style={styles.controls}>
<IconButton style={styles.closeIcon} iconColor="white" disabled={!state.connected} icon="arrow-collapse-all" loading={applyingAudio} compact="true" mode="contained" size={32} onPress={()=>actions.setFullscreen(false)} />
<IconButton style={styles.closeIcon} iconColor="white" disabled={!state.connected} containerColor={Colors.primary} icon={state.audioEnabled ? 'microphone' : 'microphone-off'} loading={applyingAudio} compact="true" mode="contained" size={32} onPress={toggleAudio} />
<IconButton style={styles.closeIcon} iconColor="white" disabled={!state.connected} containerColor={Colors.primary} icon={state.videoEnabled ? 'video-outline' : 'video-off-outline'} loading={applyingVideo} compact="true" mode="contained" size={32} onPress={toggleVideo} />
<IconButton style={styles.closeIcon} iconColor="white" containerColor={Colors.danger} icon="phone-hangup-outline" compact="true" mode="contained" size={32} onPress={end} />
</Surface>
</Surface>
</Surface>
)}
<Confirm show={alert} params={alertParams} />
</View>
);

View File

@ -11,7 +11,7 @@ export function useCall() {
const offset = useRef(false);
const [state, setState] = useState({
strings: display.state.strings,
strings: display.state.strings,
calls: [] as { callId: string, card: Card }[],
calling: null as null | Card,
localStream: null as null|MediaStream,
@ -23,6 +23,8 @@ export function useCall() {
connected: false,
duration: 0,
failed: false,
width: 0,
height: 0,
})
// eslint-disable-next-line @typescript-eslint/no-explicit-any
@ -30,6 +32,11 @@ export function useCall() {
setState((s) => ({ ...s, ...value }))
}
useEffect(() => {
const { width, height, strings } = display.state;
updateState({ width, height, strings });
}, [display.state]);
useEffect(() => {
const interval = setInterval(() => {
if (offset.current) {

View File

@ -7,6 +7,8 @@ export function useDisplayContext() {
const [state, setState] = useState({
strings: getLanguageStrings(),
layout: null,
width: 0,
height: 0,
});
const SMALL_LARGE = 650;
@ -17,7 +19,7 @@ export function useDisplayContext() {
useEffect(() => {
const layout = dim.width < SMALL_LARGE ? 'small' : 'large';
updateState({layout});
updateState({layout, width: dim.width, height: dim.height});
}, [dim.width]);
const actions = {};

View File

@ -21,7 +21,6 @@ export const styles = StyleSheet.create({
width: '100%',
height: '100%',
borderRadius: 16,
overflow: 'hidden',
paddingLeft: 16,
paddingRight: 8,
},