This repository has no description
10 kB
316 lines
1'use client';
2
3import {
4 type UrlCard,
5 type Collection,
6 type User,
7 CollectionAccessType,
8} from '@/api-client';
9import {
10 ActionIcon,
11 Button,
12 Collapse,
13 CopyButton,
14 Group,
15 Menu,
16 Tooltip,
17} from '@mantine/core';
18import { Fragment, useState } from 'react';
19import { FiPlus } from 'react-icons/fi';
20import { BsPin, BsPinFill, BsThreeDots, BsTrash2Fill } from 'react-icons/bs';
21import RemoveCardFromCollectionModal from '../removeCardFromCollectionModal/RemoveCardFromCollectionModal';
22import RemoveCardFromLibraryModal from '../removeCardFromLibraryModal/RemoveCardFromLibraryModal';
23import AddCardToModal from '@/features/cards/components/addCardToModal/AddCardToModal';
24import { MdIosShare, MdOutlineStickyNote2 } from 'react-icons/md';
25import NoteCardInline from '@/features/notes/components/noteCardInline/NoteCardInline';
26import { useAuth } from '@/hooks/useAuth';
27import { useRouter } from 'next/navigation';
28import { IoMdCheckmark } from 'react-icons/io';
29import { notifications } from '@mantine/notifications';
30import { BiCopy } from 'react-icons/bi';
31import { CardSaveAnalyticsContext } from '@/features/analytics/types';
32import { TbPlugConnected } from 'react-icons/tb';
33import AddConnectionModal from '@/features/connections/components/addConnectionModal/AddConnectionModal';
34import { AiOutlineDisconnect } from 'react-icons/ai';
35
36interface Props {
37 id: string;
38 cardContent: UrlCard['cardContent'];
39 cardCount: number;
40 cardAuthor?: User;
41 authorHandle?: string;
42 note?: UrlCard['note'];
43 currentCollection?: Collection;
44 urlLibraryCount: number;
45 urlIsInLibrary: boolean;
46 urlIsConnected?: boolean;
47 urlConnectionCount: number;
48 viaCardId?: string;
49 semblePageUrl?: string;
50 analyticsContext?: CardSaveAnalyticsContext;
51 isPinnedInCollection?: boolean;
52 onTogglePinInCollection?: () => void;
53}
54
55export default function UrlCardActions(props: Props) {
56 const { isAuthenticated, user } = useAuth();
57
58 const userId = user?.id;
59 const userHandle = user?.handle;
60
61 const isAuthor =
62 !!user &&
63 (props.authorHandle
64 ? userHandle === props.authorHandle
65 : userId === props.cardAuthor?.id);
66
67 const isOpenCollection =
68 props.currentCollection?.accessType === CollectionAccessType.OPEN;
69
70 const isCardAdder = userId === props.cardAuthor?.id;
71 const isCollectionOwner = userId === props.currentCollection?.author.id;
72
73 const canRemoveFromOpenCollection =
74 !!user && isOpenCollection && (isCardAdder || isCollectionOwner);
75
76 const canRemoveFromLibrary = isAuthor && props.urlIsInLibrary;
77
78 const [showNote, setShowNote] = useState(false);
79 const [showRemoveFromCollectionModal, setShowRemoveFromCollectionModal] =
80 useState(false);
81 const [showRemoveFromLibaryModal, setShowRemoveFromLibraryModal] =
82 useState(false);
83 const [showAddToModal, setShowAddToModal] = useState(false);
84 const [showAddConnectionModal, setShowAddConnectionModal] = useState(false);
85
86 const router = useRouter();
87
88 return (
89 <Fragment>
90 {props.note && (
91 <Collapse expanded={showNote}>
92 <NoteCardInline
93 note={props.note}
94 cardContent={props.cardContent}
95 cardAuthor={props.cardAuthor}
96 isOwner={isAuthenticated && isAuthor}
97 onClose={() => setShowNote(false)}
98 />
99 </Collapse>
100 )}
101 <Group justify="space-between">
102 <Group gap={'xs'}>
103 <Tooltip label="Save or update" withArrow>
104 <Button
105 variant="light"
106 color={'gray'}
107 size="xs"
108 radius={'xl'}
109 leftSection={
110 props.urlLibraryCount > 0 ? (
111 props.urlIsInLibrary ? (
112 <IoMdCheckmark size={18} />
113 ) : (
114 <FiPlus size={18} />
115 )
116 ) : undefined
117 }
118 onClick={(e) => {
119 e.stopPropagation();
120 if (!isAuthenticated) {
121 router.push('/login');
122 return;
123 }
124 setShowAddToModal(true);
125 }}
126 >
127 {props.urlLibraryCount ? (
128 props.urlLibraryCount
129 ) : props.urlIsInLibrary ? (
130 <IoMdCheckmark size={18} />
131 ) : (
132 <FiPlus size={18} />
133 )}
134 </Button>
135 </Tooltip>
136 <Tooltip label="Connect to another card" withArrow>
137 <Button
138 variant="light"
139 color="gray"
140 c={props.urlIsConnected ? 'green' : 'gray'}
141 size="xs"
142 radius={'xl'}
143 leftSection={
144 props.urlConnectionCount > 0 ? (
145 <TbPlugConnected size={15} />
146 ) : undefined
147 }
148 onClick={(e) => {
149 e.stopPropagation();
150 if (!isAuthenticated) {
151 router.push('/login');
152 return;
153 }
154 setShowAddConnectionModal(true);
155 }}
156 >
157 {props.urlConnectionCount ? (
158 props.urlConnectionCount
159 ) : (
160 <TbPlugConnected size={15} />
161 )}
162 </Button>
163 </Tooltip>
164
165 {props.note && (
166 <Tooltip label="View note" withArrow>
167 <ActionIcon
168 variant="light"
169 color="gray"
170 radius={'xl'}
171 onClick={(e) => {
172 e.stopPropagation();
173 setShowNote((prev) => !prev);
174 }}
175 >
176 <MdOutlineStickyNote2 />
177 </ActionIcon>
178 </Tooltip>
179 )}
180 </Group>
181
182 <Menu shadow="sm">
183 <Menu.Target>
184 <ActionIcon
185 variant="light"
186 color={'gray'}
187 radius={'xl'}
188 onClick={(e) => e.stopPropagation()}
189 >
190 <BsThreeDots size={18} />
191 </ActionIcon>
192 </Menu.Target>
193 <Menu.Dropdown>
194 <CopyButton value={props.cardContent.url}>
195 {({ copy }) => (
196 <Menu.Item
197 leftSection={<BiCopy />}
198 onClick={(e) => {
199 e.stopPropagation();
200 copy();
201 notifications.show({
202 message: 'Link copied!',
203 position: 'top-center',
204 });
205 }}
206 >
207 Copy link
208 </Menu.Item>
209 )}
210 </CopyButton>
211
212 <CopyButton
213 value={`${process.env.NEXT_PUBLIC_APP_URL}/url?id=${props.cardContent.url}`}
214 >
215 {({ copy }) => (
216 <Menu.Item
217 leftSection={<MdIosShare />}
218 onClick={(e) => {
219 e.stopPropagation();
220 copy();
221 notifications.show({
222 message: 'Semble link copied!',
223 position: 'top-center',
224 });
225 }}
226 >
227 Share Semble page
228 </Menu.Item>
229 )}
230 </CopyButton>
231
232 {props.currentCollection &&
233 props.onTogglePinInCollection &&
234 isCollectionOwner && (
235 <Menu.Item
236 leftSection={
237 props.isPinnedInCollection ? <BsPinFill /> : <BsPin />
238 }
239 onClick={(e) => {
240 e.stopPropagation();
241 props.onTogglePinInCollection?.();
242 }}
243 >
244 {props.isPinnedInCollection
245 ? 'Unpin from collection'
246 : 'Pin to collection'}
247 </Menu.Item>
248 )}
249 {props.currentCollection &&
250 (isAuthor || canRemoveFromOpenCollection) && (
251 <Menu.Item
252 leftSection={<AiOutlineDisconnect />}
253 onClick={(e) => {
254 e.stopPropagation();
255 setShowRemoveFromCollectionModal(true);
256 }}
257 >
258 Remove from this collection
259 </Menu.Item>
260 )}
261 {canRemoveFromLibrary && (
262 <Menu.Item
263 color="red"
264 leftSection={<BsTrash2Fill />}
265 onClick={(e) => {
266 e.stopPropagation();
267 setShowRemoveFromLibraryModal(true);
268 }}
269 >
270 Remove from library
271 </Menu.Item>
272 )}
273 </Menu.Dropdown>
274 </Menu>
275 </Group>
276
277 {isAuthenticated && (
278 <>
279 <AddCardToModal
280 isOpen={showAddToModal}
281 onClose={() => setShowAddToModal(false)}
282 url={props.cardContent.url}
283 cardContent={props.cardContent}
284 cardId={props.id}
285 note={props.note?.text}
286 isInYourLibrary={props.urlIsInLibrary}
287 urlLibraryCount={props.urlLibraryCount}
288 viaCardId={props.viaCardId}
289 analyticsContext={props.analyticsContext}
290 />
291
292 <AddConnectionModal
293 isOpen={showAddConnectionModal}
294 onClose={() => setShowAddConnectionModal(false)}
295 sourceUrl={props.cardContent.url}
296 targetUrl={props.semblePageUrl}
297 />
298
299 {props.currentCollection && (
300 <RemoveCardFromCollectionModal
301 isOpen={showRemoveFromCollectionModal}
302 onClose={() => setShowRemoveFromCollectionModal(false)}
303 cardId={props.id}
304 collectionId={props.currentCollection.id}
305 />
306 )}
307 <RemoveCardFromLibraryModal
308 isOpen={showRemoveFromLibaryModal}
309 onClose={() => setShowRemoveFromLibraryModal(false)}
310 cardId={props.id}
311 />
312 </>
313 )}
314 </Fragment>
315 );
316}