mirror of
https://github.com/balzack/databag.git
synced 2025-04-23 18:15:19 +00:00
rendering profile image in mobile app
This commit is contained in:
parent
5991ec9b0c
commit
8ded9553dd
@ -1,5 +1,5 @@
|
||||
import React, {useState, useContext} from 'react';
|
||||
import {View, SafeAreaView} from 'react-native';
|
||||
import {View, SafeAreaView, useColorScheme} from 'react-native';
|
||||
import {styles} from './Session.styled';
|
||||
import {BottomNavigation, Button, Text} from 'react-native-paper';
|
||||
import {DisplayContext} from '../context/DisplayContext';
|
||||
@ -10,7 +10,7 @@ import {Registry} from '../registry/Registry';
|
||||
import {Profile} from '../profile/Profile';
|
||||
import {Details} from '../details/Details';
|
||||
|
||||
import {NavigationContainer} from '@react-navigation/native';
|
||||
import {NavigationContainer, DefaultTheme, DarkTheme} from '@react-navigation/native';
|
||||
import {createDrawerNavigator} from '@react-navigation/drawer';
|
||||
|
||||
const ChannelsRoute = () => <Channels />;
|
||||
@ -24,6 +24,7 @@ const ProfileDrawer = createDrawerNavigator();
|
||||
const DetailsDrawer = createDrawerNavigator();
|
||||
|
||||
export function Session() {
|
||||
const scheme = useColorScheme();
|
||||
const display = useContext(DisplayContext);
|
||||
const [index, setIndex] = useState(0);
|
||||
const [routes] = useState([
|
||||
@ -65,7 +66,7 @@ export function Session() {
|
||||
/>
|
||||
)}
|
||||
{display.state.layout === 'large' && (
|
||||
<NavigationContainer>
|
||||
<NavigationContainer theme={scheme === 'dark' ? DarkTheme : DefaultTheme}>
|
||||
<DetailsScreen nav={sessionNav} />
|
||||
</NavigationContainer>
|
||||
)}
|
||||
|
45
app/client/mobile/src/settings/Settings.styled.ts
Normal file
45
app/client/mobile/src/settings/Settings.styled.ts
Normal file
@ -0,0 +1,45 @@
|
||||
import {StyleSheet} from 'react-native';
|
||||
|
||||
export const styles = StyleSheet.create({
|
||||
settings: {
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
padding: 8,
|
||||
gap: 2,
|
||||
},
|
||||
header: {
|
||||
fontSize: 22,
|
||||
textAlign: 'center',
|
||||
textWrap: 'nowrap',
|
||||
textOverflow: 'ellipsis',
|
||||
overflow: 'hidden',
|
||||
},
|
||||
image: {
|
||||
position: 'relative',
|
||||
width: '90%',
|
||||
maxWidth: 250,
|
||||
marginTop: 8,
|
||||
marginBottom: 24,
|
||||
},
|
||||
logo: {
|
||||
aspectRatio: 1,
|
||||
resizeMode: 'contain',
|
||||
borderRadius: 8,
|
||||
width: null,
|
||||
height: null,
|
||||
},
|
||||
editBar: {
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
width: '100%',
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
},
|
||||
editLogo: {
|
||||
fontSize: 18,
|
||||
borderRadius: 8,
|
||||
backgroundColor: '#44444444',
|
||||
},
|
||||
})
|
@ -1,14 +1,26 @@
|
||||
import React, {useContext} from 'react';
|
||||
import {AppContext} from '../context/AppContext';
|
||||
import {Button} from 'react-native-paper';
|
||||
import {SafeAreaView} from 'react-native';
|
||||
import {Button, Text} from 'react-native-paper';
|
||||
import {SafeAreaView, View, Image} from 'react-native';
|
||||
import {styles} from './Settings.styled';
|
||||
import {useSettings} from './useSettings.hook';
|
||||
|
||||
export function Settings() {
|
||||
const app = useContext(AppContext);
|
||||
const { state, actions } = useSettings();
|
||||
|
||||
console.log("HERE");
|
||||
|
||||
return (
|
||||
<SafeAreaView>
|
||||
<Button mode="contained" onPress={app.actions.accountLogout}>
|
||||
<SafeAreaView style={styles.settings}>
|
||||
<Text style={styles.header}>{`${state.profile.handle}${state.profile.node ? '/' + state.profile.node : ''}`}</Text>
|
||||
<View style={styles.image}>
|
||||
<Image style={styles.logo} resizeMode={'contain'} source={{ uri: state.imageUrl }} />
|
||||
<View style={styles.editBar}>
|
||||
<Button style={styles.editLogo} mode="text">{state.strings.edit}</Button>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
|
||||
<Button mode="contained" onPress={actions.logout}>
|
||||
Logout
|
||||
</Button>
|
||||
</SafeAreaView>
|
||||
|
235
app/client/mobile/src/settings/useSettings.hook.ts
Normal file
235
app/client/mobile/src/settings/useSettings.hook.ts
Normal file
@ -0,0 +1,235 @@
|
||||
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
|
||||
|
||||
export function useSettings() {
|
||||
const display = useContext(DisplayContext) as ContextType
|
||||
const app = useContext(AppContext) as ContextType
|
||||
const debounce = useRef(setTimeout(() => {}, 0))
|
||||
|
||||
const [state, setState] = useState({
|
||||
config: {} as Config,
|
||||
profile: {} as Profile,
|
||||
profileSet: false,
|
||||
imageUrl: null,
|
||||
strings: display.state.strings,
|
||||
timeFormat: '12h',
|
||||
dateFormat: 'mm/dd',
|
||||
all: false,
|
||||
password: '',
|
||||
confirm: '',
|
||||
username: '',
|
||||
taken: false,
|
||||
checked: true,
|
||||
name: '',
|
||||
description: '',
|
||||
location: '',
|
||||
handle: '',
|
||||
secretText: '',
|
||||
secretImage: '',
|
||||
code: '',
|
||||
sealPassword: '',
|
||||
sealConfirm: '',
|
||||
sealDelete: '',
|
||||
secretCopied: false,
|
||||
})
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const updateState = (value: any) => {
|
||||
setState((s) => ({ ...s, ...value }))
|
||||
}
|
||||
|
||||
const getSession = () => {
|
||||
const session = app.state?.session
|
||||
const settings = session?.getSettings()
|
||||
const identity = session?.getIdentity()
|
||||
if (!settings || !identity) {
|
||||
console.log('session not set in settings hook')
|
||||
}
|
||||
return { settings, identity }
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
const { settings, identity } = getSession()
|
||||
const setConfig = (config: Config) => {
|
||||
updateState({ config })
|
||||
}
|
||||
settings.addConfigListener(setConfig)
|
||||
const setProfile = (profile: Profile) => {
|
||||
const { handle, name, location, description } = profile
|
||||
const url = identity.getProfileImageUrl()
|
||||
updateState({
|
||||
profile,
|
||||
handle,
|
||||
name,
|
||||
location,
|
||||
description,
|
||||
imageUrl: url,
|
||||
profileSet: true,
|
||||
})
|
||||
}
|
||||
identity.addProfileListener(setProfile)
|
||||
return () => {
|
||||
settings.removeConfigListener(setConfig)
|
||||
identity.removeProfileListener(setProfile)
|
||||
}
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
const {
|
||||
strings,
|
||||
dateFormat,
|
||||
timeFormat,
|
||||
} = display.state
|
||||
updateState({
|
||||
strings,
|
||||
dateFormat,
|
||||
timeFormat,
|
||||
})
|
||||
}, [display.state])
|
||||
|
||||
const actions = {
|
||||
getUsernameStatus: async (username: string) => {
|
||||
const { settings } = getSession()
|
||||
return await settings.getUsernameStatus(username)
|
||||
},
|
||||
setLogin: async () => {
|
||||
const { settings } = getSession()
|
||||
await settings.setLogin(state.handle, state.password)
|
||||
},
|
||||
enableNotifications: async () => {
|
||||
const { settings } = getSession()
|
||||
await settings.enableNotifications()
|
||||
},
|
||||
disableNotifications: async () => {
|
||||
const { settings } = getSession()
|
||||
await settings.disableNotifications()
|
||||
},
|
||||
enableRegistry: async () => {
|
||||
const { settings } = getSession()
|
||||
await settings.enableRegistry()
|
||||
},
|
||||
disableRegistry: async () => {
|
||||
const { settings } = getSession()
|
||||
await settings.disableRegistry()
|
||||
},
|
||||
enableMFA: async () => {
|
||||
const { settings } = getSession()
|
||||
const { secretImage, secretText } = await settings.enableMFA()
|
||||
updateState({ secretImage, secretText });
|
||||
},
|
||||
disableMFA: async () => {
|
||||
const { settings } = getSession()
|
||||
await settings.disableMFA()
|
||||
},
|
||||
confirmMFA: async () => {
|
||||
const { settings } = getSession()
|
||||
await settings.confirmMFA(state.code)
|
||||
},
|
||||
setCode: (code: string) => {
|
||||
updateState({ code });
|
||||
},
|
||||
copySecret: () => {
|
||||
navigator.clipboard.writeText(state.secretText);
|
||||
updateState({ secretCopied: true });
|
||||
setTimeout(() => {
|
||||
updateState({ secretCopied: false });
|
||||
}, 1000);
|
||||
},
|
||||
setSeal: async () => {
|
||||
const { settings } = getSession()
|
||||
await settings.setSeal(state.sealPassword)
|
||||
},
|
||||
clearSeal: async () => {
|
||||
const { settings } = getSession()
|
||||
await settings.clearSeal()
|
||||
},
|
||||
unlockSeal: async () => {
|
||||
const { settings } = getSession()
|
||||
await settings.unlockSeal(state.sealPassword)
|
||||
},
|
||||
forgetSeal: async () => {
|
||||
const { settings } = getSession()
|
||||
await settings.forgetSeal()
|
||||
},
|
||||
updateSeal: async () => {
|
||||
const { settings } = getSession();
|
||||
await settings.updateSeal(state.sealPassword);
|
||||
},
|
||||
setProfileData: async (
|
||||
name: string,
|
||||
location: string,
|
||||
description: string
|
||||
) => {
|
||||
const { identity } = getSession()
|
||||
await identity.setProfileData(name, location, description)
|
||||
},
|
||||
setProfileImage: async (image: string) => {
|
||||
const { identity } = getSession()
|
||||
await identity.setProfileImage(image)
|
||||
},
|
||||
getProfileImageUrl: () => {
|
||||
const { identity } = getSession()
|
||||
return identity.getProfileImageUrl()
|
||||
},
|
||||
setDateFormat: (format: string) => {
|
||||
display.actions.setDateFormat(format)
|
||||
},
|
||||
setTimeFormat: (format: string) => {
|
||||
display.actions.setTimeFormat(format)
|
||||
},
|
||||
setAll: (all: boolean) => {
|
||||
updateState({ all })
|
||||
},
|
||||
logout: async () => {
|
||||
await app.actions.accountLogout(state.all)
|
||||
},
|
||||
setHandle: (handle: string) => {
|
||||
updateState({ handle, taken: false, checked: false })
|
||||
clearTimeout(debounce.current)
|
||||
if (!handle || handle === state.profile.handle) {
|
||||
updateState({ available: true, checked: true })
|
||||
} else {
|
||||
debounce.current = setTimeout(async () => {
|
||||
const { settings } = getSession()
|
||||
const available = await settings.getUsernameStatus(handle)
|
||||
updateState({ taken: !available, checked: true })
|
||||
}, DEBOUNCE_MS)
|
||||
}
|
||||
},
|
||||
setPassword: (password: string) => {
|
||||
updateState({ password })
|
||||
},
|
||||
setConfirm: (confirm: string) => {
|
||||
updateState({ confirm })
|
||||
},
|
||||
setName: (name: string) => {
|
||||
updateState({ name })
|
||||
},
|
||||
setLocation: (location: string) => {
|
||||
updateState({ location })
|
||||
},
|
||||
setDescription: (description: string) => {
|
||||
updateState({ description })
|
||||
},
|
||||
setDetails: async () => {
|
||||
const { identity } = getSession()
|
||||
const { name, location, description } = state
|
||||
await identity.setProfileData(name, location, description)
|
||||
},
|
||||
setSealDelete: (sealDelete: string) => {
|
||||
updateState({ sealDelete });
|
||||
},
|
||||
setSealPassword: (sealPassword: string) => {
|
||||
updateState({ sealPassword });
|
||||
},
|
||||
setSealConfirm: (sealConfirm: string) => {
|
||||
updateState({ sealConfirm });
|
||||
},
|
||||
}
|
||||
|
||||
return { state, actions }
|
||||
}
|
@ -47,6 +47,11 @@ export class IdentityModule implements Identity {
|
||||
private async init() {
|
||||
this.revision = await this.store.getProfileRevision(this.guid);
|
||||
this.profile = await this.store.getProfileData(this.guid);
|
||||
if (this.profile.image) {
|
||||
this.imageUrl = `data:image/png;base64,${this.profile.image}`
|
||||
} else {
|
||||
this.imageUrl = avatar
|
||||
}
|
||||
this.syncing = false;
|
||||
await this.sync();
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user