This repository has no description
0

Configure Feed

Select the types of activity you want to include in your feed.

semble / src / webapp / features / feeds / components / feedControls / FeedControls.tsx
9.2 kB 279 lines
1'use client'; 2 3import { Button, Group, Menu, Popover, Scroller } from '@mantine/core'; 4import { ActivitySource, UrlType, ActivityType } from '@semble/types'; 5import { Fragment, useState } from 'react'; 6import { FaSeedling } from 'react-icons/fa6'; 7import { IoMdCheckmark } from 'react-icons/io'; 8import { TbSettings } from 'react-icons/tb'; 9import { getUrlTypeIcon } from '@/lib/utils/icon'; 10import { upperFirst } from '@mantine/hooks'; 11import { MdFilterList } from 'react-icons/md'; 12import { FaAsterisk } from 'react-icons/fa'; 13import { LinkButton } from '@/components/link/MantineLink'; 14import { useUserSettings } from '@/features/settings/lib/queries/useUserSettings'; 15import { 16 activityTypeOptions, 17 feedOptions, 18 FeedView, 19 sourceOptions, 20} from '@/features/feeds/lib/feedOptions'; 21import Link from 'next/link'; 22 23export default function FeedControls() { 24 const { settings, updateSetting } = useUserSettings(); 25 26 const [typePopoverOpened, setTypePopoverOpened] = useState(false); 27 28 const selectedSource = 29 sourceOptions.find((o) => o.value === settings.feedSource) || 30 sourceOptions[0]; 31 const selectedFeed = 32 feedOptions.find((o) => o.value === settings.feedView) || feedOptions[0]; 33 34 const handleSourceClick = (source: ActivitySource | null) => { 35 updateSetting('feedSource', source); 36 if (source === ActivitySource.MARGIN) { 37 if (settings.feedView === 'following') { 38 updateSetting('feedView', 'global'); 39 } 40 if (settings.feedActivityType !== null) { 41 updateSetting('feedActivityType', null); 42 } 43 } 44 }; 45 46 const handleFeedClick = (feed: FeedView) => { 47 updateSetting('feedView', feed); 48 }; 49 50 const handleTypeClick = (type?: UrlType) => { 51 updateSetting('feedUrlType', type ?? null); 52 setTypePopoverOpened(false); 53 }; 54 55 const handleActivityTypeClick = (activityType: ActivityType | null) => { 56 updateSetting('feedActivityType', activityType); 57 }; 58 59 const hasActiveFilters = 60 settings.feedSource !== null || 61 settings.feedView !== 'global' || 62 settings.feedUrlType !== null || 63 settings.feedActivityType !== null; 64 65 const handleClear = () => { 66 updateSetting('feedSource', null); 67 updateSetting('feedView', 'global'); 68 updateSetting('feedUrlType', null); 69 updateSetting('feedActivityType', null); 70 }; 71 72 const isMarginSource = settings.feedSource === ActivitySource.MARGIN; 73 74 const SelectedTypeIcon = 75 settings.feedUrlType === null 76 ? FaAsterisk 77 : getUrlTypeIcon(settings.feedUrlType); 78 79 return ( 80 <Group gap={'xs'} justify="space-between" wrap="nowrap"> 81 <Scroller> 82 <Menu width={200} position="bottom-start"> 83 <Menu.Target> 84 <Button variant="light" color="cyan" leftSection={<MdFilterList />}> 85 {selectedSource?.label} 86 {` / ${selectedFeed?.label}`} 87 {settings.feedUrlType && ` / ${upperFirst(settings.feedUrlType)}`} 88 {settings.feedActivityType && 89 ` / ${activityTypeOptions.find((o) => o.value === settings.feedActivityType)?.label}`} 90 </Button> 91 </Menu.Target> 92 93 <Menu.Dropdown> 94 <Menu.Label>Feed</Menu.Label> 95 {feedOptions.map((option) => ( 96 <Menu.Item 97 key={option.value} 98 onClick={() => handleFeedClick(option.value)} 99 rightSection={ 100 option.value === settings.feedView ? <IoMdCheckmark /> : null 101 } 102 closeMenuOnClick={false} 103 > 104 {option.label} 105 </Menu.Item> 106 ))} 107 108 <Menu.Label>Source</Menu.Label> 109 <Menu.Sub> 110 <Menu.Sub.Target> 111 <Menu.Sub.Item 112 fz="md" 113 fw={600} 114 leftSection={selectedSource?.icon} 115 > 116 {selectedSource?.label} 117 </Menu.Sub.Item> 118 </Menu.Sub.Target> 119 120 <Menu.Sub.Dropdown> 121 {sourceOptions.map((option) => ( 122 <Menu.Item 123 key={String(option.value)} 124 onClick={() => handleSourceClick(option.value)} 125 leftSection={option.icon} 126 rightSection={ 127 option.value === settings.feedSource ? ( 128 <IoMdCheckmark /> 129 ) : null 130 } 131 closeMenuOnClick={false} 132 > 133 {option.label} 134 </Menu.Item> 135 ))} 136 </Menu.Sub.Dropdown> 137 </Menu.Sub> 138 139 <Menu.Label>Activity Type</Menu.Label> 140 <Menu.Sub> 141 <Menu.Sub.Target> 142 <Menu.Sub.Item 143 fz="md" 144 fw={600} 145 disabled={isMarginSource} 146 leftSection={ 147 settings.feedActivityType 148 ? activityTypeOptions.find( 149 (o) => o.value === settings.feedActivityType, 150 )?.icon 151 : null 152 } 153 > 154 {settings.feedActivityType 155 ? (activityTypeOptions.find( 156 (o) => o.value === settings.feedActivityType, 157 )?.label ?? 'All') 158 : 'All'} 159 </Menu.Sub.Item> 160 </Menu.Sub.Target> 161 162 <Menu.Sub.Dropdown> 163 <Menu.Item 164 onClick={() => handleActivityTypeClick(null)} 165 rightSection={ 166 settings.feedActivityType === null ? ( 167 <IoMdCheckmark /> 168 ) : null 169 } 170 closeMenuOnClick={false} 171 > 172 All 173 </Menu.Item> 174 {activityTypeOptions.map((option) => ( 175 <Menu.Item 176 key={option.value} 177 onClick={() => handleActivityTypeClick(option.value)} 178 leftSection={option.icon} 179 rightSection={ 180 settings.feedActivityType === option.value ? ( 181 <IoMdCheckmark /> 182 ) : null 183 } 184 closeMenuOnClick={false} 185 > 186 {option.label} 187 </Menu.Item> 188 ))} 189 </Menu.Sub.Dropdown> 190 </Menu.Sub> 191 192 <Menu.Label>Card Type</Menu.Label> 193 <Popover 194 opened={typePopoverOpened} 195 onChange={setTypePopoverOpened} 196 shadow="sm" 197 > 198 <Popover.Target> 199 <Menu.Item 200 variant="light" 201 leftSection={<SelectedTypeIcon />} 202 closeMenuOnClick={false} 203 onClick={() => { 204 setTypePopoverOpened((o) => !o); 205 }} 206 > 207 {settings.feedUrlType 208 ? upperFirst(settings.feedUrlType) 209 : 'All Cards'} 210 </Menu.Item> 211 </Popover.Target> 212 213 <Popover.Dropdown maw={300} p={'xs'}> 214 <Group gap={6}> 215 <Button 216 size="xs" 217 color="lime" 218 variant={settings.feedUrlType === null ? 'filled' : 'light'} 219 leftSection={<FaAsterisk />} 220 onClick={() => handleTypeClick()} 221 > 222 All Cards 223 </Button> 224 225 {Object.values(UrlType).map((type) => { 226 const Icon = getUrlTypeIcon(type); 227 228 return ( 229 <Button 230 key={type} 231 size="xs" 232 color="lime" 233 variant={ 234 settings.feedUrlType === type ? 'filled' : 'light' 235 } 236 leftSection={<Icon />} 237 onClick={() => handleTypeClick(type)} 238 > 239 {upperFirst(type)} 240 </Button> 241 ); 242 })} 243 </Group> 244 </Popover.Dropdown> 245 </Popover> 246 247 <Menu.Divider /> 248 <Menu.Item 249 component={Link} 250 href="/settings/feed" 251 leftSection={<TbSettings />} 252 > 253 Feed settings 254 </Menu.Item> 255 256 {hasActiveFilters && ( 257 <Fragment> 258 <Menu.Divider /> 259 <Menu.Item onClick={handleClear} color="red"> 260 Clear filters 261 </Menu.Item> 262 </Fragment> 263 )} 264 </Menu.Dropdown> 265 </Menu> 266 </Scroller> 267 268 <Group gap={'xs'} wrap="nowrap"> 269 <LinkButton 270 href={'/explore/open-collections'} 271 color="green" 272 variant="light" 273 > 274 <FaSeedling /> 275 </LinkButton> 276 </Group> 277 </Group> 278 ); 279}