|
|
|
@ -56,7 +56,7 @@ import { |
|
|
|
import { normalizeUrl } from './lib/url' |
|
|
|
import { normalizeUrl } from './lib/url' |
|
|
|
import modalManager from './services/modal-manager.service' |
|
|
|
import modalManager from './services/modal-manager.service' |
|
|
|
import { decodeRssArticlePathSegment, encodeRssArticlePathSegment } from '@/lib/rss-article' |
|
|
|
import { decodeRssArticlePathSegment, encodeRssArticlePathSegment } from '@/lib/rss-article' |
|
|
|
import { routes } from './routes' |
|
|
|
import { matchAppRoute } from './routes' |
|
|
|
import { useScreenSize, useScreenSizeOptional } from './providers/ScreenSizeProvider' |
|
|
|
import { useScreenSize, useScreenSizeOptional } from './providers/ScreenSizeProvider' |
|
|
|
import { NoteDrawerContext, useNoteDrawer, useNoteDrawerOptional } from '@/contexts/note-drawer-context' |
|
|
|
import { NoteDrawerContext, useNoteDrawer, useNoteDrawerOptional } from '@/contexts/note-drawer-context' |
|
|
|
import { |
|
|
|
import { |
|
|
|
@ -67,6 +67,9 @@ import { |
|
|
|
} from '@/contexts/primary-note-view-context' |
|
|
|
} from '@/contexts/primary-note-view-context' |
|
|
|
import { SecondaryPageContext, useSecondaryPage, useSecondaryPageOptional } from '@/contexts/secondary-page-context' |
|
|
|
import { SecondaryPageContext, useSecondaryPage, useSecondaryPageOptional } from '@/contexts/secondary-page-context' |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** Survives React StrictMode remount so initial URL → secondary stack is not built twice. */ |
|
|
|
|
|
|
|
let historyLocationSeedApplied = false |
|
|
|
|
|
|
|
|
|
|
|
/** Lazy-loaded so PageManager does not synchronously import SpellsPage (avoids HMR cycle: SpellsPage → PrimaryPageLayout → PageManager → SpellsPage). */ |
|
|
|
/** Lazy-loaded so PageManager does not synchronously import SpellsPage (avoids HMR cycle: SpellsPage → PrimaryPageLayout → PageManager → SpellsPage). */ |
|
|
|
const SpellsPageLazy = lazy(() => import('./pages/primary/SpellsPage')) |
|
|
|
const SpellsPageLazy = lazy(() => import('./pages/primary/SpellsPage')) |
|
|
|
/** Lazy NoteList pages break: PageManager → … → NoteList → NoteCard → useSmartNoteNavigation → PageManager */ |
|
|
|
/** Lazy NoteList pages break: PageManager → … → NoteList → NoteCard → useSmartNoteNavigation → PageManager */ |
|
|
|
@ -1235,8 +1238,6 @@ export function PageManager({ maxStackSize = 5 }: { maxStackSize?: number }) { |
|
|
|
// Don't clear noteId here — scheduled in the drawer-close effect after the sheet animation.
|
|
|
|
// Don't clear noteId here — scheduled in the drawer-close effect after the sheet animation.
|
|
|
|
}, [drawerOpen]) |
|
|
|
}, [drawerOpen]) |
|
|
|
const ignorePopStateRef = useRef(false) |
|
|
|
const ignorePopStateRef = useRef(false) |
|
|
|
/** Avoid duplicating history entries when drawer/mode deps re-run the PageManager effect. */ |
|
|
|
|
|
|
|
const historySeedDoneRef = useRef(false) |
|
|
|
|
|
|
|
/** When set before closing the note drawer, replaceState uses this URL instead of buildPrimaryPageUrl (popstate edge cases). */ |
|
|
|
/** When set before closing the note drawer, replaceState uses this URL instead of buildPrimaryPageUrl (popstate edge cases). */ |
|
|
|
const pendingDrawerCloseUrlRef = useRef<string | null>(null) |
|
|
|
const pendingDrawerCloseUrlRef = useRef<string | null>(null) |
|
|
|
|
|
|
|
|
|
|
|
@ -1285,8 +1286,8 @@ export function PageManager({ maxStackSize = 5 }: { maxStackSize?: number }) { |
|
|
|
}, [primaryNoteView, drawerOpen]) |
|
|
|
}, [primaryNoteView, drawerOpen]) |
|
|
|
|
|
|
|
|
|
|
|
useEffect(() => { |
|
|
|
useEffect(() => { |
|
|
|
if (!historySeedDoneRef.current) { |
|
|
|
if (historyLocationSeedApplied) return |
|
|
|
historySeedDoneRef.current = true |
|
|
|
historyLocationSeedApplied = true |
|
|
|
if (['/npub1', '/nprofile1'].some((prefix) => window.location.pathname.startsWith(prefix))) { |
|
|
|
if (['/npub1', '/nprofile1'].some((prefix) => window.location.pathname.startsWith(prefix))) { |
|
|
|
window.history.replaceState( |
|
|
|
window.history.replaceState( |
|
|
|
null, |
|
|
|
null, |
|
|
|
@ -1564,7 +1565,6 @@ export function PageManager({ maxStackSize = 5 }: { maxStackSize?: number }) { |
|
|
|
// which is handled elsewhere
|
|
|
|
// which is handled elsewhere
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const onPopState = (e: PopStateEvent) => { |
|
|
|
const onPopState = (e: PopStateEvent) => { |
|
|
|
if (ignorePopStateRef.current) { |
|
|
|
if (ignorePopStateRef.current) { |
|
|
|
@ -2579,49 +2579,34 @@ function syncSecondaryStackWhenPopStateStateIsNull(pre: TStackItem[], locUrl: st |
|
|
|
|
|
|
|
|
|
|
|
function findAndCreateComponent(url: string, index: number) { |
|
|
|
function findAndCreateComponent(url: string, index: number) { |
|
|
|
const path = url.split('?')[0].split('#')[0] |
|
|
|
const path = url.split('?')[0].split('#')[0] |
|
|
|
logger.component('PageManager', 'findAndCreateComponent called', { url, path, routes: routes.length }) |
|
|
|
const matched = matchAppRoute(path) |
|
|
|
|
|
|
|
if (!matched?.element) { |
|
|
|
for (const { matcher, element } of routes) { |
|
|
|
logger.component('PageManager', 'No matching route found', { path, url }) |
|
|
|
const match = matcher(path) |
|
|
|
return {} |
|
|
|
logger.component('PageManager', 'Trying route matcher', { path, matchResult: !!match, matchParams: match ? (match as any).params : null }) |
|
|
|
} |
|
|
|
if (!match) continue |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (!element) { |
|
|
|
const ref = createRef<TPageRef>() |
|
|
|
logger.component('PageManager', 'No element for this route', { path }) |
|
|
|
|
|
|
|
return {} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
const ref = createRef<TPageRef>() |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Decode URL parameters for relay pages
|
|
|
|
// Decode URL parameters for relay pages
|
|
|
|
const params = { ...(match as any).params } |
|
|
|
const params = { ...matched.params } |
|
|
|
if (params.url && typeof params.url === 'string') { |
|
|
|
if (params.url && typeof params.url === 'string') { |
|
|
|
params.url = decodeURIComponent(params.url) |
|
|
|
params.url = decodeURIComponent(params.url) |
|
|
|
logger.component('PageManager', 'Decoded URL parameter', { url: params.url }) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const noteRouteId = typeof params.id === 'string' ? params.id : undefined |
|
|
|
const noteRouteId = typeof params.id === 'string' ? params.id : undefined |
|
|
|
const initialEvent = noteRouteId ? navigationEventStore.peekEvent(noteRouteId) : undefined |
|
|
|
const initialEvent = noteRouteId ? navigationEventStore.peekEvent(noteRouteId) : undefined |
|
|
|
logger.component('PageManager', 'Creating component with params', { |
|
|
|
try { |
|
|
|
params, |
|
|
|
const component = cloneSecondaryRouteElement(matched.element, { |
|
|
|
|
|
|
|
...params, |
|
|
|
index, |
|
|
|
index, |
|
|
|
hasInitialEvent: !!initialEvent |
|
|
|
ref, |
|
|
|
|
|
|
|
...(initialEvent ? { initialEvent } : {}) |
|
|
|
}) |
|
|
|
}) |
|
|
|
try { |
|
|
|
return { component, ref } |
|
|
|
const component = cloneSecondaryRouteElement(element, { |
|
|
|
} catch (error) { |
|
|
|
...params, |
|
|
|
logger.error('PageManager', 'Error creating component', { error, params }) |
|
|
|
index, |
|
|
|
return {} |
|
|
|
ref, |
|
|
|
|
|
|
|
...(initialEvent ? { initialEvent } : {}) |
|
|
|
|
|
|
|
}) |
|
|
|
|
|
|
|
logger.component('PageManager', 'Component created successfully', { hasComponent: !!component }) |
|
|
|
|
|
|
|
return { component, ref } |
|
|
|
|
|
|
|
} catch (error) { |
|
|
|
|
|
|
|
logger.error('PageManager', 'Error creating component', { error, params }) |
|
|
|
|
|
|
|
return {} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
logger.component('PageManager', 'No matching route found', { path, url }) |
|
|
|
|
|
|
|
return {} |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
function pushNewPageToStack( |
|
|
|
function pushNewPageToStack( |
|
|
|
|