You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

176 lines
8.6 KiB

import { match } from 'path-to-regexp'
import {
isValidElement,
lazy,
Suspense,
type ComponentType,
type LazyExoticComponent,
type ReactElement
} from 'react'
/** Lazy + Suspense so importing `routes` does not sync-pull pages that depend on PageManager (breaks Vite HMR cycles). */
const FollowingListPageLazy = lazy(() => import('./pages/secondary/FollowingListPage'))
const FollowersListPageLazy = lazy(() => import('./pages/secondary/FollowersListPage'))
const GeneralSettingsPageLazy = lazy(() => import('./pages/secondary/GeneralSettingsPage'))
const MuteListPageLazy = lazy(() => import('./pages/secondary/MuteListPage'))
const BookmarkListPageLazy = lazy(() => import('./pages/secondary/BookmarkListPage'))
const NotificationThreadFollowListPageLazy = lazy(() =>
import('./pages/secondary/NotificationThreadWatchListPage').then((m) => ({
default: m.NotificationThreadFollowListPage
}))
)
const NotificationThreadMuteListPageLazy = lazy(() =>
import('./pages/secondary/NotificationThreadWatchListPage').then((m) => ({
default: m.NotificationThreadMuteListPage
}))
)
const PinListPageLazy = lazy(() => import('./pages/secondary/PinListPage'))
const ProfileBadgesListPageLazy = lazy(() => import('./pages/secondary/ProfileBadgesListPage'))
const InterestListPageLazy = lazy(() => import('./pages/secondary/InterestListPage'))
const NoteListPageLazy = lazy(() => import('./pages/secondary/NoteListPage'))
const NotePageLazy = lazy(() => import('./pages/secondary/NotePage'))
const OthersRelaySettingsPageLazy = lazy(() => import('./pages/secondary/OthersRelaySettingsPage'))
const PostSettingsPageLazy = lazy(() => import('./pages/secondary/PostSettingsPage'))
const ProfileEditorPageLazy = lazy(() => import('./pages/secondary/ProfileEditorPage'))
const ProfileListPageLazy = lazy(() => import('./pages/secondary/ProfileListPage'))
const ProfilePageLazy = lazy(() => import('./pages/secondary/ProfilePage'))
const RelayPageLazy = lazy(() => import('./pages/secondary/RelayPage'))
const RelayReviewsPageLazy = lazy(() => import('./pages/secondary/RelayReviewsPage'))
const RelaySettingsPageLazy = lazy(() => import('./pages/secondary/RelaySettingsPage'))
const CacheSettingsPageLazy = lazy(() => import('./pages/secondary/CacheSettingsPage'))
const RssFeedSettingsPageLazy = lazy(() => import('./pages/secondary/RssFeedSettingsPage'))
const FollowSetsSettingsPageLazy = lazy(() => import('./pages/secondary/FollowSetsSettingsPage'))
const PersonalListsSettingsPageLazy = lazy(() => import('./pages/secondary/PersonalListsSettingsPage'))
const UserEmojiListPageLazy = lazy(() => import('./pages/secondary/UserEmojiListPage'))
const EmojiSetsSettingsPageLazy = lazy(() => import('./pages/secondary/EmojiSetsSettingsPage'))
const SearchPageLazy = lazy(() => import('./pages/secondary/SearchPage'))
const SettingsPageLazy = lazy(() => import('./pages/secondary/SettingsPage'))
const WalletPageLazy = lazy(() => import('./pages/secondary/WalletPage'))
const FollowPacksRedirectLazy = lazy(() => import('./pages/secondary/FollowPacksRedirect'))
const RssArticlePageLazy = lazy(() => import('./pages/secondary/RssArticlePage'))
const CalendarDayEventsPageLazy = lazy(() => import('./pages/secondary/CalendarDayEventsPage'))
const routeSuspenseFallback = null
function SR(C: LazyExoticComponent<ComponentType<any>>): ReactElement {
return (
<Suspense fallback={routeSuspenseFallback}>
<C />
</Suspense>
)
}
const notePageElement = SR(NotePageLazy)
const noteListPageElement = SR(NoteListPageLazy)
const rssArticlePageElement = SR(RssArticlePageLazy)
/** Primary segments used in contextual `/…/notes/:id` and `/…/rss-item/:key` routes. */
const CONTEXTUAL_ROUTE_PREFIXES =
'discussions|search|profile|home|feed|spells|explore|rss|calendar'
const contextualNotePathRe = new RegExp(
`^/(${CONTEXTUAL_ROUTE_PREFIXES})/notes/([^/?#]+)$`
)
const standardNotePathRe = /^\/notes\/([^/?#]+)$/
const contextualRssItemPathRe = new RegExp(
`^/(${CONTEXTUAL_ROUTE_PREFIXES})/rss-item/([^/?#]+)$`
)
const standardRssItemPathRe = /^\/rss-item\/([^/?#]+)$/
const ROUTES = [
{ path: '/notes', element: noteListPageElement },
{ path: '/notes/:id', element: notePageElement },
{ path: '/discussions/notes/:id', element: notePageElement },
{ path: '/search/notes/:id', element: notePageElement },
{ path: '/profile/notes/:id', element: notePageElement },
{ path: '/explore/notes/:id', element: notePageElement },
{ path: '/home/notes/:id', element: notePageElement },
{ path: '/feed/notes/:id', element: notePageElement },
{ path: '/spells/notes/:id', element: notePageElement },
{ path: '/rss/notes/:id', element: notePageElement },
{ path: '/calendar/notes/:id', element: notePageElement },
{ path: '/calendar/day/:ymd', element: SR(CalendarDayEventsPageLazy) },
{ path: '/rss-item/:articleKey', element: rssArticlePageElement },
{ path: '/rss/rss-item/:articleKey', element: rssArticlePageElement },
{ path: '/feed/rss-item/:articleKey', element: rssArticlePageElement },
{ path: '/search/rss-item/:articleKey', element: rssArticlePageElement },
{ path: '/profile/rss-item/:articleKey', element: rssArticlePageElement },
{ path: '/spells/rss-item/:articleKey', element: rssArticlePageElement },
{ path: '/explore/rss-item/:articleKey', element: rssArticlePageElement },
{ path: '/home/rss-item/:articleKey', element: rssArticlePageElement },
{ path: '/users', element: SR(ProfileListPageLazy) },
{ path: '/users/:id/following', element: SR(FollowingListPageLazy) },
{ path: '/users/:id/followers', element: SR(FollowersListPageLazy) },
{ path: '/users/:id/relays', element: SR(OthersRelaySettingsPageLazy) },
{ path: '/users/:id', element: SR(ProfilePageLazy) },
{ path: '/relays/:url/reviews', element: SR(RelayReviewsPageLazy) },
{ path: '/relays/:url', element: SR(RelayPageLazy) },
{ path: '/home/relays/:url', element: SR(RelayPageLazy) },
{ path: '/explore/relays/:url', element: SR(RelayPageLazy) },
{ path: '/search', element: SR(SearchPageLazy) },
{ path: '/settings', element: SR(SettingsPageLazy) },
{ path: '/settings/relays', element: SR(RelaySettingsPageLazy) },
{ path: '/settings/cache', element: SR(CacheSettingsPageLazy) },
{ path: '/settings/wallet', element: SR(WalletPageLazy) },
{ path: '/settings/posts', element: SR(PostSettingsPageLazy) },
{ path: '/settings/general', element: SR(GeneralSettingsPageLazy) },
{ path: '/settings/rss-feeds', element: SR(RssFeedSettingsPageLazy) },
{ path: '/settings/follow-sets', element: SR(FollowSetsSettingsPageLazy) },
{ path: '/settings/emoji-sets', element: SR(EmojiSetsSettingsPageLazy) },
{ path: '/settings/personal-lists', element: SR(PersonalListsSettingsPageLazy) },
{ path: '/profile-editor', element: SR(ProfileEditorPageLazy) },
{ path: '/mutes', element: SR(MuteListPageLazy) },
{ path: '/bookmarks', element: SR(BookmarkListPageLazy) },
{ path: '/notification-thread-follow', element: SR(NotificationThreadFollowListPageLazy) },
{ path: '/notification-thread-mute', element: SR(NotificationThreadMuteListPageLazy) },
{ path: '/pins', element: SR(PinListPageLazy) },
{ path: '/profile-badges', element: SR(ProfileBadgesListPageLazy) },
{ path: '/interests', element: SR(InterestListPageLazy) },
{ path: '/user-emojis', element: SR(UserEmojiListPageLazy) },
{ path: '/follow-packs', element: SR(FollowPacksRedirectLazy) }
]
export const routes = ROUTES.map(({ path, element }) => ({
path,
element: isValidElement(element) ? element : null,
matcher: match(path)
}))
export type TMatchedAppRoute = {
element: ReactElement | null
params: Record<string, string>
}
/**
* Resolve secondary-panel routes without scanning the full table for common deep links
* (contextual note / RSS article URLs are tried first; everything else falls back to matchers).
*/
export function matchAppRoute(pathname: string): TMatchedAppRoute | null {
const path = pathname.split('?')[0].split('#')[0]
const ctxNote = contextualNotePathRe.exec(path)
if (ctxNote) {
return { element: notePageElement, params: { id: ctxNote[2]! } }
}
const stdNote = standardNotePathRe.exec(path)
if (stdNote) {
return { element: notePageElement, params: { id: stdNote[1]! } }
}
const ctxRss = contextualRssItemPathRe.exec(path)
if (ctxRss) {
return { element: rssArticlePageElement, params: { articleKey: ctxRss[2]! } }
}
const stdRss = standardRssItemPathRe.exec(path)
if (stdRss) {
return { element: rssArticlePageElement, params: { articleKey: stdRss[1]! } }
}
for (const { matcher, element } of routes) {
const m = matcher(path)
if (m) {
return { element, params: (m as { params: Record<string, string> }).params }
}
}
return null
}