implementing call gui

This commit is contained in:
balzack 2025-01-26 22:31:26 -08:00
parent a60c104f3c
commit 8ff0443aea
7 changed files with 128 additions and 17 deletions

View File

@ -1,5 +1,7 @@
PODS:
- boost (1.84.0)
- BVLinearGradient (2.8.3):
- React-Core
- DoubleConversion (1.1.6)
- FBLazyVector (0.75.4)
- Firebase/CoreOnly (11.5.0):
@ -1862,6 +1864,7 @@ PODS:
DEPENDENCIES:
- boost (from `../node_modules/react-native/third-party-podspecs/boost.podspec`)
- BVLinearGradient (from `../node_modules/react-native-linear-gradient`)
- DoubleConversion (from `../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec`)
- FBLazyVector (from `../node_modules/react-native/Libraries/FBLazyVector`)
- fmt (from `../node_modules/react-native/third-party-podspecs/fmt.podspec`)
@ -1970,6 +1973,8 @@ SPEC REPOS:
EXTERNAL SOURCES:
boost:
:podspec: "../node_modules/react-native/third-party-podspecs/boost.podspec"
BVLinearGradient:
:path: "../node_modules/react-native-linear-gradient"
DoubleConversion:
:podspec: "../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec"
FBLazyVector:
@ -2140,6 +2145,7 @@ EXTERNAL SOURCES:
SPEC CHECKSUMS:
boost: 4cb898d0bf20404aab1850c656dcea009429d6c1
BVLinearGradient: 880f91a7854faff2df62518f0281afb1c60d49a3
DoubleConversion: 76ab83afb40bddeeee456813d9c04f67f78771b5
FBLazyVector: 430e10366de01d1e3d57374500b1b150fe482e6d
Firebase: 7a56fe4f56b5ab81b86a6822f5b8f909ae6fc7e2

View File

@ -39,6 +39,7 @@
"react-native-gesture-handler": "^2.19.0",
"react-native-image-crop-picker": "^0.41.2",
"react-native-keyboard-aware-scroll-view": "^0.9.5",
"react-native-linear-gradient": "^2.8.3",
"react-native-paper": "^5.12.5",
"react-native-reanimated": "^3.15.2",
"react-native-rsa-native": "^2.0.5",

View File

