This repository has no description
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}