Franknoirot/cmd bar (#328)

* Add XState and naive ActionBar

* Add basic dialog and combobox

* Selectable commands in command bar

* Add a few (broken) file actions

* Home commands

* Add subcommand descriptions, cleanup on navigate

* Refactor: move command creation and types to lib

* Refactor to allow any machine to add commands

* Add auth to command bar, add ability to hide cmds

* Refactor: consolidate theme utilities

* Add settings as machine and command set

* Fix: type tweaks

* Fix: only allow auth to navigate from signin

* Remove zustand-powered settings

* Fix: remove zustand settings from App

* Fix: browser infinite redirect

* Feature: allow commands to be hidden per-platform

* Fix: tsc errors

* Fix: hide default project directory from cmd bar

* Polish: transitions, css tweaks

* Feature: label current value in options settings

* Fix broken debug panel UI

* Refactor: move settings toasts to actions

* Tweak: css rounding

* Fix: set default directory recursion and reload 🐞

* Refactor: move machines to their own directory

* Fix formatting

* @Irev-Dev clean-up catches, import cleanup
This commit is contained in:
Frank Noirot
2023-08-28 20:31:49 -04:00
committed by GitHub
parent 6f0fae625f
commit 32d928ae0c
33 changed files with 1556 additions and 475 deletions

View File

@ -24,7 +24,16 @@ import {
} from './lib/tauriFS'
import { metadata, type Metadata } from 'tauri-plugin-fs-extra-api'
import DownloadAppBanner from './components/DownloadAppBanner'
import { GlobalStateProvider } from './hooks/useAuthMachine'
import {
AuthMachineCommandProvider,
GlobalStateProvider,
} from './hooks/useAuthMachine'
import SettingsCommandProvider from './components/SettingsCommandProvider'
import {
SETTINGS_PERSIST_KEY,
settingsMachine,
} from './machines/settingsMachine'
import { ContextFrom } from 'xstate'
const prependRoutes =
(routesObject: Record<string, string>) => (prepend: string) => {
@ -68,7 +77,15 @@ const addGlobalContextToElements = (
'element' in route
? {
...route,
element: <GlobalStateProvider>{route.element}</GlobalStateProvider>,
element: (
<GlobalStateProvider>
<AuthMachineCommandProvider>
<SettingsCommandProvider>
{route.element}
</SettingsCommandProvider>
</AuthMachineCommandProvider>
</GlobalStateProvider>
),
}
: route
)
@ -95,26 +112,23 @@ const router = createBrowserRouter(
request,
params,
}): Promise<IndexLoaderData | Response> => {
const store = localStorage.getItem('store')
if (store === null) {
return redirect(paths.ONBOARDING.INDEX)
} else {
const status = JSON.parse(store).state.onboardingStatus || ''
const notEnRouteToOnboarding =
!request.url.includes(paths.ONBOARDING.INDEX) &&
request.method === 'GET'
// '' is the initial state, 'done' and 'dismissed' are the final states
const hasValidOnboardingStatus =
(status !== undefined && status.length === 0) ||
!(status === 'done' || status === 'dismissed')
const shouldRedirectToOnboarding =
notEnRouteToOnboarding && hasValidOnboardingStatus
const fetchedStorage = localStorage?.getItem(SETTINGS_PERSIST_KEY)
const persistedSettings = JSON.parse(fetchedStorage || '{}') as Partial<
ContextFrom<typeof settingsMachine>
>
if (shouldRedirectToOnboarding) {
return redirect(
makeUrlPathRelative(paths.ONBOARDING.INDEX) + status
)
}
const status = persistedSettings.onboardingStatus || ''
const notEnRouteToOnboarding = !request.url.includes(
paths.ONBOARDING.INDEX
)
// '' is the initial state, 'done' and 'dismissed' are the final states
const hasValidOnboardingStatus =
status.length === 0 || !(status === 'done' || status === 'dismissed')
const shouldRedirectToOnboarding =
notEnRouteToOnboarding && hasValidOnboardingStatus
if (shouldRedirectToOnboarding) {
return redirect(makeUrlPathRelative(paths.ONBOARDING.INDEX) + status)
}
if (params.id && params.id !== 'new') {
@ -164,9 +178,23 @@ const router = createBrowserRouter(
if (!isTauri()) {
return redirect(paths.FILE + '/new')
}
const projectDir = await initializeProjectDirectory()
const projectsNoMeta = (await readDir(projectDir.dir)).filter(
const fetchedStorage = localStorage?.getItem(SETTINGS_PERSIST_KEY)
const persistedSettings = JSON.parse(fetchedStorage || '{}') as Partial<
ContextFrom<typeof settingsMachine>
>
const projectDir = await initializeProjectDirectory(
persistedSettings.defaultDirectory || ''
)
if (projectDir !== persistedSettings.defaultDirectory) {
localStorage.setItem(
SETTINGS_PERSIST_KEY,
JSON.stringify({
...persistedSettings,
defaultDirectory: projectDir,
})
)
}
const projectsNoMeta = (await readDir(projectDir)).filter(
isProjectDirectory
)
const projects = await Promise.all(