From a90fd5d26f1a8aa5149627a68a8dd6b13b26fec5 Mon Sep 17 00:00:00 2001 From: Paul Frazee Date: Fri, 30 Dec 2022 15:48:34 -0600 Subject: [PATCH] Add dark mode toggle --- src/App.native.tsx | 7 +- src/state/models/root-store.ts | 4 + src/state/models/shell-ui.ts | 22 ++- src/view/shell/mobile/Menu.tsx | 344 +++++++++++++++++---------------- 4 files changed, 207 insertions(+), 170 deletions(-) diff --git a/src/App.native.tsx b/src/App.native.tsx index fa523cd8..3cce2554 100644 --- a/src/App.native.tsx +++ b/src/App.native.tsx @@ -5,12 +5,13 @@ import {RootSiblingParent} from 'react-native-root-siblings' import {GestureHandlerRootView} from 'react-native-gesture-handler' import SplashScreen from 'react-native-splash-screen' import {SafeAreaProvider} from 'react-native-safe-area-context' +import {observer} from 'mobx-react-lite' import {ThemeProvider} from './view/lib/ThemeContext' import * as view from './view/index' import {RootStoreModel, setupState, RootStoreProvider} from './state' import {MobileShell} from './view/shell/mobile' -function App() { +const App = observer(() => { const [rootStore, setRootStore] = useState( undefined, ) @@ -41,7 +42,7 @@ function App() { - + @@ -50,6 +51,6 @@ function App() { ) -} +}) export default App diff --git a/src/state/models/root-store.ts b/src/state/models/root-store.ts index 26f947c5..54578b4a 100644 --- a/src/state/models/root-store.ts +++ b/src/state/models/root-store.ts @@ -67,6 +67,7 @@ export class RootStoreModel { me: this.me.serialize(), nav: this.nav.serialize(), onboard: this.onboard.serialize(), + shell: this.shell.serialize(), } } @@ -84,6 +85,9 @@ export class RootStoreModel { if (hasProp(v, 'onboard')) { this.onboard.hydrate(v.onboard) } + if (hasProp(v, 'shell')) { + this.shell.hydrate(v.shell) + } } } diff --git a/src/state/models/shell-ui.ts b/src/state/models/shell-ui.ts index 6b3134ff..52d08168 100644 --- a/src/state/models/shell-ui.ts +++ b/src/state/models/shell-ui.ts @@ -1,5 +1,6 @@ import {makeAutoObservable} from 'mobx' import {ProfileViewModel} from './profile-view' +import {isObj, hasProp} from '../lib/type-guards' export class ConfirmModal { name = 'confirm' @@ -135,6 +136,7 @@ export interface ComposerOpts { } export class ShellUiModel { + darkMode = false minimalShellMode = false isMainMenuOpen = false isModalActive = false @@ -156,7 +158,25 @@ export class ShellUiModel { composerOpts: ComposerOpts | undefined constructor() { - makeAutoObservable(this) + makeAutoObservable(this, {serialize: false, hydrate: false}) + } + + serialize(): unknown { + return { + darkMode: this.darkMode, + } + } + + hydrate(v: unknown) { + if (isObj(v)) { + if (hasProp(v, 'darkMode') && typeof v.darkMode === 'boolean') { + this.darkMode = v.darkMode + } + } + } + + setDarkMode(v: boolean) { + this.darkMode = v } setMinimalShellMode(v: boolean) { diff --git a/src/view/shell/mobile/Menu.tsx b/src/view/shell/mobile/Menu.tsx index 84c3f494..81f2164d 100644 --- a/src/view/shell/mobile/Menu.tsx +++ b/src/view/shell/mobile/Menu.tsx @@ -6,6 +6,7 @@ import { View, ViewStyle, } from 'react-native' +import {observer} from 'mobx-react-lite' import VersionNumber from 'react-native-version-number' import {s, colors} from '../../lib/styles' import {useStores} from '../../../state' @@ -18,187 +19,198 @@ import { } from '../../lib/icons' import {UserAvatar} from '../../com/util/UserAvatar' import {Text} from '../../com/util/text/Text' +import {ToggleButton} from '../../com/util/forms/ToggleButton' import {CreateSceneModal} from '../../../state/models/shell-ui' import {usePalette} from '../../lib/hooks/usePalette' -export const Menu = ({ - visible, - onClose, -}: { - visible: boolean - onClose: () => void -}) => { - const pal = usePalette('default') - const store = useStores() +export const Menu = observer( + ({visible, onClose}: {visible: boolean; onClose: () => void}) => { + const pal = usePalette('default') + const store = useStores() - useEffect(() => { - if (visible) { - // trigger a refresh in case memberships have changed recently - // TODO this impacts performance, need to find the right time to do this - // store.me.refreshMemberships() - } - }, [store, visible]) + useEffect(() => { + if (visible) { + // trigger a refresh in case memberships have changed recently + // TODO this impacts performance, need to find the right time to do this + // store.me.refreshMemberships() + } + }, [store, visible]) - // events - // = + // events + // = - const onNavigate = (url: string) => { - onClose() - if (url === '/notifications') { - store.nav.switchTo(1, true) - } else { - store.nav.switchTo(0, true) - if (url !== '/') { - store.nav.navigate(url) + const onNavigate = (url: string) => { + onClose() + if (url === '/notifications') { + store.nav.switchTo(1, true) + } else { + store.nav.switchTo(0, true) + if (url !== '/') { + store.nav.navigate(url) + } } } - } - const onPressCreateScene = () => { - onClose() - store.shell.openModal(new CreateSceneModal()) - } + const onPressCreateScene = () => { + onClose() + store.shell.openModal(new CreateSceneModal()) + } - // rendering - // = + // rendering + // = - const MenuItem = ({ - icon, - label, - count, - url, - bold, - onPress, - }: { - icon: JSX.Element - label: string - count?: number - url?: string - bold?: boolean - onPress?: () => void - }) => ( - onNavigate(url || '/')}> - - {icon} - {count ? ( - - {count} - - ) : undefined} - - - {label} - - - ) - - return ( - + const MenuItem = ({ + icon, + label, + count, + url, + bold, + onPress, + }: { + icon: JSX.Element + label: string + count?: number + url?: string + bold?: boolean + onPress?: () => void + }) => ( onNavigate(`/profile/${store.me.handle}`)} - style={styles.profileCard}> - - - - {store.me.displayName || store.me.handle} + style={styles.menuItem} + onPress={onPress ? onPress : () => onNavigate(url || '/')}> + + {icon} + {count ? ( + + {count} + + ) : undefined} + + + {label} + + + ) + + return ( + + onNavigate(`/profile/${store.me.handle}`)} + style={styles.profileCard}> + + + + {store.me.displayName || store.me.handle} + + + @{store.me.handle} + + + + onNavigate('/search')}> + } + size={25} + /> + + Search - - @{store.me.handle} + + + } size="26" /> + } + label="Home" + url="/" + /> + } size="28" /> + } + label="Notifications" + url="/notifications" + count={store.me.notificationCount} + /> + + + + Scenes + + {store.me.memberships + ? store.me.memberships.memberships.map((membership, i) => ( + + } + label={membership.displayName || membership.handle} + url={`/profile/${membership.handle}`} + /> + )) + : undefined} + + + } + size="30" + /> + } + label="Create a scene" + onPress={onPressCreateScene} + /> + } + size="30" + strokeWidth={2} + /> + } + label="Settings" + url="/settings" + /> + + + store.shell.setDarkMode(!store.shell.darkMode)} + /> + + + + Build version {VersionNumber.appVersion} ( + {VersionNumber.buildVersion}) - - onNavigate('/search')}> - } - size={25} - /> - - Search - - - - } size="26" />} - label="Home" - url="/" - /> - } size="28" />} - label="Notifications" - url="/notifications" - count={store.me.notificationCount} - /> - - - Scenes - - {store.me.memberships - ? store.me.memberships.memberships.map((membership, i) => ( - - } - label={membership.displayName || membership.handle} - url={`/profile/${membership.handle}`} - /> - )) - : undefined} - - - } size="30" /> - } - label="Create a scene" - onPress={onPressCreateScene} - /> - } - size="30" - strokeWidth={2} - /> - } - label="Settings" - url="/settings" - /> - - - - Build version {VersionNumber.appVersion} ({VersionNumber.buildVersion} - ) - - - - ) -} + ) + }, +) const styles = StyleSheet.create({ view: {