From 23bf7fd00500c3e5db860427ef3383d90532a61c Mon Sep 17 00:00:00 2001 From: codytseng Date: Sun, 26 Jan 2025 16:28:47 +0800 Subject: [PATCH] feat: scroll to top when jumping to the current page --- src/PageManager.tsx | 64 +++++++--- .../BottomNavigationBarItem.tsx | 3 +- src/components/Sidebar/SidebarItem.tsx | 3 +- src/layouts/PrimaryPageLayout/index.tsx | 7 +- src/layouts/SecondaryPageLayout/index.tsx | 118 +++++++++++------- src/pages/primary/MePage/index.tsx | 12 +- src/pages/primary/NoteListPage/index.tsx | 12 +- .../primary/NotificationListPage/index.tsx | 8 +- .../secondary/FollowingListPage/index.tsx | 9 +- src/pages/secondary/HomePage/index.tsx | 9 +- src/pages/secondary/LoadingPage/index.tsx | 9 +- src/pages/secondary/MuteListPage/index.tsx | 9 +- src/pages/secondary/NotFoundPage/index.tsx | 9 +- src/pages/secondary/NoteListPage/index.tsx | 10 +- src/pages/secondary/NotePage/index.tsx | 14 ++- .../OthersRelaySettingsPage/index.tsx | 8 +- .../secondary/ProfileEditorPage/index.tsx | 10 +- src/pages/secondary/ProfileListPage/index.tsx | 10 +- src/pages/secondary/ProfilePage/index.tsx | 12 +- src/pages/secondary/RelayPage/index.tsx | 11 +- .../secondary/RelaySettingsPage/index.tsx | 10 +- src/pages/secondary/SettingsPage/index.tsx | 8 +- src/types.ts | 2 + 23 files changed, 235 insertions(+), 132 deletions(-) diff --git a/src/PageManager.tsx b/src/PageManager.tsx index 5922cdec..b8b5af9a 100644 --- a/src/PageManager.tsx +++ b/src/PageManager.tsx @@ -4,7 +4,17 @@ import { Separator } from '@/components/ui/separator' import { cn } from '@/lib/utils' import NoteListPage from '@/pages/primary/NoteListPage' import HomePage from '@/pages/secondary/HomePage' -import { cloneElement, createContext, ReactNode, useContext, useEffect, useState } from 'react' +import { TPageRef } from '@/types' +import { + cloneElement, + createContext, + createRef, + ReactNode, + RefObject, + useContext, + useEffect, + useState +} from 'react' import MePage from './pages/primary/MePage' import NotificationListPage from './pages/primary/NotificationListPage' import { useScreenSize } from './providers/ScreenSizeProvider' @@ -26,13 +36,20 @@ type TSecondaryPageContext = { type TStackItem = { index: number url: string - component: React.ReactNode | null + component: React.ReactElement | null + ref: RefObject | null +} + +const PRIMARY_PAGE_REF_MAP = { + home: createRef(), + notifications: createRef(), + me: createRef() } const PRIMARY_PAGE_MAP = { - home: , - notifications: , - me: + home: , + notifications: , + me: } const PrimaryPageContext = createContext(undefined) @@ -111,13 +128,22 @@ 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 newComponent = findAndCreateComponent(state.url, state.index) - if (newComponent) { - newStack.push({ index: state.index, url: state.url, component: newComponent }) + const { component, ref } = findAndCreateComponent(state.url, state.index) + if (component) { + newStack.push({ + index: state.index, + url: state.url, + component, + ref + }) } } else if (!topItem.component) { // Load the component if it's not cached - topItem.component = findAndCreateComponent(topItem.url, state.index) + const { component, ref } = findAndCreateComponent(topItem.url, state.index) + if (component) { + topItem.component = component + topItem.ref = ref + } } if (newStack.length === 0) { window.history.replaceState(null, '', '/') @@ -139,6 +165,7 @@ export function PageManager({ maxStackSize = 5 }: { maxStackSize?: number }) { setPrimaryPages((prev) => [...prev, { name: page, element: PRIMARY_PAGE_MAP[page] }]) } setCurrentPrimaryPage(page) + PRIMARY_PAGE_REF_MAP[page].current?.scrollToTop() if (isSmallScreen) { clearSecondaryPages() } @@ -146,7 +173,13 @@ export function PageManager({ maxStackSize = 5 }: { maxStackSize?: number }) { const pushSecondaryPage = (url: string, index?: number) => { setSecondaryStack((prevStack) => { - if (isCurrentPage(prevStack, url)) return prevStack + if (isCurrentPage(prevStack, url)) { + const currentItem = prevStack[prevStack.length - 1] + if (currentItem?.ref?.current) { + currentItem.ref.current.scrollToTop() + } + return prevStack + } const { newStack, newItem } = pushNewPageToStack(prevStack, url, maxStackSize, index) if (newItem) { @@ -302,10 +335,11 @@ function findAndCreateComponent(url: string, index: number) { const match = matcher(path) if (!match) continue - if (!element) return null - return cloneElement(element, { ...match.params, index } as any) + if (!element) return {} + const ref = createRef() + return { component: cloneElement(element, { ...match.params, index, ref } as any), ref } } - return null + return {} } function pushNewPageToStack( @@ -317,10 +351,10 @@ function pushNewPageToStack( const currentItem = stack[stack.length - 1] const currentIndex = specificIndex ?? (currentItem ? currentItem.index + 1 : 0) - const component = findAndCreateComponent(url, currentIndex) + const { component, ref } = findAndCreateComponent(url, currentIndex) if (!component) return { newStack: stack, newItem: null } - const newItem = { component, url, index: currentIndex } + const newItem = { component, 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 diff --git a/src/components/BottomNavigationBar/BottomNavigationBarItem.tsx b/src/components/BottomNavigationBar/BottomNavigationBarItem.tsx index dce83d32..1ae8d428 100644 --- a/src/components/BottomNavigationBar/BottomNavigationBarItem.tsx +++ b/src/components/BottomNavigationBar/BottomNavigationBarItem.tsx @@ -15,9 +15,8 @@ export default function BottomNavigationBarItem({