Refactor to just CommandBar and GlobalState (#337)

* Refactor to just CommandBar and GlobalState

* @Irev-Dev review: consolidate uses of useContext
This commit is contained in:
Frank Noirot
2023-08-29 10:48:55 -04:00
committed by GitHub
parent 32d928ae0c
commit 152108f7a5
20 changed files with 297 additions and 255 deletions

View File

@ -0,0 +1,147 @@
import { useMachine } from '@xstate/react'
import { useNavigate } from 'react-router-dom'
import { paths } from '../Router'
import {
authCommandBarMeta,
authMachine,
TOKEN_PERSIST_KEY,
} from '../machines/authMachine'
import withBaseUrl from '../lib/withBaseURL'
import React, { createContext, useEffect, useRef } from 'react'
import useStateMachineCommands from '../hooks/useStateMachineCommands'
import {
SETTINGS_PERSIST_KEY,
settingsCommandBarMeta,
settingsMachine,
} from 'machines/settingsMachine'
import { toast } from 'react-hot-toast'
import { setThemeClass } from 'lib/theme'
import {
AnyStateMachine,
ContextFrom,
InterpreterFrom,
Prop,
StateFrom,
} from 'xstate'
import { useCommandsContext } from 'hooks/useCommandsContext'
type MachineContext<T extends AnyStateMachine> = {
state: StateFrom<T>
context: ContextFrom<T>
send: Prop<InterpreterFrom<T>, 'send'>
}
type GlobalContext = {
auth: MachineContext<typeof authMachine>
settings: MachineContext<typeof settingsMachine>
}
export const GlobalStateContext = createContext({} as GlobalContext)
export const GlobalStateProvider = ({
children,
}: {
children: React.ReactNode
}) => {
const navigate = useNavigate()
const { commands } = useCommandsContext()
// Settings machine setup
const retrievedSettings = useRef(
localStorage?.getItem(SETTINGS_PERSIST_KEY) || '{}'
)
const persistedSettings = Object.assign(
settingsMachine.initialState.context,
JSON.parse(retrievedSettings.current) as Partial<
(typeof settingsMachine)['context']
>
)
const [settingsState, settingsSend] = useMachine(settingsMachine, {
context: persistedSettings,
actions: {
toastSuccess: (context, event) => {
const truncatedNewValue =
'data' in event && event.data instanceof Object
? (context[Object.keys(event.data)[0] as keyof typeof context]
.toString()
.substring(0, 28) as any)
: undefined
toast.success(
event.type +
(truncatedNewValue
? ` to "${truncatedNewValue}${
truncatedNewValue.length === 28 ? '...' : ''
}"`
: '')
)
},
},
})
useStateMachineCommands({
state: settingsState,
send: settingsSend,
commands,
owner: 'settings',
commandBarMeta: settingsCommandBarMeta,
})
useEffect(
() => setThemeClass(settingsState.context.theme),
[settingsState.context.theme]
)
// Auth machine setup
const [authState, authSend] = useMachine(authMachine, {
actions: {
goToSignInPage: () => {
navigate(paths.SIGN_IN)
logout()
},
goToIndexPage: () => {
if (window.location.pathname.includes(paths.SIGN_IN)) {
navigate(paths.INDEX)
}
},
},
})
useStateMachineCommands({
state: authState,
send: authSend,
commands,
commandBarMeta: authCommandBarMeta,
owner: 'auth',
})
return (
<GlobalStateContext.Provider
value={{
auth: {
state: authState,
context: authState.context,
send: authSend,
},
settings: {
state: settingsState,
context: settingsState.context,
send: settingsSend,
},
}}
>
{children}
</GlobalStateContext.Provider>
)
}
export default GlobalStateProvider
export function logout() {
const url = withBaseUrl('/logout')
localStorage.removeItem(TOKEN_PERSIST_KEY)
return fetch(url, {
method: 'POST',
credentials: 'include',
})
}