index.tsx raw

   1  import ScrollToTopButton from '@/components/ScrollToTopButton'
   2  import { Titlebar } from '@/components/Titlebar'
   3  import { ScrollArea } from '@/components/ui/scroll-area'
   4  import { usePrimaryPage } from '@/PageManager'
   5  import { DeepBrowsingProvider } from '@/providers/DeepBrowsingProvider'
   6  import { useNostr } from '@/providers/NostrProvider'
   7  import { useUserPreferences } from '@/providers/UserPreferencesProvider'
   8  import { TPrimaryPageName } from '@/routes/primary'
   9  import { forwardRef, useEffect, useImperativeHandle, useRef } from 'react'
  10  
  11  const PrimaryPageLayout = forwardRef(
  12    (
  13      {
  14        children,
  15        titlebar,
  16        pageName,
  17        displayScrollToTopButton = false,
  18        hideTitlebarBottomBorder = false
  19      }: {
  20        children?: React.ReactNode
  21        titlebar: React.ReactNode
  22        pageName: TPrimaryPageName
  23        displayScrollToTopButton?: boolean
  24        hideTitlebarBottomBorder?: boolean
  25      },
  26      ref
  27    ) => {
  28      const { pubkey } = useNostr()
  29      const scrollAreaRef = useRef<HTMLDivElement>(null)
  30      const smallScreenScrollAreaRef = useRef<HTMLDivElement>(null)
  31      const smallScreenLastScrollTopRef = useRef(0)
  32      const { enableSingleColumnLayout } = useUserPreferences()
  33      const { current, display } = usePrimaryPage()
  34  
  35      useImperativeHandle(
  36        ref,
  37        () => ({
  38          scrollToTop: (behavior: ScrollBehavior = 'smooth') => {
  39            setTimeout(() => {
  40              if (scrollAreaRef.current) {
  41                return scrollAreaRef.current.scrollTo({ top: 0, behavior })
  42              }
  43              window.scrollTo({ top: 0, behavior })
  44            }, 10)
  45          }
  46        }),
  47        []
  48      )
  49  
  50      useEffect(() => {
  51        if (!enableSingleColumnLayout) return
  52  
  53        const isVisible = () => {
  54          return smallScreenScrollAreaRef.current?.checkVisibility
  55            ? smallScreenScrollAreaRef.current?.checkVisibility()
  56            : false
  57        }
  58  
  59        if (isVisible()) {
  60          window.scrollTo({ top: smallScreenLastScrollTopRef.current, behavior: 'instant' })
  61        }
  62        const handleScroll = () => {
  63          if (isVisible()) {
  64            smallScreenLastScrollTopRef.current = window.scrollY
  65          }
  66        }
  67        window.addEventListener('scroll', handleScroll)
  68        return () => {
  69          window.removeEventListener('scroll', handleScroll)
  70        }
  71      }, [current, enableSingleColumnLayout, display])
  72  
  73      useEffect(() => {
  74        smallScreenLastScrollTopRef.current = 0
  75      }, [pubkey])
  76  
  77      if (enableSingleColumnLayout) {
  78        return (
  79          <DeepBrowsingProvider active={current === pageName && display}>
  80            <div
  81              ref={smallScreenScrollAreaRef}
  82              style={{
  83                paddingBottom: 'env(safe-area-inset-bottom)'
  84              }}
  85            >
  86              <PrimaryPageTitlebar hideBottomBorder={hideTitlebarBottomBorder}>
  87                {titlebar}
  88              </PrimaryPageTitlebar>
  89              {children}
  90            </div>
  91            {displayScrollToTopButton && <ScrollToTopButton />}
  92          </DeepBrowsingProvider>
  93        )
  94      }
  95  
  96      return (
  97        <DeepBrowsingProvider active={current === pageName && display} scrollAreaRef={scrollAreaRef}>
  98          <ScrollArea
  99            className="h-full overflow-auto"
 100            scrollBarClassName="z-30 pt-12"
 101            ref={scrollAreaRef}
 102          >
 103            <PrimaryPageTitlebar hideBottomBorder={hideTitlebarBottomBorder}>
 104              {titlebar}
 105            </PrimaryPageTitlebar>
 106            {children}
 107            <div className="h-4" />
 108          </ScrollArea>
 109          {displayScrollToTopButton && <ScrollToTopButton scrollAreaRef={scrollAreaRef} />}
 110        </DeepBrowsingProvider>
 111      )
 112    }
 113  )
 114  PrimaryPageLayout.displayName = 'PrimaryPageLayout'
 115  export default PrimaryPageLayout
 116  
 117  export type TPrimaryPageLayoutRef = {
 118    scrollToTop: (behavior?: ScrollBehavior) => void
 119  }
 120  
 121  function PrimaryPageTitlebar({
 122    children,
 123    hideBottomBorder = false
 124  }: {
 125    children?: React.ReactNode
 126    hideBottomBorder?: boolean
 127  }) {
 128    return (
 129      <Titlebar className="p-1" hideBottomBorder={hideBottomBorder}>
 130        {children}
 131      </Titlebar>
 132    )
 133  }
 134