diff --git a/src/PageManager.tsx b/src/PageManager.tsx index 99604756..c77916ec 100644 --- a/src/PageManager.tsx +++ b/src/PageManager.tsx @@ -1,6 +1,5 @@ import Sidebar from '@/components/Sidebar' import { cn } from '@/lib/utils' -import NoteListPage from '@/pages/primary/NoteListPage' import { CurrentRelaysProvider } from '@/providers/CurrentRelaysProvider' import { TPageRef } from '@/types' import { @@ -19,23 +18,14 @@ import BottomNavigationBar from './components/BottomNavigationBar' import CreateWalletGuideToast from './components/CreateWalletGuideToast' import TooManyRelaysAlertDialog from './components/TooManyRelaysAlertDialog' import { normalizeUrl } from './lib/url' -import BookmarkPage from './pages/primary/BookmarkPage' -import ExplorePage from './pages/primary/ExplorePage' -import MePage from './pages/primary/MePage' -import NotificationListPage from './pages/primary/NotificationListPage' -import ProfilePage from './pages/primary/ProfilePage' -import RelayPage from './pages/primary/RelayPage' -import SearchPage from './pages/primary/SearchPage' -import SettingsPage from './pages/primary/SettingsPage' import { NotificationProvider } from './providers/NotificationProvider' import { useScreenSize } from './providers/ScreenSizeProvider' import { useTheme } from './providers/ThemeProvider' import { useUserPreferences } from './providers/UserPreferencesProvider' -import { routes } from './routes' +import { PRIMARY_PAGE_MAP, PRIMARY_PAGE_REF_MAP, TPrimaryPageName } from './routes/primary' +import { SECONDARY_ROUTES } from './routes/secondary' import modalManager from './services/modal-manager.service' -export type TPrimaryPageName = keyof typeof PRIMARY_PAGE_MAP - type TPrimaryPageContext = { navigate: (page: TPrimaryPageName, props?: object) => void current: TPrimaryPageName | null @@ -51,34 +41,10 @@ type TSecondaryPageContext = { type TStackItem = { index: number url: string - component: React.ReactElement | null + element: React.ReactElement | null ref: RefObject | null } -const PRIMARY_PAGE_REF_MAP = { - home: createRef(), - explore: createRef(), - notifications: createRef(), - me: createRef(), - profile: createRef(), - relay: createRef(), - search: createRef(), - bookmark: createRef(), - settings: createRef() -} - -const PRIMARY_PAGE_MAP = { - home: , - explore: , - notifications: , - me: , - profile: , - relay: , - search: , - bookmark: , - settings: -} - const PrimaryPageContext = createContext(undefined) const SecondaryPageContext = createContext(undefined) @@ -203,20 +169,20 @@ export function PageManager({ maxStackSize = 5 }: { maxStackSize?: number }) { const topItem = newStack[newStack.length - 1] as TStackItem | undefined if (!topItem) { // Create a new stack item if it's not exist (e.g. when the user refreshes the page, the stack will be empty) - const { component, ref } = findAndCreateComponent(state.url, state.index) - if (component) { + const { element, ref } = findAndCloneElement(state.url, state.index) + if (element) { newStack.push({ index: state.index, url: state.url, - component, + element, ref }) } - } else if (!topItem.component) { - // Load the component if it's not cached - const { component, ref } = findAndCreateComponent(topItem.url, state.index) - if (component) { - topItem.component = component + } else if (!topItem.element) { + // Load the element if it's not cached + const { element, ref } = findAndCloneElement(topItem.url, state.index) + if (element) { + topItem.element = element topItem.ref = ref } } @@ -316,7 +282,7 @@ export function PageManager({ maxStackSize = 5 }: { maxStackSize?: number }) { display: index === secondaryStack.length - 1 ? 'block' : 'none' }} > - {item.component} + {item.element} ))} {primaryPages.map(({ name, element, props }) => ( @@ -373,7 +339,7 @@ export function PageManager({ maxStackSize = 5 }: { maxStackSize?: number }) { display: index === secondaryStack.length - 1 ? 'block' : 'none' }} > - {item.component} + {item.element} ))} {primaryPages.map(({ name, element, props }) => ( @@ -464,7 +430,7 @@ export function PageManager({ maxStackSize = 5 }: { maxStackSize?: number }) { className="flex flex-col h-full w-full" style={{ display: index === secondaryStack.length - 1 ? 'block' : 'none' }} > - {item.component} + {item.element} ))} @@ -516,15 +482,15 @@ function isCurrentPage(stack: TStackItem[], url: string) { return currentPage.url === url } -function findAndCreateComponent(url: string, index: number) { +function findAndCloneElement(url: string, index: number) { const path = url.split('?')[0].split('#')[0] - for (const { matcher, element } of routes) { + for (const { matcher, element } of SECONDARY_ROUTES) { const match = matcher(path) if (!match) continue if (!element) return {} const ref = createRef() - return { component: cloneElement(element, { ...match.params, index, ref } as any), ref } + return { element: cloneElement(element, { ...match.params, index, ref } as any), ref } } return {} } @@ -538,15 +504,15 @@ function pushNewPageToStack( const currentItem = stack[stack.length - 1] const currentIndex = specificIndex ?? (currentItem ? currentItem.index + 1 : 0) - const { component, ref } = findAndCreateComponent(url, currentIndex) - if (!component) return { newStack: stack, newItem: null } + const { element, ref } = findAndCloneElement(url, currentIndex) + if (!element) return { newStack: stack, newItem: null } - const newItem = { component, ref, url, index: currentIndex } + const newItem = { element, ref, url, index: currentIndex } const newStack = [...stack, newItem] - const lastCachedIndex = newStack.findIndex((stack) => stack.component) - // Clear the oldest cached component if there are too many cached components + const lastCachedIndex = newStack.findIndex((stack) => stack.element) + // Clear the oldest cached element if there are too many cached elements if (newStack.length - lastCachedIndex > maxStackSize) { - newStack[lastCachedIndex].component = null + newStack[lastCachedIndex].element = null } return { newStack, newItem } } diff --git a/src/layouts/PrimaryPageLayout/index.tsx b/src/layouts/PrimaryPageLayout/index.tsx index 135249a2..bb594c65 100644 --- a/src/layouts/PrimaryPageLayout/index.tsx +++ b/src/layouts/PrimaryPageLayout/index.tsx @@ -1,10 +1,11 @@ import ScrollToTopButton from '@/components/ScrollToTopButton' import { Titlebar } from '@/components/Titlebar' import { ScrollArea } from '@/components/ui/scroll-area' -import { TPrimaryPageName, usePrimaryPage } from '@/PageManager' +import { usePrimaryPage } from '@/PageManager' import { DeepBrowsingProvider } from '@/providers/DeepBrowsingProvider' import { useNostr } from '@/providers/NostrProvider' import { useUserPreferences } from '@/providers/UserPreferencesProvider' +import { TPrimaryPageName } from '@/routes/primary' import { forwardRef, useEffect, useImperativeHandle, useRef } from 'react' const PrimaryPageLayout = forwardRef( diff --git a/src/pages/primary/BookmarkPage/index.tsx b/src/pages/primary/BookmarkPage/index.tsx index 9cc5a27f..22a58a27 100644 --- a/src/pages/primary/BookmarkPage/index.tsx +++ b/src/pages/primary/BookmarkPage/index.tsx @@ -2,24 +2,19 @@ import BookmarkList from '@/components/BookmarkList' import PrimaryPageLayout from '@/layouts/PrimaryPageLayout' import { TPageRef } from '@/types' import { BookmarkIcon } from 'lucide-react' -import { forwardRef, useImperativeHandle, useRef } from 'react' +import { forwardRef } from 'react' import { useTranslation } from 'react-i18next' -const BookmarkPage = forwardRef((_, ref) => { - const layoutRef = useRef(null) - useImperativeHandle(ref, () => layoutRef.current) - - return ( - } - displayScrollToTopButton - > - - - ) -}) +const BookmarkPage = forwardRef((_, ref) => ( + } + displayScrollToTopButton + > + + +)) BookmarkPage.displayName = 'BookmarkPage' export default BookmarkPage diff --git a/src/pages/primary/ExplorePage/index.tsx b/src/pages/primary/ExplorePage/index.tsx index 281ebe19..9cb2d183 100644 --- a/src/pages/primary/ExplorePage/index.tsx +++ b/src/pages/primary/ExplorePage/index.tsx @@ -7,6 +7,7 @@ import { BIG_RELAY_URLS, ExtendedKind } from '@/constants' import PrimaryPageLayout from '@/layouts/PrimaryPageLayout' import { getReplaceableEventIdentifier } from '@/lib/event' import { useUserTrust } from '@/providers/UserTrustProvider' +import { TPageRef } from '@/types' import { Compass, Plus } from 'lucide-react' import { NostrEvent } from 'nostr-tools' import { forwardRef, useCallback, useMemo, useState } from 'react' @@ -14,7 +15,7 @@ import { useTranslation } from 'react-i18next' type TExploreTabs = 'following' | 'explore' | 'reviews' -const ExplorePage = forwardRef((_, ref) => { +const ExplorePage = forwardRef((_, ref) => { const { hideUntrustedNotes } = useUserTrust() const [tab, setTab] = useState('explore') diff --git a/src/pages/primary/MePage/index.tsx b/src/pages/primary/MePage/index.tsx index dcb842d8..52034ba7 100644 --- a/src/pages/primary/MePage/index.tsx +++ b/src/pages/primary/MePage/index.tsx @@ -12,6 +12,7 @@ import { toBookmarks, toProfile, toRelaySettings, toSettings, toWallet } from '@ import { cn } from '@/lib/utils' import { useSecondaryPage } from '@/PageManager' import { useNostr } from '@/providers/NostrProvider' +import { TPageRef } from '@/types' import { ArrowDownUp, Bookmark, @@ -25,7 +26,7 @@ import { import { forwardRef, HTMLProps, useState } from 'react' import { useTranslation } from 'react-i18next' -const MePage = forwardRef((_, ref) => { +const MePage = forwardRef((_, ref) => { const { t } = useTranslation() const { push } = useSecondaryPage() const { pubkey } = useNostr() diff --git a/src/pages/primary/NoteListPage/index.tsx b/src/pages/primary/NoteListPage/index.tsx index d743ff78..39a6ceea 100644 --- a/src/pages/primary/NoteListPage/index.tsx +++ b/src/pages/primary/NoteListPage/index.tsx @@ -24,14 +24,15 @@ import FeedButton from './FeedButton' import FollowingFeed from './FollowingFeed' import RelaysFeed from './RelaysFeed' -const NoteListPage = forwardRef((_, ref) => { +const NoteListPage = forwardRef((_, ref) => { const { t } = useTranslation() const { addRelayUrls, removeRelayUrls } = useCurrentRelays() const layoutRef = useRef(null) const { pubkey } = useNostr() const { feedInfo, relayUrls, isReady, switchFeed } = useFeed() const [showRelayDetails, setShowRelayDetails] = useState(false) - useImperativeHandle(ref, () => layoutRef.current) + + useImperativeHandle(ref, () => layoutRef.current as TPageRef) useEffect(() => { if (layoutRef.current) { diff --git a/src/pages/primary/NotificationListPage/index.tsx b/src/pages/primary/NotificationListPage/index.tsx index b30b70e7..392e7a51 100644 --- a/src/pages/primary/NotificationListPage/index.tsx +++ b/src/pages/primary/NotificationListPage/index.tsx @@ -2,11 +2,12 @@ import HideUntrustedContentButton from '@/components/HideUntrustedContentButton' import NotificationList from '@/components/NotificationList' import PrimaryPageLayout from '@/layouts/PrimaryPageLayout' import { usePrimaryPage } from '@/PageManager' +import { TPageRef } from '@/types' import { Bell } from 'lucide-react' import { forwardRef, useEffect, useRef } from 'react' import { useTranslation } from 'react-i18next' -const NotificationListPage = forwardRef((_, ref) => { +const NotificationListPage = forwardRef((_, ref) => { const { current } = usePrimaryPage() const firstRenderRef = useRef(true) const notificationListRef = useRef<{ refresh: () => void }>(null) diff --git a/src/pages/primary/ProfilePage/index.tsx b/src/pages/primary/ProfilePage/index.tsx index 7b4646c8..d2dbb465 100644 --- a/src/pages/primary/ProfilePage/index.tsx +++ b/src/pages/primary/ProfilePage/index.tsx @@ -1,11 +1,12 @@ import Profile from '@/components/Profile' import PrimaryPageLayout from '@/layouts/PrimaryPageLayout' import { useNostr } from '@/providers/NostrProvider' +import { TPageRef } from '@/types' import { UserRound } from 'lucide-react' import { forwardRef } from 'react' import { useTranslation } from 'react-i18next' -const ProfilePage = forwardRef((_, ref) => { +const ProfilePage = forwardRef((_, ref) => { const { pubkey } = useNostr() return ( diff --git a/src/pages/primary/RelayPage/index.tsx b/src/pages/primary/RelayPage/index.tsx index 523e7da5..260ca8f1 100644 --- a/src/pages/primary/RelayPage/index.tsx +++ b/src/pages/primary/RelayPage/index.tsx @@ -1,10 +1,11 @@ import Relay from '@/components/Relay' import PrimaryPageLayout from '@/layouts/PrimaryPageLayout' import { normalizeUrl, simplifyUrl } from '@/lib/url' +import { TPageRef } from '@/types' import { Server } from 'lucide-react' import { forwardRef, useMemo } from 'react' -const RelayPage = forwardRef(({ url }: { url?: string }, ref) => { +const RelayPage = forwardRef(({ url }: { url?: string }, ref) => { const normalizedUrl = useMemo(() => (url ? normalizeUrl(url) : undefined), [url]) return ( diff --git a/src/pages/primary/SearchPage/index.tsx b/src/pages/primary/SearchPage/index.tsx index d60c6d8f..c3dc8760 100644 --- a/src/pages/primary/SearchPage/index.tsx +++ b/src/pages/primary/SearchPage/index.tsx @@ -2,10 +2,10 @@ import SearchBar, { TSearchBarRef } from '@/components/SearchBar' import SearchResult from '@/components/SearchResult' import PrimaryPageLayout, { TPrimaryPageLayoutRef } from '@/layouts/PrimaryPageLayout' import { usePrimaryPage } from '@/PageManager' -import { TSearchParams } from '@/types' +import { TPageRef, TSearchParams } from '@/types' import { forwardRef, useEffect, useImperativeHandle, useMemo, useRef, useState } from 'react' -const SearchPage = forwardRef((_, ref) => { +const SearchPage = forwardRef((_, ref) => { const { current, display } = usePrimaryPage() const [input, setInput] = useState('') const [searchParams, setSearchParams] = useState(null) diff --git a/src/pages/primary/SettingsPage/index.tsx b/src/pages/primary/SettingsPage/index.tsx index 9ec7ad21..fde84826 100644 --- a/src/pages/primary/SettingsPage/index.tsx +++ b/src/pages/primary/SettingsPage/index.tsx @@ -2,24 +2,19 @@ import Settings from '@/components/Settings' import PrimaryPageLayout from '@/layouts/PrimaryPageLayout' import { TPageRef } from '@/types' import { SettingsIcon } from 'lucide-react' -import { forwardRef, useImperativeHandle, useRef } from 'react' +import { forwardRef } from 'react' import { useTranslation } from 'react-i18next' -const SettingsPage = forwardRef((_, ref) => { - const layoutRef = useRef(null) - useImperativeHandle(ref, () => layoutRef.current) - - return ( - } - displayScrollToTopButton - > - - - ) -}) +const SettingsPage = forwardRef((_, ref) => ( + } + displayScrollToTopButton + > + + +)) SettingsPage.displayName = 'SettingsPage' export default SettingsPage diff --git a/src/routes/primary.tsx b/src/routes/primary.tsx new file mode 100644 index 00000000..c6def55e --- /dev/null +++ b/src/routes/primary.tsx @@ -0,0 +1,46 @@ +import BookmarkPage from '@/pages/primary/BookmarkPage' +import ExplorePage from '@/pages/primary/ExplorePage' +import MePage from '@/pages/primary/MePage' +import NoteListPage from '@/pages/primary/NoteListPage' +import NotificationListPage from '@/pages/primary/NotificationListPage' +import ProfilePage from '@/pages/primary/ProfilePage' +import RelayPage from '@/pages/primary/RelayPage' +import SearchPage from '@/pages/primary/SearchPage' +import SettingsPage from '@/pages/primary/SettingsPage' +import { TPageRef } from '@/types' +import { createRef, ForwardRefExoticComponent, RefAttributes } from 'react' + +type RouteConfig = { + key: string + component: ForwardRefExoticComponent> +} + +const PRIMARY_ROUTE_CONFIGS: RouteConfig[] = [ + { key: 'home', component: NoteListPage }, + { key: 'explore', component: ExplorePage }, + { key: 'notifications', component: NotificationListPage }, + { key: 'me', component: MePage }, + { key: 'profile', component: ProfilePage }, + { key: 'relay', component: RelayPage }, + { key: 'search', component: SearchPage }, + { key: 'bookmark', component: BookmarkPage }, + { key: 'settings', component: SettingsPage } +] + +export const PRIMARY_PAGE_REF_MAP = PRIMARY_ROUTE_CONFIGS.reduce( + (acc, { key }) => { + acc[key] = createRef() + return acc + }, + {} as Record> +) + +export const PRIMARY_PAGE_MAP = PRIMARY_ROUTE_CONFIGS.reduce( + (acc, { key, component: Component }) => { + acc[key] = + return acc + }, + {} as Record +) + +export type TPrimaryPageName = keyof typeof PRIMARY_PAGE_MAP diff --git a/src/routes.tsx b/src/routes/secondary.tsx similarity index 50% rename from src/routes.tsx rename to src/routes/secondary.tsx index 7cb9e053..950c6067 100644 --- a/src/routes.tsx +++ b/src/routes/secondary.tsx @@ -1,27 +1,28 @@ +import AppearanceSettingsPage from '@/pages/secondary/AppearanceSettingsPage' +import BookmarkPage from '@/pages/secondary/BookmarkPage' +import FollowingListPage from '@/pages/secondary/FollowingListPage' +import GeneralSettingsPage from '@/pages/secondary/GeneralSettingsPage' +import MuteListPage from '@/pages/secondary/MuteListPage' +import NoteListPage from '@/pages/secondary/NoteListPage' +import NotePage from '@/pages/secondary/NotePage' +import OthersRelaySettingsPage from '@/pages/secondary/OthersRelaySettingsPage' +import PostSettingsPage from '@/pages/secondary/PostSettingsPage' +import ProfileEditorPage from '@/pages/secondary/ProfileEditorPage' +import ProfileListPage from '@/pages/secondary/ProfileListPage' +import ProfilePage from '@/pages/secondary/ProfilePage' +import RelayPage from '@/pages/secondary/RelayPage' +import RelayReviewsPage from '@/pages/secondary/RelayReviewsPage' +import RelaySettingsPage from '@/pages/secondary/RelaySettingsPage' +import RizfulPage from '@/pages/secondary/RizfulPage' +import SearchPage from '@/pages/secondary/SearchPage' +import SettingsPage from '@/pages/secondary/SettingsPage' +import TranslationPage from '@/pages/secondary/TranslationPage' +import WalletPage from '@/pages/secondary/WalletPage' import { match } from 'path-to-regexp' import { isValidElement } from 'react' -import AppearanceSettingsPage from './pages/secondary/AppearanceSettingsPage' -import BookmarkPage from './pages/secondary/BookmarkPage' -import FollowingListPage from './pages/secondary/FollowingListPage' -import GeneralSettingsPage from './pages/secondary/GeneralSettingsPage' -import MuteListPage from './pages/secondary/MuteListPage' -import NoteListPage from './pages/secondary/NoteListPage' -import NotePage from './pages/secondary/NotePage' -import OthersRelaySettingsPage from './pages/secondary/OthersRelaySettingsPage' -import PostSettingsPage from './pages/secondary/PostSettingsPage' -import ProfileEditorPage from './pages/secondary/ProfileEditorPage' -import ProfileListPage from './pages/secondary/ProfileListPage' -import ProfilePage from './pages/secondary/ProfilePage' -import RelayPage from './pages/secondary/RelayPage' -import RelayReviewsPage from './pages/secondary/RelayReviewsPage' -import RelaySettingsPage from './pages/secondary/RelaySettingsPage' -import RizfulPage from './pages/secondary/RizfulPage' -import SearchPage from './pages/secondary/SearchPage' -import SettingsPage from './pages/secondary/SettingsPage' -import TranslationPage from './pages/secondary/TranslationPage' -import WalletPage from './pages/secondary/WalletPage' -const ROUTES = [ +// Right column routes +const SECONDARY_ROUTE_CONFIGS = [ { path: '/notes', element: }, { path: '/notes/:id', element: }, { path: '/users', element: }, @@ -44,7 +45,7 @@ const ROUTES = [ { path: '/bookmarks', element: } ] -export const routes = ROUTES.map(({ path, element }) => ({ +export const SECONDARY_ROUTES = SECONDARY_ROUTE_CONFIGS.map(({ path, element }) => ({ path, element: isValidElement(element) ? element : null, matcher: match(path)