2023-07-27 18:59:40 -04:00
|
|
|
import { App } from './App'
|
2023-08-10 13:30:32 -04:00
|
|
|
import {
|
|
|
|
createBrowserRouter,
|
|
|
|
Outlet,
|
|
|
|
redirect,
|
|
|
|
RouterProvider,
|
|
|
|
} from 'react-router-dom'
|
2023-07-27 18:59:40 -04:00
|
|
|
import { ErrorPage } from './components/ErrorPage'
|
|
|
|
import { Settings } from './routes/Settings'
|
2023-08-10 13:30:32 -04:00
|
|
|
import Onboarding, {
|
|
|
|
onboardingRoutes,
|
|
|
|
onboardingPaths,
|
|
|
|
} from './routes/Onboarding'
|
2023-07-27 18:59:40 -04:00
|
|
|
import SignIn from './routes/SignIn'
|
|
|
|
import { Auth } from './Auth'
|
2023-08-15 21:56:24 -04:00
|
|
|
import { isTauri } from './lib/isTauri'
|
|
|
|
import Home from './routes/Home'
|
|
|
|
import { FileEntry, readDir, readTextFile } from '@tauri-apps/api/fs'
|
|
|
|
import makeUrlPathRelative from './lib/makeUrlPathRelative'
|
|
|
|
import {
|
|
|
|
initializeProjectDirectory,
|
|
|
|
isProjectDirectory,
|
|
|
|
PROJECT_ENTRYPOINT,
|
|
|
|
} from './lib/tauriFS'
|
|
|
|
import { metadata, type Metadata } from 'tauri-plugin-fs-extra-api'
|
2023-07-27 18:59:40 -04:00
|
|
|
|
2023-08-10 13:30:32 -04:00
|
|
|
const prependRoutes =
|
|
|
|
(routesObject: Record<string, string>) => (prepend: string) => {
|
|
|
|
return Object.fromEntries(
|
|
|
|
Object.entries(routesObject).map(([constName, path]) => [
|
|
|
|
constName,
|
|
|
|
prepend + path,
|
|
|
|
])
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
export const paths = {
|
|
|
|
INDEX: '/',
|
2023-08-15 21:56:24 -04:00
|
|
|
HOME: '/home',
|
|
|
|
FILE: '/file',
|
2023-08-10 13:30:32 -04:00
|
|
|
SETTINGS: '/settings',
|
|
|
|
SIGN_IN: '/signin',
|
|
|
|
ONBOARDING: prependRoutes(onboardingPaths)(
|
2023-08-15 21:56:24 -04:00
|
|
|
'/onboarding'
|
2023-08-10 13:30:32 -04:00
|
|
|
) as typeof onboardingPaths,
|
|
|
|
}
|
|
|
|
|
2023-08-15 21:56:24 -04:00
|
|
|
export type IndexLoaderData = {
|
|
|
|
code: string | null
|
|
|
|
}
|
|
|
|
|
|
|
|
export type ProjectWithEntryPointMetadata = FileEntry & {
|
|
|
|
entrypoint_metadata: Metadata
|
|
|
|
}
|
|
|
|
export type HomeLoaderData = {
|
|
|
|
projects: ProjectWithEntryPointMetadata[]
|
|
|
|
}
|
|
|
|
|
2023-07-27 18:59:40 -04:00
|
|
|
const router = createBrowserRouter([
|
|
|
|
{
|
2023-08-10 13:30:32 -04:00
|
|
|
path: paths.INDEX,
|
2023-08-15 21:56:24 -04:00
|
|
|
loader: () =>
|
|
|
|
isTauri() ? redirect(paths.HOME) : redirect(paths.FILE + '/new'),
|
|
|
|
},
|
|
|
|
{
|
|
|
|
path: paths.FILE + '/:id',
|
2023-07-27 18:59:40 -04:00
|
|
|
element: (
|
|
|
|
<Auth>
|
2023-08-10 13:30:32 -04:00
|
|
|
<Outlet />
|
2023-07-27 18:59:40 -04:00
|
|
|
<App />
|
|
|
|
</Auth>
|
|
|
|
),
|
|
|
|
errorElement: <ErrorPage />,
|
2023-08-15 21:56:24 -04:00
|
|
|
loader: async ({
|
|
|
|
request,
|
|
|
|
params,
|
|
|
|
}): Promise<IndexLoaderData | Response> => {
|
2023-07-27 18:59:40 -04:00
|
|
|
const store = localStorage.getItem('store')
|
|
|
|
if (store === null) {
|
2023-08-10 13:30:32 -04:00
|
|
|
return redirect(paths.ONBOARDING.INDEX)
|
2023-07-27 18:59:40 -04:00
|
|
|
} else {
|
2023-08-10 13:30:32 -04:00
|
|
|
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) {
|
2023-08-15 21:56:24 -04:00
|
|
|
return redirect(makeUrlPathRelative(paths.ONBOARDING.INDEX) + status)
|
2023-07-27 18:59:40 -04:00
|
|
|
}
|
|
|
|
}
|
2023-08-15 21:56:24 -04:00
|
|
|
|
|
|
|
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)
|
|
|
|
|
|
|
|
return {
|
|
|
|
code,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return {
|
|
|
|
code: '',
|
|
|
|
}
|
2023-07-27 18:59:40 -04:00
|
|
|
},
|
2023-08-10 13:30:32 -04:00
|
|
|
children: [
|
|
|
|
{
|
2023-08-15 21:56:24 -04:00
|
|
|
path: makeUrlPathRelative(paths.SETTINGS),
|
2023-08-10 13:30:32 -04:00
|
|
|
element: <Settings />,
|
|
|
|
},
|
|
|
|
{
|
2023-08-15 21:56:24 -04:00
|
|
|
path: makeUrlPathRelative(paths.ONBOARDING.INDEX),
|
2023-08-10 13:30:32 -04:00
|
|
|
element: <Onboarding />,
|
|
|
|
children: onboardingRoutes,
|
|
|
|
},
|
|
|
|
],
|
2023-07-27 18:59:40 -04:00
|
|
|
},
|
2023-08-15 21:56:24 -04:00
|
|
|
{
|
|
|
|
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,
|
|
|
|
}))
|
|
|
|
)
|
|
|
|
|
|
|
|
return {
|
|
|
|
projects,
|
|
|
|
}
|
|
|
|
},
|
|
|
|
children: [
|
|
|
|
{
|
|
|
|
path: makeUrlPathRelative(paths.SETTINGS),
|
|
|
|
element: <Settings />,
|
|
|
|
},
|
|
|
|
],
|
|
|
|
},
|
2023-07-27 18:59:40 -04:00
|
|
|
{
|
2023-08-10 13:30:32 -04:00
|
|
|
path: paths.SIGN_IN,
|
2023-07-27 18:59:40 -04:00
|
|
|
element: <SignIn />,
|
|
|
|
},
|
|
|
|
])
|
|
|
|
|
|
|
|
/**
|
|
|
|
* All routes in the app, used in src/index.tsx
|
|
|
|
* @returns RouterProvider
|
|
|
|
*/
|
|
|
|
export const Router = () => {
|
|
|
|
return <RouterProvider router={router} />
|
|
|
|
}
|