FeedButton.tsx raw
1 import FeedSwitcher from '@/components/FeedSwitcher'
2 import RelayIcon from '@/components/RelayIcon'
3 import { Drawer, DrawerContent } from '@/components/ui/drawer'
4 import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover'
5 import { simplifyUrl } from '@/lib/url'
6 import { cn } from '@/lib/utils'
7 import { useFavoriteRelays } from '@/providers/FavoriteRelaysProvider'
8 import { useFeed } from '@/providers/FeedProvider'
9 import { useScreenSize } from '@/providers/ScreenSizeProvider'
10 import { ChevronDown, Server, Star, UsersRound } from 'lucide-react'
11 import { forwardRef, HTMLAttributes, useMemo, useState } from 'react'
12 import { useTranslation } from 'react-i18next'
13
14 export default function FeedButton({ className }: { className?: string }) {
15 const { isSmallScreen } = useScreenSize()
16 const [open, setOpen] = useState(false)
17
18 if (isSmallScreen) {
19 return (
20 <>
21 <FeedSwitcherTrigger className={className} onClick={() => setOpen(true)} />
22 <Drawer open={open} onOpenChange={setOpen}>
23 <DrawerContent className="max-h-[85vh]">
24 <div
25 className="flex-1 overflow-y-auto overscroll-contain py-3 px-4"
26 style={{
27 touchAction: 'pan-y',
28 WebkitOverflowScrolling: 'touch'
29 }}
30 >
31 <FeedSwitcher close={() => setOpen(false)} />
32 </div>
33 </DrawerContent>
34 </Drawer>
35 </>
36 )
37 }
38
39 return (
40 <Popover open={open} onOpenChange={setOpen}>
41 <PopoverTrigger asChild>
42 <FeedSwitcherTrigger className={className} />
43 </PopoverTrigger>
44 <PopoverContent sideOffset={0} side="bottom" className="w-[400px] p-0 overflow-hidden">
45 <div
46 className="max-h-[calc(100vh-16rem)] overflow-y-auto overscroll-contain py-3 px-4"
47 onWheel={(e) => e.stopPropagation()}
48 onTouchMove={(e) => e.stopPropagation()}
49 >
50 <FeedSwitcher close={() => setOpen(false)} />
51 </div>
52 </PopoverContent>
53 </Popover>
54 )
55 }
56
57 const FeedSwitcherTrigger = forwardRef<HTMLDivElement, HTMLAttributes<HTMLDivElement>>(
58 ({ className, ...props }, ref) => {
59 const { t } = useTranslation()
60 const { feedInfo, relayUrls } = useFeed()
61 const { relaySets } = useFavoriteRelays()
62 const activeRelaySet = useMemo(() => {
63 return feedInfo?.feedType === 'relays' && feedInfo.id
64 ? relaySets.find((set) => set.id === feedInfo.id)
65 : undefined
66 }, [feedInfo, relaySets])
67 const title = useMemo(() => {
68 if (feedInfo?.feedType === 'following') {
69 return t('Following')
70 }
71 if (feedInfo?.feedType === 'pinned') {
72 return t('Special Follow')
73 }
74 if (relayUrls.length === 0) {
75 return t('Choose a feed')
76 }
77 if (feedInfo?.feedType === 'relay') {
78 return simplifyUrl(feedInfo?.id ?? '')
79 }
80 if (feedInfo?.feedType === 'relays') {
81 return activeRelaySet?.name ?? activeRelaySet?.id
82 }
83 }, [feedInfo, activeRelaySet])
84
85 const icon = useMemo(() => {
86 if (feedInfo?.feedType === 'following') return <UsersRound />
87 if (feedInfo?.feedType === 'pinned') return <Star />
88 if (feedInfo?.feedType === 'relay' && feedInfo.id) {
89 return <RelayIcon url={feedInfo.id} />
90 }
91
92 return <Server />
93 }, [feedInfo])
94
95 return (
96 <div
97 className={cn('flex items-center gap-2 clickable px-3 h-full rounded-xl', className)}
98 ref={ref}
99 {...props}
100 >
101 {icon}
102 <div className="text-lg font-semibold truncate">{title}</div>
103 <ChevronDown />
104 </div>
105 )
106 }
107 )
108