@ -25,6 +25,11 @@ export const styles = StyleSheet.create({
alignItems: 'center',
justifyContent: 'center',
},
call: {
width: '100%',
height: '100%',
display: 'flex',
},
image: {
width: '100%',
height: '100%',
@ -32,8 +37,32 @@ export const styles = StyleSheet.create({
},
frame: {
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
borderRadius: 8,
overflow: 'hidden',
padding: 2,
position: 'relative',
},
closeIcon: {
borderRadius: 8,
},
name: {
fontSize: 28,
minWidth: '50%',
color: '#aaaaaa',
paddingLeft: 16,
width: '100%',
height: '100%',
padding: 16,
},
overlap: {
display: 'flex',
flexDirection: 'row',
position: 'absolute',
alignItems: 'center',
width: '100%',
justifyContent: 'center',
paddingBottom: 8,
paddingTop: 8,
gap: 32,
},
});

View File

@ -1,5 +1,5 @@
import React, { useEffect, useState } from 'react';
import { Image, SafeAreaView, Modal, ScrollView, View } from 'react-native';
import { useWindowDimensions, Image, SafeAreaView, Modal, ScrollView, View } from 'react-native';
import { Surface, Icon, Divider, Button, IconButton, Text, TextInput} from 'react-native-paper';
import {styles} from './Calling.styled';
import {useCalling} from './useCalling.hook';
@ -7,11 +7,28 @@ import {BlurView} from '@react-native-community/blur';
import { Confirm } from '../confirm/Confirm';
import { ActivityIndicator } from 'react-native-paper';
import FastImage from 'react-native-fast-image'
import LinearGradient from 'react-native-linear-gradient';
import { Colors } from '../constants/Colors';
export function Calling({ callCard }: { callCard: string }) {
const { state, actions } = useCalling();
const [alert, setAlert] = useState(false);
const [connecting, setConnecting] = useState(false);
const [ending, setEnding] = useState(false);
const {height, width} = useWindowDimensions();
const end = async () => {
if (!ending) {
setEnding(true);
try {
await actions.end();
} catch (err) {
console.log(err);
setAlert(true);
}
setEnding(false);
}
}
const call = async (cardId: string) => {
if (!connecting) {
@ -44,6 +61,10 @@ export function Calling({ callCard }: { callCard: string }) {
}
}, [callCard]);
const overlap = (width + 128) > height;
const frameWidth = width > height ? height : width - 16;
const frameHeight = frameWidth;
//const frameHeight = overlap ? frameWidth : frameWidth + 128;
return (
<View style={(connecting || state.calling || state.ringing.length > 0 || alert) ? styles.active : styles.inactive}>
<BlurView style={styles.blur} blurType="dark" blurAmount={9} reducedTransparencyFallbackColor="dark" />
@ -51,13 +72,43 @@ export function Calling({ callCard }: { callCard: string }) {
<ActivityIndicator size={72} />
)}
{ state.calling && (
<View style={styles.frame}>
<Surface style={{ ...styles.frame, width: frameWidth, height: frameHeight }}>
<Image
style={styles.image}
resizeMode="contain"
source={{ uri: state.calling.imageUrl }}
onLayout={actions.loaded}
/>
</View>
{ state.loaded && (
<LinearGradient style={{...styles.overlap, height: frameHeight / 2, top: 2, borderRadius: 8}} start={{x: 0, y: 0}} end={{x: 0, y: 0.5}} colors={['rgba(64,64,64,1)', 'rgba(64,64,64, 0)']}>
<LinearGradient style={{...styles.overlap, height: frameHeight / 2, top: 2, borderRadius: 8}} start={{x: 0, y: 0}} end={{x: 0, y: 0.5}} colors={['rgba(64,64,64,1)', 'rgba(64,64,64, 0)']}>
</LinearGradient>
</LinearGradient>
)}
{ state.loaded && (
<LinearGradient style={{...styles.overlap, height: frameHeight / 2, bottom: 2, borderRadius: 8}} start={{x: 0, y: 0.5}} end={{x: 0, y: 1}} colors={['rgba(64,64,64,0)', 'rgba(64,64,64, 1)']}>
<LinearGradient style={{...styles.overlap, height: frameHeight / 2, bottom: 2, borderRadius: 8}} start={{x: 0, y: 0.5}} end={{x: 0, y: 1}} colors={['rgba(64,64,64,0)', 'rgba(64,64,64, 1)']}>
</LinearGradient>
</LinearGradient>
)}
{ state.loaded && (
<View style={{ ...styles.overlap, top: 0 }}>
{ state.calling.name && (
<Text style={styles.name} adjustsFontSizeToFit={true} numberOfLines={1}>{ state.calling.name }</Text>
)}
{ !state.calling.name && (
<Text style={styles.name} adjustsFontSizeToFit={true} numberOfLines={1}>{ `${state.calling.handle}/${state.calling.node}` }</Text>
)}
</View>
)}
{ state.loaded && (
<View style={{ ...styles.overlap, bottom: 0 }}>
<IconButton style={styles.closeIcon} iconColor="white" containerColor={Colors.primary} icon="microphone" compact="true" mode="contained" size={32} onPress={end} />
<IconButton style={styles.closeIcon} iconColor="white" containerColor={Colors.primary} icon="video-outline" compact="true" mode="contained" size={32} onPress={end} />
<IconButton style={styles.closeIcon} iconColor="white" containerColor={Colors.danger} icon="phone-hangup-outline" compact="true" mode="contained" size={32} onPress={end} />
</View>
)}
</Surface>
)}
<Confirm show={alert} params={alertParams} />
</View>

View File

@ -27,6 +27,8 @@ export function useCalling() {
cards: [],
calling: null as null | Card,
failed: false,
loaded: false,
panelOffset: 0,
})
// eslint-disable-next-line @typescript-eslint/no-explicit-any
@ -55,8 +57,6 @@ export function useCalling() {
};
const linkStatus = async (status: string) => {
console.log("LINK STATUS: ", status);
if (call.current) {
const { policy, peer, link } = call.current;
if (status === 'connected') {
@ -84,8 +84,6 @@ console.log("LINK STATUS: ", status);
try {
peer.close();
link.close();
link.clearStatusListener();
link.clearMessageListener();
} catch (err) {
console.log(err);
}
@ -96,8 +94,6 @@ console.log("LINK STATUS: ", status);
}
const linkMessage = async (message: any) => {
console.log("LINK MSG: ", message);
if (call.current) {
const { peer, link, candidates, policy } = call.current;
try {
@ -135,13 +131,9 @@ console.log("LINK MSG: ", message);
updateState({ failed: true });
}
}
console.log("LINK MSG: done");
}
const peerCandidate = async (candidate) => {
console.log("PEER CANDIDATE");
if (call.current && candidate) {
const { link } = call.current;
await link.sendMessage({ candidate });
@ -149,7 +141,6 @@ console.log("PEER CANDIDATE");
}
const peerNegotiate = async () => {
console.log("PEER NEGOTIATE");
if (call.current) {
const { peer, link } = call.current;
const description = await peer.createOffer(constraints);
@ -204,6 +195,20 @@ console.log("PEER NEGOTIATE");
}, [app.state.session]);
const actions = {
end: async () => {
if (!call.current) {
throw new Error('no active call');
}
const { link, peer } = call.current;
try {
peer.close();
link.close();
} catch (err) {
console.log(err);
}
call.current = null;
updateState({ calling: null });
},
accept: async (callId: string, call: Call) => {
if (call.current) {
throw new Error('active call in progress');
@ -239,6 +244,14 @@ console.log("PEER NEGOTIATE");
link.setMessageListener(linkMessage);
updateState({ calling: card });
},
loaded: (e) => {
const { width, height } = e.nativeEvent.layout;
if (width > (height + 80)) {
updateState({ panelOffset: 0, loaded: true });
} else {
updateState({ panelOffset: ((height - width) - 80) / 2, loaded: true });
}
}
}
return { state, actions }

View File

@ -1,6 +1,6 @@
export const Colors = {
primary: '#408060',
danger: '#ff8888',
danger: '#ff6666',
placeholder: '#888888',
unsaved: '#555555',
confirmed: '#aaaaaa',

View File

@ -4461,6 +4461,7 @@ __metadata:
react-native-gesture-handler: ^2.19.0
react-native-image-crop-picker: ^0.41.2
react-native-keyboard-aware-scroll-view: ^0.9.5
react-native-linear-gradient: ^2.8.3
react-native-paper: ^5.12.5
react-native-reanimated: ^3.15.2
react-native-rsa-native: ^2.0.5
@ -10705,6 +10706,16 @@ __metadata:
languageName: node
linkType: hard
"react-native-linear-gradient@npm:^2.8.3":
version: 2.8.3
resolution: "react-native-linear-gradient@npm:2.8.3"
peerDependencies:
react: "*"
react-native: "*"
checksum: f980d324e551bbc475c6406bdc5250a08242020cbe653412fa169bbdf51e28a502e225de105b4d696d6a1a1b733d44782469020f4936d8b3ce0e2c78e51cf58f
languageName: node
linkType: hard
"react-native-paper@npm:^5.12.5":
version: 5.13.1
resolution: "react-native-paper@npm:5.13.1"