diff --git a/src/components/BottomNavigationBar/index.tsx b/src/components/BottomNavigationBar/index.tsx index 5c16b138..2f0bd225 100644 --- a/src/components/BottomNavigationBar/index.tsx +++ b/src/components/BottomNavigationBar/index.tsx @@ -1,15 +1,18 @@ import { cn } from '@/lib/utils' +import { useDeepBrowsing } from '@/providers/DeepBrowsingProvider' +import AccountButton from './AccountButton' import HomeButton from './HomeButton' import NotificationsButton from './NotificationsButton' import PostButton from './PostButton' -import AccountButton from './AccountButton' -export default function BottomNavigationBar({ visible = true }: { visible?: boolean }) { +export default function BottomNavigationBar() { + const { deepBrowsing } = useDeepBrowsing() + return (
void }) { const { t } = useTranslation() + const { deepBrowsing } = useDeepBrowsing() return ( -
+
+ scrollAreaRef?: React.RefObject className?: string - visible?: boolean }) { const { isSmallScreen } = useScreenSize() + const { deepBrowsing, lastScrollTop } = useDeepBrowsing() + const visible = !deepBrowsing && lastScrollTop > 800 const handleScrollToTop = () => { - if (isSmallScreen) { + if (!scrollAreaRef) { window.scrollTo({ top: 0, behavior: 'smooth' }) return } diff --git a/src/components/Titlebar/index.tsx b/src/components/Titlebar/index.tsx index 709a08ee..7395eff9 100644 --- a/src/components/Titlebar/index.tsx +++ b/src/components/Titlebar/index.tsx @@ -1,19 +1,22 @@ import { cn } from '@/lib/utils' +import { useDeepBrowsing } from '@/providers/DeepBrowsingProvider' +import { useScreenSize } from '@/providers/ScreenSizeProvider' export function Titlebar({ children, - className, - visible = true + className }: { children?: React.ReactNode className?: string - visible?: boolean }) { + const { isSmallScreen } = useScreenSize() + const { deepBrowsing } = useDeepBrowsing() + return (
diff --git a/src/layouts/PrimaryPageLayout/index.tsx b/src/layouts/PrimaryPageLayout/index.tsx index 9136ea10..dd1c2703 100644 --- a/src/layouts/PrimaryPageLayout/index.tsx +++ b/src/layouts/PrimaryPageLayout/index.tsx @@ -3,8 +3,9 @@ import ScrollToTopButton from '@/components/ScrollToTopButton' import { Titlebar } from '@/components/Titlebar' import { ScrollArea } from '@/components/ui/scroll-area' import { TPrimaryPageName, usePrimaryPage } from '@/PageManager' +import { DeepBrowsingProvider } from '@/providers/DeepBrowsingProvider' import { useScreenSize } from '@/providers/ScreenSizeProvider' -import { forwardRef, useEffect, useImperativeHandle, useRef, useState } from 'react' +import { forwardRef, useEffect, useImperativeHandle, useRef } from 'react' const PrimaryPageLayout = forwardRef( ( @@ -22,8 +23,6 @@ const PrimaryPageLayout = forwardRef( ref ) => { const scrollAreaRef = useRef(null) - const [visible, setVisible] = useState(true) - const [lastScrollTop, setLastScrollTop] = useState(0) const { isSmallScreen } = useScreenSize() const { current } = usePrimaryPage() @@ -44,77 +43,39 @@ const PrimaryPageLayout = forwardRef( useEffect(() => { if (isSmallScreen) { window.scrollTo({ top: 0 }) - setVisible(true) return } }, [current]) - useEffect(() => { - if (current !== pageName) return - - const handleScroll = () => { - const atBottom = isSmallScreen - ? window.innerHeight + window.scrollY >= document.body.offsetHeight - 20 - : scrollAreaRef.current - ? scrollAreaRef.current?.clientHeight + scrollAreaRef.current?.scrollTop >= - scrollAreaRef.current?.scrollHeight - 20 - : false - if (atBottom) { - setVisible(true) - return - } - - const scrollTop = (isSmallScreen ? window.scrollY : scrollAreaRef.current?.scrollTop) || 0 - const diff = scrollTop - lastScrollTop - if (scrollTop <= 800) { - setVisible(true) - setLastScrollTop(scrollTop) - return - } - - if (diff > 20) { - setVisible(false) - setLastScrollTop(scrollTop) - } else if (diff < -20) { - setVisible(true) - setLastScrollTop(scrollTop) - } - } - - if (isSmallScreen) { - window.addEventListener('scroll', handleScroll) - return () => { - window.removeEventListener('scroll', handleScroll) - } - } - - scrollAreaRef.current?.addEventListener('scroll', handleScroll) - return () => { - scrollAreaRef.current?.removeEventListener('scroll', handleScroll) - } - }, [lastScrollTop, isSmallScreen, current]) + if (isSmallScreen) { + return ( + +
+ {titlebar && {titlebar}} + {children} + {displayScrollToTopButton && } + +
+
+ ) + } return ( - - {titlebar && ( - {titlebar} - )} -
{children}
- {displayScrollToTopButton && ( - 800} - /> - )} - {isSmallScreen && } -
+ + + {titlebar && {titlebar}} + {children} + + {displayScrollToTopButton && } + ) } ) @@ -125,16 +86,6 @@ export type TPrimaryPageLayoutRef = { scrollToTop: () => void } -function PrimaryPageTitlebar({ - children, - visible = true -}: { - children?: React.ReactNode - visible?: boolean -}) { - return ( - - {children} - - ) +function PrimaryPageTitlebar({ children }: { children?: React.ReactNode }) { + return {children} } diff --git a/src/layouts/SecondaryPageLayout/index.tsx b/src/layouts/SecondaryPageLayout/index.tsx index 53325394..59f4ccb0 100644 --- a/src/layouts/SecondaryPageLayout/index.tsx +++ b/src/layouts/SecondaryPageLayout/index.tsx @@ -4,8 +4,9 @@ import ScrollToTopButton from '@/components/ScrollToTopButton' import { Titlebar } from '@/components/Titlebar' import { ScrollArea } from '@/components/ui/scroll-area' import { useSecondaryPage } from '@/PageManager' +import { DeepBrowsingProvider } from '@/providers/DeepBrowsingProvider' import { useScreenSize } from '@/providers/ScreenSizeProvider' -import { useEffect, useRef, useState } from 'react' +import { useEffect, useRef } from 'react' export default function SecondaryPageLayout({ children, @@ -23,118 +24,64 @@ export default function SecondaryPageLayout({ displayScrollToTopButton?: boolean }): JSX.Element { const scrollAreaRef = useRef(null) - const [visible, setVisible] = useState(true) - const [lastScrollTop, setLastScrollTop] = useState(0) const { isSmallScreen } = useScreenSize() const { currentIndex } = useSecondaryPage() useEffect(() => { if (isSmallScreen) { window.scrollTo({ top: 0 }) - setVisible(true) return } }, []) - useEffect(() => { - if (currentIndex !== index) return - - const handleScroll = () => { - const atBottom = isSmallScreen - ? window.innerHeight + window.scrollY >= document.body.offsetHeight - 20 - : scrollAreaRef.current - ? scrollAreaRef.current?.clientHeight + scrollAreaRef.current?.scrollTop >= - scrollAreaRef.current?.scrollHeight - 20 - : false - if (atBottom) { - setVisible(true) - return - } - - const scrollTop = (isSmallScreen ? window.scrollY : scrollAreaRef.current?.scrollTop) || 0 - const diff = scrollTop - lastScrollTop - if (scrollTop <= 800) { - setVisible(true) - setLastScrollTop(scrollTop) - return - } - - if (diff > 20) { - setVisible(false) - setLastScrollTop(scrollTop) - } else if (diff < -20) { - setVisible(true) - setLastScrollTop(scrollTop) - } - } - - if (isSmallScreen) { - window.addEventListener('scroll', handleScroll) - return () => { - window.removeEventListener('scroll', handleScroll) - } - } - - scrollAreaRef.current?.addEventListener('scroll', handleScroll) - return () => { - scrollAreaRef.current?.removeEventListener('scroll', handleScroll) - } - }, [lastScrollTop, isSmallScreen, currentIndex]) + if (isSmallScreen) { + return ( + +
+ +
{children}
+ {displayScrollToTopButton && } + +
+
+ ) + } return ( - - -
{children}
- {displayScrollToTopButton && ( - 800} /> - )} - {isSmallScreen && } -
+ + + +
{children}
+
+ {displayScrollToTopButton && } +
) } export function SecondaryPageTitlebar({ title, controls, - hideBackButton = false, - visible = true + hideBackButton = false }: { title?: React.ReactNode controls?: React.ReactNode hideBackButton?: boolean - visible?: boolean }): JSX.Element { - const { isSmallScreen } = useScreenSize() - - if (isSmallScreen) { - return ( - - {title} -
{controls}
-
- ) - } - return ( - -
- {title} -
+ + {title}
{controls}
) diff --git a/src/providers/DeepBrowsingProvider.tsx b/src/providers/DeepBrowsingProvider.tsx new file mode 100644 index 00000000..8348f233 --- /dev/null +++ b/src/providers/DeepBrowsingProvider.tsx @@ -0,0 +1,80 @@ +import { createContext, useContext, useEffect, useState } from 'react' + +type TDeepBrowsingContext = { + deepBrowsing: boolean + lastScrollTop: number +} + +const DeepBrowsingContext = createContext(undefined) + +export const useDeepBrowsing = () => { + const context = useContext(DeepBrowsingContext) + if (!context) { + throw new Error('useDeepBrowsing must be used within a DeepBrowsingProvider') + } + return context +} + +export function DeepBrowsingProvider({ + children, + active, + scrollAreaRef +}: { + children: React.ReactNode + active: boolean + scrollAreaRef?: React.RefObject +}) { + const [deepBrowsing, setDeepBrowsing] = useState(false) + const [lastScrollTop, setLastScrollTop] = useState(0) + + useEffect(() => { + if (!active) return + + const handleScroll = () => { + const atBottom = !scrollAreaRef + ? window.innerHeight + window.scrollY >= document.body.offsetHeight - 20 + : scrollAreaRef.current + ? scrollAreaRef.current?.clientHeight + scrollAreaRef.current?.scrollTop >= + scrollAreaRef.current?.scrollHeight - 20 + : false + if (atBottom) { + setDeepBrowsing(false) + return + } + + const scrollTop = (!scrollAreaRef ? window.scrollY : scrollAreaRef.current?.scrollTop) || 0 + const diff = scrollTop - lastScrollTop + if (scrollTop <= 800) { + setDeepBrowsing(false) + setLastScrollTop(scrollTop) + return + } + + if (diff > 20) { + setDeepBrowsing(true) + setLastScrollTop(scrollTop) + } else if (diff < -20) { + setDeepBrowsing(false) + setLastScrollTop(scrollTop) + } + } + + if (!scrollAreaRef) { + window.addEventListener('scroll', handleScroll) + return () => { + window.removeEventListener('scroll', handleScroll) + } + } + + scrollAreaRef.current?.addEventListener('scroll', handleScroll) + return () => { + scrollAreaRef.current?.removeEventListener('scroll', handleScroll) + } + }, [lastScrollTop, active]) + + return ( + + {children} + + ) +}