formatting code

This commit is contained in:
Roland Osborne 2024-09-27 09:48:36 -07:00
parent ed369424cd
commit c2642f534e
14 changed files with 1029 additions and 535 deletions

View File

@ -11,28 +11,37 @@ export class LocalStore implements SqlStore {
public async open(path: string) {
this.db = await SQLite.openDatabase({name: path, location: 'default'});
await this.localStoreSet("CREATE TABLE IF NOT EXISTS local_store (key text, value text, unique(key));");
await this.localStoreSet(
'CREATE TABLE IF NOT EXISTS local_store (key text, value text, unique(key));',
);
}
public async get(key: string, value: string, unset: string): Promise<string> {
try {
const rows = await this.localStoreGet(`SELECT * FROM local_store WHERE key='${key}';`);
const rows = await this.localStoreGet(
`SELECT * FROM local_store WHERE key='${key}';`,
);
if (rows.length == 1 && rows[0].value != null) {
return rows[0].value;
}
}
catch(err) {
} catch (err) {
console.log(err);
}
return unset;
}
public async set(key: string, value: string): Promise<void> {
await this.localStoreSet('INSERT OR REPLACE INTO local_store (key, value) values (?, ?)', [key, value]);
await this.localStoreSet(
'INSERT OR REPLACE INTO local_store (key, value) values (?, ?)',
[key, value],
);
}
public async clear(key: string): Promise<void> {
await this.localStoreSet('INSERT OR REPLACE INTO local_store (key, value) values (?, null)', [key]);
await this.localStoreSet(
'INSERT OR REPLACE INTO local_store (key, value) values (?, null)',
[key],
);
}
private async localStoreSet(

View File

@ -1,109 +1,134 @@
import { Crypto } from 'databag-client-sdk';
import {Crypto} from 'databag-client-sdk';
import CryptoJS from 'crypto-js';
import { JSEncrypt } from 'jsencrypt'
import { RSA } from 'react-native-rsa-native';
import { generateSecureRandom } from 'react-native-securerandom';
import {JSEncrypt} from 'jsencrypt';
import {RSA} from 'react-native-rsa-native';
import {generateSecureRandom} from 'react-native-securerandom';
export class NativeCrypto implements Crypto {
// generate salt for pbk function
public async pbkdfSalt(): Promise<{ saltHex: string }> {
public async pbkdfSalt(): Promise<{saltHex: string}> {
const salt = await generateSecureRandom(16);
const saltHex = this.uint8ToHexStr(salt);
return { saltHex };
return {saltHex};
}
// generate aes key with pbkdf2
public async pbkdfKey(saltHex: string, password: string): Promise<{ aesKeyHex: string }> {
public async pbkdfKey(
saltHex: string,
password: string,
): Promise<{aesKeyHex: string}> {
const salt = CryptoJS.enc.Hex.parse(saltHex);
const aes = CryptoJS.PBKDF2(password, salt, { keySize: 256 / 32, iterations: 1024, hasher: CryptoJS.algo.SHA1, });
const aes = CryptoJS.PBKDF2(password, salt, {
keySize: 256 / 32,
iterations: 1024,
hasher: CryptoJS.algo.SHA1,
});
const aesKeyHex = aes.toString();
return { aesKeyHex };
return {aesKeyHex};
}
// generate random aes key
public async aesKey(): Promise<{ aesKeyHex: string }> {
public async aesKey(): Promise<{aesKeyHex: string}> {
const aes = await generateSecureRandom(32);
const aesHex = this.uint8ToHexStr(aes);
return { aesKeyHex };
return {aesKeyHex};
}
// generate iv to use to aes function
public async aesIv(): Promise<{ ivHex: string }> {
public async aesIv(): Promise<{ivHex: string}> {
const iv = await generateSecureRandom(16);
const ivHex = this.uint8ToHexStr(iv);
return { ivHex };
return {ivHex};
}
// encrypt data with aes key and iv
public async aesEncrypt(data: string, ivHex: string, aesKeyHex: string): Promise<{ encryptedDataB64: string }> {
public async aesEncrypt(
data: string,
ivHex: string,
aesKeyHex: string,
): Promise<{encryptedDataB64: string}> {
const iv = CryptoJS.enc.Hex.parse(ivHex);
const key = CryptoJS.enc.Hex.parse(aesKeyHex);
const encrypted = CryptoJS.AES.encrypt(data, key, { iv });
const encrypted = CryptoJS.AES.encrypt(data, key, {iv});
const encryptedDataB64 = encrypted.ciphertext.toString(CryptoJS.enc.Base64);
return { encryptedDataB64 };
return {encryptedDataB64};
}
// decrypt data with aes key and iv
public async aesDecrypt(encryptedDataB64: string, ivHex: string, aesKeyHex: string): Promise<{ data: string }> {
public async aesDecrypt(
encryptedDataB64: string,
ivHex: string,
aesKeyHex: string,
): Promise<{data: string}> {
const iv = CryptoJS.enc.Hex.parse(ivHex);
const key = CryptoJS.enc.Hex.parse(aesKeyHex);
const ciphertext = CryptoJS.enc.Base64.parse(encryptedDataB64);
const cipher = CryptoJS.lib.CipherParams.create({ ciphertext, iv });
const decrypted = CryptoJS.AES.decrypt(cipher, key, { iv });
const cipher = CryptoJS.lib.CipherParams.create({ciphertext, iv});
const decrypted = CryptoJS.AES.decrypt(cipher, key, {iv});
const data = decrypted.toString(CryptoJS.enc.Utf8);
return { data };
return {data};
}
// generate rsa key
public async rsaKey(): Promise<{ publicKeyB64: string, privateKeyB64: string }> {
const crypto = new JSEncrypt({ default_key_size: '2048' });
public async rsaKey(): Promise<{
publicKeyB64: string;
privateKeyB64: string;
}> {
const crypto = new JSEncrypt({default_key_size: '2048'});
crypto.getKey();
const publicKey = crypto.getPublicKey();
const publicKeyB64 = this.convertPem(publicKey);
const privateKey = crypto.getPrivateKey();
const privateKeyB64 = this.convertPem(privateKey);
return { publicKeyB64, privateKeyB64 };
return {publicKeyB64, privateKeyB64};
}
// encrypt data with public rsa key
public async rsaEncrypt(data: string, publicKeyB64: string): Promise<{ encryptedDataB64: string }> {
public async rsaEncrypt(
data: string,
publicKeyB64: string,
): Promise<{encryptedDataB64: string}> {
const crypto = new JSEncrypt();
crypto.setPublicKey(publicKeyB64);
const encryptedDataB64 = crypto.encrypt(data);
if (!encryptedDataB64) {
throw new Error('rsaEncrypt failed');
}
return { encryptedDataB64 };
return {encryptedDataB64};
}
// decrypt data with private rsa key
public async rsaDecrypt(encryptedDataB64: string, privateKeyB64: string): Promise<{ data: string }> {
public async rsaDecrypt(
encryptedDataB64: string,
privateKeyB64: string,
): Promise<{data: string}> {
const crypto = new JSEncrypt();
crypto.setPrivateKey(privateKeyB64);
const data = await RSA.decrypt(encryptedDataB64, privateKeyB64);
if (!data) {
throw new Error('rsaDecrypt failed');
}
return { data };
return {data};
}
private convertPem(pem: string): string {
const lines = pem.split('\n');
let encoded = '';
for(let i = 0;i < lines.length;i++){
if (lines[i].trim().length > 0 &&
lines[i].indexOf('-BEGIN RSA PRIVATE KEY-') < 0 &&
lines[i].indexOf('-BEGIN RSA PUBLIC KEY-') < 0 &&
lines[i].indexOf('-BEGIN PUBLIC KEY-') < 0 &&
lines[i].indexOf('-END PUBLIC KEY-') < 0 &&
lines[i].indexOf('-END RSA PRIVATE KEY-') < 0 &&
lines[i].indexOf('-END RSA PUBLIC KEY-') < 0) {
for (let i = 0; i < lines.length; i++) {
if (
lines[i].trim().length > 0 &&
lines[i].indexOf('-BEGIN RSA PRIVATE KEY-') < 0 &&
lines[i].indexOf('-BEGIN RSA PUBLIC KEY-') < 0 &&
lines[i].indexOf('-BEGIN PUBLIC KEY-') < 0 &&
lines[i].indexOf('-END PUBLIC KEY-') < 0 &&
lines[i].indexOf('-END RSA PRIVATE KEY-') < 0 &&
lines[i].indexOf('-END RSA PUBLIC KEY-') < 0
) {
encoded += lines[i].trim();
}
}
return encoded
};
return encoded;
}
private uint8ToHexStr(bin: Uint8Array) {
let hex = '';

View File

@ -1,10 +1,5 @@
import React, {useState} from 'react';
import {
Platform,
KeyboardAvoidingView,
View,
Image,
} from 'react-native';
import {Platform, KeyboardAvoidingView, View, Image} from 'react-native';
import {KeyboardAwareScrollView} from 'react-native-keyboard-aware-scroll-view';
import {useAccess} from './useAccess.hook';
import {styles} from './Access.styled';
@ -135,12 +130,14 @@ export function Access() {
left={<TextInput.Icon style={styles.icon} icon="lock" />}
right={
showPassword ? (
<TextInput.Icon style={styles.icon}
<TextInput.Icon
style={styles.icon}
icon="eye-off"
onPress={() => setShowPassword(false)}
/>
) : (
<TextInput.Icon style={styles.icon}
<TextInput.Icon
style={styles.icon}
icon="eye"
onPress={() => setShowPassword(true)}
/>
@ -174,7 +171,9 @@ export function Access() {
autoComplete="off"
autoCorrect={false}
label={state.strings.token}
left={<TextInput.Icon style={styles.icon} icon="ticket-account" />}
left={
<TextInput.Icon style={styles.icon} icon="ticket-account" />
}
onChangeText={value => actions.setToken(value)}
/>
<TextInput
@ -216,7 +215,12 @@ export function Access() {
autoComplete="off"
autoCorrect={false}
label={state.strings.token}
left={<TextInput.Icon style={styles.icon} icon="ticket-account" />}
left={
<TextInput.Icon
style={styles.icon}
icon="ticket-account"
/>
}
onChangeText={value => actions.setToken(value)}
/>
)}
@ -257,12 +261,14 @@ export function Access() {
left={<TextInput.Icon style={styles.icon} icon="lock" />}
right={
showPassword ? (
<TextInput.Icon style={styles.icon}
<TextInput.Icon
style={styles.icon}
icon="eye-off"
onPress={() => setShowPassword(false)}
/>
) : (
<TextInput.Icon style={styles.icon}
<TextInput.Icon
style={styles.icon}
icon="eye"
onPress={() => setShowPassword(true)}
/>
@ -283,12 +289,14 @@ export function Access() {
left={<TextInput.Icon style={styles.icon} icon="lock" />}
right={
showPassword ? (
<TextInput.Icon style={styles.icon}
<TextInput.Icon
style={styles.icon}
icon="eye-off"
onPress={() => setShowConfirm(false)}
/>
) : (
<TextInput.Icon style={styles.icon}
<TextInput.Icon
style={styles.icon}
icon="eye"
onPress={() => setShowConfirm(true)}
/>
@ -343,12 +351,14 @@ export function Access() {
left={<TextInput.Icon style={styles.icon} icon="lock" />}
right={
showPassword ? (
<TextInput.Icon style={styles.icon}
<TextInput.Icon
style={styles.icon}
icon="eye-off"
onPress={() => setShowPassword(false)}
/>
) : (
<TextInput.Icon style={styles.icon}
<TextInput.Icon
style={styles.icon}
icon="eye"
onPress={() => setShowPassword(true)}
/>

View File

@ -1,5 +1,4 @@
export const Colors = {
primary: '#66aa88',
danger: '#ff8888',
}
};

View File

@ -8,7 +8,7 @@ const SETTINGS_DB = 'ls_v001.db';
export function useAppContext() {
const local = useRef(new LocalStore());
const sdk = useRef(new DatabagSDK(new NativeCrypto()))
const sdk = useRef(new DatabagSDK(new NativeCrypto()));
const [state, setState] = useState({
session: null as null | Session,
fullDayTime: false,
@ -22,9 +22,11 @@ export function useAppContext() {
const setup = async () => {
await local.current.open(SETTINGS_DB);
const fullDayTime = await local.current.get('time_format', '12h') === '24h';
const monthFirstDate = await local.current.get('date_format', 'month_first') === 'month_first';
const fullDayTime =
(await local.current.get('time_format', '12h')) === '24h';
const monthFirstDate =
(await local.current.get('date_format', 'month_first')) === 'month_first';
const store = new SessionStore();
await store.open(DATABAG_DB);
const session: Session | null = await sdk.current.initOfflineStore(store);
@ -39,11 +41,14 @@ export function useAppContext() {
const actions = {
setMonthFirstDate: async (monthFirstDate: boolean) => {
updateState({ monthFirstDate });
await local.current.set('date_format', monthFirstDate ? 'month_first' : 'day_first');
updateState({monthFirstDate});
await local.current.set(
'date_format',
monthFirstDate ? 'month_first' : 'day_first',
);
},
setFullDayTime: async (fullDayTime: boolean) => {
updateState({ fullDayTime });
updateState({fullDayTime});
await local.current.set('time_format', fullDayTime ? '24h' : '12h');
},
accountLogin: async (
@ -86,7 +91,7 @@ export function useAppContext() {
accountRemove: async () => {
if (state.session) {
await sdk.current.remove(state.session);
updateState({ session: null });
updateState({session: null});
}
},
accountCreate: async (

View File

@ -123,9 +123,6 @@ export const styles = StyleSheet.create({
fontSize: 16,
},
controlSwitch: {
transform: [
{ scaleX: 0.7 },
{scaleY: 0.7 },
]
transform: [{scaleX: 0.7}, {scaleY: 0.7}],
},
});

View File

@ -1,21 +1,29 @@
import { useState } from 'react';
import { Modal, TouchableOpacity, SafeAreaView, View, Image } from 'react-native';
import { Surface, IconButton, Button, Switch, Icon, Text, Menu } from 'react-native-paper';
import { styles } from './Identity.styled';
import { useIdentity } from './useIdentity.hook';
import {useState} from 'react';
import {Modal, TouchableOpacity, SafeAreaView, View, Image} from 'react-native';
import {
Surface,
IconButton,
Button,
Switch,
Icon,
Text,
Menu,
} from 'react-native-paper';
import {styles} from './Identity.styled';
import {useIdentity} from './useIdentity.hook';
import {KeyboardAwareScrollView} from 'react-native-keyboard-aware-scroll-view';
import {BlurView} from '@react-native-community/blur';
export function Identity({ openSettings }) {
export function Identity({openSettings}) {
const [menu, setMenu] = useState(false);
const { state, actions } = useIdentity();
const {state, actions} = useIdentity();
const [logout, setLogout] = useState(false);
const [applyingLogout, setApplyingLogout] = useState(false);
const showLogout = () => {
setMenu(false);
setLogout(true);
}
};
const applyLogout = async () => {
if (!applyingLogout) {
@ -24,25 +32,46 @@ export function Identity({ openSettings }) {
setLogout(false);
setApplyingLogout(false);
}
}
};
return (
<>
<SafeAreaView style={styles.identity}>
<TouchableOpacity style={styles.identityData} activeOpacity={1} onPress={() => setMenu(true)}>
<TouchableOpacity
style={styles.identityData}
activeOpacity={1}
onPress={() => setMenu(true)}>
<View style={styles.image}>
{state.profile.imageSet && (
<Image style={styles.logoSet} resizeMode={'contain'} source={state.imageUrl} />
<Image
style={styles.logoSet}
resizeMode={'contain'}
source={state.imageUrl}
/>
)}
{!state.profile.imageSet && (
<Image style={styles.logoUnset} resizeMode={'contain'} source={state.imageUrl} />
<Image
style={styles.logoUnset}
resizeMode={'contain'}
source={state.imageUrl}
/>
)}
</View>
<View style={styles.details}>
{state.profile.name && (
<Text style={styles.name} adjustsFontSizeToFit={true} numberOfLines={1}>{state.profile.name}</Text>
<Text
style={styles.name}
adjustsFontSizeToFit={true}
numberOfLines={1}>
{state.profile.name}
</Text>
)}
<Text style={styles.username} adjustsFontSizeToFit={true} numberOfLines={1}>{`${state.profile.handle}${state.profile.node ? '/' + state.profile.node : ''}`}</Text>
<Text
style={styles.username}
adjustsFontSizeToFit={true}
numberOfLines={1}>{`${state.profile.handle}${
state.profile.node ? '/' + state.profile.node : ''
}`}</Text>
</View>
<Icon size={18} source="chevron-right" />
</TouchableOpacity>
@ -50,10 +79,29 @@ export function Identity({ openSettings }) {
visible={menu}
onDismiss={() => setMenu(false)}
anchorPosition="top"
anchor={<View style={styles.anchor}><Text> </Text></View>}>
<Menu.Item leadingIcon="cog-outline" onPress={() => {setMenu(false); openSettings()}} title={state.strings.settings} />
<Menu.Item leadingIcon="contacts-outline" onPress={() => {}} title={state.strings.contacts} />
<Menu.Item leadingIcon="logout" onPress={showLogout} title={state.strings.logout} />
anchor={
<View style={styles.anchor}>
<Text> </Text>
</View>
}>
<Menu.Item
leadingIcon="cog-outline"
onPress={() => {
setMenu(false);
openSettings();
}}
title={state.strings.settings}
/>
<Menu.Item
leadingIcon="contacts-outline"
onPress={() => {}}
title={state.strings.contacts}
/>
<Menu.Item
leadingIcon="logout"
onPress={showLogout}
title={state.strings.logout}
/>
</Menu>
</SafeAreaView>
<Modal
@ -69,24 +117,44 @@ export function Identity({ openSettings }) {
blurAmount={2}
reducedTransparencyFallbackColor="dark"
/>
<KeyboardAwareScrollView style={styles.container} contentContainerStyle={styles.content}>
<KeyboardAwareScrollView
style={styles.container}
contentContainerStyle={styles.content}>
<Surface elevation={5} mode="flat" style={styles.surface}>
<Text style={styles.modalLabel}>{ state.strings.loggingOut }</Text>
<IconButton style={styles.modalClose} icon="close" size={24} onPress={() => setLogout(false)} />
<Text style={styles.modalLabel}>{state.strings.loggingOut}</Text>
<IconButton
style={styles.modalClose}
icon="close"
size={24}
onPress={() => setLogout(false)}
/>
<View style={styles.allControl}>
<Text style={styles.controlLabel}>{state.strings.allDevices}</Text>
<Switch style={styles.controlSwitch} value={state.all} onValueChange={actions.setAll} />
<Text style={styles.controlLabel}>
{state.strings.allDevices}
</Text>
<Switch
style={styles.controlSwitch}
value={state.all}
onValueChange={actions.setAll}
/>
</View>
<View style={styles.modalControls}>
<Button mode="outlined" onPress={() => setLogout(false)}>{ state.strings.cancel }</Button>
<Button mode="contained" loading={applyingLogout} onPress={applyLogout}>{ state.strings.logout }</Button>
<Button mode="outlined" onPress={() => setLogout(false)}>
{state.strings.cancel}
</Button>
<Button
mode="contained"
loading={applyingLogout}
onPress={applyLogout}>
{state.strings.logout}
</Button>
</View>
</Surface>
</KeyboardAwareScrollView>
</KeyboardAwareScrollView>
</View>
</Modal>
</>
)
);
}

View File

@ -1,11 +1,11 @@
import { useEffect, useState, useContext, useRef } from 'react'
import { AppContext } from '../context/AppContext'
import { DisplayContext } from '../context/DisplayContext';
import { ContextType } from '../context/ContextType'
import {useEffect, useState, useContext, useRef} from 'react';
import {AppContext} from '../context/AppContext';
import {DisplayContext} from '../context/DisplayContext';
import {ContextType} from '../context/ContextType';
export function useIdentity() {
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({
all: false,
@ -14,41 +14,40 @@ export function useIdentity() {
profileSet: false,
imageUrl: null,
strings: display.state.strings,
})
});
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const updateState = (value: any) => {
setState((s) => ({ ...s, ...value }))
}
setState(s => ({...s, ...value}));
};
useEffect(() => {
const identity = app.state.session?.getIdentity()
const identity = app.state.session?.getIdentity();
if (!identity) {
console.log('session not set in identity hook')
console.log('session not set in identity hook');
} else {
const setProfile = (profile: Profile) => {
updateState({
profile,
profileSet: true,
imageUrl: { uri: identity.getProfileImageUrl() },
})
}
identity.addProfileListener(setProfile)
imageUrl: {uri: identity.getProfileImageUrl()},
});
};
identity.addProfileListener(setProfile);
return () => {
identity.removeProfileListener(setProfile)
}
identity.removeProfileListener(setProfile);
};
}
}, [])
}, []);
const actions = {
setAll: (all) => {
updateState({ all });
setAll: all => {
updateState({all});
},
logout: async () => {
await app.actions.accountLogout(state.all);
}
}
},
};
return { state, actions }
return {state, actions};
}

View File

@ -1,7 +1,13 @@
import React, {useState, useContext} from 'react';
import {View, useColorScheme} from 'react-native';
import {styles} from './Session.styled';
import {BottomNavigation, Surface, Menu, Button, Text} from 'react-native-paper';
import {
BottomNavigation,
Surface,
Menu,
Button,
Text,
} from 'react-native-paper';
import {Settings} from '../settings/Settings';
import {Channels} from '../channels/Channels';
import {Contacts} from '../contacts/Contacts';
@ -11,7 +17,11 @@ import {Details} from '../details/Details';
import {Identity} from '../identity/Identity';
import {useSession} from './useSession.hook';
import {NavigationContainer, DefaultTheme, DarkTheme} from '@react-navigation/native';
import {
NavigationContainer,
DefaultTheme,
DarkTheme,
} from '@react-navigation/native';
import {createDrawerNavigator} from '@react-navigation/drawer';
const ChannelsRoute = () => <Channels />;
@ -67,7 +77,8 @@ export function Session() {
/>
)}
{state.layout === 'large' && (
<NavigationContainer theme={scheme === 'dark' ? DarkTheme : DefaultTheme}>
<NavigationContainer
theme={scheme === 'dark' ? DarkTheme : DefaultTheme}>
<DetailsScreen nav={sessionNav} />
</NavigationContainer>
)}
@ -157,7 +168,7 @@ function SettingsScreen({nav}) {
id="SettingsDrawer"
drawerContent={Settings}
screenOptions={{
drawerStyle: { width: '40%' },
drawerStyle: {width: '40%'},
drawerPosition: 'right',
drawerType: 'front',
headerShown: false,
@ -178,8 +189,7 @@ function HomeScreen({nav}) {
<Surface elevation={2} mode="flat">
<Identity openSettings={nav.settings.openDrawer} />
</Surface>
<Surface style={styles.channels} elevation={1} mode="flat">
</Surface>
<Surface style={styles.channels} elevation={1} mode="flat"></Surface>
</View>
<View style={styles.right}>
<Text>CONVERSATION</Text>

View File

@ -1,34 +1,34 @@
import { useEffect, useState, useContext, useRef } from 'react'
import { AppContext } from '../context/AppContext'
import { DisplayContext } from '../context/DisplayContext'
import { ContextType } from '../context/ContextType'
import {useEffect, useState, useContext, useRef} from 'react';
import {AppContext} from '../context/AppContext';
import {DisplayContext} from '../context/DisplayContext';
import {ContextType} from '../context/ContextType';
const DEBOUNCE_MS = 1000
const DEBOUNCE_MS = 1000;
export function useSession() {
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({
layout: null,
strings: {},
})
});
// 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;
updateState({ layout, strings });
const {layout, strings} = display.state;
updateState({layout, strings});
}, [display.state.layout, display.state.strings]);
const actions = {
logout: async () => {
await app.actions.accountLogout();
}
}
},
};
return { state, actions }
return {state, actions};
}

View File

@ -1,5 +1,5 @@
import {StyleSheet} from 'react-native';
import { Colors } from '../constants/Colors';
import {Colors} from '../constants/Colors';
export const styles = StyleSheet.create({
modal: {
@ -260,10 +260,7 @@ export const styles = StyleSheet.create({
color: Colors.danger,
},
controlSwitch: {
transform: [
{ scaleX: 0.7 },
{scaleY: 0.7 },
]
transform: [{scaleX: 0.7}, {scaleY: 0.7}],
},
input: {
width: '100%',
@ -302,4 +299,4 @@ export const styles = StyleSheet.create({
fontSize: 16,
color: Colors.danger,
},
})
});

File diff suppressed because it is too large Load Diff

View File

@ -1,14 +1,14 @@
import { useEffect, useState, useContext, useRef } from 'react'
import { AppContext } from '../context/AppContext'
import { DisplayContext } from '../context/DisplayContext'
import { ContextType } from '../context/ContextType'
import {useEffect, useState, useContext, useRef} from 'react';
import {AppContext} from '../context/AppContext';
import {DisplayContext} from '../context/DisplayContext';
import {ContextType} from '../context/ContextType';
const DEBOUNCE_MS = 1000
const DEBOUNCE_MS = 1000;
export function useSettings() {
const display = useContext(DisplayContext) as ContextType
const app = useContext(AppContext) as ContextType
const debounce = useRef(setTimeout(() => {}, 0))
const display = useContext(DisplayContext) as ContextType;
const app = useContext(AppContext) as ContextType;
const debounce = useRef(setTimeout(() => {}, 0));
const [state, setState] = useState({
config: {} as Config,
@ -36,32 +36,32 @@ export function useSettings() {
secretCopied: false,
monthFirstDate: true,
fullDayTime: false,
})
});
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const updateState = (value: any) => {
setState((s) => ({ ...s, ...value }))
}
setState(s => ({...s, ...value}));
};
const getSession = () => {
const session = app.state?.session
const settings = session?.getSettings()
const identity = session?.getIdentity()
const session = app.state?.session;
const settings = session?.getSettings();
const identity = session?.getIdentity();
if (!settings || !identity) {
console.log('session not set in settings hook')
console.log('session not set in settings hook');
}
return { settings, identity }
}
return {settings, identity};
};
useEffect(() => {
const { settings, identity } = getSession()
const {settings, identity} = getSession();
const setConfig = (config: Config) => {
updateState({ config })
}
settings.addConfigListener(setConfig)
updateState({config});
};
settings.addConfigListener(setConfig);
const setProfile = (profile: Profile) => {
const { handle, name, location, description } = profile
const url = identity.getProfileImageUrl()
const {handle, name, location, description} = profile;
const url = identity.getProfileImageUrl();
updateState({
profile,
handle,
@ -70,194 +70,188 @@ export function useSettings() {
description,
imageUrl: url,
profileSet: true,
})
}
identity.addProfileListener(setProfile)
});
};
identity.addProfileListener(setProfile);
return () => {
settings.removeConfigListener(setConfig)
identity.removeProfileListener(setProfile)
}
}, [])
settings.removeConfigListener(setConfig);
identity.removeProfileListener(setProfile);
};
}, []);
useEffect(() => {
const { fullDayTime, monthFirstDate } = app.state;
updateState({ fullDayTime, monthFirstDate });
const {fullDayTime, monthFirstDate} = app.state;
updateState({fullDayTime, monthFirstDate});
}, [app.state.fullDayTime, app.state.monthFirstDate]);
useEffect(() => {
const {
strings,
dateFormat,
timeFormat,
} = display.state
const {strings, dateFormat, timeFormat} = display.state;
updateState({
strings,
dateFormat,
timeFormat,
})
}, [display.state])
});
}, [display.state]);
const actions = {
getUsernameStatus: async (username: string) => {
const { settings } = getSession()
return await settings.getUsernameStatus(username)
const {settings} = getSession();
return await settings.getUsernameStatus(username);
},
setLogin: async () => {
const { settings } = getSession()
await settings.setLogin(state.handle, state.password)
const {settings} = getSession();
await settings.setLogin(state.handle, state.password);
},
enableNotifications: async () => {
const { settings } = getSession()
await settings.enableNotifications()
const {settings} = getSession();
await settings.enableNotifications();
},
disableNotifications: async () => {
const { settings } = getSession()
await settings.disableNotifications()
const {settings} = getSession();
await settings.disableNotifications();
},
enableRegistry: async () => {
const { settings } = getSession()
await settings.enableRegistry()
const {settings} = getSession();
await settings.enableRegistry();
},
disableRegistry: async () => {
const { settings } = getSession()
await settings.disableRegistry()
const {settings} = getSession();
await settings.disableRegistry();
},
enableMFA: async () => {
const { settings } = getSession()
const { secretImage, secretText } = await settings.enableMFA()
updateState({ secretImage, secretText });
const {settings} = getSession();
const {secretImage, secretText} = await settings.enableMFA();
updateState({secretImage, secretText});
},
disableMFA: async () => {
const { settings } = getSession()
await settings.disableMFA()
const {settings} = getSession();
await settings.disableMFA();
},
confirmMFA: async () => {
const { settings } = getSession()
await settings.confirmMFA(state.code)
const {settings} = getSession();
await settings.confirmMFA(state.code);
},
setCode: (code: string) => {
updateState({ code });
updateState({code});
},
copySecret: () => {
navigator.clipboard.writeText(state.secretText);
updateState({ secretCopied: true });
updateState({secretCopied: true});
setTimeout(() => {
updateState({ secretCopied: false });
updateState({secretCopied: false});
}, 1000);
},
setSeal: async () => {
const { settings } = getSession()
await settings.setSeal(state.sealPassword)
const {settings} = getSession();
await settings.setSeal(state.sealPassword);
},
clearSeal: async () => {
const { settings } = getSession()
await settings.clearSeal()
const {settings} = getSession();
await settings.clearSeal();
},
unlockSeal: async () => {
const { settings } = getSession()
await settings.unlockSeal(state.sealPassword)
const {settings} = getSession();
await settings.unlockSeal(state.sealPassword);
},
forgetSeal: async () => {
const { settings } = getSession()
await settings.forgetSeal()
const {settings} = getSession();
await settings.forgetSeal();
},
updateSeal: async () => {
const { settings } = getSession();
const {settings} = getSession();
await settings.updateSeal(state.sealPassword);
},
setProfileData: async (
name: string,
location: string,
description: string
description: string,
) => {
const { identity } = getSession()
await identity.setProfileData(name, location, description)
const {identity} = getSession();
await identity.setProfileData(name, location, description);
},
setProfileImage: async (image: string) => {
const { identity } = getSession()
await identity.setProfileImage(image)
const {identity} = getSession();
await identity.setProfileImage(image);
},
getProfileImageUrl: () => {
const { identity } = getSession()
return identity.getProfileImageUrl()
const {identity} = getSession();
return identity.getProfileImageUrl();
},
setDateFormat: (format: string) => {
display.actions.setDateFormat(format)
display.actions.setDateFormat(format);
},
setTimeFormat: (format: string) => {
display.actions.setTimeFormat(format)
display.actions.setTimeFormat(format);
},
setAll: (all: boolean) => {
updateState({ all })
updateState({all});
},
logout: async () => {
await app.actions.accountLogout(state.all)
await app.actions.accountLogout(state.all);
},
remove: async () => {
await app.actions.accountRemove()
await app.actions.accountRemove();
},
setHandle: (handle: string) => {
updateState({ handle, taken: false, checked: false })
clearTimeout(debounce.current)
updateState({handle, taken: false, checked: false});
clearTimeout(debounce.current);
if (!handle || handle === state.profile.handle) {
updateState({ available: true, checked: true })
updateState({available: true, checked: true});
} else {
debounce.current = setTimeout(async () => {
const { settings } = getSession()
const {settings} = getSession();
try {
const available = await settings.getUsernameStatus(handle)
updateState({ taken: !available, checked: true })
}
catch (err) {
const available = await settings.getUsernameStatus(handle);
updateState({taken: !available, checked: true});
} catch (err) {
console.log(err);
}
}, DEBOUNCE_MS)
}, DEBOUNCE_MS);
}
},
setPassword: (password: string) => {
updateState({ password })
updateState({password});
},
setConfirm: (confirm: string) => {
updateState({ confirm })
updateState({confirm});
},
setRemove: (remove: string) => {
updateState({ remove });
updateState({remove});
},
setName: (name: string) => {
updateState({ name })
updateState({name});
},
setLocation: (location: string) => {
updateState({ location })
updateState({location});
},
setDescription: (description: string) => {
updateState({ description })
updateState({description});
},
setDetails: async () => {
const { identity } = getSession()
const { name, location, description } = state
await identity.setProfileData(name, location, description)
const {identity} = getSession();
const {name, location, description} = state;
await identity.setProfileData(name, location, description);
},
setSealDelete: (sealDelete: string) => {
updateState({ sealDelete });
updateState({sealDelete});
},
setSealPassword: (sealPassword: string) => {
updateState({ sealPassword });
updateState({sealPassword});
},
setSealConfirm: (sealConfirm: string) => {
updateState({ sealConfirm });
updateState({sealConfirm});
},
setFullDayTime: async (flag: boolean) => {
try {
await app.actions.setFullDayTime(flag);
}
catch (err) {
} catch (err) {
console.log(err);
}
},
setMonthFirstDate: async (flag: boolean) => {
await app.actions.setMonthFirstDate(flag);
},
}
};
return { state, actions }
return {state, actions};
}

View File

@ -41,7 +41,8 @@ export function InputCode({onChangeText, style}) {
<Text style={styles.text}>{code.charAt(5)}</Text>
</View>
</View>
<TextInput style={styles.input}
<TextInput
style={styles.input}
keyboardType={Platform.OS === 'ios' ? 'numeric' : 'number-pad'}
onChangeText={updateCode}
autoCorrect={false}