mirror of
https://github.com/balzack/databag.git
synced 2025-05-04 23:45:21 +00:00
removing temp files
This commit is contained in:
parent
e9d1a1e676
commit
ceb9365bdd
@ -1,83 +0,0 @@
|
|||||||
import {StyleSheet} from 'react-native';
|
|
||||||
import { Colors } from '../constants/Colors';
|
|
||||||
|
|
||||||
export const styles = StyleSheet.create({
|
|
||||||
blur: {
|
|
||||||
position: 'absolute',
|
|
||||||
top: 0,
|
|
||||||
left: 0,
|
|
||||||
width: '100%',
|
|
||||||
height: '100%',
|
|
||||||
},
|
|
||||||
active: {
|
|
||||||
display: 'flex',
|
|
||||||
width: '100%',
|
|
||||||
height: '100%',
|
|
||||||
position: 'absolute',
|
|
||||||
alignItems: 'center',
|
|
||||||
justifyContent: 'center',
|
|
||||||
backgroundColor: 'rgb(64,64,64)',
|
|
||||||
},
|
|
||||||
inactive: {
|
|
||||||
display: 'none',
|
|
||||||
width: '100%',
|
|
||||||
height: '100%',
|
|
||||||
position: 'absolute',
|
|
||||||
alignItems: 'center',
|
|
||||||
justifyContent: 'center',
|
|
||||||
backgroundColor: 'rgb(64,64,64)',
|
|
||||||
},
|
|
||||||
container: {
|
|
||||||
width: '100%',
|
|
||||||
height: '100%',
|
|
||||||
display: 'flex',
|
|
||||||
alignItems: 'center',
|
|
||||||
justifyContent: 'center',
|
|
||||||
},
|
|
||||||
call: {
|
|
||||||
width: '100%',
|
|
||||||
height: '100%',
|
|
||||||
display: 'flex',
|
|
||||||
},
|
|
||||||
image: {
|
|
||||||
width: '100%',
|
|
||||||
height: '100%',
|
|
||||||
borderRadius: 8,
|
|
||||||
},
|
|
||||||
frame: {
|
|
||||||
display: 'flex',
|
|
||||||
alignItems: 'center',
|
|
||||||
justifyContent: 'center',
|
|
||||||
borderRadius: 8,
|
|
||||||
overflow: 'hidden',
|
|
||||||
padding: 2,
|
|
||||||
position: 'absolute',
|
|
||||||
},
|
|
||||||
closeIcon: {
|
|
||||||
borderRadius: 8,
|
|
||||||
},
|
|
||||||
name: {
|
|
||||||
fontSize: 28,
|
|
||||||
minWidth: '50%',
|
|
||||||
color: '#aaaaaa',
|
|
||||||
paddingLeft: 16,
|
|
||||||
width: '100%',
|
|
||||||
textAlign: 'center',
|
|
||||||
},
|
|
||||||
overlap: {
|
|
||||||
display: 'flex',
|
|
||||||
flexDirection: 'row',
|
|
||||||
position: 'absolute',
|
|
||||||
alignItems: 'center',
|
|
||||||
justifyContent: 'center',
|
|
||||||
paddingBottom: 8,
|
|
||||||
paddingTop: 8,
|
|
||||||
gap: 32,
|
|
||||||
},
|
|
||||||
local: {
|
|
||||||
position: 'absolute',
|
|
||||||
right: 16,
|
|
||||||
width: '20%',
|
|
||||||
height: '20%',
|
|
||||||
},
|
|
||||||
});
|
|
@ -1,170 +0,0 @@
|
|||||||
import React, { useEffect, useState } from 'react';
|
|
||||||
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';
|
|
||||||
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';
|
|
||||||
import { RTCView } from 'react-native-webrtc';
|
|
||||||
|
|
||||||
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 [applyingVideo, setApplyingVideo] = useState(false);
|
|
||||||
const [applyingAudio, setApplyingAudio] = useState(false);
|
|
||||||
|
|
||||||
const toggleVideo = async () => {
|
|
||||||
if (!applyingVideo) {
|
|
||||||
setApplyingVideo(true);
|
|
||||||
try {
|
|
||||||
if (state.video && state.videoEnabled) {
|
|
||||||
await actions.disableVideo();
|
|
||||||
} else if (state.video && !state.videoEnabled) {
|
|
||||||
await actions.enableVideo();
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
console.log(err);
|
|
||||||
setAlert(true);
|
|
||||||
}
|
|
||||||
setApplyingVideo(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const toggleAudio = async () => {
|
|
||||||
if (!applyingAudio) {
|
|
||||||
setApplyingAudio(true);
|
|
||||||
try {
|
|
||||||
if (state.audio && state.audioEnabled) {
|
|
||||||
await actions.disableAudio();
|
|
||||||
} else if (state.audio && !state.audioEnabled) {
|
|
||||||
await actions.enableAudio();
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
console.log(err);
|
|
||||||
setAlert(true);
|
|
||||||
}
|
|
||||||
setApplyingAudio(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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) {
|
|
||||||
setConnecting(true);
|
|
||||||
try {
|
|
||||||
await actions.call(cardId);
|
|
||||||
} catch (err) {
|
|
||||||
console.log(err);
|
|
||||||
setAlert(true);
|
|
||||||
}
|
|
||||||
setConnecting(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const alertParams = {
|
|
||||||
title: state.strings.operationFailed,
|
|
||||||
prompt: state.strings.tryAgain,
|
|
||||||
cancel: {
|
|
||||||
label: state.strings.close,
|
|
||||||
action: () => {
|
|
||||||
setAlert(false);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const { cardId } = callCard;
|
|
||||||
if (cardId) {
|
|
||||||
call(cardId);
|
|
||||||
}
|
|
||||||
}, [callCard]);
|
|
||||||
|
|
||||||
const overlap = (width + 128) > height;
|
|
||||||
const frameWidth = width > height ? height : width - 16;
|
|
||||||
const frameHeight = frameWidth;
|
|
||||||
const frameOffset = (height - frameHeight) / 8;
|
|
||||||
return (
|
|
||||||
<SafeAreaView style={(connecting || state.calling || state.ringing.length > 0 || alert) ? styles.active : styles.inactive}>
|
|
||||||
<View style={styles.container}>
|
|
||||||
{ connecting && !state.calling && (
|
|
||||||
<ActivityIndicator size={72} />
|
|
||||||
)}
|
|
||||||
{ state.calling && (
|
|
||||||
<View style={{ ...styles.frame, top: frameOffset, width: frameWidth, height: frameHeight }}>
|
|
||||||
<Image
|
|
||||||
style={styles.image}
|
|
||||||
resizeMode="contain"
|
|
||||||
source={{ uri: state.calling.imageUrl }}
|
|
||||||
onLayout={actions.loaded}
|
|
||||||
/>
|
|
||||||
{ state.loaded && (
|
|
||||||
<LinearGradient style={{...styles.overlap, width: '100%', 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, width: '100%', 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>
|
|
||||||
)}
|
|
||||||
{ state.loaded && (
|
|
||||||
<LinearGradient style={{...styles.overlap, width: '100%', 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, width: '100%', 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>
|
|
||||||
)}
|
|
||||||
{ state.loaded && (
|
|
||||||
<LinearGradient style={{...styles.overlap, height: '100%', width: 16, right: 0}} start={{x: 0, y: 0}} end={{x: 1, y: 0}} colors={['rgba(64,64,64,0)', 'rgba(64,64,64, 1)']} />
|
|
||||||
)}
|
|
||||||
{ state.loaded && (
|
|
||||||
<LinearGradient style={{...styles.overlap, height: '100%', width: 16, left: 0}} start={{x: 1, y: 0}} end={{x: 0, y: 0}} colors={['rgba(64,64,64,0)', 'rgba(64,64,64, 1)']} />
|
|
||||||
)}
|
|
||||||
</View>
|
|
||||||
)}
|
|
||||||
{ state.calling && 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.calling && state.loaded && (
|
|
||||||
<View style={{ ...styles.overlap, bottom: frameOffset }}>
|
|
||||||
<View style={{ paddingTop: 8, paddingBottom: 8, paddingLeft: 16, paddingRight: 16, gap: 16, display: 'flex', flexDirection: 'row', borderRadius: 16, backgroundColor: 'rgba(128,128,128,0.5)' }}>
|
|
||||||
<IconButton style={styles.closeIcon} iconColor="white" containerColor={Colors.primary} icon={state.audioEnabled ? 'microphone-off' : 'microphone'} loading={applyingAudio} disabled={!state.audio} compact="true" mode="contained" size={32} onPress={toggleAudio} />
|
|
||||||
<IconButton style={styles.closeIcon} iconColor="white" containerColor={Colors.primary} icon={state.videoEnabled ? 'video-off-outline' : 'video-outline'} loading={applyingVideo} disabled={!state.video} 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} />
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
)}
|
|
||||||
{ state.calling && state.loaded && state.local && (
|
|
||||||
<RTCView
|
|
||||||
style={styles.local}
|
|
||||||
mirror={true}
|
|
||||||
objectFit={'contain'}
|
|
||||||
streamURL={state.local.toURL()}
|
|
||||||
zOrder={2}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</View>
|
|
||||||
<Confirm show={alert} params={alertParams} />
|
|
||||||
</SafeAreaView>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
@ -1,303 +0,0 @@
|
|||||||
import { useState, useContext, useEffect, useRef } from 'react'
|
|
||||||
import { DisplayContext } from '../context/DisplayContext';
|
|
||||||
import { AppContext } from '../context/AppContext'
|
|
||||||
import { ContextType } from '../context/ContextType'
|
|
||||||
import { Link, type Card } from 'databag-client-sdk';
|
|
||||||
|
|
||||||
import {
|
|
||||||
ScreenCapturePickerView,
|
|
||||||
RTCPeerConnection,
|
|
||||||
RTCIceCandidate,
|
|
||||||
RTCSessionDescription,
|
|
||||||
RTCView,
|
|
||||||
MediaStream,
|
|
||||||
MediaStreamTrack,
|
|
||||||
mediaDevices,
|
|
||||||
registerGlobals
|
|
||||||
} from 'react-native-webrtc';
|
|
||||||
|
|
||||||
export function useCalling() {
|
|
||||||
const app = useContext(AppContext) as ContextType;
|
|
||||||
const display = useContext(DisplayContext) as ContextType;
|
|
||||||
const call = useRef(null as { policy: string, peer: RTCPeerConnection, link: Link, candidates: RTCIceCandidate[] } | null);
|
|
||||||
const stream = useRef(null);
|
|
||||||
const [state, setState] = useState({
|
|
||||||
strings: {},
|
|
||||||
ringing: [],
|
|
||||||
calls: [],
|
|
||||||
cards: [],
|
|
||||||
calling: null as null | Card,
|
|
||||||
failed: false,
|
|
||||||
loaded: false,
|
|
||||||
panelOffset: 0,
|
|
||||||
local: null,
|
|
||||||
audio: null,
|
|
||||||
audioEnabled: false,
|
|
||||||
video: null,
|
|
||||||
videoEnabled: false,
|
|
||||||
videoAdded: false,
|
|
||||||
})
|
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
||||||
const updateState = (value: any) => {
|
|
||||||
setState((s) => ({ ...s, ...value }))
|
|
||||||
}
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const calls = state.ringing
|
|
||||||
.map(ring => ({ callId: ring.callId, card: state.cards.find(card => ring.cardId === card.cardId) }) )
|
|
||||||
.filter(ring => (ring.card && !ring.card.blocked));
|
|
||||||
updateState({ calls });
|
|
||||||
}, [state.ringing, state.cards]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const { strings } = display.state;
|
|
||||||
updateState({ strings });
|
|
||||||
}, [display.state]);
|
|
||||||
|
|
||||||
const constraints = {
|
|
||||||
mandatory: {
|
|
||||||
OfferToReceiveAudio: true,
|
|
||||||
OfferToReceiveVideo: false,
|
|
||||||
VoiceActivityDetection: true
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const linkStatus = async (status: string) => {
|
|
||||||
if (call.current) {
|
|
||||||
const { policy, peer, link } = call.current;
|
|
||||||
if (status === 'connected') {
|
|
||||||
try {
|
|
||||||
stream.current = await mediaDevices.getUserMedia({
|
|
||||||
audio: true,
|
|
||||||
video: {
|
|
||||||
frameRate: 30,
|
|
||||||
facingMode: 'user'
|
|
||||||
}
|
|
||||||
});
|
|
||||||
const audio = stream.current.getTracks().find(track => track.kind === 'audio');
|
|
||||||
const video = stream.current.getTracks().find(track => track.kind === 'video');
|
|
||||||
if (audio) {
|
|
||||||
audio.enabled = true;
|
|
||||||
peer.addTrack(audio, stream.current);
|
|
||||||
}
|
|
||||||
if (video) {
|
|
||||||
video.enabled = false;
|
|
||||||
}
|
|
||||||
updateState({ audio, video, audioAdded: true, audioEnabled: true, videoAdded: false, videoEnabled: false });
|
|
||||||
} catch (err) {
|
|
||||||
console.log(err);
|
|
||||||
updateState({ failed: true });
|
|
||||||
}
|
|
||||||
} else if (status === 'closed') {
|
|
||||||
try {
|
|
||||||
peer.close();
|
|
||||||
link.close();
|
|
||||||
} catch (err) {
|
|
||||||
console.log(err);
|
|
||||||
}
|
|
||||||
call.current = null;
|
|
||||||
stream.current = null;
|
|
||||||
updateState({ calling: null, failed: false, audio: null, video: null });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const linkMessage = async (message: any) => {
|
|
||||||
if (call.current) {
|
|
||||||
const { peer, link, candidates, policy } = call.current;
|
|
||||||
try {
|
|
||||||
if (message.description) {
|
|
||||||
if (message.description.type === 'offer' && peer.signalingState !== 'stable') {
|
|
||||||
if (policy === 'polite') {
|
|
||||||
const rollback = new RTCSessionDescription({ type: 'rollback' });
|
|
||||||
await peer.setLocalDescription(rollback);
|
|
||||||
} else {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const offer = new RTCSessionDescription(message.description);
|
|
||||||
await peer.setRemoteDescription(offer);
|
|
||||||
if (message.description.type === 'offer') {
|
|
||||||
const description = await peer.createAnswer();
|
|
||||||
await peer.setLocalDescription(description);
|
|
||||||
link.sendMessage({ description });
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const candidate of candidates) {
|
|
||||||
await peer.addIceCandidate(candidate);
|
|
||||||
};
|
|
||||||
candidates.length = 0;
|
|
||||||
} else if (message.candidate) {
|
|
||||||
const candidate = new RTCIceCandidate(message.candidate);
|
|
||||||
if (peer.remoteDescription == null) {
|
|
||||||
candidates.push(candidate);
|
|
||||||
} else {
|
|
||||||
await peer.addIceCandidate(candidate);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
console.log(err);
|
|
||||||
updateState({ failed: true });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const peerCandidate = async (candidate) => {
|
|
||||||
if (call.current && candidate) {
|
|
||||||
const { link } = call.current;
|
|
||||||
await link.sendMessage({ candidate });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const peerNegotiate = async () => {
|
|
||||||
if (call.current) {
|
|
||||||
const { peer, link } = call.current;
|
|
||||||
const description = await peer.createOffer(constraints);
|
|
||||||
await peer.setLocalDescription(description);
|
|
||||||
await link.sendMessage({ description });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const transmit = (ice: { urls: string; username: string; credential: string }[]) => {
|
|
||||||
const peerConnection = new RTCPeerConnection({ iceServers: ice });
|
|
||||||
peerConnection.addEventListener( 'connectionstatechange', event => {
|
|
||||||
console.log("CONNECTION STATE", event);
|
|
||||||
});
|
|
||||||
peerConnection.addEventListener( 'icecandidate', event => {
|
|
||||||
peerCandidate(event.candidate);
|
|
||||||
});
|
|
||||||
peerConnection.addEventListener( 'icecandidateerror', event => {
|
|
||||||
console.log("ICE ERROR");
|
|
||||||
});
|
|
||||||
peerConnection.addEventListener( 'iceconnectionstatechange', event => {
|
|
||||||
console.log("ICE STATE CHANGE", event);
|
|
||||||
});
|
|
||||||
peerConnection.addEventListener( 'negotiationneeded', event => {
|
|
||||||
peerNegotiate();
|
|
||||||
});
|
|
||||||
peerConnection.addEventListener( 'signalingstatechange', event => {
|
|
||||||
console.log("ICE SIGNALING", event);
|
|
||||||
});
|
|
||||||
peerConnection.addEventListener( 'track', event => {
|
|
||||||
//stream.current.addTrack(event.track, stream.current);
|
|
||||||
});
|
|
||||||
return peerConnection;
|
|
||||||
}
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (app.state.session) {
|
|
||||||
const setRinging = (ringing: { cardId: string, callId: string }[]) => {
|
|
||||||
updateState({ ringing });
|
|
||||||
}
|
|
||||||
const setContacts = (cards: Card[]) => {
|
|
||||||
updateState({ cards });
|
|
||||||
}
|
|
||||||
const ring = app.state.session.getRing();
|
|
||||||
ring.addRingingListener(setRinging);
|
|
||||||
const contact = app.state.session.getContact();
|
|
||||||
contact.addCardListener(setContacts);
|
|
||||||
return () => {
|
|
||||||
ring.removeRingingListener(setRinging);
|
|
||||||
contact.removeCardListener(setContacts);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, [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;
|
|
||||||
stream.current = null;
|
|
||||||
updateState({ calling: null, audio: null, video: null });
|
|
||||||
},
|
|
||||||
accept: async (callId: string, call: Call) => {
|
|
||||||
if (call.current) {
|
|
||||||
throw new Error('active call in progress');
|
|
||||||
}
|
|
||||||
const { cardId, node } = call;
|
|
||||||
const ring = app.state.session.getRing();
|
|
||||||
const link = await ring.accept(cardId, callId, node);
|
|
||||||
const ice = link.getIce();
|
|
||||||
const peer = transmit(ice);
|
|
||||||
const policy = 'impolite';
|
|
||||||
const candidates = [];
|
|
||||||
call.current = { policy, peer, link, candidates };
|
|
||||||
link.setStatusListener(linkStatus);
|
|
||||||
link.setMessageListener(linkMessage);
|
|
||||||
updateState({ calling: call.card });
|
|
||||||
},
|
|
||||||
call: async (cardId: string) => {
|
|
||||||
if (call.current) {
|
|
||||||
throw new Error('active call in proegress');
|
|
||||||
}
|
|
||||||
const card = state.cards.find(contact => contact.cardId === cardId);
|
|
||||||
if (!card) {
|
|
||||||
throw new Error('calling contact not found');
|
|
||||||
}
|
|
||||||
const contact = app.state.session.getContact();
|
|
||||||
const link = await contact.callCard(cardId);
|
|
||||||
const ice = link.getIce();
|
|
||||||
const peer = transmit(ice);
|
|
||||||
const policy = 'polite';
|
|
||||||
const candidates = [];
|
|
||||||
call.current = { policy, peer, link, candidates };
|
|
||||||
link.setStatusListener(linkStatus);
|
|
||||||
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 });
|
|
||||||
}
|
|
||||||
},
|
|
||||||
enableAudio: async () => {
|
|
||||||
if (!call.current || !state.audio || !state.audioAdded) {
|
|
||||||
throw new Error('cannot unmute audio');
|
|
||||||
}
|
|
||||||
state.audio.enabled = true;
|
|
||||||
updateState({ audioEnabled: true });
|
|
||||||
},
|
|
||||||
disableAudio: () => {
|
|
||||||
if (!call.current || !state.audio || !state.audioAdded) {
|
|
||||||
throw new Error('cannot mute audio');
|
|
||||||
}
|
|
||||||
state.audio.enabled = false;
|
|
||||||
updateState({ audioEnabled: false });
|
|
||||||
},
|
|
||||||
enableVideo: () => {
|
|
||||||
if (!call.current || !state.video) {
|
|
||||||
throw new Error('cannot start video');
|
|
||||||
}
|
|
||||||
if (!state.videoAdded) {
|
|
||||||
call.current.peer.addTrack(state.video, stream.current);
|
|
||||||
const local = new MediaStream();
|
|
||||||
local.addTrack(state.video, local);
|
|
||||||
updateState({ local });
|
|
||||||
}
|
|
||||||
state.video.enabled = true;
|
|
||||||
updateState({ videoAdded: true, videoEnabled: true });
|
|
||||||
},
|
|
||||||
disableVideo: () => {
|
|
||||||
if (!call.current || !state.video) {
|
|
||||||
throw new Error('cannot stop video');
|
|
||||||
}
|
|
||||||
state.video.enabled = false;
|
|
||||||
updateState({ videoEnabled: false });
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
return { state, actions }
|
|
||||||
}
|
|
@ -1 +0,0 @@
|
|||||||
/Users/rolandosborne/Library/Developer/CoreSimulator/Devices/A017730F-AC73-4ABE-AE12-C105BCEA1AD5/data/Containers/Data/Application/DC451C73-674B-47A7-B70C-D76CB45E7F8C/Library/LocalDatabase/db_v239.db
|
|
@ -1,213 +0,0 @@
|
|||||||
import React, {useState} from 'react';
|
|
||||||
import {SafeAreaView, View, useColorScheme} from 'react-native';
|
|
||||||
import {styles} from './Session.styled';
|
|
||||||
import {BottomNavigation, Surface, Text} from 'react-native-paper';
|
|
||||||
import {Settings} from '../settings/Settings';
|
|
||||||
import {Channels} from '../channels/Channels';
|
|
||||||
import {Contacts} from '../contacts/Contacts';
|
|
||||||
import {Registry} from '../registry/Registry';
|
|
||||||
import {Profile, ContactParams} from '../profile/Profile';
|
|
||||||
import {Details} from '../details/Details';
|
|
||||||
import {Identity} from '../identity/Identity';
|
|
||||||
import {useSession} from './useSession.hook';
|
|
||||||
|
|
||||||
import {
|
|
||||||
NavigationContainer,
|
|
||||||
DefaultTheme,
|
|
||||||
DarkTheme,
|
|
||||||
} from '@react-navigation/native';
|
|
||||||
import {createDrawerNavigator} from '@react-navigation/drawer';
|
|
||||||
import { createStackNavigator } from '@react-navigation/stack';
|
|
||||||
|
|
||||||
const SettingsDrawer = createDrawerNavigator();
|
|
||||||
const ContactsDrawer = createDrawerNavigator();
|
|
||||||
const RegistryDrawer = createDrawerNavigator();
|
|
||||||
const ProfileDrawer = createDrawerNavigator();
|
|
||||||
const DetailsDrawer = createDrawerNavigator();
|
|
||||||
|
|
||||||
const ContactStack = createStackNavigator();
|
|
||||||
const TopicStack = createStackNavigator();
|
|
||||||
|
|
||||||
export function Session() {
|
|
||||||
const {state} = useSession();
|
|
||||||
const scheme = useColorScheme();
|
|
||||||
const [index, setIndex] = useState(0);
|
|
||||||
const [routes] = useState([
|
|
||||||
{
|
|
||||||
key: 'channels',
|
|
||||||
title: 'Channels',
|
|
||||||
focusedIcon: 'comment-multiple',
|
|
||||||
unfocusedIcon: 'comment-multiple-outline',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'contacts',
|
|
||||||
title: 'Contacts',
|
|
||||||
focusedIcon: 'contacts',
|
|
||||||
unfocusedIcon: 'contacts-outline',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'settings',
|
|
||||||
title: 'Settings',
|
|
||||||
focusedIcon: 'cog',
|
|
||||||
unfocusedIcon: 'cog-outline',
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
const sessionNav = {strings: state.strings};
|
|
||||||
|
|
||||||
const ChannelsRoute = () => <Channels />;
|
|
||||||
const SettingsRoute = () => <Settings showLogout={true} />;
|
|
||||||
|
|
||||||
const renderScene = BottomNavigation.SceneMap({
|
|
||||||
channels: ChannelsRoute,
|
|
||||||
contacts: () => <ContactTab />,
|
|
||||||
settings: SettingsRoute,
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
|
||||||
<View style={styles.screen}>
|
|
||||||
{state.layout !== 'large' && (
|
|
||||||
<NavigationContainer
|
|
||||||
theme={scheme === 'dark' ? DarkTheme : DefaultTheme}>
|
|
||||||
<BottomNavigation
|
|
||||||
screenOptions={{lazy: false}}
|
|
||||||
barStyle={{ height: 92 }}
|
|
||||||
labeled={false}
|
|
||||||
navigationState={{index, routes}}
|
|
||||||
onIndexChange={setIndex}
|
|
||||||
renderScene={renderScene}
|
|
||||||
/>
|
|
||||||
</NavigationContainer>
|
|
||||||
)}
|
|
||||||
{state.layout === 'large' && (
|
|
||||||
<NavigationContainer
|
|
||||||
theme={scheme === 'dark' ? DarkTheme : DefaultTheme}>
|
|
||||||
<DetailsScreen nav={sessionNav} />
|
|
||||||
</NavigationContainer>
|
|
||||||
)}
|
|
||||||
</View>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function ContactTab() {
|
|
||||||
return (
|
|
||||||
<SafeAreaView style={{ width: '100%', height: '100%' }}>
|
|
||||||
<ContactStack.Navigator initialRouteName="contacts" screenOptions={{ headerShown: false }}>
|
|
||||||
<ContactStack.Screen name="contacts" options={{ headerBackTitleVisible: false }}>
|
|
||||||
{(props) => <Contacts openRegistry={()=>{console.log('openreg')}} openContact={(params: ContactParams)=>{console.log('opencon', params)}} />}
|
|
||||||
</ContactStack.Screen>
|
|
||||||
</ContactStack.Navigator>
|
|
||||||
</SafeAreaView>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function DetailsScreen({nav}) {
|
|
||||||
return (
|
|
||||||
<DetailsDrawer.Navigator
|
|
||||||
id="DetailsDrawer"
|
|
||||||
drawerContent={Details}
|
|
||||||
screenOptions={{
|
|
||||||
drawerPosition: 'right',
|
|
||||||
drawerType: 'front',
|
|
||||||
headerShown: false,
|
|
||||||
}}>
|
|
||||||
<DetailsDrawer.Screen name="details">
|
|
||||||
{({navigation}) => (
|
|
||||||
<ProfileScreen nav={{...nav, details: navigation}} />
|
|
||||||
)}
|
|
||||||
</DetailsDrawer.Screen>
|
|
||||||
</DetailsDrawer.Navigator>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function ProfileScreen({nav}) {
|
|
||||||
return (
|
|
||||||
<ProfileDrawer.Navigator
|
|
||||||
id="ProfileDrawer"
|
|
||||||
drawerContent={Profile}
|
|
||||||
screenOptions={{
|
|
||||||
drawerPosition: 'right',
|
|
||||||
drawerType: 'front',
|
|
||||||
headerShown: false,
|
|
||||||
}}>
|
|
||||||
<ProfileDrawer.Screen name="registry">
|
|
||||||
{({navigation}) => (
|
|
||||||
<RegistryScreen nav={{...nav, profile: navigation}} />
|
|
||||||
)}
|
|
||||||
</ProfileDrawer.Screen>
|
|
||||||
</ProfileDrawer.Navigator>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function RegistryScreen({nav}) {
|
|
||||||
return (
|
|
||||||
<RegistryDrawer.Navigator
|
|
||||||
id="RegistryDrawer"
|
|
||||||
drawerContent={Registry}
|
|
||||||
screenOptions={{
|
|
||||||
drawerPosition: 'right',
|
|
||||||
drawerType: 'front',
|
|
||||||
headerShown: false,
|
|
||||||
}}>
|
|
||||||
<RegistryDrawer.Screen name="contacts">
|
|
||||||
{({navigation}) => (
|
|
||||||
<ContactsScreen nav={{...nav, registry: navigation}} />
|
|
||||||
)}
|
|
||||||
</RegistryDrawer.Screen>
|
|
||||||
</RegistryDrawer.Navigator>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function ContactsScreen({nav}) {
|
|
||||||
return (
|
|
||||||
<ContactsDrawer.Navigator
|
|
||||||
id="ContactsDrawer"
|
|
||||||
drawerContent={() => <Contacts onRegistry={()=>{console.log('openreg')}} openContact={(params: ContactParams)=>{console.log('opencon', params)}} />}
|
|
||||||
screenOptions={{
|
|
||||||
drawerPosition: 'right',
|
|
||||||
drawerType: 'front',
|
|
||||||
headerShown: false,
|
|
||||||
}}>
|
|
||||||
<ContactsDrawer.Screen name="settings">
|
|
||||||
{({navigation}) => (
|
|
||||||
<SettingsScreen nav={{...nav, contacts: navigation}} />
|
|
||||||
)}
|
|
||||||
</ContactsDrawer.Screen>
|
|
||||||
</ContactsDrawer.Navigator>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function SettingsScreen({nav}) {
|
|
||||||
return (
|
|
||||||
<SettingsDrawer.Navigator
|
|
||||||
id="SettingsDrawer"
|
|
||||||
drawerContent={Settings}
|
|
||||||
screenOptions={{
|
|
||||||
drawerStyle: {width: '40%'},
|
|
||||||
drawerPosition: 'right',
|
|
||||||
drawerType: 'front',
|
|
||||||
headerShown: false,
|
|
||||||
}}>
|
|
||||||
<SettingsDrawer.Screen name="home">
|
|
||||||
{({navigation}) => <HomeScreen nav={{...nav, settings: navigation}} />}
|
|
||||||
</SettingsDrawer.Screen>
|
|
||||||
</SettingsDrawer.Navigator>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function HomeScreen({nav}) {
|
|
||||||
return (
|
|
||||||
<View style={styles.frame}>
|
|
||||||
<View style={styles.left}>
|
|
||||||
<Surface elevation={2} mode="flat">
|
|
||||||
<Identity openSettings={nav.settings.openDrawer} />
|
|
||||||
</Surface>
|
|
||||||
<Surface style={styles.channels} elevation={1} mode="flat">
|
|
||||||
<Text>CHANNELS</Text>
|
|
||||||
</Surface>
|
|
||||||
</View>
|
|
||||||
<View style={styles.right}>
|
|
||||||
<Text>CONVERSATION</Text>
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
);
|
|
||||||
}
|
|
@ -1,73 +0,0 @@
|
|||||||
message:
|
|
||||||
| header
|
|
||||||
| share
|
|
||||||
| edit
|
|
||||||
| delete
|
|
||||||
| block
|
|
||||||
| report
|
|
||||||
| text
|
|
||||||
| carousel
|
|
||||||
| image
|
|
||||||
| audio
|
|
||||||
| video
|
|
||||||
| binary
|
|
||||||
| sanitize and tap
|
|
||||||
| unlock seal while open
|
|
||||||
| thumb as object not callback
|
|
||||||
| show skeleton until confirmed
|
|
||||||
| show skeleton until asset ready
|
|
||||||
add:
|
|
||||||
| text
|
|
||||||
| carousel
|
|
||||||
| image
|
|
||||||
| audio
|
|
||||||
| video
|
|
||||||
| binary
|
|
||||||
header:
|
|
||||||
| status icons
|
|
||||||
| details icon
|
|
||||||
details:
|
|
||||||
| subject
|
|
||||||
| actions
|
|
||||||
| members
|
|
||||||
channels:
|
|
||||||
- use names
|
|
||||||
| highlight active
|
|
||||||
| sdk dont parse unconfirmed: warn: invalid sealed topic
|
|
||||||
| show unseale summary
|
|
||||||
contacts:
|
|
||||||
| avoid self connection
|
|
||||||
| direct message
|
|
||||||
settings:
|
|
||||||
| blocked items
|
|
||||||
|
|
||||||
*****
|
|
||||||
|
|
||||||
contact indicator
|
|
||||||
call module
|
|
||||||
welcome component
|
|
||||||
node admin
|
|
||||||
share sheet [mobile]
|
|
||||||
splash screen [mobile]
|
|
||||||
avoid flash of access [mobile]
|
|
||||||
tool tips [web]
|
|
||||||
manage block entities
|
|
||||||
persist offsync contacts [web]
|
|
||||||
merge strings
|
|
||||||
light theme
|
|
||||||
|
|
||||||
*****
|
|
||||||
|
|
||||||
fdroid w/ unified push support
|
|
||||||
deploy docker/ios/android/fdroid/openwrt
|
|
||||||
api updates
|
|
||||||
group management
|
|
||||||
reactions and read receipt
|
|
||||||
photo album
|
|
||||||
music playlist
|
|
||||||
repeater bot
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
x
Reference in New Issue
Block a user