diff --git a/frontend/apps/desktop/src/app-api.ts b/frontend/apps/desktop/src/app-api.ts index 0ca9de1cf..ffb8de2fb 100644 --- a/frontend/apps/desktop/src/app-api.ts +++ b/frontend/apps/desktop/src/app-api.ts @@ -1,5 +1,5 @@ -import type {NavRoute} from '@mintter/app/utils/navigation' import {resolveHmIdToAppRoute} from '@mintter/app/utils/navigation' +import {NavRoute, defaultRoute, navRouteSchema} from '@mintter/app/utils/routes' import type {AppWindowEvent} from '@mintter/app/utils/window-events' import {observable} from '@trpc/server/observable' import { @@ -71,12 +71,20 @@ info('App UserData: ', userDataPath) export function openInitialWindows() { const windowsState = getWindowsState() - if (!Object.keys(windowsState).length) { - trpc.createAppWindow({routes: [{key: 'documents'}]}) + const validWindowEntries = Object.entries(windowsState).filter( + ([windowId, window]) => { + if (window.routes.length === 0) return false + return window.routes.every((route) => { + return navRouteSchema.safeParse(route).success + }) + }, + ) + if (!validWindowEntries.length) { + trpc.createAppWindow({routes: [defaultRoute]}) return } try { - Object.entries(windowsState).forEach(([windowId, window]) => { + validWindowEntries.forEach(([windowId, window]) => { trpc.createAppWindow({ routes: window.routes, routeIndex: window.routeIndex, @@ -87,7 +95,7 @@ export function openInitialWindows() { }) } catch (error) { info(`[MAIN]: openInitialWindows Error: ${JSON.stringify(error)}`) - trpc.createAppWindow({routes: [{key: 'documents'}]}) + trpc.createAppWindow({routes: [defaultRoute]}) return } } diff --git a/frontend/apps/desktop/src/app-menu.ts b/frontend/apps/desktop/src/app-menu.ts index 2d68bc790..ce66e2d96 100644 --- a/frontend/apps/desktop/src/app-menu.ts +++ b/frontend/apps/desktop/src/app-menu.ts @@ -1,6 +1,7 @@ // this menu is visible on macOS only // the keyboard shortcuts apply to every platform +import {defaultRoute} from '@mintter/app/utils/routes' import {Menu, MenuItem} from 'electron' import {dispatchFocusedWindowAppEvent, openRoute, trpc} from './app-api' @@ -65,7 +66,7 @@ export function createAppMenu() { label: 'New Window', accelerator: 'CmdOrCtrl+Shift+n', click: () => { - trpc.createAppWindow({routes: [{key: 'documents'}]}) + trpc.createAppWindow({routes: [defaultRoute]}) }, }, {type: 'separator'}, @@ -106,7 +107,7 @@ export function createAppMenu() { label: 'Documents', accelerator: 'CmdOrCtrl+1', click: () => { - openRoute({key: 'documents'}) + openRoute({key: 'documents', tab: 'trusted'}) }, }, { diff --git a/frontend/apps/desktop/src/app-windows.ts b/frontend/apps/desktop/src/app-windows.ts index c369b05dc..62f3778fa 100644 --- a/frontend/apps/desktop/src/app-windows.ts +++ b/frontend/apps/desktop/src/app-windows.ts @@ -1,5 +1,6 @@ import appError from '@mintter/app/errors' -import type {NavRoute, NavState} from '@mintter/app/utils/navigation' +import type {NavState} from '@mintter/app/utils/navigation' +import {NavRoute} from '@mintter/app/utils/routes' import type {AppWindowEvent} from '@mintter/app/utils/window-events' import {getRouteWindowType} from '@mintter/app/utils/window-types' import {BrowserWindow, app, nativeTheme} from 'electron' @@ -73,7 +74,7 @@ let windowsState = ({} as Record) export function getWindowsState() { - return windowsState + return windowsState || {} } function getAWindow() { diff --git a/frontend/apps/desktop/src/main.ts b/frontend/apps/desktop/src/main.ts index f80b15910..f74b2c338 100644 --- a/frontend/apps/desktop/src/main.ts +++ b/frontend/apps/desktop/src/main.ts @@ -1,3 +1,4 @@ +import {defaultRoute} from '@mintter/app/utils/routes' import {IS_PROD_DESKTOP} from '@mintter/shared' import * as Sentry from '@sentry/electron/main' import {BrowserWindow, Menu, app, ipcMain, nativeTheme, shell} from 'electron' @@ -75,7 +76,7 @@ app.on('did-become-active', () => { if (BrowserWindow.getAllWindows().length === 0) { log.debug('[MAIN]: will open the home window') trpc.createAppWindow({ - routes: [{key: 'documents'}], + routes: [defaultRoute], }) } }) @@ -137,7 +138,7 @@ if (!gotTheLock) { if (BrowserWindow.getAllWindows().length === 0) { log.debug('[MAIN]: will open the home window') trpc.createAppWindow({ - routes: [{key: 'documents'}], + routes: [defaultRoute], }) } }) diff --git a/frontend/packages/app/components/changes-list.tsx b/frontend/packages/app/components/changes-list.tsx index 956f43a7a..7d29708fc 100644 --- a/frontend/packages/app/components/changes-list.tsx +++ b/frontend/packages/app/components/changes-list.tsx @@ -38,7 +38,8 @@ import { usePublishDocToGroup, } from '../models/groups' import {useOpenUrl} from '../open-url' -import {NavRoute, useNavRoute} from '../utils/navigation' +import {useNavRoute} from '../utils/navigation' +import {NavRoute} from '../utils/routes' import {AccessoryContainer} from './accessory-sidebar' import {AccountLinkAvatar} from './account-link-avatar' import {useAppDialog} from './dialog' diff --git a/frontend/packages/app/components/citations.tsx b/frontend/packages/app/components/citations.tsx index 5e50b0862..4fbdbc453 100644 --- a/frontend/packages/app/components/citations.tsx +++ b/frontend/packages/app/components/citations.tsx @@ -5,7 +5,7 @@ import {PanelCard} from '@mintter/ui' import {useAccount} from '../models/accounts' import {useEntityTimeline} from '../models/changes' import {useDocTextContent, usePublication} from '../models/documents' -import {PublicationRoute} from '../utils/navigation' +import {PublicationRoute} from '../utils/routes' import {AccessoryContainer} from './accessory-sidebar' import {AccountLinkAvatar} from './account-link-avatar' diff --git a/frontend/packages/app/components/edit-doc-button.tsx b/frontend/packages/app/components/edit-doc-button.tsx index ba420b27f..2fbb104d3 100644 --- a/frontend/packages/app/components/edit-doc-button.tsx +++ b/frontend/packages/app/components/edit-doc-button.tsx @@ -1,12 +1,13 @@ import {useGRPCClient} from '@mintter/app/app-context' import {useDraftList} from '@mintter/app/models/documents' import {usePublicationVariant} from '@mintter/app/models/publication' -import {NavMode, NavRoute} from '@mintter/app/utils/navigation' +import {NavMode} from '@mintter/app/utils/navigation' import {useNavigate} from '@mintter/app/utils/useNavigate' import {GroupVariant, PublicationVariant} from '@mintter/shared' import {Button, Tooltip, toast} from '@mintter/ui' import {Pencil} from '@tamagui/lucide-icons' import appError from '../errors' +import {NavRoute} from '../utils/routes' export function useEditDraft( docId: string, diff --git a/frontend/packages/app/components/first-publish-dialog.tsx b/frontend/packages/app/components/first-publish-dialog.tsx index 51b0e1871..ff68e2253 100644 --- a/frontend/packages/app/components/first-publish-dialog.tsx +++ b/frontend/packages/app/components/first-publish-dialog.tsx @@ -11,7 +11,7 @@ import { import {useAppContext} from '../app-context' import {useGatewayUrl} from '../models/gateway-settings' import {useIsHMUrlReady} from '../models/networking' -import {PublicationRoute} from '../utils/navigation' +import {PublicationRoute} from '../utils/routes' export function FirstPublishDialog({ input, diff --git a/frontend/packages/app/components/pin-entity.tsx b/frontend/packages/app/components/pin-entity.tsx index bd5184e82..04f05b921 100644 --- a/frontend/packages/app/components/pin-entity.tsx +++ b/frontend/packages/app/components/pin-entity.tsx @@ -2,7 +2,7 @@ import {useHover} from '@mintter/shared' import {Button, ButtonProps, Tooltip} from '@mintter/ui' import {Pin, PinOff} from '@tamagui/lucide-icons' import {usePinAccount, usePinDocument, usePinGroup} from '../models/pins' -import {PublicationRoute} from '../utils/navigation' +import {PublicationRoute} from '../utils/routes' export function PinAccountButton({accountId}: {accountId: string}) { const {isPinned, togglePin} = usePinAccount(accountId) diff --git a/frontend/packages/app/components/publication-list-item.tsx b/frontend/packages/app/components/publication-list-item.tsx index 076013706..f7d72a241 100644 --- a/frontend/packages/app/components/publication-list-item.tsx +++ b/frontend/packages/app/components/publication-list-item.tsx @@ -14,7 +14,7 @@ import { XStack, copyTextToClipboard, } from '@mintter/ui' -import {NavRoute} from '../utils/navigation' +import {NavRoute} from '../utils/routes' import {useNavigate} from '../utils/useNavigate' import {BaseAccountLinkAvatar} from './account-link-avatar' import {ListItem, TimeAccessory} from './list-item' diff --git a/frontend/packages/app/components/quick-switcher.tsx b/frontend/packages/app/components/quick-switcher.tsx index 4a7adb0f7..ad5648720 100644 --- a/frontend/packages/app/components/quick-switcher.tsx +++ b/frontend/packages/app/components/quick-switcher.tsx @@ -21,11 +21,11 @@ import {useGroups} from '../models/groups' import {importWebCapture} from '../models/web-importer' import {AppQueryClient} from '../query-client' import { - NavRoute, isHttpUrl, resolveHmIdToAppRoute, useHmIdToAppRouteResolver, } from '../utils/navigation' +import {NavRoute} from '../utils/routes' import {useListenAppEvent} from '../utils/window-events' import './quick-switcher.css' diff --git a/frontend/packages/app/components/sidebar.tsx b/frontend/packages/app/components/sidebar.tsx index 77b961780..8b091f66c 100644 --- a/frontend/packages/app/components/sidebar.tsx +++ b/frontend/packages/app/components/sidebar.tsx @@ -47,12 +47,9 @@ import { import {getAccountName} from '../pages/account-page' import {SidebarWidth, useSidebarContext} from '../src/sidebar-context' import {getAvatarUrl} from '../utils/account-url' -import { - NavRoute, - useHmIdToAppRouteResolver, - useNavRoute, -} from '../utils/navigation' +import {useHmIdToAppRouteResolver, useNavRoute} from '../utils/navigation' import {useOpenDraft} from '../utils/open-draft' +import {NavRoute} from '../utils/routes' import {useNavigate} from '../utils/useNavigate' import {useTriggerWindowEvent} from '../utils/window-events' import {Avatar} from './avatar' @@ -166,7 +163,7 @@ function FullAppSidebar() { active={route.key == 'documents'} data-testid="menu-item-global" onPress={() => { - navigate({key: 'documents'}) + navigate({key: 'documents', tab: 'trusted'}) }} title="Documents" bold diff --git a/frontend/packages/app/components/titlebar-common.tsx b/frontend/packages/app/components/titlebar-common.tsx index 6f0860cdf..cc832f93c 100644 --- a/frontend/packages/app/components/titlebar-common.tsx +++ b/frontend/packages/app/components/titlebar-common.tsx @@ -2,7 +2,6 @@ import {ContactsPrompt} from '@mintter/app/components/contacts-prompt' import {useMyAccount} from '@mintter/app/models/accounts' import {usePublicationVariant} from '@mintter/app/models/publication' import { - NavRoute, useNavRoute, useNavigationDispatch, useNavigationState, @@ -50,6 +49,7 @@ import { import {usePinAccount, usePinDocument, usePinGroup} from '../models/pins' import {SidebarWidth, useSidebarContext} from '../src/sidebar-context' import {useOpenDraft} from '../utils/open-draft' +import {NavRoute} from '../utils/routes' import {CloneGroupDialog} from './clone-group' import {useCopyGatewayReference} from './copy-gateway-reference' import {useAppDialog} from './dialog' diff --git a/frontend/packages/app/components/titlebar-title.tsx b/frontend/packages/app/components/titlebar-title.tsx index a1732afc5..9921d06b1 100644 --- a/frontend/packages/app/components/titlebar-title.tsx +++ b/frontend/packages/app/components/titlebar-title.tsx @@ -1,10 +1,6 @@ import {useDraftTitle} from '@mintter/app/models/documents' import {usePublicationVariant} from '@mintter/app/models/publication' -import { - DraftRoute, - PublicationRoute, - useNavRoute, -} from '@mintter/app/utils/navigation' +import {useNavRoute} from '@mintter/app/utils/navigation' import { ErrorIcon, FontSizeTokens, @@ -17,7 +13,13 @@ import {Book, Contact, FileText, Library} from '@tamagui/lucide-icons' import {useEffect} from 'react' import {useAccount} from '../models/accounts' import {useGroup} from '../models/groups' -import {AccountRoute, GroupRoute, NavRoute} from '../utils/navigation' +import { + AccountRoute, + DraftRoute, + GroupRoute, + NavRoute, + PublicationRoute, +} from '../utils/routes' import {getDocumentTitle} from './publication-list-item' export function TitleContent({size = '$4'}: {size?: FontSizeTokens}) { diff --git a/frontend/packages/app/components/variants.tsx b/frontend/packages/app/components/variants.tsx index 4d84d24cf..4acd16211 100644 --- a/frontend/packages/app/components/variants.tsx +++ b/frontend/packages/app/components/variants.tsx @@ -10,8 +10,6 @@ import {usePublicationVariant} from '@mintter/app/models/publication' import {usePopoverState} from '@mintter/app/use-popover-state' import { NavContextProvider, - NavRoute, - PublicationRoute, useNavRoute, useNavigation, } from '@mintter/app/utils/navigation' @@ -77,6 +75,7 @@ import {useGatewayUrl} from '../models/gateway-settings' import {useCurrentDocumentGroups} from '../models/groups' import {getAccountName} from '../pages/account-page' import {RenamePubDialog} from '../src/rename-publication-dialog' +import {NavRoute, PublicationRoute} from '../utils/routes' import CommitDraftButton from './commit-draft-button' import {useAppDialog} from './dialog' import DiscardDraftButton from './discard-draft-button' diff --git a/frontend/packages/app/components/version-changes-info.tsx b/frontend/packages/app/components/version-changes-info.tsx index 398df4579..6bf54747c 100644 --- a/frontend/packages/app/components/version-changes-info.tsx +++ b/frontend/packages/app/components/version-changes-info.tsx @@ -1,10 +1,11 @@ import {Timestamp} from '@bufbuild/protobuf' -import {NavRoute, useNavRoute} from '../utils/navigation' -import {useNavigate} from '../utils/useNavigate' -import {ButtonText, Tooltip, XStack} from '@mintter/ui' import {formattedDateLong, formattedDateMedium} from '@mintter/shared' -import {useChange} from '../models/changes' +import {ButtonText, Tooltip, XStack} from '@mintter/ui' import {useAccount} from '../models/accounts' +import {useChange} from '../models/changes' +import {useNavRoute} from '../utils/navigation' +import {NavRoute} from '../utils/routes' +import {useNavigate} from '../utils/useNavigate' import {AccountLinkAvatar} from './account-link-avatar' export function VersionChangesInfo({version}: {version: string}) { diff --git a/frontend/packages/app/components/windows-linux-titlebar.tsx b/frontend/packages/app/components/windows-linux-titlebar.tsx index 90f71c352..433b7eefd 100644 --- a/frontend/packages/app/components/windows-linux-titlebar.tsx +++ b/frontend/packages/app/components/windows-linux-titlebar.tsx @@ -26,6 +26,7 @@ import {Contact, FileText, Library} from '@tamagui/lucide-icons' import {useMemo} from 'react' import {useNavRoute, useNavigationDispatch} from '../utils/navigation' import {useOpenDraft} from '../utils/open-draft' +import {defaultRoute} from '../utils/routes' import {useTriggerWindowEvent} from '../utils/window-events' import {WindowsLinuxWindowControls} from './window-controls' @@ -150,7 +151,7 @@ export function SystemMenu() { id: 'newwindow', title: 'New Window', accelerator: 'Ctrl+Shift+N', - onSelect: () => spawn({key: 'documents'}), + onSelect: () => spawn(defaultRoute), icon: AddSquare, }, { diff --git a/frontend/packages/app/models/documents.ts b/frontend/packages/app/models/documents.ts index b4f033082..323ccae76 100644 --- a/frontend/packages/app/models/documents.ts +++ b/frontend/packages/app/models/documents.ts @@ -42,8 +42,9 @@ import _ from 'lodash' import {useEffect, useMemo, useRef} from 'react' import {ContextFrom, fromPromise} from 'xstate' import {useGRPCClient} from '../app-context' -import {NavRoute, useNavRoute} from '../utils/navigation' +import {useNavRoute} from '../utils/navigation' import {pathNameify} from '../utils/path' +import {NavRoute} from '../utils/routes' import {useNavigate} from '../utils/useNavigate' import {useAllAccounts} from './accounts' import {DraftStatusContext, draftMachine} from './draft-machine' diff --git a/frontend/packages/app/models/pins.ts b/frontend/packages/app/models/pins.ts index fbdf98297..1b309baa2 100644 --- a/frontend/packages/app/models/pins.ts +++ b/frontend/packages/app/models/pins.ts @@ -8,7 +8,7 @@ import { } from '@mintter/shared' import {useMemo} from 'react' import {useQueryInvalidator} from '../app-context' -import {PublicationRoute} from '../utils/navigation' +import {PublicationRoute} from '../utils/routes' import {useGroupsContent} from './groups' export function usePinAccount(accountId: string) { diff --git a/frontend/packages/app/pages/main.tsx b/frontend/packages/app/pages/main.tsx index 78d5ed7d6..8ce402232 100644 --- a/frontend/packages/app/pages/main.tsx +++ b/frontend/packages/app/pages/main.tsx @@ -2,15 +2,16 @@ import {useListen} from '@mintter/app/app-context' import {AppErrorPage} from '@mintter/app//components/app-error' import {TitleBar} from '@mintter/app/components/titlebar' -import {getRouteKey, NavRoute, useNavRoute} from '@mintter/app/utils/navigation' +import {getRouteKey, useNavRoute} from '@mintter/app/utils/navigation' import {useNavigate} from '@mintter/app/utils/useNavigate' import {Spinner, YStack} from '@mintter/ui' -import {lazy, ReactElement, useMemo} from 'react' +import {ReactElement, lazy, useMemo} from 'react' import {ErrorBoundary} from 'react-error-boundary' import {QuickSwitcher} from '../components/quick-switcher' import {AppSidebar} from '../components/sidebar' import {DraftStatusContext} from '../models/draft-machine' import {SidebarContextProvider} from '../src/sidebar-context' +import {NavRoute} from '../utils/routes' import {getWindowType} from '../utils/window-types' import {NotFoundPage} from './base' import {DocumentPlaceholder} from './document-placeholder' diff --git a/frontend/packages/app/utils/navigation-container.tsx b/frontend/packages/app/utils/navigation-container.tsx index 59fb3f993..c4e43cc32 100644 --- a/frontend/packages/app/utils/navigation-container.tsx +++ b/frontend/packages/app/utils/navigation-container.tsx @@ -3,22 +3,20 @@ import {ReactNode, useEffect, useMemo} from 'react' import {useIPC} from '../app-context' import {useConfirmConnection} from '../components/contacts-prompt' import { - DocumentsRoute, NavAction, NavContextProvider, NavState, navStateReducer, setAppNavDispatch, } from './navigation' +import {defaultRoute} from './routes' import {AppWindowEvent} from './window-events' -const homeRoute: DocumentsRoute = {key: 'documents'} - export function NavigationContainer({ children, initialNav = { sidebarLocked: false, - routes: [homeRoute], + routes: [defaultRoute], routeIndex: 0, lastAction: 'replace', }, diff --git a/frontend/packages/app/utils/navigation.tsx b/frontend/packages/app/utils/navigation.tsx index b1e7c7d0e..ea4d72fd5 100644 --- a/frontend/packages/app/utils/navigation.tsx +++ b/frontend/packages/app/utils/navigation.tsx @@ -1,10 +1,4 @@ -import { - GRPCClient, - GroupVariant, - PublicationVariant, - StateStream, - unpackDocId, -} from '@mintter/shared' +import {GRPCClient, StateStream, unpackDocId} from '@mintter/shared' import { UnpackedHypermediaId, createHmId, @@ -14,71 +8,10 @@ import {useStream, useStreamSelector} from '@mintter/ui' import {Buffer} from 'buffer' import {createContext, useContext} from 'react' import {useGRPCClient} from '../app-context' +import {NavRoute, defaultRoute} from './routes' global.Buffer = global.Buffer || Buffer -export type DocumentsRoute = { - key: 'documents' - tab?: null | 'all' | 'trusted' | 'drafts' -} -export type ContactsRoute = {key: 'contacts'} -export type AccountRoute = {key: 'account'; accountId: string} - -export type EntityVersionsAccessory = {key: 'versions'} -export type PublicationCitationsAccessory = {key: 'citations'} -export type PublicationCommentsAccessory = {key: 'comments'} - -export type CommentRoute = { - key: 'comment' - commentId?: string - showThread?: boolean -} -export type CommentDraftRoute = { - key: 'comment-draft' - commentId?: string - showThread?: boolean -} - -export type PublicationRoute = { - key: 'publication' - documentId: string - versionId?: string - variants?: PublicationVariant[] - blockId?: string - accessory?: - | null - | EntityVersionsAccessory - | PublicationCitationsAccessory - | PublicationCommentsAccessory - showFirstPublicationMessage?: boolean - immediatelyPromptPush?: boolean -} -export type DraftRoute = { - key: 'draft' - draftId?: string - variant?: GroupVariant | null - contextRoute?: NavRoute -} -export type SettingsRoute = {key: 'settings'} -export type GroupsRoute = {key: 'groups'} -export type GroupRoute = { - key: 'group' - groupId: string - version?: string - accessory?: null | EntityVersionsAccessory -} -export type NavRoute = - | ContactsRoute - | AccountRoute - | SettingsRoute - | GroupsRoute - | GroupRoute - | PublicationRoute - | DraftRoute - | DocumentsRoute - | CommentRoute - | CommentDraftRoute - export type PushAction = {type: 'push'; route: NavRoute} export type ReplaceAction = {type: 'replace'; route: NavRoute} export type BackplaceAction = {type: 'backplace'; route: NavRoute} @@ -218,7 +151,7 @@ export function useNavRoute() { throw new Error('useNavRoute must be used within a NavigationProvider') return useStreamSelector( nav.state, - (state) => state.routes[state.routeIndex] || {key: 'documents'}, + (state) => state.routes[state.routeIndex] || defaultRoute, ) } diff --git a/frontend/packages/app/utils/open-draft.ts b/frontend/packages/app/utils/open-draft.ts index f3310d584..465c57c84 100644 --- a/frontend/packages/app/utils/open-draft.ts +++ b/frontend/packages/app/utils/open-draft.ts @@ -3,7 +3,8 @@ import {queryKeys} from '@mintter/app/models/query-keys' import {DocumentChange, GRPCClient, GroupVariant} from '@mintter/shared' import {useGRPCClient} from '../app-context' import appError from '../errors' -import {DraftRoute, NavMode, useNavRoute} from './navigation' +import {NavMode, useNavRoute} from './navigation' +import {DraftRoute} from './routes' import {useNavigate} from './useNavigate' async function createDraft( diff --git a/frontend/packages/app/utils/route-encoding.ts b/frontend/packages/app/utils/route-encoding.ts index a61c87445..3397c17c0 100644 --- a/frontend/packages/app/utils/route-encoding.ts +++ b/frontend/packages/app/utils/route-encoding.ts @@ -1,5 +1,5 @@ -import type {NavRoute} from './navigation' import {Buffer} from 'buffer' +import {NavRoute} from './routes' export function encodeRouteToPath(route: NavRoute): string { return `/${Buffer.from(JSON.stringify(route)) diff --git a/frontend/packages/app/utils/routes.tsx b/frontend/packages/app/utils/routes.tsx new file mode 100644 index 000000000..0c9c67ced --- /dev/null +++ b/frontend/packages/app/utils/routes.tsx @@ -0,0 +1,114 @@ +import {groupVariantSchema, publicationVariantSchema} from '@mintter/shared' +import {z} from 'zod' + +export const defaultRoute: NavRoute = {key: 'documents', tab: 'trusted'} + +export const documentsRouteSchema = z.object({ + key: z.literal('documents'), + tab: z.union([z.literal('all'), z.literal('trusted'), z.literal('drafts')]), +}) +export type DocumentsRoute = z.infer + +export const contactsRouteSchema = z.object({key: z.literal('contacts')}) +export type ContactsRoute = z.infer + +export const accountRouteSchema = z.object({ + key: z.literal('account'), + accountId: z.string(), +}) +export type AccountRoute = z.infer + +export const entityVersionsAccessorySchema = z.object({ + key: z.literal('versions'), +}) +export type EntityVersionsAccessory = z.infer< + typeof entityVersionsAccessorySchema +> + +export const publicationCitationsAccessorySchema = z.object({ + key: z.literal('citations'), +}) +export type PublicationCitationsAccessory = z.infer< + typeof publicationCitationsAccessorySchema +> + +export const publicationCommentsAccessorySchema = z.object({ + key: z.literal('comments'), +}) +export type PublicationCommentsAccessory = z.infer< + typeof publicationCommentsAccessorySchema +> + +export const commentRouteSchema = z.object({ + key: z.literal('comment'), + commentId: z.string().optional(), + showThread: z.boolean().optional(), +}) +export type CommentRoute = z.infer + +export const commentDraftRouteSchema = z.object({ + key: z.literal('comment-draft'), + commentId: z.string().optional(), + showThread: z.boolean().optional(), +}) +export type CommentDraftRoute = z.infer + +export const publicationRouteSchema = z.object({ + key: z.literal('publication'), + documentId: z.string(), + versionId: z.string().optional(), + variants: z.array(publicationVariantSchema).optional(), + blockId: z.string().optional(), + accessory: z + .discriminatedUnion('key', [ + entityVersionsAccessorySchema, + publicationCitationsAccessorySchema, + publicationCommentsAccessorySchema, + ]) + .nullable() + .optional(), + showFirstPublicationMessage: z.boolean().optional(), + immediatelyPromptPush: z.boolean().optional(), +}) +export type PublicationRoute = z.infer + +export const draftRouteSchema = z.object({ + key: z.literal('draft'), + draftId: z.string().optional(), + variant: groupVariantSchema.nullable(), + contextRoute: z + .discriminatedUnion('key', [documentsRouteSchema, publicationRouteSchema]) + .optional(), +}) +export type DraftRoute = z.infer + +export const settingsRouteSchema = z.object({key: z.literal('settings')}) +export type SettingsRoute = z.infer + +export const groupsRouteSchema = z.object({key: z.literal('groups')}) +export type GroupsRoute = z.infer + +export const groupRouteSchema = z.object({ + key: z.literal('group'), + groupId: z.string(), + version: z.string().optional(), + accessory: z + .discriminatedUnion('key', [entityVersionsAccessorySchema]) + .nullable() + .optional(), +}) +export type GroupRoute = z.infer + +export const navRouteSchema = z.discriminatedUnion('key', [ + contactsRouteSchema, + accountRouteSchema, + settingsRouteSchema, + groupsRouteSchema, + groupRouteSchema, + publicationRouteSchema, + draftRouteSchema, + documentsRouteSchema, + commentRouteSchema, + commentDraftRouteSchema, +]) +export type NavRoute = z.infer diff --git a/frontend/packages/app/utils/useNavigate.tsx b/frontend/packages/app/utils/useNavigate.tsx index 96dcd812a..630981928 100644 --- a/frontend/packages/app/utils/useNavigate.tsx +++ b/frontend/packages/app/utils/useNavigate.tsx @@ -1,7 +1,8 @@ import {startTransition, useCallback} from 'react' import {useIPC} from '../app-context' -import {NavMode, NavRoute, useNavigationDispatch} from './navigation' +import {NavMode, useNavigationDispatch} from './navigation' import {encodeRouteToPath} from './route-encoding' +import {NavRoute} from './routes' import {getRouteWindowType, getWindowType} from './window-types' export function useNavigate(requestedMode: NavMode = 'push') { diff --git a/frontend/packages/app/utils/window-types.ts b/frontend/packages/app/utils/window-types.ts index ae9e788da..f009e9dd0 100644 --- a/frontend/packages/app/utils/window-types.ts +++ b/frontend/packages/app/utils/window-types.ts @@ -1,4 +1,4 @@ -import {NavRoute} from './navigation' +import {NavRoute} from './routes' export type WindowTypeInfo = { key: 'settings' | 'comment' | 'main'