feat: persist color theme setting

This commit is contained in:
Jeremy Kahn 2022-09-05 17:35:40 -05:00
parent 82673d54fd
commit 7fb46bfe27
5 changed files with 71 additions and 26 deletions

View File

@ -52,6 +52,7 @@ test('persists user settings if none were already persisted', async () => {
}) })
expect(mockSetItem).toHaveBeenCalledWith(PersistedStorageKeys.USER_SETTINGS, { expect(mockSetItem).toHaveBeenCalledWith(PersistedStorageKeys.USER_SETTINGS, {
colorMode: 'dark',
userId: 'abc123', userId: 'abc123',
}) })
}) })

View File

@ -3,6 +3,7 @@ import { BrowserRouter as Router, Routes, Route } from 'react-router-dom'
import { v4 as uuid } from 'uuid' import { v4 as uuid } from 'uuid'
import localforage from 'localforage' import localforage from 'localforage'
import { SettingsContext } from 'contexts/SettingsContext'
import { Home } from 'pages/Home/' import { Home } from 'pages/Home/'
import { PublicRoom } from 'pages/PublicRoom/' import { PublicRoom } from 'pages/PublicRoom/'
import { UserSettings } from 'models/settings' import { UserSettings } from 'models/settings'
@ -22,8 +23,11 @@ function Bootstrap({
getUuid = uuid, getUuid = uuid,
}: BootstrapProps) { }: BootstrapProps) {
const [hasLoadedSettings, setHasLoadedSettings] = useState(false) const [hasLoadedSettings, setHasLoadedSettings] = useState(false)
const [settings, setSettings] = useState({ userId: getUuid() }) const [userSettings, setUserSettings] = useState<UserSettings>({
const { userId } = settings userId: getUuid(),
colorMode: 'dark',
})
const { userId } = userSettings
useEffect(() => { useEffect(() => {
;(async () => { ;(async () => {
@ -35,35 +39,54 @@ function Bootstrap({
) )
if (persistedUserSettings) { if (persistedUserSettings) {
setSettings(persistedUserSettings) setUserSettings(persistedUserSettings)
} else { } else {
await persistedStorage.setItem( await persistedStorage.setItem(
PersistedStorageKeys.USER_SETTINGS, PersistedStorageKeys.USER_SETTINGS,
settings userSettings
) )
} }
setHasLoadedSettings(true) setHasLoadedSettings(true)
})() })()
}, [hasLoadedSettings, persistedStorage, settings, userId]) }, [hasLoadedSettings, persistedStorage, userSettings, userId])
const settingsContextValue = {
updateUserSettings: async (changedSettings: Partial<UserSettings>) => {
const newSettings = {
...userSettings,
...changedSettings,
}
await persistedStorage.setItem(
PersistedStorageKeys.USER_SETTINGS,
newSettings
)
setUserSettings(newSettings)
},
getUserSettings: () => ({ ...userSettings }),
}
return ( return (
<Router> <Router>
<Shell userPeerId={userId}> <SettingsContext.Provider value={settingsContextValue}>
{hasLoadedSettings ? ( <Shell userPeerId={userId}>
<Routes> {hasLoadedSettings ? (
{['/', '/index.html'].map(path => ( <Routes>
<Route key={path} path={path} element={<Home />} /> {['/', '/index.html'].map(path => (
))} <Route key={path} path={path} element={<Home />} />
<Route ))}
path="/public/:roomId" <Route
element={<PublicRoom userId={userId} />} path="/public/:roomId"
/> element={<PublicRoom userId={userId} />}
</Routes> />
) : ( </Routes>
<></> ) : (
)} <></>
</Shell> )}
</Shell>
</SettingsContext.Provider>
</Router> </Router>
) )
} }

View File

@ -1,11 +1,12 @@
import { import {
forwardRef,
PropsWithChildren, PropsWithChildren,
SyntheticEvent,
forwardRef,
useCallback, useCallback,
useContext,
useEffect, useEffect,
useMemo, useMemo,
useState, useState,
SyntheticEvent,
} from 'react' } from 'react'
import { Link } from 'react-router-dom' import { Link } from 'react-router-dom'
import CssBaseline from '@mui/material/CssBaseline' import CssBaseline from '@mui/material/CssBaseline'
@ -34,6 +35,7 @@ import Brightness4Icon from '@mui/icons-material/Brightness4'
import Brightness7Icon from '@mui/icons-material/Brightness7' import Brightness7Icon from '@mui/icons-material/Brightness7'
import { ShellContext } from 'contexts/ShellContext' import { ShellContext } from 'contexts/ShellContext'
import { SettingsContext } from 'contexts/SettingsContext'
import { AlertOptions } from 'models/shell' import { AlertOptions } from 'models/shell'
import { PeerNameDisplay } from 'components/PeerNameDisplay' import { PeerNameDisplay } from 'components/PeerNameDisplay'
@ -98,6 +100,7 @@ const DrawerHeader = styled('div')(({ theme }) => ({
})) }))
export const Shell = ({ children, userPeerId }: ShellProps) => { export const Shell = ({ children, userPeerId }: ShellProps) => {
const settingsContext = useContext(SettingsContext)
const [isAlertShowing, setIsAlertShowing] = useState(false) const [isAlertShowing, setIsAlertShowing] = useState(false)
const [isDrawerOpen, setIsDrawerOpen] = useState(false) const [isDrawerOpen, setIsDrawerOpen] = useState(false)
const [doShowPeers, setDoShowPeers] = useState(false) const [doShowPeers, setDoShowPeers] = useState(false)
@ -125,20 +128,21 @@ export const Shell = ({ children, userPeerId }: ShellProps) => {
[numberOfPeers, setDoShowPeers, setNumberOfPeers, setTitle, showAlert] [numberOfPeers, setDoShowPeers, setNumberOfPeers, setTitle, showAlert]
) )
const [mode, setMode] = useState<'light' | 'dark'>('dark') const colorMode = settingsContext.getUserSettings().colorMode
const handleColorModeToggleClick = () => { const handleColorModeToggleClick = () => {
setMode(prevMode => (prevMode === 'light' ? 'dark' : 'light')) const newMode = colorMode === 'light' ? 'dark' : 'light'
settingsContext.updateUserSettings({ colorMode: newMode })
} }
const theme = useMemo( const theme = useMemo(
() => () =>
createTheme({ createTheme({
palette: { palette: {
mode, mode: colorMode,
}, },
}), }),
[mode] [colorMode]
) )
const handleAlertClose = ( const handleAlertClose = (

View File

@ -0,0 +1,16 @@
import { createContext } from 'react'
import { UserSettings } from 'models/settings'
interface SettingsContextProps {
updateUserSettings: (settings: Partial<UserSettings>) => Promise<void>
getUserSettings: () => UserSettings
}
export const SettingsContext = createContext<SettingsContextProps>({
updateUserSettings: () => Promise.resolve(),
getUserSettings: () => ({
userId: '',
colorMode: 'dark',
}),
})

View File

@ -1,3 +1,4 @@
export interface UserSettings { export interface UserSettings {
colorMode: 'dark' | 'light'
userId: string userId: string
} }