Xstate Auth migration (#250)
* auth migrate progress, web only * wrap home in state provider * use consistent logged spelling * use createActorContext * typo * fix wraping problem
This commit is contained in:
235
src/Router.tsx
235
src/Router.tsx
@ -24,6 +24,7 @@ import {
|
||||
} from './lib/tauriFS'
|
||||
import { metadata, type Metadata } from 'tauri-plugin-fs-extra-api'
|
||||
import DownloadAppBanner from './components/DownloadAppBanner'
|
||||
import { GlobalStateProvider } from './hooks/useAuthMachine'
|
||||
|
||||
const prependRoutes =
|
||||
(routesObject: Record<string, string>) => (prepend: string) => {
|
||||
@ -58,124 +59,142 @@ export type HomeLoaderData = {
|
||||
projects: ProjectWithEntryPointMetadata[]
|
||||
}
|
||||
|
||||
const router = createBrowserRouter([
|
||||
{
|
||||
path: paths.INDEX,
|
||||
loader: () =>
|
||||
isTauri() ? redirect(paths.HOME) : redirect(paths.FILE + '/new'),
|
||||
},
|
||||
{
|
||||
path: paths.FILE + '/:id',
|
||||
element: (
|
||||
<Auth>
|
||||
<Outlet />
|
||||
<App />
|
||||
{!isTauri() && import.meta.env.PROD && <DownloadAppBanner />}
|
||||
</Auth>
|
||||
),
|
||||
errorElement: <ErrorPage />,
|
||||
id: paths.FILE,
|
||||
loader: async ({
|
||||
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
|
||||
type CreateBrowserRouterArg = Parameters<typeof createBrowserRouter>[0]
|
||||
|
||||
if (shouldRedirectToOnboarding) {
|
||||
return redirect(makeUrlPathRelative(paths.ONBOARDING.INDEX) + status)
|
||||
const addGlobalContextToElements = (
|
||||
routes: CreateBrowserRouterArg
|
||||
): CreateBrowserRouterArg =>
|
||||
routes.map((route) =>
|
||||
'element' in route
|
||||
? {
|
||||
...route,
|
||||
element: <GlobalStateProvider>{route.element}</GlobalStateProvider>,
|
||||
}
|
||||
}
|
||||
: route
|
||||
)
|
||||
|
||||
if (params.id && params.id !== 'new') {
|
||||
// Note that PROJECT_ENTRYPOINT is hardcoded until we support multiple files
|
||||
const code = await readTextFile(params.id + '/' + PROJECT_ENTRYPOINT)
|
||||
const entrypoint_metadata = await metadata(
|
||||
params.id + '/' + PROJECT_ENTRYPOINT
|
||||
)
|
||||
const children = await readDir(params.id)
|
||||
const router = createBrowserRouter(
|
||||
addGlobalContextToElements([
|
||||
{
|
||||
path: paths.INDEX,
|
||||
loader: () =>
|
||||
isTauri() ? redirect(paths.HOME) : redirect(paths.FILE + '/new'),
|
||||
},
|
||||
{
|
||||
path: paths.FILE + '/:id',
|
||||
element: (
|
||||
<Auth>
|
||||
<Outlet />
|
||||
<App />
|
||||
{!isTauri() && import.meta.env.PROD && <DownloadAppBanner />}
|
||||
</Auth>
|
||||
),
|
||||
errorElement: <ErrorPage />,
|
||||
id: paths.FILE,
|
||||
loader: async ({
|
||||
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
|
||||
|
||||
if (shouldRedirectToOnboarding) {
|
||||
return redirect(
|
||||
makeUrlPathRelative(paths.ONBOARDING.INDEX) + status
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if (params.id && params.id !== 'new') {
|
||||
// Note that PROJECT_ENTRYPOINT is hardcoded until we support multiple files
|
||||
const code = await readTextFile(params.id + '/' + PROJECT_ENTRYPOINT)
|
||||
const entrypoint_metadata = await metadata(
|
||||
params.id + '/' + PROJECT_ENTRYPOINT
|
||||
)
|
||||
const children = await readDir(params.id)
|
||||
|
||||
return {
|
||||
code,
|
||||
project: {
|
||||
name: params.id.slice(params.id.lastIndexOf('/') + 1),
|
||||
path: params.id,
|
||||
children,
|
||||
entrypoint_metadata,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
code,
|
||||
project: {
|
||||
name: params.id.slice(params.id.lastIndexOf('/') + 1),
|
||||
path: params.id,
|
||||
children,
|
||||
entrypoint_metadata,
|
||||
},
|
||||
code: '',
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
code: '',
|
||||
}
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: makeUrlPathRelative(paths.SETTINGS),
|
||||
element: <Settings />,
|
||||
},
|
||||
{
|
||||
path: makeUrlPathRelative(paths.ONBOARDING.INDEX),
|
||||
element: <Onboarding />,
|
||||
children: onboardingRoutes,
|
||||
},
|
||||
],
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: makeUrlPathRelative(paths.SETTINGS),
|
||||
element: <Settings />,
|
||||
},
|
||||
{
|
||||
path: makeUrlPathRelative(paths.ONBOARDING.INDEX),
|
||||
element: <Onboarding />,
|
||||
children: onboardingRoutes,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: paths.HOME,
|
||||
element: (
|
||||
<Auth>
|
||||
<Outlet />
|
||||
<Home />
|
||||
</Auth>
|
||||
),
|
||||
loader: async () => {
|
||||
if (!isTauri()) {
|
||||
return redirect(paths.FILE + '/new')
|
||||
}
|
||||
{
|
||||
path: paths.HOME,
|
||||
element: (
|
||||
<Auth>
|
||||
<Outlet />
|
||||
<Home />
|
||||
</Auth>
|
||||
),
|
||||
loader: async () => {
|
||||
if (!isTauri()) {
|
||||
return redirect(paths.FILE + '/new')
|
||||
}
|
||||
|
||||
const projectDir = await initializeProjectDirectory()
|
||||
const projectsNoMeta = (await readDir(projectDir.dir)).filter(
|
||||
isProjectDirectory
|
||||
)
|
||||
const projects = await Promise.all(
|
||||
projectsNoMeta.map(async (p) => ({
|
||||
entrypoint_metadata: await metadata(
|
||||
p.path + '/' + PROJECT_ENTRYPOINT
|
||||
),
|
||||
...p,
|
||||
}))
|
||||
)
|
||||
const projectDir = await initializeProjectDirectory()
|
||||
const projectsNoMeta = (await readDir(projectDir.dir)).filter(
|
||||
isProjectDirectory
|
||||
)
|
||||
const projects = await Promise.all(
|
||||
projectsNoMeta.map(async (p) => ({
|
||||
entrypoint_metadata: await metadata(
|
||||
p.path + '/' + PROJECT_ENTRYPOINT
|
||||
),
|
||||
...p,
|
||||
}))
|
||||
)
|
||||
|
||||
return {
|
||||
projects,
|
||||
}
|
||||
return {
|
||||
projects,
|
||||
}
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: makeUrlPathRelative(paths.SETTINGS),
|
||||
element: <Settings />,
|
||||
},
|
||||
],
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: makeUrlPathRelative(paths.SETTINGS),
|
||||
element: <Settings />,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: paths.SIGN_IN,
|
||||
element: <SignIn />,
|
||||
},
|
||||
])
|
||||
{
|
||||
path: paths.SIGN_IN,
|
||||
element: <SignIn />,
|
||||
},
|
||||
])
|
||||
)
|
||||
|
||||
/**
|
||||
* All routes in the app, used in src/index.tsx
|
||||
|
Reference in New Issue
Block a user