From 2ef4d337d2dfc046ab330949e3e0264190458e09 Mon Sep 17 00:00:00 2001 From: balzack Date: Fri, 20 Sep 2024 22:59:40 -0700 Subject: [PATCH] rendering identity component --- .../mobile/src/identity/Identity.styled.ts | 48 +++++++++++++++ app/client/mobile/src/identity/Identity.tsx | 36 +++++++++++ .../mobile/src/identity/useIdentity.hook.ts | 49 +++++++++++++++ .../mobile/src/session/Session.styled.ts | 6 +- app/client/mobile/src/session/Session.tsx | 27 +++++---- .../mobile/src/session/useSession.hook.ts | 34 +++++++++++ .../mobile/src/settings/Settings.styled.ts | 9 ++- app/client/mobile/src/settings/Settings.tsx | 60 ++++++++++++++----- 8 files changed, 240 insertions(+), 29 deletions(-) create mode 100644 app/client/mobile/src/identity/Identity.styled.ts create mode 100644 app/client/mobile/src/identity/Identity.tsx create mode 100644 app/client/mobile/src/identity/useIdentity.hook.ts create mode 100644 app/client/mobile/src/session/useSession.hook.ts diff --git a/app/client/mobile/src/identity/Identity.styled.ts b/app/client/mobile/src/identity/Identity.styled.ts new file mode 100644 index 00000000..3461d9cc --- /dev/null +++ b/app/client/mobile/src/identity/Identity.styled.ts @@ -0,0 +1,48 @@ +import {StyleSheet} from 'react-native'; + +export const styles = StyleSheet.create({ + identity: { + display: 'flex', + flexDirection: 'row', + alignItems: 'flex-begin', + }, + identityData: { + flexGrow: 1, + display: 'flex', + flexDirection: 'row', + alignItems: 'center', + padding: 4, + }, + anchor: { + backgroundColor: 'red', + width: 1, + height: 1, + }, + image: { + position: 'relative', + width: 48, + height: 48, + }, + logo: { + aspectRatio: 1, + resizeMode: 'contain', + borderRadius: 4, + width: null, + height: null, + }, + details: { + flexGrow: 1, + display: 'flex', + flexDirection: 'column', + alignItems: 'center', + paddingLeft: 8, + paddingRight: 8, + width: 100, + }, + name: { + fontSize: 14, + }, + username: { + fontSize: 16, + }, +}); diff --git a/app/client/mobile/src/identity/Identity.tsx b/app/client/mobile/src/identity/Identity.tsx new file mode 100644 index 00000000..b10d0097 --- /dev/null +++ b/app/client/mobile/src/identity/Identity.tsx @@ -0,0 +1,36 @@ +import { useState } from 'react'; +import { TouchableOpacity, SafeAreaView, View, Image } from 'react-native'; +import { Icon, Text, Menu } from 'react-native-paper'; +import { styles } from './Identity.styled'; +import { useIdentity } from './useIdentity.hook'; + +export function Identity({ openSettings }) { + const [menu, setMenu] = useState(false); + const { state, actions } = useIdentity(); + + return ( + + setMenu(true)}> + + + + + {state.profile.name && ( + {state.profile.name} + )} + {`${state.profile.handle}${state.profile.node ? '/' + state.profile.node : ''}`} + + + + setMenu(false)} + anchorPosition="top" + anchor={ }> + {setMenu(false); openSettings()}} title={state.strings.settings} /> + {}} title={state.strings.contacts} /> + {}} title={state.strings.logout} /> + + + ) +} diff --git a/app/client/mobile/src/identity/useIdentity.hook.ts b/app/client/mobile/src/identity/useIdentity.hook.ts new file mode 100644 index 00000000..3c4f7eed --- /dev/null +++ b/app/client/mobile/src/identity/useIdentity.hook.ts @@ -0,0 +1,49 @@ +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 [state, setState] = useState({ + all: false, + strings: display.state.strings, + profile: {} as Profile, + profileSet: false, + imageUrl: null, + }) + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const updateState = (value: any) => { + setState((s) => ({ ...s, ...value })) + } + + useEffect(() => { + const identity = app.state.session?.getIdentity() + if (!identity) { + console.log('session not set in identity hook') + } else { + const setProfile = (profile: Profile) => { + updateState({ + profile, + profileSet: true, + imageUrl: identity.getProfileImageUrl(), + }) + } + identity.addProfileListener(setProfile) + return () => { + identity.removeProfileListener(setProfile) + } + } + }, []) + + const actions = { + logout: async () => { + await app.actions.accountLogout(); + } + } + + return { state, actions } +} diff --git a/app/client/mobile/src/session/Session.styled.ts b/app/client/mobile/src/session/Session.styled.ts index cc25ef48..7dfed9d2 100644 --- a/app/client/mobile/src/session/Session.styled.ts +++ b/app/client/mobile/src/session/Session.styled.ts @@ -12,12 +12,14 @@ export const styles = StyleSheet.create({ left: { height: '100%', width: '33%', - minWidth: 325, - backgroundColor: 'yellow', + maxWidth: 300, }, right: { height: '100%', display: 'flex', flexGrow: 1, }, + channels: { + flexGrow: 1, + }, }); diff --git a/app/client/mobile/src/session/Session.tsx b/app/client/mobile/src/session/Session.tsx index 2d232a70..e494a6e1 100644 --- a/app/client/mobile/src/session/Session.tsx +++ b/app/client/mobile/src/session/Session.tsx @@ -1,14 +1,15 @@ import React, {useState, useContext} from 'react'; -import {View, SafeAreaView, useColorScheme} from 'react-native'; +import {View, useColorScheme} from 'react-native'; import {styles} from './Session.styled'; -import {BottomNavigation, Button, Text} from 'react-native-paper'; -import {DisplayContext} from '../context/DisplayContext'; +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'; import {Registry} from '../registry/Registry'; import {Profile} from '../profile/Profile'; import {Details} from '../details/Details'; +import {Identity} from '../identity/Identity'; +import {useSession} from './useSession.hook'; import {NavigationContainer, DefaultTheme, DarkTheme} from '@react-navigation/native'; import {createDrawerNavigator} from '@react-navigation/drawer'; @@ -24,8 +25,8 @@ const ProfileDrawer = createDrawerNavigator(); const DetailsDrawer = createDrawerNavigator(); export function Session() { + const {state, actions} = useSession(); const scheme = useColorScheme(); - const display = useContext(DisplayContext); const [index, setIndex] = useState(0); const [routes] = useState([ { @@ -47,7 +48,7 @@ export function Session() { unfocusedIcon: 'cog-outline', }, ]); - const sessionNav = {settings: null}; + const sessionNav = {strings: state.strings}; const renderScene = BottomNavigation.SceneMap({ channels: ChannelsRoute, @@ -57,7 +58,7 @@ export function Session() { return ( - {display.state.layout !== 'large' && ( + {state.layout !== 'large' && ( )} - {display.state.layout === 'large' && ( + {state.layout === 'large' && ( @@ -169,14 +170,20 @@ function SettingsScreen({nav}) { } function HomeScreen({nav}) { + const [menu, setMenu] = useState(false); + return ( - + - nav.settings.openDrawer()}>IDENTITY + + + + + CONVERSATION - + ); } diff --git a/app/client/mobile/src/session/useSession.hook.ts b/app/client/mobile/src/session/useSession.hook.ts new file mode 100644 index 00000000..ad8ecc8a --- /dev/null +++ b/app/client/mobile/src/session/useSession.hook.ts @@ -0,0 +1,34 @@ +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 useSession() { + 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 })) + } + + useEffect(() => { + 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 } +} diff --git a/app/client/mobile/src/settings/Settings.styled.ts b/app/client/mobile/src/settings/Settings.styled.ts index 0f6de3d6..368b9947 100644 --- a/app/client/mobile/src/settings/Settings.styled.ts +++ b/app/client/mobile/src/settings/Settings.styled.ts @@ -57,6 +57,13 @@ export const styles = StyleSheet.create({ right: 0, backgroundColor: 'transparent', }, + modalControls: { + display: 'flex', + flexDirection: 'row', + gap: 16, + paddingTop: 16, + justifyContent: 'flex-end', + }, header: { fontSize: 22, textAlign: 'center', @@ -83,7 +90,7 @@ export const styles = StyleSheet.create({ }, editDetails: { position: 'absolute', - bottom: -12, + bottom: -11, right: 12, zIndex: 3, }, diff --git a/app/client/mobile/src/settings/Settings.tsx b/app/client/mobile/src/settings/Settings.tsx index 9e0f144e..f596669f 100644 --- a/app/client/mobile/src/settings/Settings.tsx +++ b/app/client/mobile/src/settings/Settings.tsx @@ -11,8 +11,9 @@ export function Settings() { const { state, actions } = useSettings(); const [alert, setAlert] = useState(false); const [details, setDetails] = useState(false); + const [savingDetails, setSavingDetails] = useState(false); - const SelectImage = async () => { + const selectImage = async () => { try { const full = await ImagePicker.openPicker({ mediaType: 'photo', width: 256, height: 256 }); const crop = await ImagePicker.openCropper({ path: full.path, width: 256, height: 256, cropperCircleOverlay: true, includeBase64: true }); @@ -29,19 +30,37 @@ export function Settings() { } } + const saveDetails = async () => { + if (!savingDetails) { + setSavingDetails(true); + try { + await actions.setDetails(); + setDetails(false); + } + catch (err) { + console.log(err); + setDetails(false); + setAlert(true); + } + setSavingDetails(false); + } + } + return ( <> {`${state.profile.handle}${state.profile.node ? '/' + state.profile.node : ''}`} - + + {state.strings.edit} + - + @@ -55,7 +74,7 @@ export function Settings() { {state.strings.name} )} {state.profile.name && ( - {state.profile.name} + {state.profile.name} )} @@ -91,9 +110,11 @@ export function Settings() { setAlert(false)} - contentContainerStyle={styles.modal}> + supportedOrientations={['portrait', 'landscape']} + onRequestClose={() => setAlert(false)}> - - {state.strings.error} - {state.strings.tryAgain} - - + + + {state.strings.error} + {state.strings.tryAgain} + + + } onChangeText={value => actions.setDescription(value)} /> + + + + +