mirror of
https://github.com/balzack/databag.git
synced 2025-04-19 16:15:16 +00:00
fixed lint warnings
This commit is contained in:
parent
1c93ad161f
commit
6c4f0f1181
@ -1,6 +1,6 @@
|
||||
import { Staging } from 'databag-client-sdk'
|
||||
import { Staging } from 'databag-client-sdk';
|
||||
import RNFS from 'react-native-fs';
|
||||
import fileType from 'react-native-file-type'
|
||||
import fileType from 'react-native-file-type';
|
||||
|
||||
export class StagingFiles implements Staging {
|
||||
|
||||
@ -10,7 +10,7 @@ export class StagingFiles implements Staging {
|
||||
if (entry.name.startsWith('dbTmp_')) {
|
||||
await RNFS.unlink(entry.path);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public async read(source: any): Promise<{ size: number, getData: (position: number, length: number)=>Promise<string>, close: ()=>Promise<void> }> {
|
||||
@ -19,19 +19,19 @@ export class StagingFiles implements Staging {
|
||||
const size = stat.size;
|
||||
const getData = async (position: number, length: number) => {
|
||||
return await RNFS.read(path, length, position, 'base64');
|
||||
}
|
||||
const close = async ()=>{}
|
||||
};
|
||||
const close = async ()=>{};
|
||||
return { size, getData, close };
|
||||
}
|
||||
|
||||
public async write(): Promise<{ setData: (data: string)=>Promise<void>, getUrl: ()=>Promise<string>, close: ()=>Promise<void> }> {
|
||||
let set = false;
|
||||
let extension = '';
|
||||
const path = RNFS.DocumentDirectoryPath + `/dbTmp_${Date.now()}`
|
||||
const path = RNFS.DocumentDirectoryPath + `/dbTmp_${Date.now()}`;
|
||||
const setData = async (data: string) => {
|
||||
set = true;
|
||||
await RNFS.appendFile(path, data, 'base64');
|
||||
}
|
||||
};
|
||||
const getUrl = async () => {
|
||||
if (!extension) {
|
||||
try {
|
||||
@ -44,8 +44,8 @@ export class StagingFiles implements Staging {
|
||||
extension = '.dat';
|
||||
}
|
||||
}
|
||||
return `file://${path}${extension}`
|
||||
}
|
||||
return `file://${path}${extension}`;
|
||||
};
|
||||
const close = async () => {
|
||||
if (set) {
|
||||
try {
|
||||
@ -54,8 +54,8 @@ export class StagingFiles implements Staging {
|
||||
console.log(err);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
return { setData, getUrl, close };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -68,6 +68,8 @@ export const styles = StyleSheet.create({
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
gap: 8,
|
||||
minHeight: 0,
|
||||
flexGrow: 1,
|
||||
},
|
||||
scroll: {
|
||||
flexGrow: 1,
|
||||
@ -190,10 +192,6 @@ export const styles = StyleSheet.create({
|
||||
modalClose: {
|
||||
backgroundColor: 'transparent',
|
||||
},
|
||||
frame: {
|
||||
minHeight: 0,
|
||||
flexGrow: 1,
|
||||
},
|
||||
line: {
|
||||
width: '100%',
|
||||
},
|
||||
|
@ -80,8 +80,8 @@ export function Access() {
|
||||
autoCapitalize="none"
|
||||
autoComplete="off"
|
||||
autoCorrect={false}
|
||||
label={Platform.OS==='ios'?state.strings.server:undefined}
|
||||
placeholder={Platform.OS!=='ios'?state.strings.server:undefined}
|
||||
label={Platform.OS === 'ios' ? state.strings.server : undefined}
|
||||
placeholder={Platform.OS !== 'ios' ? state.strings.server : undefined}
|
||||
value={state.node}
|
||||
left={<TextInput.Icon style={styles.icon} icon="server" />}
|
||||
onChangeText={value => actions.setNode(value)}
|
||||
@ -92,8 +92,8 @@ export function Access() {
|
||||
autoCapitalize="none"
|
||||
autoComplete="off"
|
||||
autoCorrect={false}
|
||||
label={Platform.OS==='ios'?state.strings.username:undefined}
|
||||
placeholder={Platform.OS!=='ios'?state.strings.username:undefined}
|
||||
label={Platform.OS === 'ios' ? state.strings.username : undefined}
|
||||
placeholder={Platform.OS !== 'ios' ? state.strings.username : undefined}
|
||||
value={state.username}
|
||||
left={<TextInput.Icon style={styles.icon} icon="account" />}
|
||||
onChangeText={value => actions.setUsername(value)}
|
||||
@ -105,8 +105,8 @@ export function Access() {
|
||||
autoComplete="off"
|
||||
autoCorrect={false}
|
||||
value={state.password}
|
||||
label={Platform.OS==='ios'?state.strings.password:undefined}
|
||||
placeholder={Platform.OS!=='ios'?state.strings.password:undefined}
|
||||
label={Platform.OS === 'ios' ? state.strings.password : undefined}
|
||||
placeholder={Platform.OS !== 'ios' ? state.strings.password : undefined}
|
||||
secureTextEntry={!showPassword}
|
||||
left={<TextInput.Icon style={styles.icon} icon="lock" />}
|
||||
right={
|
||||
@ -152,8 +152,8 @@ export function Access() {
|
||||
autoCapitalize="none"
|
||||
autoComplete="off"
|
||||
autoCorrect={false}
|
||||
label={Platform.OS==='ios'?state.strings.token:undefined}
|
||||
placeholder={Platform.OS!=='ios'?state.strings.token:undefined}
|
||||
label={Platform.OS === 'ios' ? state.strings.token : undefined}
|
||||
placeholder={Platform.OS !== 'ios' ? state.strings.token : undefined}
|
||||
left={<TextInput.Icon style={styles.icon} icon="ticket-account" />}
|
||||
onChangeText={value => actions.setToken(value)}
|
||||
/>
|
||||
@ -163,8 +163,8 @@ export function Access() {
|
||||
autoCapitalize="none"
|
||||
autoComplete="off"
|
||||
autoCorrect={false}
|
||||
label={Platform.OS==='ios'?state.strings.server:undefined}
|
||||
placeholder={Platform.OS!=='ios'?state.strings.server:undefined}
|
||||
label={Platform.OS === 'ios' ? state.strings.server : undefined}
|
||||
placeholder={Platform.OS !== 'ios' ? state.strings.server : undefined}
|
||||
value={state.node}
|
||||
left={<TextInput.Icon style={styles.icon} icon="server" />}
|
||||
onChangeText={value => actions.setNode(value)}
|
||||
@ -198,8 +198,8 @@ export function Access() {
|
||||
autoCapitalize="none"
|
||||
autoComplete="off"
|
||||
autoCorrect={false}
|
||||
label={Platform.OS==='ios'?state.strings.token:undefined}
|
||||
placeholder={Platform.OS!=='ios'?state.strings.token:undefined}
|
||||
label={Platform.OS === 'ios' ? state.strings.token : undefined}
|
||||
placeholder={Platform.OS !== 'ios' ? state.strings.token : undefined}
|
||||
left={<TextInput.Icon style={styles.icon} icon="ticket-account" />}
|
||||
onChangeText={value => actions.setToken(value)}
|
||||
/>
|
||||
@ -211,8 +211,8 @@ export function Access() {
|
||||
autoCapitalize="none"
|
||||
autoComplete="off"
|
||||
autoCorrect={false}
|
||||
label={Platform.OS==='ios'?state.strings.server:undefined}
|
||||
placeholder={Platform.OS!=='ios'?state.strings.server:undefined}
|
||||
label={Platform.OS === 'ios' ? state.strings.server : undefined}
|
||||
placeholder={Platform.OS !== 'ios' ? state.strings.server : undefined}
|
||||
value={state.node}
|
||||
left={<TextInput.Icon style={styles.icon} icon="server" />}
|
||||
onChangeText={value => actions.setNode(value)}
|
||||
@ -224,8 +224,8 @@ export function Access() {
|
||||
autoComplete="off"
|
||||
autoCorrect={false}
|
||||
error={state.taken}
|
||||
label={Platform.OS==='ios'?state.strings.username:undefined}
|
||||
placeholder={Platform.OS!=='ios'?state.strings.username:undefined}
|
||||
label={Platform.OS === 'ios' ? state.strings.username : undefined}
|
||||
placeholder={Platform.OS !== 'ios' ? state.strings.username : undefined}
|
||||
value={state.username}
|
||||
left={<TextInput.Icon style={styles.icon} icon="account" />}
|
||||
onChangeText={value => actions.setUsername(value)}
|
||||
@ -238,8 +238,8 @@ export function Access() {
|
||||
autoCorrect={false}
|
||||
textContentType={'oneTimeCode'}
|
||||
value={state.password}
|
||||
label={Platform.OS==='ios'?state.strings.password:undefined}
|
||||
placeholder={Platform.OS!=='ios'?state.strings.password:undefined}
|
||||
label={Platform.OS === 'ios' ? state.strings.password : undefined}
|
||||
placeholder={Platform.OS !== 'ios' ? state.strings.password : undefined}
|
||||
secureTextEntry={!showPassword}
|
||||
left={<TextInput.Icon style={styles.icon} icon="lock" />}
|
||||
right={
|
||||
@ -259,8 +259,8 @@ export function Access() {
|
||||
autoCorrect={false}
|
||||
textContentType={'oneTimeCode'}
|
||||
value={state.confirm}
|
||||
label={Platform.OS==='ios'?state.strings.confirmPassword:undefined}
|
||||
placeholder={Platform.OS!=='ios'?state.strings.confirmPassword:undefined}
|
||||
label={Platform.OS === 'ios' ? state.strings.confirmPassword : undefined}
|
||||
placeholder={Platform.OS !== 'ios' ? state.strings.confirmPassword : undefined}
|
||||
secureTextEntry={!showConfirm}
|
||||
left={<TextInput.Icon style={styles.icon} icon="lock" />}
|
||||
right={
|
||||
@ -299,8 +299,8 @@ export function Access() {
|
||||
autoCapitalize="none"
|
||||
autoComplete="off"
|
||||
autoCorrect={false}
|
||||
label={Platform.OS==='ios'?state.strings.server:undefined}
|
||||
placeholder={Platform.OS!=='ios'?state.strings.server:undefined}
|
||||
label={Platform.OS === 'ios' ? state.strings.server : undefined}
|
||||
placeholder={Platform.OS !== 'ios' ? state.strings.server : undefined}
|
||||
value={state.node}
|
||||
left={<TextInput.Icon style={styles.icon} icon="server" />}
|
||||
onChangeText={value => actions.setNode(value)}
|
||||
@ -311,8 +311,8 @@ export function Access() {
|
||||
autoCapitalize="none"
|
||||
autoComplete="off"
|
||||
autoCorrect={false}
|
||||
label={Platform.OS==='ios'?state.strings.password:undefined}
|
||||
placeholder={Platform.OS!=='ios'?state.strings.password:undefined}
|
||||
label={Platform.OS === 'ios' ? state.strings.password : undefined}
|
||||
placeholder={Platform.OS !== 'ios' ? state.strings.password : undefined}
|
||||
value={state.password}
|
||||
secureTextEntry={!showPassword}
|
||||
left={<TextInput.Icon style={styles.icon} icon="lock" />}
|
||||
|
@ -32,7 +32,7 @@ export function useAccess() {
|
||||
|
||||
useEffect(() => {
|
||||
SplashScreen.hide();
|
||||
}, []);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const {username, token, node, secure, mode} = state;
|
||||
|
@ -1,5 +1,5 @@
|
||||
import React, {useEffect, useState} from 'react';
|
||||
import {FlatList, SafeAreaView, Image, View, Pressable, TouchableOpacity, Modal} from 'react-native';
|
||||
import {FlatList, View, TouchableOpacity, Modal} from 'react-native';
|
||||
import {Text, Button, IconButton, Divider, Surface, Icon, useTheme} from 'react-native-paper';
|
||||
import {useAccounts} from './useAccounts.hook';
|
||||
import {styles} from './Accounts.styled';
|
||||
@ -27,6 +27,7 @@ export function Accounts({ setup }: { setup: ()=>void }) {
|
||||
|
||||
useEffect(() => {
|
||||
loadAccounts();
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
const loadAccounts = async () => {
|
||||
@ -39,7 +40,7 @@ export function Accounts({ setup }: { setup: ()=>void }) {
|
||||
}
|
||||
setLoading(false);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const accessAccount = async (accountId: number) => {
|
||||
if (!accessing) {
|
||||
@ -54,7 +55,7 @@ export function Accounts({ setup }: { setup: ()=>void }) {
|
||||
}
|
||||
setAccessing(null);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const addAccount = async () => {
|
||||
if (!adding) {
|
||||
@ -69,14 +70,14 @@ export function Accounts({ setup }: { setup: ()=>void }) {
|
||||
}
|
||||
setAdding(false);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const failedParams = {
|
||||
title: state.strings.operationFailed,
|
||||
prompt: state.strings.tryAgain,
|
||||
cancel: {
|
||||
label: state.strings.close,
|
||||
action: ()=>{setFailed(false)},
|
||||
action: ()=>{setFailed(false);},
|
||||
},
|
||||
};
|
||||
|
||||
@ -91,7 +92,7 @@ export function Accounts({ setup }: { setup: ()=>void }) {
|
||||
}
|
||||
setBlocking(null);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const removeAccount = (accountId: number) => {
|
||||
if (!remove) {
|
||||
@ -116,12 +117,12 @@ export function Accounts({ setup }: { setup: ()=>void }) {
|
||||
},
|
||||
cancel: {
|
||||
label: state.strings.cancel,
|
||||
action: () => {setRemove(false)},
|
||||
action: () => {setRemove(false);},
|
||||
},
|
||||
})
|
||||
});
|
||||
setRemove(true);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const copyToken = async () => {
|
||||
if (!tokenCopy) {
|
||||
@ -159,12 +160,12 @@ export function Accounts({ setup }: { setup: ()=>void }) {
|
||||
showsVerticalScrollIndicator={false}
|
||||
renderItem={({item}) => {
|
||||
const options = [
|
||||
<IconButton key="disable" style={styles.icon} loading={accessing === item.accountId} iconColor={Colors.primary} mode="contained" icon="lock-open-variant-outline" onPress={() => {accessAccount(item.accountId)}} />,
|
||||
<IconButton key="reset" style={styles.icon} loading={blocking === item.accountId} iconColor={Colors.pending} mode="contained" icon={item.disabled ? 'account-check-outline' : 'account-cancel-outline'} onPress={()=>{blockAccount(item.accountId, !item.disabled)}} />,
|
||||
<IconButton key="remove" style={styles.icon} loading={removing === item.accountId} iconColor={Colors.offsync} mode="contained" icon="trash-can-outline" onPress={()=>{removeAccount(item.accountId)}} />
|
||||
<IconButton key="disable" style={styles.icon} loading={accessing === item.accountId} iconColor={Colors.primary} mode="contained" icon="lock-open-variant-outline" onPress={() => {accessAccount(item.accountId);}} />,
|
||||
<IconButton key="reset" style={styles.icon} loading={blocking === item.accountId} iconColor={Colors.pending} mode="contained" icon={item.disabled ? 'account-check-outline' : 'account-cancel-outline'} onPress={()=>{blockAccount(item.accountId, !item.disabled);}} />,
|
||||
<IconButton key="remove" style={styles.icon} loading={removing === item.accountId} iconColor={Colors.offsync} mode="contained" icon="trash-can-outline" onPress={()=>{removeAccount(item.accountId);}} />,
|
||||
];
|
||||
return (
|
||||
<Card
|
||||
<Card
|
||||
containerStyle={{
|
||||
...styles.card,
|
||||
borderColor: theme.colors.outlineVariant,
|
||||
|
@ -1,4 +1,4 @@
|
||||
import {useEffect, useState, useContext, useRef} from 'react';
|
||||
import {useEffect, useState, useContext} from 'react';
|
||||
import {AppContext} from '../context/AppContext';
|
||||
import {DisplayContext} from '../context/DisplayContext';
|
||||
import {ContextType} from '../context/ContextType';
|
||||
@ -31,7 +31,7 @@ export function useAccounts() {
|
||||
updateState({ loading: false });
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const { layout, strings} = display.state;
|
||||
|
@ -1,4 +1,4 @@
|
||||
import React from 'react'
|
||||
import React from 'react';
|
||||
import { View, Image } from 'react-native';
|
||||
import {useTheme, Text, Icon} from 'react-native-paper';
|
||||
import { styles } from './Base.styled';
|
||||
@ -9,13 +9,13 @@ import { Colors } from '../constants/Colors';
|
||||
|
||||
export function Base() {
|
||||
const theme = useTheme();
|
||||
const { state, actions } = useBase();
|
||||
const { state } = useBase();
|
||||
|
||||
return (
|
||||
<View style={{ ...styles.base, backgroundColor: theme.colors.base }}>
|
||||
<Text style={styles.title}>Databag</Text>
|
||||
<Text style={styles.description}>{ state.strings.communication }</Text>
|
||||
<Image style={styles.image} source={theme.colors.name == 'light' ? light : dark} resizeMode="contain" />
|
||||
<Image style={styles.image} source={theme.colors.name === 'light' ? light : dark} resizeMode="contain" />
|
||||
{ (state.profileSet === false || state.cardSet === false || state.channelSet === false) && (
|
||||
<View style={styles.steps}>
|
||||
{ state.profileSet === false && (
|
||||
|
@ -1,48 +1,48 @@
|
||||
import { useState, useContext, useEffect } from 'react'
|
||||
import { useState, useContext, useEffect } from 'react';
|
||||
import { DisplayContext } from '../context/DisplayContext';
|
||||
import { AppContext } from '../context/AppContext';
|
||||
import { ContextType } from '../context/ContextType'
|
||||
import { ContextType } from '../context/ContextType';
|
||||
|
||||
export function useBase() {
|
||||
const app = useContext(AppContext) as ContextType
|
||||
const display = useContext(DisplayContext) as ContextType
|
||||
const app = useContext(AppContext) as ContextType;
|
||||
const display = useContext(DisplayContext) as ContextType;
|
||||
const [state, setState] = useState({
|
||||
strings: display.state.strings,
|
||||
profileSet: null as null | boolean,
|
||||
cardSet: null as null | boolean,
|
||||
channelSet: null as null | boolean,
|
||||
})
|
||||
});
|
||||
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const updateState = (value: any) => {
|
||||
setState((s) => ({ ...s, ...value }))
|
||||
}
|
||||
setState((s) => ({ ...s, ...value }));
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const setProfile = (profile: Profile) => {
|
||||
updateState({ profileSet: Boolean(profile.name) });
|
||||
}
|
||||
};
|
||||
const setCards = (cards: Card[]) => {
|
||||
updateState({ cardSet: cards.length > 0 });
|
||||
}
|
||||
};
|
||||
const setChannels = ({ channels, cardId }: { channels: Channel[]; cardId: string | null }) => {
|
||||
updateState({ channelSet: channels.length > 0 });
|
||||
}
|
||||
updateState({ channelSet: cardId && channels.length > 0 });
|
||||
};
|
||||
|
||||
const { identity, contact, content } = app.state.session
|
||||
identity.addProfileListener(setProfile)
|
||||
contact.addCardListener(setCards)
|
||||
content.addChannelListener(setChannels)
|
||||
const { identity, contact, content } = app.state.session;
|
||||
identity.addProfileListener(setProfile);
|
||||
contact.addCardListener(setCards);
|
||||
content.addChannelListener(setChannels);
|
||||
|
||||
return () => {
|
||||
identity.removeProfileListener(setProfile);
|
||||
contact.removeCardListener(setCards);
|
||||
content.removeChannelListener(setChannels);
|
||||
}
|
||||
}, []);
|
||||
};
|
||||
}, [app.state.session]);
|
||||
|
||||
const actions = {
|
||||
}
|
||||
};
|
||||
|
||||
return { state, actions }
|
||||
return { state, actions };
|
||||
}
|
||||
|
@ -1,5 +1,4 @@
|
||||
import {StyleSheet} from 'react-native';
|
||||
import { Colors } from '../constants/Colors';
|
||||
|
||||
export const styles = StyleSheet.create({
|
||||
active: {
|
||||
|
@ -1,9 +1,8 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { Animated, useAnimatedValue, useWindowDimensions, Dimensions, Image, View } from 'react-native';
|
||||
import { Animated, useAnimatedValue, Image, 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 { styles } from './Call.styled';
|
||||
import { Text, Surface, IconButton } from 'react-native-paper';
|
||||
import { Confirm } from '../confirm/Confirm';
|
||||
import { Colors } from '../constants/Colors';
|
||||
import { RTCView } from 'react-native-webrtc';
|
||||
@ -14,11 +13,7 @@ export function Call() {
|
||||
const [ending, setEnding] = useState(false);
|
||||
const [applyingAudio, setApplyingAudio] = useState(false);
|
||||
const [applyingVideo, setApplyingVideo] = 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 {height, width} = useWindowDimensions();
|
||||
const opacity = useAnimatedValue(0)
|
||||
const opacity = useAnimatedValue(0);
|
||||
|
||||
const toggleAudio = async () => {
|
||||
if (!applyingAudio) {
|
||||
@ -35,7 +30,7 @@ export function Call() {
|
||||
}
|
||||
setApplyingAudio(false);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const toggleVideo = async () => {
|
||||
if (!applyingVideo) {
|
||||
@ -52,7 +47,7 @@ export function Call() {
|
||||
}
|
||||
setApplyingVideo(false);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const end = async () => {
|
||||
if (!ending) {
|
||||
@ -65,7 +60,7 @@ export function Call() {
|
||||
}
|
||||
setEnding(false);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const alertParams = {
|
||||
title: state.strings.operationFailed,
|
||||
@ -92,6 +87,7 @@ export function Call() {
|
||||
useNativeDriver: true,
|
||||
}).start();
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [state.calling, state.fullscreen]);
|
||||
|
||||
const viewStyle = state.calling && state.fullscreen ? styles.active : styles.inactive;
|
||||
@ -114,7 +110,7 @@ export function Call() {
|
||||
resizeMode="contain"
|
||||
source={{ uri: state.calling.imageUrl }}
|
||||
/>
|
||||
<Text style={styles.duration}>{ `${Math.floor(state.duration/60)}:${(state.duration % 60).toString().padStart(2, '0')}` }</Text>
|
||||
<Text style={styles.duration}>{ `${Math.floor(state.duration / 60)}:${(state.duration % 60).toString().padStart(2, '0')}` }</Text>
|
||||
</View>
|
||||
)}
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { useState, useContext, useEffect, useRef } from 'react'
|
||||
import { RingContext } from '../context/RingContext'
|
||||
import { DisplayContext } from '../context/DisplayContext'
|
||||
import { ContextType } from '../context/ContextType'
|
||||
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() {
|
||||
@ -25,12 +25,12 @@ export function useCall() {
|
||||
failed: false,
|
||||
width: 0,
|
||||
height: 0,
|
||||
})
|
||||
});
|
||||
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const updateState = (value: any) => {
|
||||
setState((s) => ({ ...s, ...value }))
|
||||
}
|
||||
setState((s) => ({ ...s, ...value }));
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const { width, height, strings } = display.state;
|
||||
@ -41,14 +41,14 @@ export function useCall() {
|
||||
const interval = setInterval(() => {
|
||||
if (offset.current) {
|
||||
const now = new Date();
|
||||
const duration = Math.floor((now.getTime() / 1000) - offsetTime.current);
|
||||
updateState({ duration });
|
||||
}
|
||||
const duration = Math.floor((now.getTime() / 1000) - offsetTime.current);
|
||||
updateState({ duration });
|
||||
}
|
||||
}, 1000);
|
||||
return () => {
|
||||
clearInterval(interval);
|
||||
}
|
||||
}, []);
|
||||
};
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const { calls, calling, fullscreen, localStream, remoteStream, remoteVideo, localVideo, audioEnabled, videoEnabled, connected, connectedTime, failed } = ring.state;
|
||||
|
@ -20,7 +20,7 @@ export const en = {
|
||||
server: 'Server',
|
||||
token: 'Token',
|
||||
delayMessage: 'Key generation can take several minutes.',
|
||||
noAccess: 'No Access',
|
||||
noAccess: 'No Access',
|
||||
connecting: 'Connecting',
|
||||
|
||||
setup: 'Setup',
|
||||
@ -294,7 +294,7 @@ export const en = {
|
||||
disable: 'Disable',
|
||||
confirmDisable: 'Disabling Multi-Factor Authentication',
|
||||
disablePrompt: 'Are you sure you want to disable multi-factor authentication',
|
||||
}
|
||||
};
|
||||
|
||||
export const fr = {
|
||||
viewTerms: 'Voir les conditions d\'utilisation',
|
||||
@ -590,7 +590,7 @@ export const fr = {
|
||||
disable: 'Désactiver',
|
||||
confirmDisable: "Désactivation de l'authentification multi-facteurs",
|
||||
disablePrompt: "Êtes-vous sûr de vouloir désactiver l'authentification multi-facteurs",
|
||||
}
|
||||
};
|
||||
|
||||
export const sp = {
|
||||
viewTerms: 'Ver los términos de servicio',
|
||||
@ -630,7 +630,7 @@ export const sp = {
|
||||
accessingLink: 'Utilice el siguiente enlace para acceder a la cuenta especificada',
|
||||
accessingToken: 'Utilice el siguiente token para acceder a la cuenta especificada desde la pantalla de inicio de sesión',
|
||||
|
||||
noAccess: 'Sin Acceso',
|
||||
noAccess: 'Sin Acceso',
|
||||
membership: 'Afiliación',
|
||||
channelHost: 'Anfitrión del Tema',
|
||||
channelGuest: 'Invitado de Tema',
|
||||
@ -885,7 +885,7 @@ export const sp = {
|
||||
disable: 'Desactivar',
|
||||
confirmDisable: 'Desactivación de la autenticación de dos factores',
|
||||
disablePrompt: '¿Estás seguro de que quieres desactivar la autenticación de dos factores?',
|
||||
}
|
||||
};
|
||||
|
||||
export const pt = {
|
||||
viewTerms: 'Ver os termos de serviço',
|
||||
@ -1180,7 +1180,7 @@ export const pt = {
|
||||
disable: 'Desativar',
|
||||
confirmDisable: 'Desativando Autenticação de Dois Fatores',
|
||||
disablePrompt: 'Tem certeza de que deseja desativar a autenticação de dois fatores?',
|
||||
}
|
||||
};
|
||||
|
||||
export const de = {
|
||||
viewTerms: 'Nutzungsbedingungen anzeigen',
|
||||
@ -1213,7 +1213,7 @@ export const de = {
|
||||
accounts: 'Konten',
|
||||
noAccounts: 'Keine Konten',
|
||||
selectShare: 'Wählen Sie Das Thema Zum Teilen Aus',
|
||||
|
||||
|
||||
addingTitle: 'Konto hinzufügen',
|
||||
addingLink: 'Verwenden Sie den folgenden Link, um ein Konto zu erstellen',
|
||||
addingToken: 'Verwenden Sie das folgende Token, um ein Konto vom Anmeldebildschirm aus zu erstellen',
|
||||
@ -1475,7 +1475,7 @@ export const de = {
|
||||
disable: 'Deaktivieren',
|
||||
confirmDisable: 'Deaktivierung der Zwei-Faktor-Authentifizierung',
|
||||
disablePrompt: 'Sind Sie sicher, dass Sie die Zwei-Faktor-Authentifizierung deaktivieren möchten?',
|
||||
}
|
||||
};
|
||||
|
||||
export const ru = {
|
||||
viewTerms: 'Просмотреть условия обслуживания',
|
||||
@ -1770,7 +1770,7 @@ export const ru = {
|
||||
disable: 'Отключить',
|
||||
confirmDisable: 'Отключение двухфакторной аутентификации',
|
||||
disablePrompt: 'Вы уверены, что хотите отключить двухфакторную аутентификацию?',
|
||||
}
|
||||
};
|
||||
|
||||
export const el = {
|
||||
viewTerms: 'Δείτε τους όρους υπηρεσίας',
|
||||
@ -1972,7 +1972,7 @@ export const el = {
|
||||
enableService: 'Υπηρεσία cloudflare',
|
||||
serviceHint: 'Ενεργοποίηση υπηρεσίας CloudFlare',
|
||||
serverUrl: 'URL διακομιστή WebRTC',
|
||||
urlHint: 'turn:ip:port?transport=udp',
|
||||
urlHint: 'turn:ip:port?transport=udp',
|
||||
webUsername: 'Όνομα χρήστη WebRTC',
|
||||
webPassword: 'Κωδικός πρόσβασης WebRTC',
|
||||
failedLoad: 'Αποτυχία φόρτωσης',
|
||||
@ -2037,7 +2037,7 @@ export const el = {
|
||||
disable: 'Καθιστώ ανίκανο',
|
||||
confirmDisable: 'Απενεργοποίηση ελέγχου ταυτότητας πολλαπλών παραγόντων',
|
||||
disablePrompt: 'Είστε βέβαιοι ότι θέλετε να απενεργοποιήσετε τον έλεγχο ταυτότητας πολλαπλών παραγόντων',
|
||||
}
|
||||
};
|
||||
|
||||
export function getLanguageStrings() {
|
||||
const locale = Platform.OS === 'ios' ? NativeModules.SettingsManager?.settings.AppleLocale || NativeModules.SettingsManager?.settings.AppleLanguages[0] : NativeModules.I18nManager?.localeIdentifier;
|
||||
|
@ -478,7 +478,7 @@ Falls Sie Fragen zu diesen Bedingungen oder zum Dienst haben, können Sie uns ü
|
||||
|
||||
Wir freuen uns, Sie als Teil der Databag-Community begrüßen zu dürfen, und wir hoffen, dass Sie unsere Dienste genießen.
|
||||
`,
|
||||
pt:
|
||||
pt:
|
||||
`Agradeço por se juntar à comunidade Databag! Estes Termos de Serviço (os "Termos") abrangem seus direitos e obrigações relacionados ao seu acesso e uso dos serviços da Databag, incluindo, mas não se limitando ao Databag (coletivamente, o "Serviço"). Todas as referências a "você", "seu" ou "usuário" se referem ao usuário do Serviço. Além destes Termos, por favor, reveja a Política de Privacidade da Databag, que descreve nossas práticas relacionadas à coleta e uso de suas informações. Estes Termos se aplicam também à nossa Política de Privacidade. Ao usar o Serviço, você declara e concorda que leu, entende e concorda em cumprir tanto estes Termos quanto nossa Política de Privacidade como acordos vinculativos. Além disso, você concorda que estes Termos e nossa Política de Privacidade se aplicam ao seu uso anterior, se houver.
|
||||
|
||||
POR FAVOR, LEIA CUIDADOSAMENTE ESTES TERMOS, POIS CONTÊM INFORMAÇÕES IMPORTANTES SOBRE SEUS DIREITOS E RESPONSABILIDADES, INCLUINDO A LIMITAÇÃO DE NOSSA RESPONSABILIDADE. SE VOCÊ NÃO ACEITAR ESTE ACORDO EM SUA TOTALIDADE, NÃO PODERÁ ACESSAR OU UTILIZAR O SERVIÇO.
|
||||
@ -825,5 +825,5 @@ support@databag.com
|
||||
|
||||
© 2022 Databag, Inc.
|
||||
|
||||
`
|
||||
}
|
||||
`,
|
||||
};
|
||||
|
@ -4,7 +4,6 @@ import {SafeAreaView, Modal, FlatList, View} from 'react-native';
|
||||
import {styles} from './Content.styled';
|
||||
import {useContent} from './useContent.hook';
|
||||
import {Channel} from '../channel/Channel';
|
||||
import {Focus} from 'databag-client-sdk';
|
||||
import {BlurView} from '@react-native-community/blur';
|
||||
import {Card} from '../card/Card';
|
||||
import {Confirm} from '../confirm/Confirm';
|
||||
@ -35,23 +34,25 @@ export function Content({share, closeAll, openConversation, textCard}: { share:
|
||||
actions.setSharing({ cardId, channelId, filePath, mimeType });
|
||||
}
|
||||
open(cardId, channelId);
|
||||
}
|
||||
};
|
||||
|
||||
const open = (cardId: string | null, channelId: string) => {
|
||||
actions.setFocus(cardId, channelId);
|
||||
openConversation();
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (share) {
|
||||
closeAll();
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [share]);
|
||||
|
||||
useEffect(() => {
|
||||
if (textCard.cardId) {
|
||||
openTopic(textCard.cardId);
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [textCard]);
|
||||
|
||||
const openTopic = async (cardId: string) => {
|
||||
@ -65,7 +66,7 @@ export function Content({share, closeAll, openConversation, textCard}: { share:
|
||||
setAlert(true);
|
||||
}
|
||||
setAdding(false);
|
||||
}
|
||||
};
|
||||
|
||||
const addTopic = async () => {
|
||||
setAdding(true);
|
||||
@ -73,7 +74,7 @@ export function Content({share, closeAll, openConversation, textCard}: { share:
|
||||
const id = await actions.addTopic(
|
||||
sealedTopic,
|
||||
subjectTopic,
|
||||
members.filter(id => Boolean(cards.find(card => card.cardId === id))),
|
||||
members.filter(memberId => Boolean(cards.find(card => card.cardId === memberId))),
|
||||
);
|
||||
setAdd(false);
|
||||
setSubjectTopic('');
|
||||
|
@ -48,7 +48,7 @@ export function useContent() {
|
||||
return 0;
|
||||
};
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
|
||||
const updateState = (value: any) => {
|
||||
setState(s => ({...s, ...value}));
|
||||
};
|
||||
@ -94,19 +94,19 @@ export function useContent() {
|
||||
};
|
||||
|
||||
const selectImage = () => {
|
||||
if (contacts.length == 0) {
|
||||
if (contacts.length === 0) {
|
||||
return notes;
|
||||
} else if (contacts.length == 1) {
|
||||
} else if (contacts.length === 1) {
|
||||
if (contacts[0]) {
|
||||
return contacts[0].imageUrl;
|
||||
} else {
|
||||
return unknown;
|
||||
}
|
||||
} else if (contacts.length == 2) {
|
||||
} else if (contacts.length === 2) {
|
||||
return iii_group;
|
||||
} else if (contacts.length == 3) {
|
||||
} else if (contacts.length === 3) {
|
||||
return iiii_group;
|
||||
} else if (contacts.length == 4) {
|
||||
} else if (contacts.length === 4) {
|
||||
return iiiii_group;
|
||||
} else {
|
||||
return group;
|
||||
@ -204,7 +204,7 @@ export function useContent() {
|
||||
const sorted = filtered.sort((a, b) => {
|
||||
const aUpdated = a?.lastTopic?.created;
|
||||
const bUpdated = b?.lastTopic?.created;
|
||||
if (aUpdated == bUpdated) {
|
||||
if (aUpdated === bUpdated) {
|
||||
return 0;
|
||||
} else if (!aUpdated) {
|
||||
return 1;
|
||||
@ -231,7 +231,7 @@ export function useContent() {
|
||||
content.removeChannelListener(setChannels);
|
||||
settings.removeConfigListener(setConfig);
|
||||
};
|
||||
}, []);
|
||||
}, [app.state.session]);
|
||||
|
||||
const actions = {
|
||||
setSharing: app.actions.setSharing,
|
||||
@ -244,9 +244,9 @@ export function useContent() {
|
||||
setFocus: async (cardId: string | null, channelId: string) => {
|
||||
await app.actions.setFocus(cardId, channelId);
|
||||
},
|
||||
openTopic: async (cardId: string) => {
|
||||
const content = app.state.session.getContent()
|
||||
const card = state.cards.find(card => card.cardId === cardId)
|
||||
openTopic: async (contactId: string) => {
|
||||
const content = app.state.session.getContent();
|
||||
const card = state.cards.find(member => member.cardId === contactId);
|
||||
if (card) {
|
||||
const sealable = card.sealable && state.sealSet;
|
||||
const thread = state.sorted.find(channel => {
|
||||
@ -256,21 +256,21 @@ export function useContent() {
|
||||
}
|
||||
return false;
|
||||
});
|
||||
if (thread) {
|
||||
if (thread) {
|
||||
return thread.channelId;
|
||||
} else {
|
||||
} else {
|
||||
const topic = await content.addChannel(sealable, sealable ? 'sealed' : 'superbasic', {}, [cardId]);
|
||||
return topic.id;
|
||||
}
|
||||
}
|
||||
},
|
||||
addTopic: async (sealed: boolean, subject: string, contacts: string[]) => {
|
||||
const content = app.state.session.getContent()
|
||||
const content = app.state.session.getContent();
|
||||
if (sealed) {
|
||||
const topic = await content.addChannel(true, 'sealed', { subject }, contacts)
|
||||
const topic = await content.addChannel(true, 'sealed', { subject }, contacts);
|
||||
return topic.id;
|
||||
} else {
|
||||
const topic = await content.addChannel(false, 'superbasic', { subject }, contacts)
|
||||
const topic = await content.addChannel(false, 'superbasic', { subject }, contacts);
|
||||
return topic.id;
|
||||
}
|
||||
},
|
||||
|
@ -1,9 +1,9 @@
|
||||
import React, { ReactNode, createContext } from 'react'
|
||||
import { useRingContext } from './useRingContext.hook'
|
||||
import React, { ReactNode, createContext } from 'react';
|
||||
import { useRingContext } from './useRingContext.hook';
|
||||
|
||||
export const RingContext = createContext({})
|
||||
export const RingContext = createContext({});
|
||||
|
||||
export function RingContextProvider({ children }: { children: ReactNode }) {
|
||||
const { state, actions } = useRingContext()
|
||||
return <RingContext.Provider value={{ state, actions }}>{children}</RingContext.Provider>
|
||||
const { state, actions } = useRingContext();
|
||||
return <RingContext.Provider value={{ state, actions }}>{children}</RingContext.Provider>;
|
||||
}
|
||||
|
@ -4,7 +4,7 @@ import {Platform, PermissionsAndroid} from 'react-native';
|
||||
import {SessionStore} from '../SessionStore';
|
||||
import {NativeCrypto} from '../NativeCrypto';
|
||||
import {LocalStore} from '../LocalStore';
|
||||
import { StagingFiles } from '../StagingFiles'
|
||||
import { StagingFiles } from '../StagingFiles';
|
||||
import messaging from '@react-native-firebase/messaging';
|
||||
|
||||
const DATABAG_DB = 'db_v244.db';
|
||||
@ -85,7 +85,7 @@ export function useAppContext() {
|
||||
console.log(err);
|
||||
return { token: '', type: '' };
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const actions = {
|
||||
setMonthFirstDate: async (monthFirstDate: boolean) => {
|
||||
@ -171,7 +171,7 @@ export function useAppContext() {
|
||||
}
|
||||
},
|
||||
clearFocus: () => {
|
||||
if (state.session) {
|
||||
if (state.session) {
|
||||
state.session.clearFocus();
|
||||
updateState({ focus: null });
|
||||
}
|
||||
|
@ -20,7 +20,7 @@ export function useDisplayContext() {
|
||||
useEffect(() => {
|
||||
const layout = dim.width < SMALL_LARGE ? 'small' : 'large';
|
||||
updateState({layout, width: dim.width, height: dim.height});
|
||||
}, [dim.width]);
|
||||
}, [dim.height, dim.width]);
|
||||
|
||||
const actions = {};
|
||||
|
||||
|
@ -1,27 +1,21 @@
|
||||
import { useState, useContext, useEffect, useRef } from 'react'
|
||||
import { DisplayContext } from '../context/DisplayContext';
|
||||
import { AppContext } from '../context/AppContext'
|
||||
import { ContextType } from '../context/ContextType'
|
||||
import { useState, useContext, useEffect, useRef } from 'react';
|
||||
import { AppContext } from '../context/AppContext';
|
||||
import { ContextType } from '../context/ContextType';
|
||||
import { Link, type Card } from 'databag-client-sdk';
|
||||
import InCallManager from 'react-native-incall-manager';
|
||||
|
||||
import {
|
||||
ScreenCapturePickerView,
|
||||
RTCPeerConnection,
|
||||
RTCIceCandidate,
|
||||
RTCSessionDescription,
|
||||
RTCView,
|
||||
MediaStream,
|
||||
MediaiStreamTrack,
|
||||
mediaDevices,
|
||||
registerGlobals
|
||||
} from 'react-native-webrtc';
|
||||
|
||||
const CLOSE_POLL_MS = 100;
|
||||
|
||||
export function useRingContext() {
|
||||
const app = useContext(AppContext) as ContextType;
|
||||
const display = useContext(DisplayContext) as ContextType;
|
||||
const call = useRef(null as { peer: RTCPeerConnection, link: Link, candidates: RTCIceCandidate[] } | null);
|
||||
const sourceStream = useRef(null as null|MediaStream);
|
||||
const localStream = useRef(null as null|MediaStream);
|
||||
@ -52,12 +46,12 @@ export function useRingContext() {
|
||||
connectedTime: 0,
|
||||
failed: false,
|
||||
fullscreen: false,
|
||||
})
|
||||
});
|
||||
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const updateState = (value: any) => {
|
||||
setState((s) => ({ ...s, ...value }))
|
||||
}
|
||||
setState((s) => ({ ...s, ...value }));
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const calls = ringing.map(ring => ({ callId: ring.callId, card: cards.find(card => ring.cardId === card.cardId) }))
|
||||
@ -65,18 +59,9 @@ export function useRingContext() {
|
||||
updateState({ calls });
|
||||
}, [ringing, cards]);
|
||||
|
||||
const constraints = {
|
||||
mandatory: {
|
||||
OfferToReceiveAudio: true,
|
||||
OfferToReceiveVideo: false,
|
||||
VoiceActivityDetection: true
|
||||
}
|
||||
};
|
||||
|
||||
const linkStatus = async (status: string) => {
|
||||
if (call.current) {
|
||||
try {
|
||||
const { peer, link } = call.current;
|
||||
if (status === 'connected') {
|
||||
const now = new Date();
|
||||
const connectedTime = Math.floor(now.getTime() / 1000);
|
||||
@ -89,10 +74,10 @@ export function useRingContext() {
|
||||
console.log(err);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const updatePeer = async (type: string, data?: any) => {
|
||||
peerUpdate.current.push({ type, data });
|
||||
const updatePeer = async (mode: string, value?: any) => {
|
||||
peerUpdate.current.push({ type: mode, data: value });
|
||||
|
||||
if (!updatingPeer.current) {
|
||||
updatingPeer.current = true;
|
||||
@ -114,13 +99,13 @@ export function useRingContext() {
|
||||
const offer = new RTCSessionDescription(data.description);
|
||||
await peer.setRemoteDescription(offer);
|
||||
if (data.description.type === 'offer') {
|
||||
const description = await peer.createAnswer();
|
||||
await peer.setLocalDescription(description);
|
||||
link.sendMessage({ description });
|
||||
const desc = await peer.createAnswer();
|
||||
await peer.setLocalDescription(desc);
|
||||
link.sendMessage({ description: desc });
|
||||
}
|
||||
for (const candidate of candidates) {
|
||||
await peer.addIceCandidate(candidate);
|
||||
};
|
||||
}
|
||||
call.current.candidates = [];
|
||||
} else if (data.candidate) {
|
||||
const candidate = new RTCIceCandidate(data.candidate);
|
||||
@ -153,7 +138,7 @@ export function useRingContext() {
|
||||
}
|
||||
if (data.kind === 'video') {
|
||||
InCallManager.setForceSpeakerphoneOn(true);
|
||||
updateState({ localVideo: true })
|
||||
updateState({ localVideo: true });
|
||||
}
|
||||
break;
|
||||
default:
|
||||
@ -167,7 +152,7 @@ export function useRingContext() {
|
||||
}
|
||||
updatingPeer.current = false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const setup = async (link: Link, card: Card, polite: boolean) => {
|
||||
|
||||
@ -179,8 +164,8 @@ export function useRingContext() {
|
||||
audio: true,
|
||||
video: {
|
||||
frameRate: 30,
|
||||
facingMode: 'user'
|
||||
}
|
||||
facingMode: 'user',
|
||||
},
|
||||
});
|
||||
InCallManager.start({media: 'audio'});
|
||||
localAudio.current = sourceStream.current.getTracks().find(track => track.kind === 'audio');
|
||||
@ -201,7 +186,7 @@ export function useRingContext() {
|
||||
localStream: localStream.current, remoteStream: remoteStream.current });
|
||||
link.setStatusListener(linkStatus);
|
||||
link.setMessageListener((msg: any) => updatePeer('message', msg));
|
||||
}
|
||||
};
|
||||
|
||||
const cleanup = async () => {
|
||||
closing.current = true;
|
||||
@ -232,11 +217,11 @@ export function useRingContext() {
|
||||
updateState({ calling: null, connected: false, connectedTime: 0, fullscreen: false, failed: false,
|
||||
localStream: null, remoteStream: null, localVideo: false, remoteVideo: false });
|
||||
closing.current = false;
|
||||
}
|
||||
};
|
||||
|
||||
const transmit = (ice: { urls: string; username: string; credential: string }[]) => {
|
||||
const peerConnection = new RTCPeerConnection({ iceServers: ice });
|
||||
peerConnection.addEventListener( 'connectionstatechange', event => {
|
||||
peerConnection.addEventListener( 'connectionstatechange', () => {
|
||||
if (peerConnection.connectionState === 'failed') {
|
||||
cleanup();
|
||||
}
|
||||
@ -245,31 +230,31 @@ export function useRingContext() {
|
||||
updatePeer('candidate', event.candidate);
|
||||
});
|
||||
peerConnection.addEventListener( 'icecandidateerror', event => {
|
||||
console.log("ICE ERROR");
|
||||
console.log('ICE ERROR', event);
|
||||
});
|
||||
peerConnection.addEventListener( 'iceconnectionstatechange', event => {
|
||||
console.log("ICE STATE CHANGE", event);
|
||||
console.log('ICE STATE CHANGE', event);
|
||||
});
|
||||
peerConnection.addEventListener( 'negotiationneeded', event => {
|
||||
peerConnection.addEventListener( 'negotiationneeded', () => {
|
||||
updatePeer('negotiate');
|
||||
});
|
||||
peerConnection.addEventListener( 'signalingstatechange', event => {
|
||||
console.log("ICE SIGNALING", event);
|
||||
console.log('ICE SIGNALING', event);
|
||||
});
|
||||
peerConnection.addEventListener( 'track', event => {
|
||||
updatePeer('remote_track', event.track);
|
||||
});
|
||||
return peerConnection;
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (app.state.session) {
|
||||
const setRing = (ringing: { cardId: string, callId: string }[]) => {
|
||||
setRinging(ringing);
|
||||
}
|
||||
const setContacts = (cards: Card[]) => {
|
||||
setCards(cards);
|
||||
}
|
||||
const setRing = (incoming: { cardId: string, callId: string }[]) => {
|
||||
setRinging(incoming);
|
||||
};
|
||||
const setContacts = (contacts: Card[]) => {
|
||||
setCards(contacts);
|
||||
};
|
||||
const ring = app.state.session.getRing();
|
||||
ring.addRingingListener(setRinging);
|
||||
const contact = app.state.session.getContact();
|
||||
@ -278,8 +263,9 @@ export function useRingContext() {
|
||||
ring.removeRingingListener(setRing);
|
||||
contact.removeCardListener(setContacts);
|
||||
cleanup();
|
||||
}
|
||||
};
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [app.state.session]);
|
||||
|
||||
const actions = {
|
||||
@ -330,7 +316,7 @@ export function useRingContext() {
|
||||
},
|
||||
enableAudio: async () => {
|
||||
if (closing.current || !call.current) {
|
||||
throw new Error('cannot unmute audio')
|
||||
throw new Error('cannot unmute audio');
|
||||
}
|
||||
if (!localAudio.current) {
|
||||
throw new Error('audio not available');
|
||||
@ -377,8 +363,8 @@ export function useRingContext() {
|
||||
}
|
||||
updateState({ videoEnabled: false });
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
return { state, actions }
|
||||
return { state, actions };
|
||||
}
|
||||
|
||||
|
@ -95,10 +95,10 @@ export const styles = StyleSheet.create({
|
||||
color: Colors.placeholder,
|
||||
},
|
||||
add: {
|
||||
height: 72,
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
flexDirection: 'column',
|
||||
paddingTop: 8,
|
||||
paddingBottom: 8,
|
||||
},
|
||||
icon: {
|
||||
flexShrink: 0,
|
||||
@ -138,7 +138,6 @@ export const styles = StyleSheet.create({
|
||||
alignItems: 'center',
|
||||
width: '100%',
|
||||
minWidth: 0,
|
||||
alignItems: 'center',
|
||||
},
|
||||
largeHeader: {
|
||||
paddingLeft: 16,
|
||||
@ -159,12 +158,6 @@ export const styles = StyleSheet.create({
|
||||
minWidth: 0,
|
||||
flexShrink: 1,
|
||||
},
|
||||
add: {
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
paddingTop: 8,
|
||||
paddingBottom: 8,
|
||||
},
|
||||
message: {
|
||||
width: '100%',
|
||||
fontSize: 14,
|
||||
@ -200,5 +193,5 @@ export const styles = StyleSheet.create({
|
||||
separator: {
|
||||
width: 1,
|
||||
height: '100%',
|
||||
}
|
||||
},
|
||||
});
|
||||
|
@ -1,5 +1,5 @@
|
||||
import React, { useEffect, useState, useRef } from 'react';
|
||||
import {Animated, useAnimatedValue, KeyboardAvoidingView, Modal, Platform, ScrollView, Pressable, View, FlatList, TouchableOpacity} from 'react-native';
|
||||
import {Animated, useAnimatedValue, Modal, ScrollView, Pressable, View, FlatList } from 'react-native';
|
||||
import {styles} from './Conversation.styled';
|
||||
import {useConversation} from './useConversation.hook';
|
||||
import {Message} from '../message/Message';
|
||||
@ -7,14 +7,14 @@ import {Surface, Icon, Text, TextInput, Menu, IconButton, Divider} from 'react-n
|
||||
import { ActivityIndicator } from 'react-native-paper';
|
||||
import { Colors } from '../constants/Colors';
|
||||
import { Confirm } from '../confirm/Confirm';
|
||||
import ColorPicker from 'react-native-wheel-color-picker'
|
||||
import ColorPicker from 'react-native-wheel-color-picker';
|
||||
import {BlurView} from '@react-native-community/blur';
|
||||
import ImagePicker from 'react-native-image-crop-picker'
|
||||
import ImagePicker from 'react-native-image-crop-picker';
|
||||
import { ImageFile } from './imageFile/ImageFile';
|
||||
import { VideoFile } from './videoFile/VideoFile';
|
||||
import { AudioFile } from './audioFile/AudioFile';
|
||||
import { BinaryFile } from './binaryFile/BinaryFile';
|
||||
import DocumentPicker from 'react-native-document-picker'
|
||||
import DocumentPicker from 'react-native-document-picker';
|
||||
|
||||
const SCROLL_THRESHOLD = 16;
|
||||
|
||||
@ -40,8 +40,8 @@ export function Conversation({close, openDetails, wide}: {close: ()=>void, openD
|
||||
const contentHeight = useRef(0);
|
||||
const contentLead = useRef(null);
|
||||
const scrollOffset = useRef(0);
|
||||
const busy = useRef(false);
|
||||
const scale = useAnimatedValue(0)
|
||||
const busy = useRef(false);
|
||||
const scale = useAnimatedValue(0);
|
||||
|
||||
const alertParams = {
|
||||
title: state.strings.operationFailed,
|
||||
@ -68,6 +68,7 @@ export function Conversation({close, openDetails, wide}: {close: ()=>void, openD
|
||||
useNativeDriver: false,
|
||||
}).start();
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [state.assets]);
|
||||
|
||||
const sendMessage = async () => {
|
||||
@ -84,7 +85,7 @@ export function Conversation({close, openDetails, wide}: {close: ()=>void, openD
|
||||
setSending(false);
|
||||
busy.current = false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const loadMore = async () => {
|
||||
if (!more) {
|
||||
@ -92,12 +93,12 @@ export function Conversation({close, openDetails, wide}: {close: ()=>void, openD
|
||||
await actions.more();
|
||||
setMore(false);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const onClose = () => {
|
||||
actions.close();
|
||||
close();
|
||||
}
|
||||
};
|
||||
|
||||
const onContent = (width, height) => {
|
||||
const currentLead = state.topics.length > 0 ? state.topics[0].topicId : null;
|
||||
@ -110,7 +111,7 @@ export function Conversation({close, openDetails, wide}: {close: ()=>void, openD
|
||||
}
|
||||
contentLead.current = currentLead;
|
||||
contentHeight.current = height;
|
||||
}
|
||||
};
|
||||
|
||||
const onScroll = (ev) => {
|
||||
const { contentOffset } = ev.nativeEvent;
|
||||
@ -125,7 +126,7 @@ export function Conversation({close, openDetails, wide}: {close: ()=>void, openD
|
||||
}
|
||||
}
|
||||
scrollOffset.current = offset;
|
||||
}
|
||||
};
|
||||
|
||||
const addImage = async () => {
|
||||
try {
|
||||
@ -135,7 +136,7 @@ export function Conversation({close, openDetails, wide}: {close: ()=>void, openD
|
||||
catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const addVideo = async () => {
|
||||
try {
|
||||
@ -145,7 +146,7 @@ export function Conversation({close, openDetails, wide}: {close: ()=>void, openD
|
||||
catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const addAudio = async () => {
|
||||
try {
|
||||
@ -153,12 +154,12 @@ export function Conversation({close, openDetails, wide}: {close: ()=>void, openD
|
||||
presentationStyle: 'fullScreen',
|
||||
copyTo: 'cachesDirectory',
|
||||
type: DocumentPicker.types.audio,
|
||||
})
|
||||
});
|
||||
actions.addAudio(audio.fileCopyUri, audio.name);
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const addBinary = async () => {
|
||||
try {
|
||||
@ -166,27 +167,27 @@ export function Conversation({close, openDetails, wide}: {close: ()=>void, openD
|
||||
presentationStyle: 'fullScreen',
|
||||
copyTo: 'cachesDirectory',
|
||||
type: DocumentPicker.types.allFiles,
|
||||
})
|
||||
});
|
||||
actions.addBinary(binary.fileCopyUri, binary.name);
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const media = state.assets.map((asset, index) => {
|
||||
if (asset.type === 'image') {
|
||||
return <ImageFile key={index} path={asset.path} disabled={sending} remove={()=>actions.removeAsset(index)} />
|
||||
return <ImageFile key={index} path={asset.path} disabled={sending} remove={()=>actions.removeAsset(index)} />;
|
||||
} else if (asset.type === 'video') {
|
||||
return <VideoFile key={index} path={asset.path} thumbPosition={(position: number) => actions.setThumbPosition(index, position)} disabled={sending} remove={()=>actions.removeAsset(index)} />
|
||||
return <VideoFile key={index} path={asset.path} thumbPosition={(position: number) => actions.setThumbPosition(index, position)} disabled={sending} remove={()=>actions.removeAsset(index)} />;
|
||||
} else if (asset.type === 'audio') {
|
||||
return <AudioFile key={index} path={asset.path} disabled={sending} remove={()=>actions.removeAsset(index)} />
|
||||
return <AudioFile key={index} path={asset.path} disabled={sending} remove={()=>actions.removeAsset(index)} />;
|
||||
} else {
|
||||
return <BinaryFile key={index} path={asset.path} disabled={sending} remove={()=>actions.removeAsset(index)} />
|
||||
return <BinaryFile key={index} path={asset.path} disabled={sending} remove={()=>actions.removeAsset(index)} />;
|
||||
}
|
||||
});
|
||||
|
||||
const containerStyle = state.layout === 'large' ? { ...styles.conversation, ...styles.largeConversation } : styles.conversation;
|
||||
const headerStyle = state.layout === 'large' ? { ...styles.header, ...styles.largeHeader } : styles.header;
|
||||
const headerStyle = state.layout === 'large' ? { ...styles.header, ...styles.largeHeader, flexDirection: 'row-reverse' } : { ...styles.header, flexDirection: 'row' };
|
||||
const padStyle = state.layout === 'large' ? styles.pad : styles.nopad;
|
||||
const inputPadStyle = state.layout === 'large' ? styles.pad : styles.indent;
|
||||
const offset = state.layout === 'large' ? state.avoid - 64 : state.avoid - 120;
|
||||
@ -194,18 +195,18 @@ export function Conversation({close, openDetails, wide}: {close: ()=>void, openD
|
||||
const disableVideo = !state.detailSet || !state.detail?.enableVideo;
|
||||
const disableAudio = !state.detailSet || !state.detail?.enableAudio;
|
||||
const disableBinary = !state.detailSet || !state.detail?.enableBinary;
|
||||
const statusStyle = state.layout === 'large' ? { ...styles.status, flexDirection: 'row-reverse' } : { ...styles.status, flexDirection: 'row' };
|
||||
|
||||
return (
|
||||
<View style={containerStyle}>
|
||||
<View style={{ ...headerStyle, flexDirection: state.layout === 'large' ? 'row-reverse' : 'row' }}>
|
||||
<View style={headerStyle}>
|
||||
<IconButton style={styles.icon} mode="contained" icon={wide ? 'close' : 'arrow-left'} size={28} onPress={onClose} />
|
||||
<View style={styles.status}>
|
||||
</View>
|
||||
<View style={styles.status} />
|
||||
<View style={styles.title}>
|
||||
{ state.detailSet && state.subject && (
|
||||
<Text adjustsFontSizeToFit={true} numberOfLines={1} style={styles.label}>{ state.subject }</Text>
|
||||
)}
|
||||
{ state.detailSet && state.host && !state.subject && state.subjectNames.length == 0 && (
|
||||
{ state.detailSet && state.host && !state.subject && state.subjectNames.length === 0 && (
|
||||
<Text adjustsFontSizeToFit={true} numberOfLines={1} style={styles.label}>{ state.strings.notes }</Text>
|
||||
)}
|
||||
{ state.detailSet && !state.subject && state.subjectNames.length > 0 && (
|
||||
@ -215,7 +216,7 @@ export function Conversation({close, openDetails, wide}: {close: ()=>void, openD
|
||||
<Text adjustsFontSizeToFit={true} numberOfLines={1} style={styles.unknown}>{ `, ${state.strings.unknownContact} (${state.unknownContacts})` }</Text>
|
||||
)}
|
||||
</View>
|
||||
<View style={{ ...styles.status, flexDirection: state.layout === 'large' ? 'row-reverse' : 'row' }}>
|
||||
<View style={statusStyle}>
|
||||
{ state.detailSet && !state.access && (
|
||||
<Icon source="alert-circle-outline" size={20} color={Colors.offsync} />
|
||||
)}
|
||||
@ -229,7 +230,7 @@ export function Conversation({close, openDetails, wide}: {close: ()=>void, openD
|
||||
<Icon source="shield-outline" size={20} />
|
||||
)}
|
||||
</View>
|
||||
<IconButton style={styles.icon} mode="contained" icon={state.layout==='large' ? 'cog-transfer-outline' : 'dots-vertical'} size={28} onPress={openDetails} />
|
||||
<IconButton style={styles.icon} mode="contained" icon={state.layout === 'large' ? 'cog-transfer-outline' : 'dots-vertical'} size={28} onPress={openDetails} />
|
||||
</View>
|
||||
<View style={padStyle}>
|
||||
<Divider style={styles.topBorder} bold={true} />
|
||||
@ -260,13 +261,13 @@ export function Conversation({close, openDetails, wide}: {close: ()=>void, openD
|
||||
select={(id)=>setSelected(id)}
|
||||
selected={selected}
|
||||
/>
|
||||
)
|
||||
);
|
||||
}}
|
||||
keyExtractor={topic => (topic.topicId)}
|
||||
/>
|
||||
{ state.loaded && state.topics.length === 0 && (
|
||||
{ state.loaded && state.topics.length === 0 && (
|
||||
<Text style={styles.empty}>{state.strings.noMessages}</Text>
|
||||
)}
|
||||
)}
|
||||
{ !state.loaded && (
|
||||
<View style={styles.loading}>
|
||||
<ActivityIndicator size="large" />
|
||||
@ -334,9 +335,9 @@ export function Conversation({close, openDetails, wide}: {close: ()=>void, openD
|
||||
</Surface>
|
||||
</Pressable>
|
||||
)}>
|
||||
<Menu.Item onPress={() => { actions.setTextSize(12); setSizeMenu(false) }} title={state.strings.textSmall} />
|
||||
<Menu.Item onPress={() => { actions.setTextSize(16); setSizeMenu(false) }} title={state.strings.textMedium} />
|
||||
<Menu.Item onPress={() => { actions.setTextSize(20); setSizeMenu(false) }} title={state.strings.textLarge} />
|
||||
<Menu.Item onPress={() => { actions.setTextSize(12); setSizeMenu(false); }} title={state.strings.textSmall} />
|
||||
<Menu.Item onPress={() => { actions.setTextSize(16); setSizeMenu(false); }} title={state.strings.textMedium} />
|
||||
<Menu.Item onPress={() => { actions.setTextSize(20); setSizeMenu(false); }} title={state.strings.textLarge} />
|
||||
</Menu>
|
||||
|
||||
<View style={styles.end}>
|
||||
@ -345,10 +346,10 @@ export function Conversation({close, openDetails, wide}: {close: ()=>void, openD
|
||||
{ sending && (
|
||||
<ActivityIndicator size="small" />
|
||||
)}
|
||||
{ !sending && state.access && state.validShare && (state.message || state.assets.length != 0) && (
|
||||
{ !sending && state.access && state.validShare && (state.message || state.assets.length !== 0) && (
|
||||
<Icon style={styles.button} source="send" size={24} color={Colors.primary} />
|
||||
)}
|
||||
{ !sending && (!state.access || !state.validShare || (!state.message && state.assets.length == 0)) && (
|
||||
{ !sending && (!state.access || !state.validShare || (!state.message && state.assets.length === 0)) && (
|
||||
<Icon style={styles.button} source="send" size={24} color={Colors.placeholder} />
|
||||
)}
|
||||
</Surface>
|
||||
|
@ -1,5 +1,4 @@
|
||||
import {StyleSheet} from 'react-native';
|
||||
import {Colors} from '../constants/Colors';
|
||||
|
||||
export const styles = StyleSheet.create({
|
||||
audio: {
|
||||
|
@ -1,13 +1,10 @@
|
||||
import React, { useEffect } from 'react';
|
||||
import { View, Image } from 'react-native'
|
||||
import React from 'react';
|
||||
import { View, Image } from 'react-native';
|
||||
import { IconButton } from 'react-native-paper';
|
||||
import { useAudioFile } from './useAudioFile.hook';
|
||||
import {styles} from './AudioFile.styled'
|
||||
import {styles} from './AudioFile.styled';
|
||||
import thumb from '../../images/audio.png';
|
||||
|
||||
export function AudioFile({ path, disabled, remove }: {path: string, disabled: boolean, remove: ()=>void}) {
|
||||
const { state, actions } = useAudioFile();
|
||||
|
||||
export function AudioFile({ disabled, remove }: {path: string, disabled: boolean, remove: ()=>void}) {
|
||||
return (
|
||||
<View style={styles.audio}>
|
||||
<Image
|
||||
|
@ -1,16 +0,0 @@
|
||||
import { useState, useEffect } from 'react'
|
||||
|
||||
export function useAudioFile(path: string) {
|
||||
const [state, setState] = useState({
|
||||
})
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const updateState = (value: any) => {
|
||||
setState((s) => ({ ...s, ...value }))
|
||||
}
|
||||
|
||||
const actions = {
|
||||
}
|
||||
|
||||
return { state, actions }
|
||||
}
|
@ -1,5 +1,4 @@
|
||||
import {StyleSheet} from 'react-native';
|
||||
import {Colors} from '../constants/Colors';
|
||||
|
||||
export const styles = StyleSheet.create({
|
||||
binary: {
|
||||
|
@ -1,12 +1,10 @@
|
||||
import React, { useEffect } from 'react';
|
||||
import { View, Image } from 'react-native'
|
||||
import { IconButton } from 'react-native-paper'
|
||||
import { useBinaryFile } from './useBinaryFile.hook';
|
||||
import {styles} from './BinaryFile.styled'
|
||||
import React from 'react';
|
||||
import { View, Image } from 'react-native';
|
||||
import { IconButton } from 'react-native-paper';
|
||||
import {styles} from './BinaryFile.styled';
|
||||
import thumb from '../../images/binary.png';
|
||||
|
||||
export function BinaryFile({ path, disabled, remove }: {path: string, disabled: boolean, remove: ()=>void}) {
|
||||
const { state, actions } = useBinaryFile();
|
||||
export function BinaryFile({ disabled, remove }: {path: string, disabled: boolean, remove: ()=>void}) {
|
||||
|
||||
return (
|
||||
<View style={styles.binary}>
|
||||
|
@ -1,16 +0,0 @@
|
||||
import { useState, useEffect } from 'react'
|
||||
|
||||
export function useBinaryFile(path: string) {
|
||||
const [state, setState] = useState({
|
||||
})
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const updateState = (value: any) => {
|
||||
setState((s) => ({ ...s, ...value }))
|
||||
}
|
||||
|
||||
const actions = {
|
||||
}
|
||||
|
||||
return { state, actions }
|
||||
}
|
@ -1,5 +1,4 @@
|
||||
import {StyleSheet} from 'react-native';
|
||||
import {Colors} from '../constants/Colors';
|
||||
|
||||
export const styles = StyleSheet.create({
|
||||
image: {
|
||||
|
@ -1,8 +1,8 @@
|
||||
import React, { useEffect } from 'react';
|
||||
import { Image, View, Animated, useAnimatedValue } from 'react-native'
|
||||
import { IconButton, Text } from 'react-native-paper';
|
||||
import { Image, View, Animated, useAnimatedValue } from 'react-native';
|
||||
import { IconButton } from 'react-native-paper';
|
||||
import { useImageFile } from './useImageFile.hook';
|
||||
import {styles} from './ImageFile.styled'
|
||||
import {styles} from './ImageFile.styled';
|
||||
|
||||
export function ImageFile({ path, disabled, remove }: {path: string, disabled: boolean, remove: ()=>void}) {
|
||||
const { state, actions } = useImageFile();
|
||||
@ -16,21 +16,12 @@ export function ImageFile({ path, disabled, remove }: {path: string, disabled: b
|
||||
useNativeDriver: true,
|
||||
}).start();
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [state.loaded]);
|
||||
|
||||
const showImage = () => {
|
||||
setModal(true);
|
||||
actions.loadImage();
|
||||
};
|
||||
|
||||
const hideImage = () => {
|
||||
setModal(false);
|
||||
actions.cancelLoad();
|
||||
}
|
||||
|
||||
return (
|
||||
<View style={styles.image}>
|
||||
<Animated.View style={[styles.thumb,{opacity},]}>
|
||||
<Animated.View style={[styles.thumb,{opacity}]}>
|
||||
<Image
|
||||
resizeMode="contain"
|
||||
height={72}
|
||||
|
@ -1,22 +1,22 @@
|
||||
import { useState, useEffect } from 'react'
|
||||
import { useState } from 'react';
|
||||
|
||||
export function useImageFile(source: any) {
|
||||
export function useImageFile() {
|
||||
const [state, setState] = useState({
|
||||
loaded: false,
|
||||
ratio: 1,
|
||||
})
|
||||
});
|
||||
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const updateState = (value: any) => {
|
||||
setState((s) => ({ ...s, ...value }))
|
||||
}
|
||||
setState((s) => ({ ...s, ...value }));
|
||||
};
|
||||
|
||||
const actions = {
|
||||
loaded: (e) => {
|
||||
const { width, height } = e.nativeEvent.source;
|
||||
updateState({ loaded: true, ratio: width / height });
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
return { state, actions }
|
||||
return { state, actions };
|
||||
}
|
||||
|
@ -1,44 +1,41 @@
|
||||
import { useState, useContext, useEffect, useRef } from 'react'
|
||||
import { Keyboard } from 'react-native'
|
||||
import { AppContext } from '../context/AppContext'
|
||||
import { DisplayContext } from '../context/DisplayContext'
|
||||
import { Focus, FocusDetail, Topic, Profile, Card, AssetType, AssetSource, HostingMode, TransformType } from 'databag-client-sdk'
|
||||
import { ContextType } from '../context/ContextType'
|
||||
import { placeholder } from '../constants/Icons';
|
||||
import { useState, useContext, useEffect, useRef } from 'react';
|
||||
import { Keyboard } from 'react-native';
|
||||
import { AppContext } from '../context/AppContext';
|
||||
import { DisplayContext } from '../context/DisplayContext';
|
||||
import { Focus, FocusDetail, Topic, Profile, Card, AssetType, AssetSource, TransformType } from 'databag-client-sdk';
|
||||
import { ContextType } from '../context/ContextType';
|
||||
import RNFS from 'react-native-fs';
|
||||
import ImageResizer from '@bam.tech/react-native-image-resizer';
|
||||
import { createThumbnail } from "react-native-create-thumbnail";
|
||||
import fileType from 'react-native-file-type'
|
||||
import { createThumbnail } from 'react-native-create-thumbnail';
|
||||
import fileType from 'react-native-file-type';
|
||||
|
||||
const IMAGE_SCALE_SIZE = (128 * 1024);
|
||||
const GIF_TYPE = 'image/gif';
|
||||
const WEBP_TYPE = 'image/webp';
|
||||
const LOAD_DEBOUNCE = 1000;
|
||||
|
||||
async function getImageThumb(path: string, type: string, size: number) {
|
||||
if (size < IMAGE_SCALE_SIZE) {
|
||||
const type = await fileType(path);
|
||||
const base = await RNFS.readFile(path, 'base64')
|
||||
return `data:image/${type.ext};base64,${base}`;
|
||||
const info = await fileType(path);
|
||||
const base = await RNFS.readFile(path, 'base64');
|
||||
return `data:image/${info.ext};base64,${base}`;
|
||||
} else {
|
||||
const thumb = await ImageResizer.createResizedImage(path, 192, 192, "JPEG", 50, 0, null);
|
||||
const base = await RNFS.readFile(thumb.path, 'base64')
|
||||
const thumb = await ImageResizer.createResizedImage(path, 192, 192, 'JPEG', 50, 0, null);
|
||||
const base = await RNFS.readFile(thumb.path, 'base64');
|
||||
return `data:image/jpeg;base64,${base}`;
|
||||
}
|
||||
}
|
||||
|
||||
async function getVideoThumb(path: string, position?: number) {
|
||||
const timeStamp = position ? position * 1000 : 0;
|
||||
const shot = await createThumbnail({ url: path, timeStamp })
|
||||
const thumb = await ImageResizer.createResizedImage('file://' + shot.path, 192, 192, "JPEG", 50, 0, null);
|
||||
const base = await RNFS.readFile(thumb.path, 'base64')
|
||||
const shot = await createThumbnail({ url: path, timeStamp });
|
||||
const thumb = await ImageResizer.createResizedImage('file://' + shot.path, 192, 192, 'JPEG', 50, 0, null);
|
||||
const base = await RNFS.readFile(thumb.path, 'base64');
|
||||
return `data:image/jpeg;base64,${base}`;
|
||||
}
|
||||
|
||||
export function useConversation() {
|
||||
const mute = useRef(false);
|
||||
const app = useContext(AppContext) as ContextType
|
||||
const display = useContext(DisplayContext) as ContextType
|
||||
const app = useContext(AppContext) as ContextType;
|
||||
const display = useContext(DisplayContext) as ContextType;
|
||||
const [state, setState] = useState({
|
||||
detail: undefined as FocusDetail | null | undefined,
|
||||
strings: display.state.strings,
|
||||
@ -66,19 +63,19 @@ export function useConversation() {
|
||||
progress: 0,
|
||||
avoid: 0,
|
||||
validShare: true,
|
||||
})
|
||||
});
|
||||
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const updateState = (value: any) => {
|
||||
setState((s) => ({ ...s, ...value }))
|
||||
}
|
||||
setState((s) => ({ ...s, ...value }));
|
||||
};
|
||||
|
||||
const updateAsset = (index: number, value: any) => {
|
||||
setState((s) => {
|
||||
s.assets[index] = { ...s.assets[index], ...value };
|
||||
return { ...s };
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
let validShare = true;
|
||||
@ -104,14 +101,14 @@ export function useConversation() {
|
||||
const { sharing, focus } = app.state;
|
||||
if (sharing && focus && state.loaded) {
|
||||
const focused = focus.getFocused();
|
||||
if (focused.cardId == sharing.cardId && focused.channelId == sharing.channelId) {
|
||||
if (focused.cardId === sharing.cardId && focused.channelId === sharing.channelId) {
|
||||
const { mimeType, filePath } = sharing;
|
||||
const ext = mimeType.toLowerCase();
|
||||
if (ext == '.jpg' || ext == 'image/jpeg' || ext == '.png' || ext == 'image/png' || ext == '.webp' || ext == 'image/webp' || ext == '.bmp' || ext == 'image/bmp' || ext == '.gif' || ext == 'image/gif') {
|
||||
actions.addImage(filePath, mimeType, IMAGE_SCALE_SIZE);
|
||||
} else if (ext == '.mp4' || ext == 'videp/mp4' || ext == '.mov' || ext == 'video/mov') {
|
||||
if (ext === '.jpg' || ext === 'image/jpeg' || ext === '.png' || ext === 'image/png' || ext === '.webp' || ext === 'image/webp' || ext === '.bmp' || ext === 'image/bmp' || ext === '.gif' || ext === 'image/gif') {
|
||||
actions.addImage(filePath, mimeType, IMAGE_SCALE_SIZE);
|
||||
} else if (ext === '.mp4' || ext === 'videp/mp4' || ext === '.mov' || ext === 'video/mov') {
|
||||
actions.addVideo(filePath, mimeType);
|
||||
} else if (ext == '.mp3' || ext == 'audio/mp3' || ext == '.aac' || ext == 'audio/aac') {
|
||||
} else if (ext === '.mp3' || ext === 'audio/mp3' || ext === '.aac' || ext === 'audio/aac') {
|
||||
actions.addAudio(filePath, mimeType);
|
||||
} else {
|
||||
actions.addBinary(filePath, filePath.split('/').pop());
|
||||
@ -119,12 +116,13 @@ export function useConversation() {
|
||||
app.actions.clearSharing();
|
||||
}
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [app.state, state.loaded]);
|
||||
|
||||
useEffect(() => {
|
||||
const { layout, strings } = display.state
|
||||
updateState({ layout, strings })
|
||||
}, [display.state])
|
||||
const { layout, strings } = display.state;
|
||||
updateState({ layout, strings });
|
||||
}, [display.state]);
|
||||
|
||||
useEffect(() => {
|
||||
const host = state.cardId == null;
|
||||
@ -132,8 +130,8 @@ export function useConversation() {
|
||||
const access = (state.detail != null);
|
||||
const subject = state.detail?.data?.subject ? state.detail.data.subject : null;
|
||||
const cards = Array.from(state.cards.values());
|
||||
const card = cards.find(entry => entry.cardId == state.cardId);
|
||||
const profileRemoved = state.detail?.members ? state.detail.members.filter(member => state.profile?.guid != member.guid) : [];
|
||||
const card = cards.find(entry => entry.cardId === state.cardId);
|
||||
const profileRemoved = state.detail?.members ? state.detail.members.filter(member => state.profile?.guid !== member.guid) : [];
|
||||
const unhostedCards = profileRemoved.map(member => state.cards.get(member.guid));
|
||||
const contactCards = card ? [ card, ...unhostedCards ] : unhostedCards;
|
||||
const subjectCards = contactCards.filter(member => Boolean(member));
|
||||
@ -160,25 +158,25 @@ export function useConversation() {
|
||||
});
|
||||
updateState({ topics: sorted, loaded: true });
|
||||
}
|
||||
}
|
||||
};
|
||||
const setCards = (cards: Card[]) => {
|
||||
const contacts = new Map<string, Card>();
|
||||
cards.forEach(card => {
|
||||
contacts.set(card.guid, card);
|
||||
});
|
||||
updateState({ cards: contacts });
|
||||
}
|
||||
};
|
||||
const setProfile = (profile: Profile) => {
|
||||
updateState({ profile });
|
||||
}
|
||||
};
|
||||
const setDetail = (focused: { cardId: string | null, channelId: string, detail: FocusDetail | null }) => {
|
||||
const detail = focused ? focused.detail : null;
|
||||
const cardId = focused.cardId;
|
||||
updateState({ detail, cardId });
|
||||
}
|
||||
};
|
||||
const setKeyboard = (event: KeyboardEvent) => {
|
||||
updateState({ avoid: event.endCoordinates.height });
|
||||
}
|
||||
};
|
||||
updateState({ assets: [], message: null, topics: [], loaded: false });
|
||||
focus.addTopicListener(setTopics);
|
||||
focus.addDetailListener(setDetail);
|
||||
@ -191,9 +189,9 @@ export function useConversation() {
|
||||
contact.removeCardListener(setCards);
|
||||
identity.removeProfileListener(setProfile);
|
||||
keyboard.remove();
|
||||
}
|
||||
};
|
||||
}
|
||||
}, [app.state.focus]);
|
||||
}, [app.state.session, app.state.focus]);
|
||||
|
||||
const actions = {
|
||||
close: () => {
|
||||
@ -246,55 +244,55 @@ export function useConversation() {
|
||||
if (sealed) {
|
||||
sources.push({ type: AssetType.Image, source: asset.path, transforms: [
|
||||
{ type: TransformType.Thumb, appId: `it${sources.length}`, thumb: async () => await getImageThumb(asset.path, asset.type, asset.size) },
|
||||
{ type: TransformType.Copy, appId: `ic${sources.length}` }
|
||||
{ type: TransformType.Copy, appId: `ic${sources.length}` },
|
||||
]});
|
||||
return { encrypted: { type: 'image', thumb: `it${sources.length-1}`, parts: `ic${sources.length-1}` } };
|
||||
return { encrypted: { type: 'image', thumb: `it${sources.length - 1}`, parts: `ic${sources.length - 1}` } };
|
||||
} else {
|
||||
sources.push({ type: AssetType.Image, source: asset.path, transforms: [
|
||||
{ type: TransformType.Thumb, appId: `it${sources.length}` },
|
||||
{ type: TransformType.Copy, appId: `ic${sources.length}` }
|
||||
{ type: TransformType.Copy, appId: `ic${sources.length}` },
|
||||
]});
|
||||
return { image: { thumb: `it${sources.length-1}`, full: `ic${sources.length-1}` } };
|
||||
return { image: { thumb: `it${sources.length - 1}`, full: `ic${sources.length - 1}` } };
|
||||
}
|
||||
} else if (asset.type === 'video') {
|
||||
if (sealed) {
|
||||
sources.push({ type: AssetType.Video, source: asset.path, transforms: [
|
||||
{ type: TransformType.Thumb, appId: `vt${sources.length}`, thumb: async () => await getVideoThumb(asset.path, asset.position) },
|
||||
{ type: TransformType.Copy, appId: `vc${sources.length}` }
|
||||
{ type: TransformType.Copy, appId: `vc${sources.length}` },
|
||||
]});
|
||||
return { encrypted: { type: 'video', thumb: `vt${sources.length-1}`, parts: `vc${sources.length-1}` } };
|
||||
return { encrypted: { type: 'video', thumb: `vt${sources.length - 1}`, parts: `vc${sources.length - 1}` } };
|
||||
} else {
|
||||
sources.push({ type: AssetType.Video, source: asset.path, transforms: [
|
||||
{ type: TransformType.Thumb, appId: `vt${sources.length}`, position: asset.position},
|
||||
{ type: TransformType.HighQuality, appId: `vh${sources.length}` },
|
||||
{ type: TransformType.LowQuality, appId: `vl${sources.length}` }
|
||||
{ type: TransformType.LowQuality, appId: `vl${sources.length}` },
|
||||
]});
|
||||
return { video: { thumb: `vt${sources.length-1}`, hd: `vh${sources.length-1}`, lq: `vl${sources.length-1}` } };
|
||||
return { video: { thumb: `vt${sources.length - 1}`, hd: `vh${sources.length - 1}`, lq: `vl${sources.length - 1}` } };
|
||||
}
|
||||
} else if (asset.type === 'audio') {
|
||||
if (sealed) {
|
||||
sources.push({ type: AssetType.Audio, source: asset.path, transforms: [
|
||||
{ type: TransformType.Copy, appId: `ac${sources.length}` }
|
||||
{ type: TransformType.Copy, appId: `ac${sources.length}` },
|
||||
]});
|
||||
return { encrypted: { type: 'audio', label: asset.label, parts: `ac${sources.length-1}` } };
|
||||
return { encrypted: { type: 'audio', label: asset.label, parts: `ac${sources.length - 1}` } };
|
||||
} else {
|
||||
sources.push({ type: AssetType.Audio, source: asset.path, transforms: [
|
||||
{ type: TransformType.Copy, appId: `ac${sources.length}` }
|
||||
{ type: TransformType.Copy, appId: `ac${sources.length}` },
|
||||
]});
|
||||
return { audio: { label: asset.label, full: `ac${sources.length-1}` } };
|
||||
return { audio: { label: asset.label, full: `ac${sources.length - 1}` } };
|
||||
}
|
||||
} else {
|
||||
const { label, extension } = asset;
|
||||
if (sealed) {
|
||||
sources.push({ type: AssetType.Binary, source: asset.path, transforms: [
|
||||
{ type: TransformType.Copy, appId: `bc${sources.length}` }
|
||||
{ type: TransformType.Copy, appId: `bc${sources.length}` },
|
||||
]});
|
||||
return { encrypted: { type: 'binary', label, extension, parts: `bc${sources.length-1}` } };
|
||||
return { encrypted: { type: 'binary', label, extension, parts: `bc${sources.length - 1}` } };
|
||||
} else {
|
||||
sources.push({ type: AssetType.Binary, source: asset.path, transforms: [
|
||||
{ type: TransformType.Copy, appId: `bc${sources.length}` }
|
||||
{ type: TransformType.Copy, appId: `bc${sources.length}` },
|
||||
]});
|
||||
return { binary: { label, extension, data: `bc${sources.length-1}` } };
|
||||
return { binary: { label, extension, data: `bc${sources.length - 1}` } };
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -333,8 +331,8 @@ export function useConversation() {
|
||||
}
|
||||
});
|
||||
return { text: state.message, textColor: state.textColorSet ? state.textColor : null, textSize: state.textSizeSet ? state.textSize : null, assets: assets.length > 0 ? assets : null };
|
||||
}
|
||||
const upload = (progress: number) => { updateState({ progress }) };
|
||||
};
|
||||
const upload = (progress: number) => { updateState({ progress }); };
|
||||
await focus.addTopic(sealed, sealed ? 'sealedtopic' : 'superbasictopic', subject, sources, upload);
|
||||
|
||||
mute.current = true;
|
||||
@ -343,7 +341,7 @@ export function useConversation() {
|
||||
}, 1000);
|
||||
updateState({ message: null, assets: [], progress: 0 });
|
||||
}
|
||||
},
|
||||
},
|
||||
addImage: (path: string, mime: string, size: number) => {
|
||||
const type = 'image';
|
||||
updateState({ assets: [ ...state.assets, { type, path, mime, size } ]});
|
||||
@ -360,7 +358,7 @@ export function useConversation() {
|
||||
const type = 'binary';
|
||||
updateState({ assets: [ ...state.assets, { type, path, label: name.split('.').shift(), extension: name.split('.').pop() } ]});
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
return { state, actions }
|
||||
return { state, actions };
|
||||
}
|
||||
|
@ -1,5 +1,4 @@
|
||||
import {StyleSheet} from 'react-native';
|
||||
import {Colors} from '../constants/Colors';
|
||||
|
||||
export const styles = StyleSheet.create({
|
||||
video: {
|
||||
@ -18,5 +17,5 @@ export const styles = StyleSheet.create({
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
justifyContent: 'flex-end',
|
||||
}
|
||||
},
|
||||
});
|
||||
|
@ -1,9 +1,9 @@
|
||||
import React, { useEffect, useRef, useState } from 'react';
|
||||
import { Pressable, View, Animated, useAnimatedValue } from 'react-native'
|
||||
import { Icon, IconButton, Text } from 'react-native-paper';
|
||||
import { Pressable, View, Animated, useAnimatedValue } from 'react-native';
|
||||
import { Icon, IconButton } from 'react-native-paper';
|
||||
import { useVideoFile } from './useVideoFile.hook';
|
||||
import {styles} from './VideoFile.styled'
|
||||
import Video, { VideoRef } from 'react-native-video'
|
||||
import {styles} from './VideoFile.styled';
|
||||
import Video, { VideoRef } from 'react-native-video';
|
||||
|
||||
export function VideoFile({ path, thumbPosition, disabled, remove }: {path: string, thumbPosition: (position: number)=>void, disabled: boolean, remove: ()=>void}) {
|
||||
const { state, actions } = useVideoFile();
|
||||
@ -17,7 +17,7 @@ export function VideoFile({ path, thumbPosition, disabled, remove }: {path: stri
|
||||
thumbPosition(pos);
|
||||
videoRef.current.seek(pos);
|
||||
setSeek(pos);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (state.loaded) {
|
||||
@ -27,11 +27,12 @@ export function VideoFile({ path, thumbPosition, disabled, remove }: {path: stri
|
||||
useNativeDriver: true,
|
||||
}).start();
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [state.loaded]);
|
||||
|
||||
return (
|
||||
<View style={styles.video}>
|
||||
<Animated.View style={[{...styles.thumb, width: 72 * state.ratio},{opacity},]}>
|
||||
<Animated.View style={[{...styles.thumb, width: 72 * state.ratio},{opacity}]}>
|
||||
<Video ref={videoRef} source={{ uri: path }} height={72} width={72 * state.ratio} paused={true} controls={false} resizeMode="contain" onLoad={actions.loaded} />
|
||||
{ !disabled && (
|
||||
<Pressable style={styles.next} height={72} width={72 * state.ratio} onPress={next}>
|
||||
|
@ -1,23 +1,23 @@
|
||||
import { useState, useEffect } from 'react'
|
||||
import { useState } from 'react';
|
||||
|
||||
export function useVideoFile(source: any) {
|
||||
export function useVideoFile() {
|
||||
const [state, setState] = useState({
|
||||
loaded: false,
|
||||
ratio: 1,
|
||||
duration: 0,
|
||||
})
|
||||
});
|
||||
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const updateState = (value: any) => {
|
||||
setState((s) => ({ ...s, ...value }))
|
||||
}
|
||||
setState((s) => ({ ...s, ...value }));
|
||||
};
|
||||
|
||||
const actions = {
|
||||
loaded: (e) => {
|
||||
const { width, height } = e.naturalSize;
|
||||
updateState({ loaded: true, ratio: width / height, duration: e.duration });
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
return { state, actions }
|
||||
return { state, actions };
|
||||
}
|
||||
|
@ -107,6 +107,7 @@ export const styles = StyleSheet.create({
|
||||
backgroundColor: 'yellow',
|
||||
},
|
||||
members: {
|
||||
width: '100%',
|
||||
flexGrow: 1,
|
||||
},
|
||||
actions: {
|
||||
@ -133,13 +134,10 @@ export const styles = StyleSheet.create({
|
||||
top: 8,
|
||||
backgroundColor: 'transparent',
|
||||
padding: 0,
|
||||
margin: 0,
|
||||
margin: 0,
|
||||
},
|
||||
membership: {
|
||||
},
|
||||
members: {
|
||||
width: '100%',
|
||||
},
|
||||
card: {
|
||||
paddingBottom: 8,
|
||||
paddingTop: 8,
|
||||
|
@ -1,4 +1,4 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import React, { useState } from 'react';
|
||||
import { SafeAreaView, Platform, Modal, ScrollView, View } from 'react-native';
|
||||
import {useTheme, Switch, Surface, Icon, Divider, Button, IconButton, Text, TextInput} from 'react-native-paper';
|
||||
import {styles} from './Details.styled';
|
||||
@ -24,34 +24,34 @@ export function Details({close, closeAll}: {close: ()=>void, closeAll: ()=>void}
|
||||
const membership = () => {
|
||||
setError(false);
|
||||
setMemberModal(true);
|
||||
}
|
||||
};
|
||||
|
||||
const remove = () => {
|
||||
const apply = async () => {
|
||||
await actions.remove();
|
||||
closeAll();
|
||||
}
|
||||
};
|
||||
confirmAction(state.strings.confirmTopic, state.strings.sureTopic, state.strings.remove, setRemoving, apply);
|
||||
}
|
||||
};
|
||||
|
||||
const leave = () => {
|
||||
const apply = async () => {
|
||||
await actions.leave();
|
||||
closeAll();
|
||||
}
|
||||
};
|
||||
confirmAction(state.strings.confirmLeave, state.strings.sureLeave, state.strings.leave, setRemoving, apply);
|
||||
}
|
||||
};
|
||||
|
||||
const block = () => {
|
||||
const apply = async () => {
|
||||
await actions.block();
|
||||
}
|
||||
};
|
||||
confirmAction(state.strings.blockTopic, state.strings.blockTopicPrompt, state.strings.block, setBlocking, apply);
|
||||
}
|
||||
};
|
||||
|
||||
const report = () => {
|
||||
confirmAction(state.strings.reportTopic, state.strings.reportTopicPrompt, state.strings.report, setReporting, actions.report);
|
||||
}
|
||||
};
|
||||
|
||||
const confirmAction = (title: string, prompt: string, label: string, loading: (boolean) => void, action: () => Promise<void>) => {
|
||||
setConfirmParams({
|
||||
@ -77,16 +77,6 @@ export function Details({close, closeAll}: {close: ()=>void, closeAll: ()=>void}
|
||||
setConfirm(true);
|
||||
};
|
||||
|
||||
const applyAction = async (loading: (boolean) => void, action: () => Promise<void>) => {
|
||||
if (!busy) {
|
||||
setBusy(true);
|
||||
loading(true);
|
||||
await setAction(action);
|
||||
loading(false);
|
||||
setBusy(false);
|
||||
}
|
||||
};
|
||||
|
||||
const setAction = async (action: () => Promise<void>) => {
|
||||
try {
|
||||
await action();
|
||||
@ -126,13 +116,13 @@ export function Details({close, closeAll}: {close: ()=>void, closeAll: ()=>void}
|
||||
setAlert(true);
|
||||
}
|
||||
setSaving(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const cards = state.channelCards.map((card, index) => (
|
||||
<Card containerStyle={{...styles.card, borderColor: theme.colors.outlineVariant }} key={index} imageUrl={card.imageUrl} name={card.name} placeholder={state.strings.name}
|
||||
handle={card.handle} node={card.node} actions={[]} />
|
||||
))
|
||||
));
|
||||
|
||||
const members = state.cards.filter(card => {
|
||||
if (state.detail && state.detail.members.find(member => member.guid === card.guid)) {
|
||||
@ -161,12 +151,12 @@ export function Details({close, closeAll}: {close: ()=>void, closeAll: ()=>void}
|
||||
setError(true);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
/>,
|
||||
];
|
||||
|
||||
return (
|
||||
<Card containerStyle={{ ...styles.card, borderColor: theme.colors.outlineVariant }} key={index} imageUrl={card.imageUrl} name={card.name} placeholder={state.strings.name} handle={card.handle} node={card.node} actions={enable} />
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
return (
|
||||
@ -180,7 +170,7 @@ export function Details({close, closeAll}: {close: ()=>void, closeAll: ()=>void}
|
||||
<Text style={styles.title}>{ state.strings.details }</Text>
|
||||
{close && (
|
||||
<View style={styles.close} />
|
||||
)}
|
||||
)}
|
||||
</SafeAreaView>
|
||||
<Divider style={styles.divider} />
|
||||
{ !state.access && (
|
||||
@ -201,8 +191,8 @@ export function Details({close, closeAll}: {close: ()=>void, closeAll: ()=>void}
|
||||
autoComplete="off"
|
||||
autoCorrect={false}
|
||||
value={state.editSubject}
|
||||
label={Platform.OS==='ios'?state.strings.subject:undefined}
|
||||
placeholder={Platform.OS!=='ios'?state.strings.subject:undefined}
|
||||
label={Platform.OS === 'ios' ? state.strings.subject : undefined}
|
||||
placeholder={Platform.OS !== 'ios' ? state.strings.subject : undefined}
|
||||
disabled={state.locked}
|
||||
left={<TextInput.Icon style={styles.icon} icon="label-outline" />}
|
||||
onChangeText={value => actions.setEditSubject(value)}
|
||||
@ -211,8 +201,8 @@ export function Details({close, closeAll}: {close: ()=>void, closeAll: ()=>void}
|
||||
<IconButton style={styles.icon} icon="undo-variant" onPress={actions.undoSubject} />
|
||||
)}
|
||||
{ state.subject !== state.editSubject && (
|
||||
<IconButton style={styles.icon} icon="content-save-outline" loading={saving} onPress={saveSubject} />
|
||||
)}
|
||||
<IconButton style={styles.icon} icon="content-save-outline" loading={saving} onPress={saveSubject} />
|
||||
)}
|
||||
</Surface>
|
||||
)}
|
||||
{ !state.host && !state.locked && (
|
||||
@ -335,6 +325,7 @@ export function Details({close, closeAll}: {close: ()=>void, closeAll: ()=>void}
|
||||
<Text style={styles.unknown}>{ state.strings.unknown }: {state.unknownContacts}</Text>
|
||||
)}
|
||||
</ScrollView>
|
||||
<Confirm show={alert} params={alertParams} />
|
||||
<Confirm show={confirm} busy={busy} params={confirmParams} />
|
||||
<Modal animationType="fade" transparent={true} supportedOrientations={['portrait', 'landscape']} visible={memberModal} onRequestClose={() => setMemberModal(false)}>
|
||||
<View style={styles.memberModal}>
|
||||
@ -368,8 +359,5 @@ export function Details({close, closeAll}: {close: ()=>void, closeAll: ()=>void}
|
||||
</View>
|
||||
</Modal>
|
||||
</View>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// input if host and unsealed
|
||||
// text otherwise
|
||||
|
@ -1,12 +1,12 @@
|
||||
import { useState, useContext, useEffect } from 'react'
|
||||
import { AppContext } from '../context/AppContext'
|
||||
import { DisplayContext } from '../context/DisplayContext'
|
||||
import { ContextType } from '../context/ContextType'
|
||||
import { useState, useContext, useEffect } from 'react';
|
||||
import { AppContext } from '../context/AppContext';
|
||||
import { DisplayContext } from '../context/DisplayContext';
|
||||
import { ContextType } from '../context/ContextType';
|
||||
import { FocusDetail, Card, Profile } from 'databag-client-sdk';
|
||||
|
||||
export function useDetails() {
|
||||
const display = useContext(DisplayContext) as ContextType
|
||||
const app = useContext(AppContext) as ContextType
|
||||
const display = useContext(DisplayContext) as ContextType;
|
||||
const app = useContext(AppContext) as ContextType;
|
||||
const [state, setState] = useState({
|
||||
cardId: null as null | string,
|
||||
channelId: '',
|
||||
@ -26,51 +26,51 @@ export function useDetails() {
|
||||
hostCard: null as null | Card,
|
||||
channelCards: [] as Card[],
|
||||
unknownContacts: 0,
|
||||
})
|
||||
});
|
||||
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const updateState = (value: any) => {
|
||||
setState((s) => ({ ...s, ...value }))
|
||||
}
|
||||
setState((s) => ({ ...s, ...value }));
|
||||
};
|
||||
|
||||
const getTimestamp = (created: number) => {
|
||||
const now = Math.floor((new Date()).getTime() / 1000)
|
||||
const now = Math.floor((new Date()).getTime() / 1000);
|
||||
const date = new Date(created * 1000);
|
||||
const offset = now - created;
|
||||
if(offset < 43200) {
|
||||
if (state.timeFormat === '12h') {
|
||||
return date.toLocaleTimeString("en-US", {hour: 'numeric', minute:'2-digit'});
|
||||
return date.toLocaleTimeString('en-US', {hour: 'numeric', minute:'2-digit'});
|
||||
}
|
||||
else {
|
||||
return date.toLocaleTimeString("en-GB", {hour: 'numeric', minute:'2-digit'});
|
||||
return date.toLocaleTimeString('en-GB', {hour: 'numeric', minute:'2-digit'});
|
||||
}
|
||||
}
|
||||
else if (offset < 31449600) {
|
||||
if (state.dateFormat === 'mm/dd') {
|
||||
return date.toLocaleDateString("en-US", {day: 'numeric', month:'numeric'});
|
||||
return date.toLocaleDateString('en-US', {day: 'numeric', month:'numeric'});
|
||||
}
|
||||
else {
|
||||
return date.toLocaleDateString("en-GB", {day: 'numeric', month:'numeric'});
|
||||
return date.toLocaleDateString('en-GB', {day: 'numeric', month:'numeric'});
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (state.dateFormat === 'mm/dd') {
|
||||
return date.toLocaleDateString("en-US");
|
||||
return date.toLocaleDateString('en-US');
|
||||
}
|
||||
else {
|
||||
return date.toLocaleDateString("en-GB");
|
||||
return date.toLocaleDateString('en-GB');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const { strings, timeFormat, dateFormat } = display.state;
|
||||
updateState({ strings, timeFormat, dateFormat });
|
||||
}, [display.state]);
|
||||
|
||||
useEffect(() => {
|
||||
const hostCard = state.cards.find(entry => entry.cardId == state.cardId);
|
||||
const profileRemoved = state.detail?.members ? state.detail.members.filter(member => state.profile?.guid != member.guid) : [];
|
||||
const hostCard = state.cards.find(entry => entry.cardId === state.cardId);
|
||||
const profileRemoved = state.detail?.members ? state.detail.members.filter(member => state.profile?.guid !== member.guid) : [];
|
||||
const contactCards = profileRemoved.map(member => state.cards.find(card => card.guid === member.guid));
|
||||
const channelCards = contactCards.filter(member => Boolean(member));
|
||||
const unknownContacts = contactCards.length - channelCards.length;
|
||||
@ -93,10 +93,10 @@ export function useDetails() {
|
||||
}
|
||||
});
|
||||
updateState({ cards: sorted });
|
||||
}
|
||||
};
|
||||
const setProfile = (profile: Profile) => {
|
||||
updateState({ profile });
|
||||
}
|
||||
};
|
||||
const setDetail = (focused: { cardId: string | null, channelId: string, detail: FocusDetail | null }) => {
|
||||
const detail = focused ? focused.detail : null;
|
||||
const cardId = focused.cardId;
|
||||
@ -108,7 +108,7 @@ export function useDetails() {
|
||||
const subject = detail?.data?.subject ? detail.data.subject : '';
|
||||
const created = detail?.created ? getTimestamp(detail.created) : '';
|
||||
updateState({ detail, editSubject: subject, subject, channelId, cardId, access, sealed, locked, host, created });
|
||||
}
|
||||
};
|
||||
focus.addDetailListener(setDetail);
|
||||
contact.addCardListener(setCards);
|
||||
identity.addProfileListener(setProfile);
|
||||
@ -116,18 +116,19 @@ export function useDetails() {
|
||||
focus.removeDetailListener(setDetail);
|
||||
contact.removeCardListener(setCards);
|
||||
identity.removeProfileListener(setProfile);
|
||||
}
|
||||
};
|
||||
}
|
||||
}, [app.state.focus, state.timeFormat, state.dateFormat]);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [app.state.session, app.state.focus, state.timeFormat, state.dateFormat]);
|
||||
|
||||
const actions = {
|
||||
remove: async () => {
|
||||
const content = app.state.session.getContent()
|
||||
const content = app.state.session.getContent();
|
||||
await content.removeChannel(state.channelId);
|
||||
app.actions.clearFocus();
|
||||
},
|
||||
leave: async () => {
|
||||
const content = app.state.session.getContent()
|
||||
const content = app.state.session.getContent();
|
||||
await content.leaveChannel(state.cardId, state.channelId);
|
||||
app.actions.clearFocus();
|
||||
},
|
||||
@ -155,10 +156,10 @@ export function useDetails() {
|
||||
updateState({ editSubject: state.subject });
|
||||
},
|
||||
saveSubject: async () => {
|
||||
const content = app.state.session.getContent()
|
||||
const content = app.state.session.getContent();
|
||||
await content.setChannelSubject(state.channelId, state.sealed ? 'sealed' : 'superbasic', { subject: state.editSubject });
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
return { state, actions }
|
||||
return { state, actions };
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { Platform, Share } from 'react-native';
|
||||
import fileType from 'react-native-file-type'
|
||||
import fileType from 'react-native-file-type';
|
||||
import RNFS from 'react-native-fs';
|
||||
import RNFetchBlob from 'rn-fetch-blob';
|
||||
|
||||
@ -7,7 +7,7 @@ export async function Download(uri: string, name: string, extension?: string) {
|
||||
|
||||
if (Platform.OS === 'ios') {
|
||||
const options = { fileCache: true, filename: name };
|
||||
const download = await RNFetchBlob.config(options).fetch("GET", uri);
|
||||
const download = await RNFetchBlob.config(options).fetch('GET', uri);
|
||||
const downloadPath = download.path();
|
||||
|
||||
const type = extension ? extension : (await fileType(downloadPath))?.ext;
|
||||
@ -31,7 +31,7 @@ export async function Download(uri: string, name: string, extension?: string) {
|
||||
await RNFS.scanFile(sharePath);
|
||||
} else {
|
||||
const options = { fileCache: true, filename: name };
|
||||
const download = await RNFetchBlob.config(options).fetch("GET", uri);
|
||||
const download = await RNFetchBlob.config(options).fetch('GET', uri);
|
||||
const downloadPath = download.path();
|
||||
|
||||
const type = extension ? extension : (await fileType(downloadPath))?.ext;
|
||||
@ -42,5 +42,5 @@ export async function Download(uri: string, name: string, extension?: string) {
|
||||
await RNFS.moveFile(downloadPath, sharePath);
|
||||
await RNFS.scanFile(sharePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6,6 +6,8 @@ export const styles = StyleSheet.create({
|
||||
paddingTop: 8,
|
||||
width: '100%',
|
||||
minWidth: 0,
|
||||
fontSize: 14,
|
||||
padding: 0,
|
||||
},
|
||||
topic: {
|
||||
paddingTop: 8,
|
||||
@ -132,11 +134,6 @@ export const styles = StyleSheet.create({
|
||||
height: 64,
|
||||
backgroundColor: 'yellow',
|
||||
},
|
||||
message: {
|
||||
width: '100%',
|
||||
fontSize: 14,
|
||||
padding: 0,
|
||||
},
|
||||
modal: {
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
@ -183,4 +180,4 @@ export const styles = StyleSheet.create({
|
||||
paddingTop: 16,
|
||||
gap: 8,
|
||||
},
|
||||
})
|
||||
});
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { useRef, useEffect, useState, useCallback } from 'react';
|
||||
import { avatar } from '../constants/Icons'
|
||||
import React, { useRef, useEffect, useState } from 'react';
|
||||
import { avatar } from '../constants/Icons';
|
||||
import { Pressable, Linking, ScrollView, View, Image, Modal } from 'react-native';
|
||||
import {Icon, Text, TextInput, IconButton, Button, Surface, Divider} from 'react-native-paper';
|
||||
import { Text, TextInput, IconButton, Button, Surface, Divider} from 'react-native-paper';
|
||||
import { Topic, Card, Profile } from 'databag-client-sdk';
|
||||
import { ImageAsset } from './imageAsset/ImageAsset';
|
||||
import { AudioAsset } from './audioAsset/AudioAsset';
|
||||
@ -10,7 +10,7 @@ import { BinaryAsset } from './binaryAsset/BinaryAsset';
|
||||
import { useMessage } from './useMessage.hook';
|
||||
import {styles} from './Message.styled';
|
||||
import { MediaAsset } from '../conversation/Conversatin';
|
||||
import { Confirm } from '../confirm/Confirm';
|
||||
import { Confirm } from '../confirm/Confirm';
|
||||
import { Shimmer } from './shimmer/Shimmer';
|
||||
import {BlurView} from '@react-native-community/blur';
|
||||
import { sanitizeUrl } from '@braintree/sanitize-url';
|
||||
@ -18,9 +18,9 @@ import { sanitizeUrl } from '@braintree/sanitize-url';
|
||||
export function Message({ topic, card, profile, host, select, selected }: { topic: Topic, card: Card | null, profile: Profile | null, host: boolean, select: (id: null | string)=>void, selected: string }) {
|
||||
const { state, actions } = useMessage();
|
||||
const { locked, data, created, topicId, status, transform } = topic;
|
||||
const { name, handle, node } = profile || card || { name: null, handle: null, node: null }
|
||||
const { text, textColor, textSize, assets } = data || { text: null, textColor: null, textSize: null }
|
||||
const textStyle = textColor && textSize ? { fontSize: textSize, color: textColor } : textColor ? { color: textColor } : textSize ? { fontSize: textSize } : {}
|
||||
const { name, handle, node } = profile || card || { name: null, handle: null, node: null };
|
||||
const { text, textColor, textSize, assets } = data || { text: null, textColor: null, textSize: null };
|
||||
const textStyle = textColor && textSize ? { fontSize: textSize, color: textColor } : textColor ? { color: textColor } : textSize ? { fontSize: textSize } : {};
|
||||
const logoUrl = profile ? profile.imageUrl : card ? card.imageUrl : avatar;
|
||||
const timestamp = actions.getTimestamp(created);
|
||||
const [editing, setEditing] = useState(false);
|
||||
@ -34,6 +34,7 @@ export function Message({ topic, card, profile, host, select, selected }: { topi
|
||||
const loadedCount = useRef(0);
|
||||
const [showAsset, setShowAsset] = useState(false);
|
||||
const [message, setMessage] = useState([]);
|
||||
const fontStyle = { fontStyle: 'italic' };
|
||||
|
||||
useEffect(() => {
|
||||
setTimeout(() => setShowAsset(true), 2000);
|
||||
@ -54,22 +55,23 @@ export function Message({ topic, card, profile, host, select, selected }: { topi
|
||||
if (parsed?.length > 0) {
|
||||
const words = parsed as string[];
|
||||
words.forEach((word, index) => {
|
||||
if (!!urlPattern.test(word)) {
|
||||
if (urlPattern.test(word)) {
|
||||
clickable.push(<Text key={index} style={textStyle}>{ plain }</Text>);
|
||||
plain = '';
|
||||
const url = !!hostPattern.test(word) ? word : `https://${word}`;
|
||||
clickable.push(<Text key={'link-' + index} onPress={() => Linking.openURL(sanitizeUrl(url))} style={{ fontStyle: 'italic' }}>{ sanitizeUrl(word) + ' ' }</Text>);
|
||||
const url = hostPattern.test(word) ? word : `https://${word}`;
|
||||
clickable.push(<Text key={'link-' + index} onPress={() => Linking.openURL(sanitizeUrl(url))} style={fontStyle}>{ sanitizeUrl(word) + ' ' }</Text>);
|
||||
}
|
||||
else {
|
||||
plain += `${word} `;
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
if (plain) {
|
||||
clickable.push(<Text key={parsed.length} style={textStyle}>{ plain }</Text>);
|
||||
}
|
||||
setMessage(clickable)
|
||||
setMessage(clickable);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [text, locked]);
|
||||
|
||||
const loaded = () => {
|
||||
@ -77,12 +79,12 @@ export function Message({ topic, card, profile, host, select, selected }: { topi
|
||||
if (loadedCount.current >= assets.length) {
|
||||
setShowAsset(true);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const edit = () => {
|
||||
setEditing(true);
|
||||
select(null);
|
||||
}
|
||||
};
|
||||
|
||||
const save = async () => {
|
||||
setSaving(true);
|
||||
@ -94,7 +96,7 @@ export function Message({ topic, card, profile, host, select, selected }: { topi
|
||||
}
|
||||
setSaving(false);
|
||||
setEditing(false);
|
||||
}
|
||||
};
|
||||
|
||||
const block = () => {
|
||||
setConfirmParams({
|
||||
@ -118,7 +120,7 @@ export function Message({ topic, card, profile, host, select, selected }: { topi
|
||||
}
|
||||
setBlocking(false);
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
setConfirmShow(true);
|
||||
@ -146,7 +148,7 @@ export function Message({ topic, card, profile, host, select, selected }: { topi
|
||||
}
|
||||
setReporting(false);
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
setConfirmShow(true);
|
||||
@ -174,7 +176,7 @@ export function Message({ topic, card, profile, host, select, selected }: { topi
|
||||
}
|
||||
setRemoving(false);
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
setConfirmShow(true);
|
||||
@ -190,19 +192,19 @@ export function Message({ topic, card, profile, host, select, selected }: { topi
|
||||
},
|
||||
});
|
||||
setConfirmShow(true);
|
||||
}
|
||||
};
|
||||
|
||||
const media = !assets ? [] : assets.map((asset: MediaAsset, index: number) => {
|
||||
if (asset.image || asset.encrypted?.type === 'image') {
|
||||
return <ImageAsset key={index} topicId={topicId} asset={asset as MediaAsset} loaded={loaded} show={showAsset} />
|
||||
return <ImageAsset key={index} topicId={topicId} asset={asset as MediaAsset} loaded={loaded} show={showAsset} />;
|
||||
} else if (asset.audio || asset.encrypted?.type === 'audio') {
|
||||
return <AudioAsset key={index} topicId={topicId} asset={asset as MediaAsset} loaded={loaded} show={showAsset} />
|
||||
return <AudioAsset key={index} topicId={topicId} asset={asset as MediaAsset} loaded={loaded} show={showAsset} />;
|
||||
} else if (asset.video || asset.encrypted?.type === 'video') {
|
||||
return <VideoAsset key={index} topicId={topicId} asset={asset as MediaAsset} loaded={loaded} show={showAsset} />
|
||||
return <VideoAsset key={index} topicId={topicId} asset={asset as MediaAsset} loaded={loaded} show={showAsset} />;
|
||||
} else if (asset.binary || asset.encrypted?.type === 'binary') {
|
||||
return <BinaryAsset key={index} topicId={topicId} asset={asset as MediaAsset} loaded={loaded} show={showAsset} />
|
||||
return <BinaryAsset key={index} topicId={topicId} asset={asset as MediaAsset} loaded={loaded} show={showAsset} />;
|
||||
} else {
|
||||
return <View key={index}></View>
|
||||
return <View key={index} />;
|
||||
}
|
||||
});
|
||||
|
||||
@ -212,7 +214,7 @@ export function Message({ topic, card, profile, host, select, selected }: { topi
|
||||
<View style={styles.content}>
|
||||
<Image style={styles.logo} resizeMode={'contain'} source={{uri: logoUrl}} />
|
||||
<View style={styles.body}>
|
||||
<Pressable style={styles.header} onPress={()=>select(topicId == selected ? null : topicId)}>
|
||||
<Pressable style={styles.header} onPress={()=>select(topicId === selected ? null : topicId)}>
|
||||
<View style={styles.name}>
|
||||
{ name && (
|
||||
<Text style={styles.handle}>{ name }</Text>
|
||||
@ -243,7 +245,7 @@ export function Message({ topic, card, profile, host, select, selected }: { topi
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
{ !locked && assets?.length > 0 && transform === 'complete' && (
|
||||
{ !locked && assets?.length > 0 && transform === 'complete' && (
|
||||
<ScrollView horizontal={true} showsHorizontalScrollIndicator={false} style={styles.carousel} contentContainerStyle={styles.assets}>
|
||||
{ media }
|
||||
</ScrollView>
|
||||
@ -280,11 +282,11 @@ export function Message({ topic, card, profile, host, select, selected }: { topi
|
||||
<BlurView style={styles.blur} blurType="dark" blurAmount={6} reducedTransparencyFallbackColor="dark" />
|
||||
</Pressable>
|
||||
<View style={styles.editArea}>
|
||||
<Surface elevation={2} style={styles.editContent}>
|
||||
<Surface elevation={2} style={styles.editContent}>
|
||||
<Text style={styles.title}>{ state.strings.edit }</Text>
|
||||
<TextInput multiline={true} mode="outlined" style={styles.message}
|
||||
outlineColor="transparent" activeOutlineColor="transparent" spellcheck={false}
|
||||
autoComplete="off" autoCapitalize="none" autoCorrect={false} placeholder={state.strings.newMessage}
|
||||
autoComplete="off" autoCapitalize="none" autoCorrect={false} placeholder={state.strings.newMessage}
|
||||
value={editText} onChangeText={value => setEditText(value)} />
|
||||
<View style={styles.controls}>
|
||||
<Button mode="outlined" onPress={()=>setEditing(false)}>
|
||||
|
@ -1,14 +1,13 @@
|
||||
import React, { useState, useEffect, useRef } from 'react';
|
||||
import { SafeAreaView, Modal, Share, Pressable, View, Image, Animated, useAnimatedValue } from 'react-native'
|
||||
import { Surface, Icon, Text, ProgressBar, IconButton } from 'react-native-paper'
|
||||
import { SafeAreaView, Modal, Pressable, View, Image, Animated, useAnimatedValue } from 'react-native';
|
||||
import { Surface, Icon, Text, ProgressBar, IconButton } from 'react-native-paper';
|
||||
import { useAudioAsset } from './useAudioAsset.hook';
|
||||
import { MediaAsset } from '../../conversation/Conversation';
|
||||
import { styles } from './AudioAsset.styled'
|
||||
import { styles } from './AudioAsset.styled';
|
||||
import {BlurView} from '@react-native-community/blur';
|
||||
import Video, { VideoRef } from 'react-native-video'
|
||||
import Video, { VideoRef } from 'react-native-video';
|
||||
import thumb from '../../images/audio.png';
|
||||
import {Colors} from '../../constants/Colors';
|
||||
import { activateKeepAwake, deactivateKeepAwake} from "@sayem314/react-native-keep-awake";
|
||||
import { activateKeepAwake, deactivateKeepAwake} from '@sayem314/react-native-keep-awake';
|
||||
|
||||
export function AudioAsset({ topicId, asset, loaded, show }: { topicId: string, asset: MediaAsset, loaded: ()=>void, show: boolean }) {
|
||||
const { state, actions } = useAudioAsset(topicId, asset);
|
||||
@ -19,38 +18,39 @@ export function AudioAsset({ topicId, asset, loaded, show }: { topicId: string,
|
||||
const [downloading, setDownloading] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (show) {
|
||||
if (show) {
|
||||
Animated.timing(opacity, {
|
||||
toValue: 1,
|
||||
duration: 100,
|
||||
useNativeDriver: true,
|
||||
}).start();
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [show]);
|
||||
|
||||
const showAudio = () => {
|
||||
setModal(true);
|
||||
actions.loadAudio();
|
||||
activateKeepAwake()
|
||||
activateKeepAwake();
|
||||
};
|
||||
|
||||
const hideAudio = () => {
|
||||
setModal(false);
|
||||
actions.cancelLoad();
|
||||
deactivateKeepAwake();
|
||||
}
|
||||
};
|
||||
|
||||
const play = () => {
|
||||
videoRef.current.resume();
|
||||
}
|
||||
};
|
||||
|
||||
const pause = () => {
|
||||
videoRef.current.pause();
|
||||
}
|
||||
};
|
||||
|
||||
const end = () => {
|
||||
videoRef.current.seek(0);
|
||||
}
|
||||
};
|
||||
|
||||
const playbackRateChange = (e) => {
|
||||
if (e.playbackRate === 0) {
|
||||
@ -58,7 +58,7 @@ export function AudioAsset({ topicId, asset, loaded, show }: { topicId: string,
|
||||
} else {
|
||||
setStatus('playing');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const download = async () => {
|
||||
if (!downloading) {
|
||||
@ -70,12 +70,12 @@ export function AudioAsset({ topicId, asset, loaded, show }: { topicId: string,
|
||||
}
|
||||
setDownloading(false);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<View style={styles.audio}>
|
||||
<Pressable onPress={showAudio}>
|
||||
<Animated.View style={[styles.container,{opacity},]}>
|
||||
<Animated.View style={[styles.container,{opacity}]}>
|
||||
<Image
|
||||
style={styles.thumb}
|
||||
resizeMode="contain"
|
||||
|
@ -1,14 +1,13 @@
|
||||
import { useState, useContext, useEffect, useRef } from 'react'
|
||||
import { AppContext } from '../../context/AppContext'
|
||||
import { DisplayContext } from '../../context/DisplayContext'
|
||||
import { Focus } from 'databag-client-sdk'
|
||||
import { ContextType } from '../../context/ContextType'
|
||||
import { useState, useContext, useRef } from 'react';
|
||||
import { AppContext } from '../../context/AppContext';
|
||||
import { DisplayContext } from '../../context/DisplayContext';
|
||||
import { ContextType } from '../../context/ContextType';
|
||||
import { MediaAsset } from '../../conversation/Conversation';
|
||||
import { Download } from '../../download';
|
||||
|
||||
export function useAudioAsset(topicId: string, asset: MediaAsset) {
|
||||
const app = useContext(AppContext) as ContextType
|
||||
const display = useContext(DisplayContext) as ContextType
|
||||
const app = useContext(AppContext) as ContextType;
|
||||
const display = useContext(DisplayContext) as ContextType;
|
||||
const [state, setState] = useState({
|
||||
strings: display.state.strings,
|
||||
dataUrl: null,
|
||||
@ -16,13 +15,13 @@ export function useAudioAsset(topicId: string, asset: MediaAsset) {
|
||||
loaded: false,
|
||||
loadPercent: 0,
|
||||
failed: false,
|
||||
})
|
||||
});
|
||||
const cancelled = useRef(false);
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
|
||||
const updateState = (value: any) => {
|
||||
setState((s) => ({ ...s, ...value }))
|
||||
}
|
||||
setState((s) => ({ ...s, ...value }));
|
||||
};
|
||||
|
||||
const actions = {
|
||||
cancelLoad: () => {
|
||||
@ -44,10 +43,10 @@ export function useAudioAsset(topicId: string, asset: MediaAsset) {
|
||||
const { focus } = app.state;
|
||||
const assetId = asset.audio ? asset.audio.full : asset.encrypted ? asset.encrypted.parts : null;
|
||||
if (focus && assetId != null && !state.loading && !state.dataUrl) {
|
||||
cancelled.current = false;
|
||||
cancelled.current = false;
|
||||
updateState({ loading: true, loadPercent: 0 });
|
||||
try {
|
||||
const dataUrl = await focus.getTopicAssetUrl(topicId, assetId, (loadPercent: number)=>{ updateState({ loadPercent }); return !cancelled.current });
|
||||
const dataUrl = await focus.getTopicAssetUrl(topicId, assetId, (loadPercent: number)=>{ updateState({ loadPercent }); return !cancelled.current; });
|
||||
updateState({ dataUrl });
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
@ -56,7 +55,7 @@ export function useAudioAsset(topicId: string, asset: MediaAsset) {
|
||||
updateState({ loading: false });
|
||||
}
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
return { state, actions }
|
||||
return { state, actions };
|
||||
}
|
||||
|
@ -1,11 +1,10 @@
|
||||
import React, { useState, useEffect, useRef } from 'react';
|
||||
import { SafeAreaView, Modal, Share, Pressable, View, Image, Animated, useAnimatedValue } from 'react-native'
|
||||
import { Text, Surface, Icon, ProgressBar, IconButton } from 'react-native-paper'
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { SafeAreaView, Modal, Pressable, View, Image, Animated, useAnimatedValue } from 'react-native';
|
||||
import { Text, Surface, Icon, ProgressBar, IconButton } from 'react-native-paper';
|
||||
import { useBinaryAsset } from './useBinaryAsset.hook';
|
||||
import { MediaAsset } from '../../conversation/Conversation';
|
||||
import { styles } from './BinaryAsset.styled'
|
||||
import { styles } from './BinaryAsset.styled';
|
||||
import {BlurView} from '@react-native-community/blur';
|
||||
import Video from 'react-native-video'
|
||||
import thumb from '../../images/binary.png';
|
||||
|
||||
export function BinaryAsset({ topicId, asset, loaded, show }: { topicId: string, asset: MediaAsset, loaded: ()=>void, show: boolean }) {
|
||||
@ -23,18 +22,9 @@ export function BinaryAsset({ topicId, asset, loaded, show }: { topicId: string,
|
||||
useNativeDriver: true,
|
||||
}).start();
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [show]);
|
||||
|
||||
const share = async () => {
|
||||
try {
|
||||
setAlert('');
|
||||
await actions.download();
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
setAlert(state.strings.operationFailed)
|
||||
}
|
||||
}
|
||||
|
||||
const showBinary = () => {
|
||||
setAlert('');
|
||||
setModal(true);
|
||||
@ -44,7 +34,7 @@ export function BinaryAsset({ topicId, asset, loaded, show }: { topicId: string,
|
||||
const hideBinary = () => {
|
||||
setModal(false);
|
||||
actions.cancelLoad();
|
||||
}
|
||||
};
|
||||
|
||||
const download = async () => {
|
||||
if (!downloading) {
|
||||
@ -56,12 +46,12 @@ export function BinaryAsset({ topicId, asset, loaded, show }: { topicId: string,
|
||||
}
|
||||
setDownloading(false);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<View style={styles.binary}>
|
||||
<Pressable onPress={showBinary}>
|
||||
<Animated.View style={[styles.container,{opacity},]}>
|
||||
<Animated.View style={[styles.container,{opacity}]}>
|
||||
<Image
|
||||
style={styles.thumb}
|
||||
resizeMode="contain"
|
||||
|
@ -1,14 +1,13 @@
|
||||
import { useState, useContext, useEffect, useRef } from 'react'
|
||||
import { AppContext } from '../../context/AppContext'
|
||||
import { useState, useContext, useRef } from 'react';
|
||||
import { AppContext } from '../../context/AppContext';
|
||||
import { DisplayContext } from '../../context/DisplayContext';
|
||||
import { Focus } from 'databag-client-sdk'
|
||||
import { ContextType } from '../../context/ContextType'
|
||||
import { ContextType } from '../../context/ContextType';
|
||||
import { MediaAsset } from '../../conversation/Conversation';
|
||||
import { Download } from '../../download';
|
||||
|
||||
export function useBinaryAsset(topicId: string, asset: MediaAsset) {
|
||||
const app = useContext(AppContext) as ContextType
|
||||
const display = useContext(DisplayContext) as ContextType
|
||||
const app = useContext(AppContext) as ContextType;
|
||||
const display = useContext(DisplayContext) as ContextType;
|
||||
const [state, setState] = useState({
|
||||
strings: display.state.strings,
|
||||
dataUrl: null,
|
||||
@ -16,13 +15,13 @@ export function useBinaryAsset(topicId: string, asset: MediaAsset) {
|
||||
loaded: false,
|
||||
loadPercent: 0,
|
||||
failed: false,
|
||||
})
|
||||
});
|
||||
const cancelled = useRef(false);
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
|
||||
const updateState = (value: any) => {
|
||||
setState((s) => ({ ...s, ...value }))
|
||||
}
|
||||
setState((s) => ({ ...s, ...value }));
|
||||
};
|
||||
|
||||
const actions = {
|
||||
cancelLoad: () => {
|
||||
@ -43,10 +42,10 @@ export function useBinaryAsset(topicId: string, asset: MediaAsset) {
|
||||
const { focus } = app.state;
|
||||
const assetId = asset.binary ? asset.binary.data : asset.encrypted ? asset.encrypted.parts : null;
|
||||
if (focus && assetId != null && !state.loading && !state.dataUrl) {
|
||||
cancelled.current = false;
|
||||
cancelled.current = false;
|
||||
updateState({ loading: true, loadPercent: 0 });
|
||||
try {
|
||||
const dataUrl = await focus.getTopicAssetUrl(topicId, assetId, (loadPercent: number)=>{ updateState({ loadPercent }); return !cancelled.current });
|
||||
const dataUrl = await focus.getTopicAssetUrl(topicId, assetId, (loadPercent: number)=>{ updateState({ loadPercent }); return !cancelled.current; });
|
||||
updateState({ dataUrl });
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
@ -54,8 +53,8 @@ export function useBinaryAsset(topicId: string, asset: MediaAsset) {
|
||||
}
|
||||
updateState({ loading: false });
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
return { state, actions }
|
||||
return { state, actions };
|
||||
}
|
||||
|
@ -48,10 +48,6 @@ export const styles = StyleSheet.create({
|
||||
bottom: '10%',
|
||||
width: '50%',
|
||||
},
|
||||
alert: {
|
||||
position: 'absolute',
|
||||
bottom: '10%'
|
||||
},
|
||||
alert: {
|
||||
position: 'absolute',
|
||||
bottom: 0,
|
||||
|
@ -1,12 +1,12 @@
|
||||
import React, { useState, useEffect, useRef } from 'react';
|
||||
import { SafeAreaView, Modal, Pressable, Animated, View, Image, useAnimatedValue } from 'react-native'
|
||||
import { Text, ProgressBar, IconButton } from 'react-native-paper'
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { SafeAreaView, Modal, Pressable, Animated, View, Image, useAnimatedValue } from 'react-native';
|
||||
import { Text, ProgressBar, IconButton } from 'react-native-paper';
|
||||
import { useImageAsset } from './useImageAsset.hook';
|
||||
import { MediaAsset } from '../../conversation/Conversation';
|
||||
import { styles } from './ImageAsset.styled'
|
||||
import { styles } from './ImageAsset.styled';
|
||||
import {BlurView} from '@react-native-community/blur';
|
||||
import { ReactNativeZoomableView } from '@openspacelabs/react-native-zoomable-view';
|
||||
import FastImage from 'react-native-fast-image'
|
||||
import FastImage from 'react-native-fast-image';
|
||||
|
||||
export function ImageAsset({ topicId, asset, loaded, show }: { topicId: string, asset: MediaAsset, loaded: ()=>void, show: boolean }) {
|
||||
const { state, actions } = useImageAsset(topicId, asset);
|
||||
@ -26,6 +26,7 @@ export function ImageAsset({ topicId, asset, loaded, show }: { topicId: string,
|
||||
if (state.loaded) {
|
||||
loaded();
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [state.loaded, show]);
|
||||
|
||||
const showImage = () => {
|
||||
@ -37,7 +38,7 @@ export function ImageAsset({ topicId, asset, loaded, show }: { topicId: string,
|
||||
const hideImage = () => {
|
||||
setModal(false);
|
||||
actions.cancelLoad();
|
||||
}
|
||||
};
|
||||
|
||||
const download = async () => {
|
||||
if (!downloading) {
|
||||
@ -49,14 +50,14 @@ export function ImageAsset({ topicId, asset, loaded, show }: { topicId: string,
|
||||
}
|
||||
setDownloading(false);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<View style={styles.image}>
|
||||
{ state.thumbUrl && (
|
||||
<Pressable onPress={showImage}>
|
||||
<Animated.Image
|
||||
style={[styles.thumb,{opacity},]}
|
||||
style={[styles.thumb,{opacity}]}
|
||||
resizeMode="contain"
|
||||
height={92}
|
||||
width={92 * state.ratio}
|
||||
|
@ -1,15 +1,13 @@
|
||||
import { useState, useContext, useEffect, useRef } from 'react'
|
||||
import { Share } from 'react-native'
|
||||
import { AppContext } from '../../context/AppContext'
|
||||
import { DisplayContext } from '../../context/DisplayContext'
|
||||
import { Focus } from 'databag-client-sdk'
|
||||
import { ContextType } from '../../context/ContextType'
|
||||
import { useState, useContext, useEffect, useRef } from 'react';
|
||||
import { AppContext } from '../../context/AppContext';
|
||||
import { DisplayContext } from '../../context/DisplayContext';
|
||||
import { ContextType } from '../../context/ContextType';
|
||||
import { MediaAsset } from '../../conversation/Conversation';
|
||||
import { Download } from '../../download';
|
||||
|
||||
export function useImageAsset(topicId: string, asset: MediaAsset) {
|
||||
const app = useContext(AppContext) as ContextType
|
||||
const display = useContext(DisplayContext) as ContextType
|
||||
const app = useContext(AppContext) as ContextType;
|
||||
const display = useContext(DisplayContext) as ContextType;
|
||||
const [state, setState] = useState({
|
||||
strings: display.state.strings,
|
||||
thumbUrl: null,
|
||||
@ -21,13 +19,13 @@ export function useImageAsset(topicId: string, asset: MediaAsset) {
|
||||
width: 0,
|
||||
height: 0,
|
||||
failed: false,
|
||||
})
|
||||
});
|
||||
const cancelled = useRef(false);
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
|
||||
const updateState = (value: any) => {
|
||||
setState((s) => ({ ...s, ...value }))
|
||||
}
|
||||
setState((s) => ({ ...s, ...value }));
|
||||
};
|
||||
|
||||
const setThumb = async () => {
|
||||
const { focus } = app.state;
|
||||
@ -44,7 +42,8 @@ export function useImageAsset(topicId: string, asset: MediaAsset) {
|
||||
|
||||
useEffect(() => {
|
||||
setThumb();
|
||||
}, [asset]);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [asset]);
|
||||
|
||||
const actions = {
|
||||
loaded: (e) => {
|
||||
@ -74,10 +73,10 @@ export function useImageAsset(topicId: string, asset: MediaAsset) {
|
||||
const { focus } = app.state;
|
||||
const assetId = asset.image ? asset.image.full : asset.encrypted ? asset.encrypted.parts : null;
|
||||
if (focus && assetId != null && !state.loading && !state.dataUrl) {
|
||||
cancelled.current = false;
|
||||
cancelled.current = false;
|
||||
updateState({ loading: true, loadPercent: 0 });
|
||||
try {
|
||||
const dataUrl = await focus.getTopicAssetUrl(topicId, assetId, (loadPercent: number)=>{ updateState({ loadPercent }); return !cancelled.current });
|
||||
const dataUrl = await focus.getTopicAssetUrl(topicId, assetId, (loadPercent: number)=>{ updateState({ loadPercent }); return !cancelled.current; });
|
||||
updateState({ dataUrl });
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
@ -85,8 +84,8 @@ export function useImageAsset(topicId: string, asset: MediaAsset) {
|
||||
}
|
||||
updateState({ loading: false });
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
return { state, actions }
|
||||
return { state, actions };
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { useEffect } from 'react';
|
||||
import React, { useEffect } from 'react';
|
||||
import { View, Animated, useAnimatedValue } from 'react-native';
|
||||
|
||||
export function Shimmer({ contentStyle }: { contentStyle: any }) {
|
||||
@ -19,11 +19,12 @@ export function Shimmer({ contentStyle }: { contentStyle: any }) {
|
||||
}),
|
||||
])
|
||||
).start();
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Animated.View style={[{},{opacity: shimmer},]}>
|
||||
<View style={contentStyle}></View>
|
||||
<Animated.View style={[{},{opacity: shimmer}]}>
|
||||
<View style={contentStyle} />
|
||||
</Animated.View>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
@ -1,21 +1,21 @@
|
||||
import { useState, useContext, useEffect } from 'react'
|
||||
import { DisplayContext } from '../context/DisplayContext'
|
||||
import { useState, useContext, useEffect } from 'react';
|
||||
import { DisplayContext } from '../context/DisplayContext';
|
||||
import { AppContext } from '../context/AppContext';
|
||||
import { ContextType } from '../context/ContextType'
|
||||
import { ContextType } from '../context/ContextType';
|
||||
|
||||
export function useMessage() {
|
||||
const app = useContext(AppContext) as ContextType
|
||||
const display = useContext(DisplayContext) as ContextType
|
||||
const app = useContext(AppContext) as ContextType;
|
||||
const display = useContext(DisplayContext) as ContextType;
|
||||
const [state, setState] = useState({
|
||||
strings: display.state.strings,
|
||||
timeFormat: display.state.timeFormat,
|
||||
dateFormat: display.state.dateFormat,
|
||||
})
|
||||
});
|
||||
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const updateState = (value: any) => {
|
||||
setState((s) => ({ ...s, ...value }))
|
||||
}
|
||||
setState((s) => ({ ...s, ...value }));
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const { strings, timeFormat, dateFormat } = display.state;
|
||||
@ -48,35 +48,35 @@ export function useMessage() {
|
||||
}
|
||||
},
|
||||
getTimestamp: (created: number) => {
|
||||
const now = Math.floor((new Date()).getTime() / 1000)
|
||||
const now = Math.floor((new Date()).getTime() / 1000);
|
||||
const date = new Date(created * 1000);
|
||||
const offset = now - created;
|
||||
if(offset < 43200) {
|
||||
if (state.timeFormat === '12h') {
|
||||
return date.toLocaleTimeString("en-US", {hour: 'numeric', minute:'2-digit'});
|
||||
return date.toLocaleTimeString('en-US', {hour: 'numeric', minute:'2-digit'});
|
||||
}
|
||||
else {
|
||||
return date.toLocaleTimeString("en-GB", {hour: 'numeric', minute:'2-digit'});
|
||||
return date.toLocaleTimeString('en-GB', {hour: 'numeric', minute:'2-digit'});
|
||||
}
|
||||
}
|
||||
else if (offset < 31449600) {
|
||||
if (state.dateFormat === 'mm/dd') {
|
||||
return date.toLocaleDateString("en-US", {day: 'numeric', month:'numeric'});
|
||||
return date.toLocaleDateString('en-US', {day: 'numeric', month:'numeric'});
|
||||
}
|
||||
else {
|
||||
return date.toLocaleDateString("en-GB", {day: 'numeric', month:'numeric'});
|
||||
return date.toLocaleDateString('en-GB', {day: 'numeric', month:'numeric'});
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (state.dateFormat === 'mm/dd') {
|
||||
return date.toLocaleDateString("en-US");
|
||||
return date.toLocaleDateString('en-US');
|
||||
}
|
||||
else {
|
||||
return date.toLocaleDateString("en-GB");
|
||||
return date.toLocaleDateString('en-GB');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
return { state, actions }
|
||||
return { state, actions };
|
||||
}
|
||||
|
@ -75,11 +75,4 @@ export const styles = StyleSheet.create({
|
||||
spacer: {
|
||||
flexGrow: 1,
|
||||
},
|
||||
alert: {
|
||||
position: 'absolute',
|
||||
bottom: 0,
|
||||
},
|
||||
alertLabel: {
|
||||
color: Colors.offsync,
|
||||
},
|
||||
});
|
||||
|
@ -1,13 +1,12 @@
|
||||
import React, { useState, useEffect, useRef } from 'react';
|
||||
import { SafeAreaView, Share, Modal, Pressable, Animated, View, Image, useAnimatedValue } from 'react-native'
|
||||
import { Text, Surface, Icon, ProgressBar, IconButton } from 'react-native-paper'
|
||||
import { SafeAreaView, Modal, Pressable, Animated, View, Image, useAnimatedValue } from 'react-native';
|
||||
import { Text, Surface, Icon, ProgressBar, IconButton } from 'react-native-paper';
|
||||
import { useVideoAsset } from './useVideoAsset.hook';
|
||||
import { MediaAsset } from '../../conversation/Conversation';
|
||||
import { styles } from './VideoAsset.styled'
|
||||
import { styles } from './VideoAsset.styled';
|
||||
import {BlurView} from '@react-native-community/blur';
|
||||
import Video, { VideoRef } from 'react-native-video'
|
||||
import { Colors } from '../../constants/Colors';
|
||||
import { activateKeepAwake, deactivateKeepAwake} from "@sayem314/react-native-keep-awake";
|
||||
import Video, { VideoRef } from 'react-native-video';
|
||||
import { activateKeepAwake, deactivateKeepAwake} from '@sayem314/react-native-keep-awake';
|
||||
|
||||
export function VideoAsset({ topicId, asset, loaded, show }: { topicId: string, asset: MediaAsset, loaded: ()=>void, show: boolean }) {
|
||||
const { state, actions } = useVideoAsset(topicId, asset);
|
||||
@ -18,7 +17,7 @@ export function VideoAsset({ topicId, asset, loaded, show }: { topicId: string,
|
||||
const [showControl, setShowControl] = useState(false);
|
||||
const clear = useRef();
|
||||
const [downloading, setDownloading] = useState(false);
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
if (state.loaded && show) {
|
||||
Animated.timing(opacity, {
|
||||
@ -30,6 +29,7 @@ export function VideoAsset({ topicId, asset, loaded, show }: { topicId: string,
|
||||
if (state.loaded) {
|
||||
loaded();
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [state.loaded, show]);
|
||||
|
||||
const showVideo = () => {
|
||||
@ -42,7 +42,7 @@ export function VideoAsset({ topicId, asset, loaded, show }: { topicId: string,
|
||||
setModal(false);
|
||||
actions.cancelLoad();
|
||||
deactivateKeepAwake();
|
||||
}
|
||||
};
|
||||
|
||||
const controls = () => {
|
||||
clearTimeout(clear.current);
|
||||
@ -50,19 +50,19 @@ export function VideoAsset({ topicId, asset, loaded, show }: { topicId: string,
|
||||
clear.current = setTimeout(() => {
|
||||
setShowControl(false);
|
||||
}, 3000);
|
||||
}
|
||||
};
|
||||
|
||||
const play = () => {
|
||||
videoRef.current.resume();
|
||||
}
|
||||
};
|
||||
|
||||
const pause = () => {
|
||||
videoRef.current.pause();
|
||||
}
|
||||
};
|
||||
|
||||
const end = () => {
|
||||
videoRef.current.seek(0);
|
||||
}
|
||||
};
|
||||
|
||||
const playbackRateChange = (e) => {
|
||||
if (e.playbackRate === 0) {
|
||||
@ -70,7 +70,7 @@ export function VideoAsset({ topicId, asset, loaded, show }: { topicId: string,
|
||||
} else {
|
||||
setStatus('playing');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const download = async () => {
|
||||
if (!downloading) {
|
||||
@ -82,13 +82,13 @@ export function VideoAsset({ topicId, asset, loaded, show }: { topicId: string,
|
||||
}
|
||||
setDownloading(false);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<View style={styles.video}>
|
||||
{ state.thumbUrl && (
|
||||
<Pressable style={styles.container} onPress={showVideo}>
|
||||
<Animated.View style={[styles.thumb,{opacity},]}>
|
||||
<Animated.View style={[styles.thumb,{opacity}]}>
|
||||
<Image
|
||||
resizeMode="contain"
|
||||
height={92}
|
||||
|
@ -1,14 +1,13 @@
|
||||
import { useState, useContext, useEffect, useRef } from 'react'
|
||||
import { AppContext } from '../../context/AppContext'
|
||||
import { useState, useContext, useEffect, useRef } from 'react';
|
||||
import { AppContext } from '../../context/AppContext';
|
||||
import { DisplayContext } from '../../context/DisplayContext';
|
||||
import { Focus } from 'databag-client-sdk'
|
||||
import { ContextType } from '../../context/ContextType'
|
||||
import { ContextType } from '../../context/ContextType';
|
||||
import { MediaAsset } from '../../conversation/Conversation';
|
||||
import { Download } from '../../download';
|
||||
|
||||
export function useVideoAsset(topicId: string, asset: MediaAsset) {
|
||||
const app = useContext(AppContext) as ContextType
|
||||
const display = useContext(DisplayContext) as ContextType
|
||||
const app = useContext(AppContext) as ContextType;
|
||||
const display = useContext(DisplayContext) as ContextType;
|
||||
const [state, setState] = useState({
|
||||
strings: display.state.strings,
|
||||
thumbUrl: null,
|
||||
@ -18,13 +17,13 @@ export function useVideoAsset(topicId: string, asset: MediaAsset) {
|
||||
loaded: false,
|
||||
loadPercent: 0,
|
||||
failed: false,
|
||||
})
|
||||
});
|
||||
const cancelled = useRef(false);
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
|
||||
const updateState = (value: any) => {
|
||||
setState((s) => ({ ...s, ...value }))
|
||||
}
|
||||
setState((s) => ({ ...s, ...value }));
|
||||
};
|
||||
|
||||
const setThumb = async () => {
|
||||
const { focus } = app.state;
|
||||
@ -41,7 +40,8 @@ export function useVideoAsset(topicId: string, asset: MediaAsset) {
|
||||
|
||||
useEffect(() => {
|
||||
setThumb();
|
||||
}, [asset]);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [asset]);
|
||||
|
||||
const actions = {
|
||||
loaded: (e) => {
|
||||
@ -67,10 +67,10 @@ export function useVideoAsset(topicId: string, asset: MediaAsset) {
|
||||
const { focus } = app.state;
|
||||
const assetId = asset.video ? asset.video.hd || asset.video.lq : asset.encrypted ? asset.encrypted.parts : null;
|
||||
if (focus && assetId != null && !state.loading && !state.dataUrl) {
|
||||
cancelled.current = false;
|
||||
cancelled.current = false;
|
||||
updateState({ loading: true, loadPercent: 0 });
|
||||
try {
|
||||
const dataUrl = await focus.getTopicAssetUrl(topicId, assetId, (loadPercent: number)=>{ updateState({ loadPercent }); return !cancelled.current });
|
||||
const dataUrl = await focus.getTopicAssetUrl(topicId, assetId, (loadPercent: number)=>{ updateState({ loadPercent }); return !cancelled.current; });
|
||||
updateState({ dataUrl });
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
@ -78,8 +78,8 @@ export function useVideoAsset(topicId: string, asset: MediaAsset) {
|
||||
}
|
||||
updateState({ loading: false });
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
return { state, actions }
|
||||
return { state, actions };
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
import React, {useState} from 'react';
|
||||
import {Icon, Text, IconButton, Divider} from 'react-native-paper';
|
||||
import {ScrollView, Image, SafeAreaView, View} from 'react-native';
|
||||
import {ScrollView, Image, View} from 'react-native';
|
||||
import {styles} from './Profile.styled';
|
||||
import {useProfile} from './useProfile.hook';
|
||||
import {Confirm} from '../confirm/Confirm';
|
||||
|
@ -126,7 +126,7 @@ export function useProfile(params: ContactParams) {
|
||||
},
|
||||
saveAndConnect: async () => {
|
||||
const contact = app.state.session?.getContact();
|
||||
const added = await contact.addAndConnectCard(state.node, state.guid);
|
||||
await contact.addAndConnectCard(state.node, state.guid);
|
||||
},
|
||||
remove: async () => {
|
||||
const contact = app.state.session?.getContact();
|
||||
|
@ -1,7 +1,7 @@
|
||||
import React, { useRef, useEffect, useState } from 'react';
|
||||
import { Animated, useAnimatedValue, View } from 'react-native';
|
||||
import { useRing } from './useRing.hook';
|
||||
import { styles } from './Ring.styled'
|
||||
import { styles } from './Ring.styled';
|
||||
import { Card as Contact } from '../card/Card';
|
||||
import { Icon, Text, Surface, IconButton, ActivityIndicator } from 'react-native-paper';
|
||||
import { Confirm } from '../confirm/Confirm';
|
||||
@ -20,7 +20,7 @@ export function Ring() {
|
||||
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 scale = useAnimatedValue(0)
|
||||
const scale = useAnimatedValue(0);
|
||||
|
||||
useEffect(() => {
|
||||
const ringing = setInterval(() => {
|
||||
@ -44,6 +44,7 @@ export function Ring() {
|
||||
useNativeDriver: false,
|
||||
}).start();
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [accepting, state.calling, state.calls]);
|
||||
|
||||
const toggleAudio = async () => {
|
||||
@ -61,7 +62,7 @@ export function Ring() {
|
||||
}
|
||||
setApplyingAudio(false);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const end = async () => {
|
||||
if (!ending) {
|
||||
@ -74,7 +75,7 @@ export function Ring() {
|
||||
}
|
||||
setEnding(false);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const accept = async (callId, card) => {
|
||||
if (!accepting) {
|
||||
@ -88,7 +89,7 @@ export function Ring() {
|
||||
}
|
||||
setAccepting(null);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const ignore = async (callId, card) => {
|
||||
if (!ignoring) {
|
||||
@ -101,7 +102,7 @@ export function Ring() {
|
||||
}
|
||||
setIgnoring(null);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const decline = async (callId, card) => {
|
||||
if (!declining) {
|
||||
@ -114,7 +115,7 @@ export function Ring() {
|
||||
}
|
||||
setDeclining(null);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const alertParams = {
|
||||
title: state.strings.operationFailed,
|
||||
@ -127,22 +128,25 @@ export function Ring() {
|
||||
},
|
||||
};
|
||||
|
||||
const calls = state.calls.map((contact, index) => {
|
||||
const calls = state.calls.map((contact) => {
|
||||
const { callId, card } = contact;
|
||||
const { name, handle, node, imageUrl } = card;
|
||||
const ignoreButton = <IconButton key="ignore" style={styles.circleIcon} iconColor="white" containerColor={Colors.pending} icon="eye-off-outline" compact="true" mode="contained" size={24} loading={ignoring===callId} onPress={()=>ignore(callId, card)} />
|
||||
const declineButton = <IconButton key="decline" style={styles.flipIcon} iconColor="white" containerColor={Colors.offsync} icon="phone-outline" compact="true" mode="contained" size={24} loading={declining===callId} onPress={()=>decline(callId, card)} />
|
||||
const acceptButton = <IconButton key="accept" style={styles.circleIcon} iconColor="white" containerColor={Colors.primary} icon="phone-outline" compact="true" mode="contained" size={24} loading={accepting===callId} onPress={()=>accept(callId, card)} />
|
||||
const ignoreButton = <IconButton key="ignore" style={styles.circleIcon} iconColor="white" containerColor={Colors.pending} icon="eye-off-outline" compact="true" mode="contained" size={24} loading={ignoring === callId} onPress={()=>ignore(callId, card)} />;
|
||||
const declineButton = <IconButton key="decline" style={styles.flipIcon} iconColor="white" containerColor={Colors.offsync} icon="phone-outline" compact="true" mode="contained" size={24} loading={declining === callId} onPress={()=>decline(callId, card)} />;
|
||||
const acceptButton = <IconButton key="accept" style={styles.circleIcon} iconColor="white" containerColor={Colors.primary} icon="phone-outline" compact="true" mode="contained" size={24} loading={accepting === callId} onPress={()=>accept(callId, card)} />;
|
||||
return (
|
||||
<Contact containerStyle={styles.card} placeholder={state.strings.name} imageUrl={imageUrl} name={name} node={node} handle={handle} actions={[ignoreButton, declineButton, acceptButton]} />
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
const sizeStyle = { width: '100%', height: scale };
|
||||
const borderStyle = state.layout === 'large' ? { ...styles.ring, borderRadius: 16 } : { ...styles.ring, borderRadius: 0 };
|
||||
|
||||
return (
|
||||
<Animated.View style={{ width: '100%', height: scale }}>
|
||||
<Animated.View style={sizeStyle}>
|
||||
<View style={(accepting || state.calling || state.calls.length > 0) ? styles.active : styles.inactive}>
|
||||
{ state.calls.length > 0 && !accepting && !state.calling && (
|
||||
<Surface elevation={4} mode="flat" style={{ ...styles.ring, borderRadius: state.layout === 'large' ? 16 : 0 }}>
|
||||
<Surface elevation={4} mode="flat" style={borderStyle}>
|
||||
{ calls[0] }
|
||||
</Surface>
|
||||
)}
|
||||
@ -152,7 +156,7 @@ export function Ring() {
|
||||
</Surface>
|
||||
)}
|
||||
{ state.calling && (
|
||||
<Surface elevation={4} mode="flat" style={{ ...styles.ring, borderRadius: state.layout === 'large' ? 16 : 0 }}>
|
||||
<Surface elevation={4} mode="flat" style={borderStyle}>
|
||||
<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}>
|
||||
@ -165,7 +169,7 @@ export function Ring() {
|
||||
</View>
|
||||
<View style={styles.status}>
|
||||
{ state.connected && (
|
||||
<Text style={styles.duration}>{ `${Math.floor(state.duration/60)}:${(state.duration % 60).toString().padStart(2, '0')}` }</Text>
|
||||
<Text style={styles.duration}>{ `${Math.floor(state.duration / 60)}:${(state.duration % 60).toString().padStart(2, '0')}` }</Text>
|
||||
)}
|
||||
{ !state.connected && (
|
||||
<View style={{ transform: [{ rotate: counter % 2 ? '15deg' : '-15deg' }] }}>
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { useState, useContext, useEffect, useRef } from 'react'
|
||||
import { RingContext } from '../context/RingContext'
|
||||
import { DisplayContext } from '../context/DisplayContext'
|
||||
import { ContextType } from '../context/ContextType'
|
||||
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 useRing() {
|
||||
@ -21,12 +21,12 @@ export function useRing() {
|
||||
connected: false,
|
||||
duration: 0,
|
||||
failed: false,
|
||||
})
|
||||
});
|
||||
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const updateState = (value: any) => {
|
||||
setState((s) => ({ ...s, ...value }))
|
||||
}
|
||||
setState((s) => ({ ...s, ...value }));
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const { layout, strings } = display.state;
|
||||
@ -37,14 +37,14 @@ export function useRing() {
|
||||
const interval = setInterval(() => {
|
||||
if (offset.current) {
|
||||
const now = new Date();
|
||||
const duration = Math.floor((now.getTime() / 1000) - offsetTime.current);
|
||||
updateState({ duration });
|
||||
}
|
||||
const duration = Math.floor((now.getTime() / 1000) - offsetTime.current);
|
||||
updateState({ duration });
|
||||
}
|
||||
}, 1000);
|
||||
return () => {
|
||||
clearInterval(interval);
|
||||
}
|
||||
}, []);
|
||||
};
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const { calls, calling, localVideo, remoteVideo, audioEnabled, connected, connectedTime, failed } = ring.state;
|
||||
|
@ -34,7 +34,7 @@ export const styles = StyleSheet.create({
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
borderRadius: 8,
|
||||
overflow: 'hidden'
|
||||
overflow: 'hidden',
|
||||
},
|
||||
header: {
|
||||
width: '100%',
|
||||
|
@ -19,6 +19,7 @@ export function Selector({ share, selected, channels }: { share: { filePath: str
|
||||
setShow(true);
|
||||
actions.clearFocus();
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [share]);
|
||||
|
||||
const select = () => {
|
||||
@ -28,7 +29,7 @@ export function Selector({ share, selected, channels }: { share: { filePath: str
|
||||
setTopic(null);
|
||||
selected(cardId, channelId);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Modal animationType="fade" transparent={true} supportedOrientations={['portrait', 'landscape']} visible={show} onRequestClose={()=>setShow(false)}>
|
||||
@ -51,8 +52,8 @@ export function Selector({ share, selected, channels }: { share: { filePath: str
|
||||
initialNumToRender={32}
|
||||
showsVerticalScrollIndicator={false}
|
||||
renderItem={({item}) => {
|
||||
const {cardId, channelId, sealed, focused, hosted, unread, imageUrl, subject, message} = item;
|
||||
const select = () => {
|
||||
const {cardId, channelId, sealed, hosted, imageUrl, subject, message} = item;
|
||||
const selection = () => {
|
||||
setTopic({ cardId, channelId });
|
||||
};
|
||||
return (
|
||||
@ -62,7 +63,7 @@ export function Selector({ share, selected, channels }: { share: { filePath: str
|
||||
...styles.channel,
|
||||
borderColor: theme.colors.outlineVariant,
|
||||
}}
|
||||
select={select}
|
||||
select={selection}
|
||||
unread={false}
|
||||
sealed={sealed}
|
||||
hosted={hosted}
|
||||
@ -85,7 +86,7 @@ export function Selector({ share, selected, channels }: { share: { filePath: str
|
||||
<Button style={styles.control} mode="outlined" onPress={()=>setShow(false)}>
|
||||
{state.strings.cancel}
|
||||
</Button>
|
||||
<Button style={styles.control} disabled={topic==null} mode="contained" onPress={select}>
|
||||
<Button style={styles.control} disabled={topic == null} mode="contained" onPress={select}>
|
||||
{state.strings.selectImage}
|
||||
</Button>
|
||||
</View>
|
||||
|
@ -1,25 +1,19 @@
|
||||
import { useState, useContext, useEffect } from 'react'
|
||||
import { AppContext } from '../context/AppContext'
|
||||
import { useContext } from 'react';
|
||||
import { AppContext } from '../context/AppContext';
|
||||
import { DisplayContext } from '../context/DisplayContext';
|
||||
import { ContextType } from '../context/ContextType'
|
||||
import { Channel } from 'databag-client-sdk';
|
||||
import { ContextType } from '../context/ContextType';
|
||||
|
||||
export function useSelector() {
|
||||
const app = useContext(AppContext) as ContextType
|
||||
const display = useContext(DisplayContext) as ContextType
|
||||
const [state, setState] = useState({
|
||||
strings: display.state.strings,
|
||||
channels: [] as Channel[],
|
||||
})
|
||||
const app = useContext(AppContext) as ContextType;
|
||||
const display = useContext(DisplayContext) as ContextType;
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const updateState = (value: any) => {
|
||||
setState((s) => ({ ...s, ...value }))
|
||||
}
|
||||
const state = {
|
||||
strings: display.state.strings,
|
||||
};
|
||||
|
||||
const actions = {
|
||||
clearFocus: app.actions.clearFocus,
|
||||
}
|
||||
};
|
||||
|
||||
return { state, actions }
|
||||
return { state, actions };
|
||||
}
|
||||
|
@ -1,25 +1,24 @@
|
||||
import React, {useState, useCallback, useEffect} from 'react';
|
||||
import {SafeAreaView, Pressable, View, useColorScheme} from 'react-native';
|
||||
import React, {useState, useCallback} from 'react';
|
||||
import {SafeAreaView, View, useColorScheme} from 'react-native';
|
||||
import {styles} from './Service.styled';
|
||||
import {IconButton, Surface, Text, Icon} from 'react-native-paper';
|
||||
import {IconButton, Surface} from 'react-native-paper';
|
||||
import {Accounts} from '../accounts/Accounts';
|
||||
import {Setup} from '../setup/Setup';
|
||||
import {useService} from './useService.hook';
|
||||
import {NavigationContainer, DefaultTheme, DarkTheme} from '@react-navigation/native';
|
||||
import {createDrawerNavigator} from '@react-navigation/drawer';
|
||||
import {Colors} from '../constants/Colors';
|
||||
|
||||
const SetupDrawer = createDrawerNavigator();
|
||||
|
||||
export function Service() {
|
||||
const { state, actions } = useService();
|
||||
const { state } = useService();
|
||||
const [tab, setTab] = useState('accounts');
|
||||
const scheme = useColorScheme();
|
||||
const showAccounts = {display: tab === 'accounts' ? 'flex' : 'none'};
|
||||
const showSetup = {display: tab === 'setup' ? 'flex' : 'none'};
|
||||
|
||||
return (
|
||||
<View style={styles.service}>
|
||||
<Surface elevation={0} style={styles.service}>
|
||||
{state.layout !== 'large' && (
|
||||
<View>
|
||||
<View style={styles.full}>
|
||||
@ -95,7 +94,7 @@ export function Service() {
|
||||
</View>
|
||||
</NavigationContainer>
|
||||
)}
|
||||
</View>
|
||||
</Surface>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
import {useEffect, useState, useContext, useRef} from 'react';
|
||||
import {useEffect, useState, useContext} from 'react';
|
||||
import {AppContext} from '../context/AppContext';
|
||||
import {DisplayContext} from '../context/DisplayContext';
|
||||
import {ContextType} from '../context/ContextType';
|
||||
|
@ -1,5 +1,5 @@
|
||||
import React, {useState, useCallback, useEffect} from 'react';
|
||||
import {SafeAreaView, Modal, Pressable, View, useColorScheme} from 'react-native';
|
||||
import {SafeAreaView, Pressable, View, useColorScheme} from 'react-native';
|
||||
import {RingContextProvider} from '../context/RingContext';
|
||||
import {styles} from './Session.styled';
|
||||
import {IconButton, Surface, Text, Icon} from 'react-native-paper';
|
||||
@ -13,8 +13,6 @@ import {Identity} from '../identity/Identity';
|
||||
import {Base} from '../base/Base';
|
||||
import {Conversation} from '../conversation/Conversation';
|
||||
import {useSession} from './useSession.hook';
|
||||
import {TransitionPresets} from '@react-navigation/stack';
|
||||
import {Focus, Card} from 'databag-client-sdk';
|
||||
import {NavigationContainer, DefaultTheme, DarkTheme} from '@react-navigation/native';
|
||||
import {createDrawerNavigator} from '@react-navigation/drawer';
|
||||
import {createNativeStackNavigator} from '@react-navigation/native-stack';
|
||||
@ -45,11 +43,11 @@ export function Session({ share }: { share: { filePath: string, mimeType: string
|
||||
|
||||
const textContact = (cardId: null|string) => {
|
||||
setTextCard({ cardId });
|
||||
}
|
||||
};
|
||||
|
||||
const callContact = (card: null|Card) => {
|
||||
setCallCard({ card });
|
||||
}
|
||||
};
|
||||
|
||||
const sessionNav = {strings: state.strings, callContact, callCard, textContact, textCard, focus, setFocus, share};
|
||||
const showContent = {display: tab === 'content' ? 'flex' : 'none'};
|
||||
@ -61,18 +59,19 @@ export function Session({ share }: { share: { filePath: string, mimeType: string
|
||||
setTimeout(() => {
|
||||
setDismissed(false);
|
||||
}, 60000);
|
||||
}
|
||||
};
|
||||
|
||||
const contentTab = () => {
|
||||
if (tab !== 'content') {
|
||||
setTab('content');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (share) {
|
||||
contentTab();
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [share]);
|
||||
|
||||
useEffect(() => {
|
||||
@ -219,7 +218,7 @@ function ContentTab({scheme, textCard, contentTab, share}: {scheme: string, text
|
||||
const openConversation = (props) => {
|
||||
props.navigation.navigate('conversation');
|
||||
contentTab();
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<NavigationContainer theme={scheme === 'dark' ? DarkTheme : DefaultTheme}>
|
||||
@ -293,7 +292,7 @@ function DetailsScreen({nav}) {
|
||||
const closeAll = (props) => {
|
||||
props.navigation.closeDrawer();
|
||||
nav.setFocus(false);
|
||||
}
|
||||
};
|
||||
|
||||
const DetailsComponent = useCallback(
|
||||
(props) => (
|
||||
@ -303,6 +302,7 @@ function DetailsScreen({nav}) {
|
||||
/>
|
||||
</Surface>
|
||||
),
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
[nav],
|
||||
);
|
||||
|
||||
@ -448,6 +448,7 @@ function SettingsScreen({nav}) {
|
||||
function HomeScreen({nav}) {
|
||||
useEffect(() => {
|
||||
nav.contacts.closeDrawer();
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [nav.callCard]);
|
||||
|
||||
return (
|
||||
@ -460,7 +461,7 @@ function HomeScreen({nav}) {
|
||||
<Content share={nav.share} textCard={nav.textCard} closeAll={()=>{}} openConversation={()=>nav.setFocus(true)} />
|
||||
</Surface>
|
||||
</View>
|
||||
<Surface style={styles.right} mode="flat">
|
||||
<Surface style={styles.right} mode="flat">
|
||||
{ !nav.focus && (
|
||||
<Base />
|
||||
)}
|
||||
|
@ -1,4 +1,4 @@
|
||||
import {useEffect, useState, useContext, useRef} from 'react';
|
||||
import {useEffect, useState, useContext} from 'react';
|
||||
import { AppState } from 'react-native';
|
||||
import {AppContext} from '../context/AppContext';
|
||||
import {DisplayContext} from '../context/DisplayContext';
|
||||
@ -25,13 +25,13 @@ export function useSession() {
|
||||
if (loaded) {
|
||||
SplashScreen.hide();
|
||||
}
|
||||
}
|
||||
const setSdkState = (state: string) => {
|
||||
updateState({ sdkState: state === 'connected' });
|
||||
}
|
||||
const setAppState = (state: string) => {
|
||||
updateState({ appState: state === 'active' });
|
||||
}
|
||||
};
|
||||
const setSdkState = (sdkState: string) => {
|
||||
updateState({ sdkState: sdkState === 'connected' });
|
||||
};
|
||||
const setAppState = (appState: string) => {
|
||||
updateState({ appState: appState === 'active' });
|
||||
};
|
||||
const session = app.state.session;
|
||||
if (session) {
|
||||
const content = session.getContent();
|
||||
@ -42,7 +42,7 @@ export function useSession() {
|
||||
session.removeStatusListener(setSdkState);
|
||||
content.removeLoadedListener(setContentState);
|
||||
sub.remove();
|
||||
}
|
||||
};
|
||||
}
|
||||
}, [app.state.session]);
|
||||
|
||||
|
@ -52,7 +52,7 @@ export function Settings({showLogout}: {showLogout: boolean}) {
|
||||
setBlockedError(true);
|
||||
}
|
||||
setBlockedMessage(true);
|
||||
}
|
||||
};
|
||||
|
||||
const unblockMessage = async (blocked: {cardId: string | null, channelId: string, topicId: string, timestamp: number}) => {
|
||||
try {
|
||||
@ -62,7 +62,7 @@ export function Settings({showLogout}: {showLogout: boolean}) {
|
||||
console.log(err);
|
||||
setBlockedError(true);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const blockedMessages = state.blockedMessages.map((blocked, index) => (
|
||||
<View key={index} style={{ ...styles.blockedItem, borderColor: theme.colors.outlineVariant }}>
|
||||
@ -80,7 +80,7 @@ export function Settings({showLogout}: {showLogout: boolean}) {
|
||||
setBlockedError(true);
|
||||
}
|
||||
setBlockedChannel(true);
|
||||
}
|
||||
};
|
||||
|
||||
const unblockChannel = async (blocked: {cardId: string | null, channelId: string, topicId: string, timestamp: number}) => {
|
||||
try {
|
||||
@ -90,7 +90,7 @@ export function Settings({showLogout}: {showLogout: boolean}) {
|
||||
console.log(err);
|
||||
setBlockedError(true);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const blockedChannels = state.blockedChannels.map((blocked, index) => (
|
||||
<View key={index} style={{ ...styles.blockedItem, borderColor: theme.colors.outlineVariant }}>
|
||||
@ -108,7 +108,7 @@ export function Settings({showLogout}: {showLogout: boolean}) {
|
||||
setBlockedError(true);
|
||||
}
|
||||
setBlockedContact(true);
|
||||
}
|
||||
};
|
||||
|
||||
const unblockContact = async (blocked: {cardId: string | null, channelId: string, topicId: string, timestamp: number}) => {
|
||||
try {
|
||||
@ -118,7 +118,7 @@ export function Settings({showLogout}: {showLogout: boolean}) {
|
||||
console.log(err);
|
||||
setBlockedError(true);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const blockedContacts = state.blockedContacts.map((blocked, index) => (
|
||||
<View key={index} style={{ ...styles.blockedItem, borderColor: theme.colors.outlineVariant }}>
|
||||
@ -669,7 +669,7 @@ export function Settings({showLogout}: {showLogout: boolean}) {
|
||||
<View style={styles.modalControls}>
|
||||
<View style={styles.modalOption}>
|
||||
<IconButton
|
||||
style={styles.optionIcon}
|
||||
style={styles.optionIcon}
|
||||
iconColor={Colors.primary}
|
||||
icon="menu-right-outline"
|
||||
size={32}
|
||||
@ -696,7 +696,7 @@ export function Settings({showLogout}: {showLogout: boolean}) {
|
||||
</Button>
|
||||
<View style={styles.modalOther}>
|
||||
<IconButton
|
||||
style={styles.optionIcon}
|
||||
style={styles.optionIcon}
|
||||
iconColor={Colors.primary}
|
||||
icon="menu-left-outline"
|
||||
size={32}
|
||||
@ -719,8 +719,8 @@ export function Settings({showLogout}: {showLogout: boolean}) {
|
||||
autoComplete="off"
|
||||
autoCorrect={false}
|
||||
value={state.sealPassword}
|
||||
label={Platform.OS==='ios'?state.strings.password:undefined}
|
||||
placeholder={Platform.OS!=='ios'?state.strings.password:undefined}
|
||||
label={Platform.OS === 'ios' ? state.strings.password : undefined}
|
||||
placeholder={Platform.OS !== 'ios' ? state.strings.password : undefined}
|
||||
secureTextEntry={!showPassword}
|
||||
left={<TextInput.Icon style={styles.icon} icon="lock" />}
|
||||
right={
|
||||
@ -739,8 +739,8 @@ export function Settings({showLogout}: {showLogout: boolean}) {
|
||||
autoComplete="off"
|
||||
autoCorrect={false}
|
||||
value={state.sealConfirm}
|
||||
label={Platform.OS==='ios'?state.strings.confirmPassword:undefined}
|
||||
placeholder={Platform.OS!=='ios'?state.strings.confirmPassword:undefined}
|
||||
label={Platform.OS === 'ios' ? state.strings.confirmPassword : undefined}
|
||||
placeholder={Platform.OS !== 'ios' ? state.strings.confirmPassword : undefined}
|
||||
secureTextEntry={!showConfirm}
|
||||
left={<TextInput.Icon style={styles.icon} icon="lock" />}
|
||||
right={
|
||||
@ -772,8 +772,8 @@ export function Settings({showLogout}: {showLogout: boolean}) {
|
||||
autoComplete="off"
|
||||
autoCorrect={false}
|
||||
value={state.sealPassword}
|
||||
label={Platform.OS==='ios'?state.strings.password:undefined}
|
||||
placeholder={Platform.OS!=='ios'?state.strings.password:undefined}
|
||||
label={Platform.OS === 'ios' ? state.strings.password : undefined}
|
||||
placeholder={Platform.OS !== 'ios' ? state.strings.password : undefined}
|
||||
secureTextEntry={!showPassword}
|
||||
left={<TextInput.Icon style={styles.icon} icon="lock" />}
|
||||
right={
|
||||
@ -789,7 +789,7 @@ export function Settings({showLogout}: {showLogout: boolean}) {
|
||||
<View style={styles.modalControls}>
|
||||
<View style={styles.modalOption}>
|
||||
<IconButton
|
||||
style={styles.optionIcon}
|
||||
style={styles.optionIcon}
|
||||
iconColor={Colors.primary}
|
||||
icon="menu-right-outline"
|
||||
size={32}
|
||||
@ -813,7 +813,7 @@ export function Settings({showLogout}: {showLogout: boolean}) {
|
||||
</Button>
|
||||
<View style={styles.modalOther}>
|
||||
<IconButton
|
||||
style={styles.optionIcon}
|
||||
style={styles.optionIcon}
|
||||
iconColor={Colors.primary}
|
||||
icon="menu-left-outline"
|
||||
size={32}
|
||||
@ -836,8 +836,8 @@ export function Settings({showLogout}: {showLogout: boolean}) {
|
||||
autoComplete="off"
|
||||
autoCorrect={false}
|
||||
value={state.sealDelete}
|
||||
label={Platform.OS==='ios'?state.strings.deleteKey:undefined}
|
||||
placeholder={Platform.OS!=='ios'?state.strings.deleteKey:undefined}
|
||||
label={Platform.OS === 'ios' ? state.strings.deleteKey : undefined}
|
||||
placeholder={Platform.OS !== 'ios' ? state.strings.deleteKey : undefined}
|
||||
left={<TextInput.Icon style={styles.icon} icon="lock" />}
|
||||
onChangeText={value => actions.setSealDelete(value)}
|
||||
/>
|
||||
@ -859,8 +859,8 @@ export function Settings({showLogout}: {showLogout: boolean}) {
|
||||
autoComplete="off"
|
||||
autoCorrect={false}
|
||||
value={state.sealPassword}
|
||||
label={Platform.OS==='ios'?state.strings.password:undefined}
|
||||
placeholder={Platform.OS!=='ios'?state.strings.password:undefined}
|
||||
label={Platform.OS === 'ios' ? state.strings.password : undefined}
|
||||
placeholder={Platform.OS !== 'ios' ? state.strings.password : undefined}
|
||||
secureTextEntry={!showPassword}
|
||||
left={<TextInput.Icon style={styles.icon} icon="lock" />}
|
||||
right={
|
||||
@ -899,8 +899,8 @@ export function Settings({showLogout}: {showLogout: boolean}) {
|
||||
autoCapitalize="none"
|
||||
autoComplete="off"
|
||||
autoCorrect={false}
|
||||
label={Platform.OS==='ios'?state.strings.name:undefined}
|
||||
placeholder={Platform.OS!=='ios'?state.strings.name:undefined}
|
||||
label={Platform.OS === 'ios' ? state.strings.name : undefined}
|
||||
placeholder={Platform.OS !== 'ios' ? state.strings.name : undefined}
|
||||
value={state.name}
|
||||
left={<TextInput.Icon style={styles.inputIcon} icon="account" />}
|
||||
onChangeText={value => actions.setName(value)}
|
||||
@ -911,8 +911,8 @@ export function Settings({showLogout}: {showLogout: boolean}) {
|
||||
autoCapitalize="none"
|
||||
autoComplete="off"
|
||||
autoCorrect={false}
|
||||
label={Platform.OS==='ios'?state.strings.location:undefined}
|
||||
placeholder={Platform.OS!=='ios'?state.strings.location:undefined}
|
||||
label={Platform.OS === 'ios' ? state.strings.location : undefined}
|
||||
placeholder={Platform.OS !== 'ios' ? state.strings.location : undefined}
|
||||
value={state.location}
|
||||
left={<TextInput.Icon style={styles.inputIcon} icon="map-marker-outline" />}
|
||||
onChangeText={value => actions.setLocation(value)}
|
||||
@ -923,8 +923,8 @@ export function Settings({showLogout}: {showLogout: boolean}) {
|
||||
autoCapitalize="none"
|
||||
autoComplete="off"
|
||||
autoCorrect={false}
|
||||
label={Platform.OS==='ios'?state.strings.description:undefined}
|
||||
placeholder={Platform.OS!=='ios'?state.strings.description:undefined}
|
||||
label={Platform.OS === 'ios' ? state.strings.description : undefined}
|
||||
placeholder={Platform.OS !== 'ios' ? state.strings.description : undefined}
|
||||
value={state.description}
|
||||
left={<TextInput.Icon style={styles.inputIcon} icon="book-open-outline" />}
|
||||
onChangeText={value => actions.setDescription(value)}
|
||||
@ -1013,8 +1013,8 @@ export function Settings({showLogout}: {showLogout: boolean}) {
|
||||
autoCapitalize="none"
|
||||
autoComplete="off"
|
||||
autoCorrect={false}
|
||||
label={Platform.OS==='ios' ? state.strings.username : undefined}
|
||||
placeholder={Platform.OS!=='ios' ? state.strings.username : undefined}
|
||||
label={Platform.OS === 'ios' ? state.strings.username : undefined}
|
||||
placeholder={Platform.OS !== 'ios' ? state.strings.username : undefined}
|
||||
value={state.handle}
|
||||
left={<TextInput.Icon style={styles.inputIcon} icon="account" />}
|
||||
right={
|
||||
@ -1035,8 +1035,8 @@ export function Settings({showLogout}: {showLogout: boolean}) {
|
||||
autoComplete="off"
|
||||
autoCorrect={false}
|
||||
value={state.password}
|
||||
label={Platform.OS==='ios' ? state.strings.password : undefined}
|
||||
placeholder={Platform.OS!=='ios' ? state.strings.password : undefined}
|
||||
label={Platform.OS === 'ios' ? state.strings.password : undefined}
|
||||
placeholder={Platform.OS !== 'ios' ? state.strings.password : undefined}
|
||||
secureTextEntry={!showPassword}
|
||||
left={<TextInput.Icon style={styles.icon} icon="lock" />}
|
||||
right={
|
||||
@ -1055,8 +1055,8 @@ export function Settings({showLogout}: {showLogout: boolean}) {
|
||||
autoComplete="off"
|
||||
autoCorrect={false}
|
||||
value={state.confirm}
|
||||
label={Platform.OS==='ios' ? state.strings.confirmPassword : undefined}
|
||||
placeholder={Platform.OS!=='ios' ? state.strings.confirmPassword : undefined}
|
||||
label={Platform.OS === 'ios' ? state.strings.confirmPassword : undefined}
|
||||
placeholder={Platform.OS !== 'ios' ? state.strings.confirmPassword : undefined}
|
||||
secureTextEntry={!showConfirm}
|
||||
left={<TextInput.Icon style={styles.icon} icon="lock" />}
|
||||
right={
|
||||
@ -1121,8 +1121,8 @@ export function Settings({showLogout}: {showLogout: boolean}) {
|
||||
autoComplete="off"
|
||||
autoCorrect={false}
|
||||
value={state.remove}
|
||||
label={Platform.OS==='ios'?state.strings.deleteKey:undefined}
|
||||
placeholder={Platform.OS!=='ios'?state.strings.deleteKey:undefined}
|
||||
label={Platform.OS === 'ios' ? state.strings.deleteKey : undefined}
|
||||
placeholder={Platform.OS !== 'ios' ? state.strings.deleteKey : undefined}
|
||||
left={<TextInput.Icon style={styles.icon} icon="delete-outline" />}
|
||||
onChangeText={value => actions.setRemove(value)}
|
||||
/>
|
||||
@ -1149,7 +1149,7 @@ export function Settings({showLogout}: {showLogout: boolean}) {
|
||||
<IconButton style={styles.blockedClose} icon="close" size={24} onPress={() => setBlockedMessage(false)} />
|
||||
</View>
|
||||
<Surface style={styles.blocked} elevation={1} mode="flat">
|
||||
{ state.blockedMessages.length == 0 && (
|
||||
{ state.blockedMessages.length === 0 && (
|
||||
<View style={styles.blockedEmpty}>
|
||||
<Text style={styles.blockedLabel}>{ state.strings.noMessages }</Text>
|
||||
</View>
|
||||
@ -1159,7 +1159,7 @@ export function Settings({showLogout}: {showLogout: boolean}) {
|
||||
{ blockedMessages }
|
||||
</View>
|
||||
)}
|
||||
</Surface>
|
||||
</Surface>
|
||||
<View style={styles.blockedDone}>
|
||||
{ blockedError && (
|
||||
<Text style={styles.blockedError}>{ state.strings.operationFailed }</Text>
|
||||
@ -1182,17 +1182,17 @@ export function Settings({showLogout}: {showLogout: boolean}) {
|
||||
<IconButton style={styles.blockedClose} icon="close" size={24} onPress={() => setBlockedChannel(false)} />
|
||||
</View>
|
||||
<Surface style={styles.blocked} elevation={1} mode="flat">
|
||||
{ state.blockedChannels.length == 0 && (
|
||||
{ state.blockedChannels.length === 0 && (
|
||||
<View style={styles.blockedEmpty}>
|
||||
<Text style={styles.blockedLabel}>{ state.strings.noTopics }</Text>
|
||||
</View>
|
||||
)}
|
||||
)}
|
||||
{ state.blockedChannels.length > 0 && (
|
||||
<View>
|
||||
{ blockedChannels }
|
||||
</View>
|
||||
)}
|
||||
</Surface>
|
||||
</Surface>
|
||||
<View style={styles.blockedDone}>
|
||||
{ blockedError && (
|
||||
<Text style={styles.blockedError}>{ state.strings.operationFailed }</Text>
|
||||
@ -1215,17 +1215,17 @@ export function Settings({showLogout}: {showLogout: boolean}) {
|
||||
<IconButton style={styles.blockedClose} icon="close" size={24} onPress={() => setBlockedContact(false)} />
|
||||
</View>
|
||||
<Surface style={styles.blocked} elevation={1} mode="flat">
|
||||
{ state.blockedContacts.length == 0 && (
|
||||
{ state.blockedContacts.length === 0 && (
|
||||
<View style={styles.blockedEmpty}>
|
||||
<Text style={styles.blockedLabel}>{ state.strings.noContacts }</Text>
|
||||
</View>
|
||||
)}
|
||||
)}
|
||||
{ state.blockedContacts.length > 0 && (
|
||||
<View>
|
||||
{ blockedContacts }
|
||||
</View>
|
||||
)}
|
||||
</Surface>
|
||||
</Surface>
|
||||
<View style={styles.blockedDone}>
|
||||
{ blockedError && (
|
||||
<Text style={styles.blockedError}>{ state.strings.operationFailed }</Text>
|
||||
|
@ -258,7 +258,7 @@ export function useSettings() {
|
||||
unblockMessage: async (cardId: string | null, channelId: string, topicId: string) => {
|
||||
const content = app.state.session.getContent();
|
||||
await content.clearBlockedChannelTopic(cardId, channelId, topicId);
|
||||
const blockedMessages = state.blockedMessages.filter(blocked => (blocked.cardId != cardId || blocked.channelId != channelId || blocked.topicId != topicId));
|
||||
const blockedMessages = state.blockedMessages.filter(blocked => (blocked.cardId !== cardId || blocked.channelId !== channelId || blocked.topicId !== topicId));
|
||||
updateState({ blockedMessages });
|
||||
},
|
||||
loadBlockedChannels: async () => {
|
||||
@ -269,7 +269,7 @@ export function useSettings() {
|
||||
unblockChannel: async (cardId: string | null, channelId: string) => {
|
||||
const content = app.state.session.getContent();
|
||||
await content.setBlockedChannel(cardId, channelId, false);
|
||||
const blockedChannels = state.blockedChannels.filter(blocked => (blocked.cardId != cardId || blocked.channelId != channelId));
|
||||
const blockedChannels = state.blockedChannels.filter(blocked => (blocked.cardId !== cardId || blocked.channelId !== channelId));
|
||||
updateState({ blockedChannels });
|
||||
},
|
||||
loadBlockedContacts: async () => {
|
||||
@ -280,38 +280,38 @@ export function useSettings() {
|
||||
unblockContact: async (cardId: string) => {
|
||||
const contact = app.state.session.getContact();
|
||||
await contact.setBlockedCard(cardId, false);
|
||||
const blockedContacts = state.blockedContacts.filter(blocked => blocked.cardId != cardId);
|
||||
const blockedContacts = state.blockedContacts.filter(blocked => blocked.cardId !== cardId);
|
||||
updateState({ blockedContacts });
|
||||
},
|
||||
getTimestamp: (created: number) => {
|
||||
const now = Math.floor((new Date()).getTime() / 1000)
|
||||
const now = Math.floor((new Date()).getTime() / 1000);
|
||||
const date = new Date(created * 1000);
|
||||
const offset = now - created;
|
||||
if(offset < 43200) {
|
||||
if (state.timeFormat === '12h') {
|
||||
return date.toLocaleTimeString("en-US", {hour: 'numeric', minute:'2-digit'});
|
||||
return date.toLocaleTimeString('en-US', {hour: 'numeric', minute:'2-digit'});
|
||||
}
|
||||
else {
|
||||
return date.toLocaleTimeString("en-GB", {hour: 'numeric', minute:'2-digit'});
|
||||
return date.toLocaleTimeString('en-GB', {hour: 'numeric', minute:'2-digit'});
|
||||
}
|
||||
}
|
||||
else if (offset < 31449600) {
|
||||
if (state.dateFormat === 'mm/dd') {
|
||||
return date.toLocaleDateString("en-US", {day: 'numeric', month:'numeric'});
|
||||
return date.toLocaleDateString('en-US', {day: 'numeric', month:'numeric'});
|
||||
}
|
||||
else {
|
||||
return date.toLocaleDateString("en-GB", {day: 'numeric', month:'numeric'});
|
||||
return date.toLocaleDateString('en-GB', {day: 'numeric', month:'numeric'});
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (state.dateFormat === 'mm/dd') {
|
||||
return date.toLocaleDateString("en-US");
|
||||
return date.toLocaleDateString('en-US');
|
||||
}
|
||||
else {
|
||||
return date.toLocaleDateString("en-GB");
|
||||
return date.toLocaleDateString('en-GB');
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
return {state, actions};
|
||||
|
@ -1,5 +1,5 @@
|
||||
import React, { useState } from 'react';
|
||||
import {SafeAreaView, TouchableOpacity, Modal, Image, View, Pressable} from 'react-native';
|
||||
import {TouchableOpacity, Modal, Image, View} from 'react-native';
|
||||
import {ActivityIndicator, Icon, Button, IconButton, RadioButton, Switch, Surface, Divider, TextInput, Text} from 'react-native-paper';
|
||||
import {styles} from './Setup.styled';
|
||||
import {useSetup} from './useSetup.hook';
|
||||
@ -12,18 +12,17 @@ import Clipboard from '@react-native-clipboard/clipboard';
|
||||
|
||||
export function Setup() {
|
||||
const { state, actions } = useSetup();
|
||||
const [mfaCode, setMfaCode] = useState('');
|
||||
const [updating, setUpdating] = useState(false);
|
||||
const [secretCopy, setSecretCopy] = useState(false);
|
||||
const [confirmingAuth, setConfirmingAuth] = useState(false);
|
||||
|
||||
const errorParams = {
|
||||
title: state.strings.operationFailed,
|
||||
title: state.strings.operationFailed,
|
||||
prompt: state.strings.tryAgain,
|
||||
cancel: {
|
||||
label: state.strings.close,
|
||||
action: actions.clearError,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const confirmAuth = async () => {
|
||||
@ -32,7 +31,7 @@ export function Setup() {
|
||||
await actions.confirmMFAuth();
|
||||
setConfirmingAuth(false);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const copySecret = async () => {
|
||||
if (!secretCopy) {
|
||||
@ -54,7 +53,7 @@ export function Setup() {
|
||||
}
|
||||
setUpdating(false);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<View style={styles.setup}>
|
||||
@ -76,14 +75,14 @@ export function Setup() {
|
||||
<View style={styles.radioSelect}>
|
||||
<View style={styles.radio}>
|
||||
<Text style={styles.radioLabel}>RSA2048</Text>
|
||||
<RadioButton.Item
|
||||
<RadioButton.Item
|
||||
disabled={state.loading}
|
||||
rippleColor="transparent"
|
||||
style={styles.radioButton}
|
||||
label=""
|
||||
mode="android"
|
||||
status={state.setup?.keyType === 'RSA2048' ? 'checked' : 'unchecked'}
|
||||
onPress={() => { actions.setKeyType('RSA2048') }}
|
||||
onPress={() => { actions.setKeyType('RSA2048'); }}
|
||||
/>
|
||||
</View>
|
||||
<View style={styles.radio}>
|
||||
@ -95,7 +94,7 @@ export function Setup() {
|
||||
label=""
|
||||
mode="android"
|
||||
status={state.setup?.keyType === 'RSA4096' ? 'checked' : 'unchecked'}
|
||||
onPress={() => { actions.setKeyType('RSA4096') }}
|
||||
onPress={() => { actions.setKeyType('RSA4096'); }}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
|
@ -41,14 +41,14 @@ export function useSetup() {
|
||||
const mfaEnabled = await service.checkMFAuth();
|
||||
setup.current = await service.getSetup();
|
||||
loading.current = false;
|
||||
const storage = Math.floor((setup.current?.accountStorage || 0) / 1073741824);
|
||||
updateState({ setup: setup.current, mfaEnabled, accountStorage: storage.toString(), loading: false });
|
||||
const storage = Math.floor((setup.current?.accountStorage || 0) / 1073741824);
|
||||
updateState({ setup: setup.current, mfaEnabled, accountStorage: storage.toString(), loading: false });
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
await new Promise((r) => setTimeout(r, DELAY_MS));
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const save = () => {
|
||||
updated.current = true;
|
||||
@ -60,7 +60,7 @@ export function useSetup() {
|
||||
const service = app.state.service;
|
||||
await service.setSetup(setup.current);
|
||||
if (updated.current) {
|
||||
save()
|
||||
save();
|
||||
} else {
|
||||
updateState({ updating: false });
|
||||
}
|
||||
@ -69,7 +69,7 @@ export function useSetup() {
|
||||
updateState({ error: true });
|
||||
}
|
||||
}, DEBOUNCE_MS);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const { layout, strings} = display.state;
|
||||
@ -82,9 +82,10 @@ export function useSetup() {
|
||||
sync();
|
||||
return () => {
|
||||
loading.current = false;
|
||||
}
|
||||
};
|
||||
}
|
||||
}, []);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [app.state.service]);
|
||||
|
||||
const actions = {
|
||||
logout: app.actions.adminLogout,
|
||||
@ -117,7 +118,7 @@ export function useSetup() {
|
||||
await service.confirmMFAuth(state.mfaCode);
|
||||
updateState({ confirmingMFAuth: false, mfaEnabled: true });
|
||||
} catch (err) {
|
||||
const { message } = err as { message: string }
|
||||
const { message } = err as { message: string };
|
||||
if (message === '401') {
|
||||
updateState({ mfaMessage: state.strings.mfaError });
|
||||
} else if (message === '429') {
|
||||
@ -142,7 +143,7 @@ export function useSetup() {
|
||||
},
|
||||
setAccountStorage: (accountStorage: string) => {
|
||||
if (setup.current) {
|
||||
const storage = parseInt(accountStorage) * 1073741824;
|
||||
const storage = parseInt(accountStorage, 10) * 1073741824;
|
||||
if (storage >= 0) {
|
||||
setup.current.accountStorage = storage;
|
||||
updateState({ setup: setup.current, accountStorage });
|
||||
@ -170,7 +171,7 @@ export function useSetup() {
|
||||
},
|
||||
setOpenAccessLimit: (openAccessLimit: string) => {
|
||||
if (setup.current) {
|
||||
const limit = parseInt(openAccessLimit);
|
||||
const limit = parseInt(openAccessLimit, 10);
|
||||
if (limit >= 0) {
|
||||
setup.current.openAccessLimit = limit;
|
||||
updateState({ setup: setup.current, openAccessLimit });
|
||||
|
@ -1,5 +1,4 @@
|
||||
import {StyleSheet} from 'react-native';
|
||||
import { Colors } from '../constants/Colors';
|
||||
|
||||
export const styles = StyleSheet.create({
|
||||
base: {
|
||||
@ -41,7 +40,7 @@ export const styles = StyleSheet.create({
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
gap: 8,
|
||||
align: 'center',
|
||||
align: 'center',
|
||||
},
|
||||
label: {
|
||||
fontSize: 18,
|
||||
|
@ -1,4 +1,4 @@
|
||||
import React from 'react'
|
||||
import React from 'react';
|
||||
import { View, Image } from 'react-native';
|
||||
import { styles } from './Welcome.styled';
|
||||
import { useWelcome } from './useWelcome.hook';
|
||||
@ -16,7 +16,7 @@ export function Welcome() {
|
||||
<View style={{ ...styles.base, backgroundColor: theme.colors.base }}>
|
||||
<Text style={styles.title}>Databag</Text>
|
||||
<Text style={styles.description}>{ state.strings.communication }</Text>
|
||||
<Image style={styles.image} source={theme.colors.name == 'light' ? light : dark} resizeMode="contain" />
|
||||
<Image style={styles.image} source={theme.colors.name === 'light' ? light : dark} resizeMode="contain" />
|
||||
<View style={styles.steps}>
|
||||
<View style={styles.step}>
|
||||
<Icon size={18} source="chevron-right" color={Colors.placeholder} />
|
||||
|
@ -1,20 +1,20 @@
|
||||
import { useState, useContext, useEffect } from 'react'
|
||||
import { useState, useContext, useEffect } from 'react';
|
||||
import { DisplayContext } from '../context/DisplayContext';
|
||||
import { AppContext } from '../context/AppContext';
|
||||
import { ContextType } from '../context/ContextType'
|
||||
import { ContextType } from '../context/ContextType';
|
||||
|
||||
export function useWelcome() {
|
||||
const app = useContext(AppContext) as ContextType
|
||||
const display = useContext(DisplayContext) as ContextType
|
||||
const app = useContext(AppContext) as ContextType;
|
||||
const display = useContext(DisplayContext) as ContextType;
|
||||
const [state, setState] = useState({
|
||||
strings: display.state.strings,
|
||||
showWelcome: null as null | boolean,
|
||||
})
|
||||
});
|
||||
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const updateState = (value: any) => {
|
||||
setState((s) => ({ ...s, ...value }))
|
||||
}
|
||||
setState((s) => ({ ...s, ...value }));
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const showWelcome = app.state.showWelcome;
|
||||
@ -25,7 +25,7 @@ export function useWelcome() {
|
||||
clearWelcome: async () => {
|
||||
await app.actions.setShowWelcome(false);
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
return { state, actions }
|
||||
return { state, actions };
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user