mirror of
https://github.com/balzack/databag.git
synced 2025-05-04 07:25:15 +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 React, {useState, useContext} from 'react';
|
||||||
import {View, SafeAreaView} from 'react-native';
|
import {View, SafeAreaView, useColorScheme} from 'react-native';
|
||||||
import {styles} from './Session.styled';
|
import {styles} from './Session.styled';
|
||||||
import {BottomNavigation, Button, Text} from 'react-native-paper';
|
import {BottomNavigation, Button, Text} from 'react-native-paper';
|
||||||
import {DisplayContext} from '../context/DisplayContext';
|
import {DisplayContext} from '../context/DisplayContext';
|
||||||
@ -10,7 +10,7 @@ import {Registry} from '../registry/Registry';
|
|||||||
import {Profile} from '../profile/Profile';
|
import {Profile} from '../profile/Profile';
|
||||||
import {Details} from '../details/Details';
|
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';
|
import {createDrawerNavigator} from '@react-navigation/drawer';
|
||||||
|
|
||||||
const ChannelsRoute = () => <Channels />;
|
const ChannelsRoute = () => <Channels />;
|
||||||
@ -24,6 +24,7 @@ const ProfileDrawer = createDrawerNavigator();
|
|||||||
const DetailsDrawer = createDrawerNavigator();
|
const DetailsDrawer = createDrawerNavigator();
|
||||||
|
|
||||||
export function Session() {
|
export function Session() {
|
||||||
|
const scheme = useColorScheme();
|
||||||
const display = useContext(DisplayContext);
|
const display = useContext(DisplayContext);
|
||||||
const [index, setIndex] = useState(0);
|
const [index, setIndex] = useState(0);
|
||||||
const [routes] = useState([
|
const [routes] = useState([
|
||||||
@ -65,7 +66,7 @@ export function Session() {
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{display.state.layout === 'large' && (
|
{display.state.layout === 'large' && (
|
||||||
<NavigationContainer>
|
<NavigationContainer theme={scheme === 'dark' ? DarkTheme : DefaultTheme}>
|
||||||
<DetailsScreen nav={sessionNav} />
|
<DetailsScreen nav={sessionNav} />
|
||||||
</NavigationContainer>
|
</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 React, {useContext} from 'react';
|
||||||
import {AppContext} from '../context/AppContext';
|
import {Button, Text} from 'react-native-paper';
|
||||||
import {Button} from 'react-native-paper';
|
import {SafeAreaView, View, Image} from 'react-native';
|
||||||
import {SafeAreaView} from 'react-native';
|
import {styles} from './Settings.styled';
|
||||||
|
import {useSettings} from './useSettings.hook';
|
||||||
|
|
||||||
export function Settings() {
|
export function Settings() {
|
||||||
const app = useContext(AppContext);
|
const { state, actions } = useSettings();
|
||||||
|
|
||||||
|
console.log("HERE");
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SafeAreaView>
|
<SafeAreaView style={styles.settings}>
|
||||||
<Button mode="contained" onPress={app.actions.accountLogout}>
|
<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
|
Logout
|
||||||
</Button>
|
</Button>
|
||||||
</SafeAreaView>
|
</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() {
|
private async init() {
|
||||||
this.revision = await this.store.getProfileRevision(this.guid);
|
this.revision = await this.store.getProfileRevision(this.guid);
|
||||||
this.profile = await this.store.getProfileData(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;
|
this.syncing = false;
|
||||||
await this.sync();
|
await this.sync();
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user