fixed lint warnings

This commit is contained in:
balzack 2025-03-24 14:22:27 -07:00
parent 1c93ad161f
commit 6c4f0f1181
70 changed files with 710 additions and 824 deletions

View File

@ -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 };
}
}
}

View File

@ -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%',
},

View File

@ -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" />}

View File

@ -32,7 +32,7 @@ export function useAccess() {
useEffect(() => {
SplashScreen.hide();
}, []);
}, []);
useEffect(() => {
const {username, token, node, secure, mode} = state;

View File

@ -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,

View File

@ -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;

View File

@ -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 && (

View File

@ -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 };
}

View File

@ -1,5 +1,4 @@
import {StyleSheet} from 'react-native';
import { Colors } from '../constants/Colors';
export const styles = StyleSheet.create({
active: {

View File

@ -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>
)}

View File

@ -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;

View File

@ -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;

View File

@ -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.
`
}
`,
};

View File

@ -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('');

View File

@ -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;
}
},

View File

@ -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>;
}

View File

@ -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 });
}

View File

@ -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 = {};

View File

@ -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 };
}

View File

@ -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%',
}
},
});

View File

@ -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>

View File

@ -1,5 +1,4 @@
import {StyleSheet} from 'react-native';
import {Colors} from '../constants/Colors';
export const styles = StyleSheet.create({
audio: {

View File

@ -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

View File

@ -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 }
}

View File

@ -1,5 +1,4 @@
import {StyleSheet} from 'react-native';
import {Colors} from '../constants/Colors';
export const styles = StyleSheet.create({
binary: {

View File

@ -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}>

View File

@ -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 }
}

View File

@ -1,5 +1,4 @@
import {StyleSheet} from 'react-native';
import {Colors} from '../constants/Colors';
export const styles = StyleSheet.create({
image: {

View File

@ -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}

View File

@ -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 };
}

View File

@ -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 };
}

View File

@ -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',
}
},
});

View File

@ -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}>

View File

@ -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 };
}

View File

@ -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,

View File

@ -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

View File

@ -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 };
}

View File

@ -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);
}
}
}
}

View File

@ -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,
},
})
});

View File

@ -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)}>

View File

@ -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"

View File

@ -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 };
}

View File

@ -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"

View File

@ -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 };
}

View File

@ -48,10 +48,6 @@ export const styles = StyleSheet.create({
bottom: '10%',
width: '50%',
},
alert: {
position: 'absolute',
bottom: '10%'
},
alert: {
position: 'absolute',
bottom: 0,

View File

@ -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}

View File

@ -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 };
}

View File

@ -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>
)
);
}

View File

@ -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 };
}

View File

@ -75,11 +75,4 @@ export const styles = StyleSheet.create({
spacer: {
flexGrow: 1,
},
alert: {
position: 'absolute',
bottom: 0,
},
alertLabel: {
color: Colors.offsync,
},
});

View File

@ -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}

View File

@ -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 };
}

View File

@ -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';

View File

@ -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();

View File

@ -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' }] }}>

View File

@ -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;

View File

@ -34,7 +34,7 @@ export const styles = StyleSheet.create({
alignItems: 'center',
justifyContent: 'center',
borderRadius: 8,
overflow: 'hidden'
overflow: 'hidden',
},
header: {
width: '100%',

View File

@ -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>

View File

@ -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 };
}

View File

@ -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>
);
}

View File

@ -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';

View File

@ -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 />
)}

View File

@ -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]);

View File

@ -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>

View File

@ -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};

View File

@ -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>

View File

@ -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 });

View File

@ -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,

View File

@ -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} />

View File

@ -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 };
}