// Routes: Portfolio -> one App (tabs) plus the workspace screens. Pure, so tests can check the deep links. // An app is keyed by its project id, or (store-only apps, no folder) by its store target ("store:..."). import { createContext, useContext } from 'react'; import type { Key } from './i18n'; import type { IconName } from './ui/icons'; export const APP_TABS = ['releases', 'catalog', 'prices', 'listings', 'art', 'health', 'reviews', 'testflight', 'growth', 'appinfo', 'checks'] as const; export type AppTab = (typeof APP_TABS)[number]; /** Tabs of a store-only app: store reads only (catalog, prices, listings, art, app info and checks need the app folder; * Growth shows the Product Page Optimization tests only). */ export const STORE_TABS: readonly AppTab[] = ['releases', 'health', 'reviews', 'testflight', 'growth']; export const tabsFor = (hasFolder: boolean): readonly AppTab[] => (hasFolder ? APP_TABS : STORE_TABS); export const TAB_KEY: Record = { releases: 'tab.releases', catalog: 'tab.catalog', prices: 'tab.prices', listings: 'tab.listings', art: 'tab.art', health: 'tab.health', reviews: 'tab.reviews', testflight: 'tab.testflight', growth: 'tab.growth', appinfo: 'tab.appinfo', checks: 'tab.checks', }; export const TAB_ICON: Record = { releases: 'rocket', catalog: 'tag', prices: 'coins', listings: 'text', art: 'grid', health: 'activity', reviews: 'message', testflight: 'plane', growth: 'chart', appinfo: 'clipboard', checks: 'shield', }; export const WORKSPACE = ['apps', 'accounts', 'ignored', 'dependencies', 'settings'] as const; export type WorkspaceScreen = (typeof WORKSPACE)[number]; export type Route = { screen: 'portfolio' } | { screen: 'app'; appId: string; tab: AppTab } | { screen: WorkspaceScreen; section?: string }; /** storebound:// deep links and the dev-only start switch ("app:health" = first app, Health tab; * "app:health:2" = second app; "settings:refresh" = a Settings section). `apps` are the app keys in list order. */ export function parseStart(v: string, apps: string[]): Route | null { const [screen, tab, n] = v.split(':'); const app = apps[Math.max(0, (Number(n) || 1) - 1)]; if (screen === 'app' && app) return { screen: 'app', appId: app, tab: APP_TABS.includes(tab as AppTab) ? tab as AppTab : 'releases' }; if (screen === 'portfolio') return { screen: 'portfolio' }; if (!(WORKSPACE as readonly string[]).includes(screen ?? '')) return null; return tab ? { screen: screen as WorkspaceScreen, section: tab } : { screen: screen as WorkspaceScreen }; } export const sameRoute = (a: Route, b: Route): boolean => JSON.stringify(a) === JSON.stringify(b); export interface Nav { route: Route; go: (r: Route) => void; openPalette: () => void; openWelcome: () => void } export const NavCtx = createContext