This repository has no description
10 kB
315 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={props.urlIsConnected ? 'green' : 'gray'}
140 size="xs"
141 radius={'xl'}
142 leftSection={
143 props.urlConnectionCount > 0 ? (
144 <TbPlugConnected size={15} />
145 ) : undefined
146 }
147 onClick={(e) => {
148 e.stopPropagation();
149 if (!isAuthenticated) {
150 router.push('/login');
151 return;
152 }
153 setShowAddConnectionModal(true);
154 }}
155 >
156 {props.urlConnectionCount ? (
157 props.urlConnectionCount
158 ) : (
159 <TbPlugConnected size={15} />
160 )}
161 </Button>
162 </Tooltip>
163
164 {props.note && (
165 <Tooltip label="View note" withArrow>
166 <ActionIcon
167 variant="light"
168 color="gray"
169 radius={'xl'}
170 onClick={(e) => {
171 e.stopPropagation();
172 setShowNote((prev) => !prev);
173 }}
174 >
175 <MdOutlineStickyNote2 />
176 </ActionIcon>
177 </Tooltip>
178 )}
179 </Group>
180
181 <Menu shadow="sm">
182 <Menu.Target>
183 <ActionIcon
184 variant="light"
185 color={'gray'}
186 radius={'xl'}
187 onClick={(e) => e.stopPropagation()}
188 >
189 <BsThreeDots size={18} />
190 </ActionIcon>
191 </Menu.Target>
192 <Menu.Dropdown>
193 <CopyButton value={props.cardContent.url}>
194 {({ copy }) => (
195 <Menu.Item
196 leftSection={<BiCopy />}
197 onClick={(e) => {
198 e.stopPropagation();
199 copy();
200 notifications.show({
201 message: 'Link copied!',
202 position: 'top-center',
203 });
204 }}
205 >
206 Copy link
207 </Menu.Item>
208 )}
209 </CopyButton>
210
211 <CopyButton
212 value={`${process.env.NEXT_PUBLIC_APP_URL}/url?id=${props.cardContent.url}`}
213 >
214 {({ copy }) => (
215 <Menu.Item
216 leftSection={<MdIosShare />}
217 onClick={(e) => {
218 e.stopPropagation();
219 copy();
220 notifications.show({
221 message: 'Semble link copied!',
222 position: 'top-center',
223 });
224 }}
225 >
226 Share Semble page
227 </Menu.Item>
228 )}
229 </CopyButton>
230
231 {props.currentCollection &&
232 props.onTogglePinInCollection &&
233 isCollectionOwner && (
234 <Menu.Item
235 leftSection={
236 props.isPinnedInCollection ? <BsPinFill /> : <BsPin />
237 }
238 onClick={(e) => {
239 e.stopPropagation();
240 props.onTogglePinInCollection?.();
241 }}
242 >
243 {props.isPinnedInCollection
244 ? 'Unpin from collection'
245 : 'Pin to collection'}
246 </Menu.Item>
247 )}
248 {props.currentCollection &&
249 (isAuthor || canRemoveFromOpenCollection) && (
250 <Menu.Item
251 leftSection={<AiOutlineDisconnect />}
252 onClick={(e) => {
253 e.stopPropagation();
254 setShowRemoveFromCollectionModal(true);
255 }}
256 >
257 Remove from this collection
258 </Menu.Item>
259 )}
260 {canRemoveFromLibrary && (
261 <Menu.Item
262 color="red"
263 leftSection={<BsTrash2Fill />}
264 onClick={(e) => {
265 e.stopPropagation();
266 setShowRemoveFromLibraryModal(true);
267 }}
268 >
269 Remove from library
270 </Menu.Item>
271 )}
272 </Menu.Dropdown>
273 </Menu>
274 </Group>
275
276 {isAuthenticated && (
277 <>
278 <AddCardToModal
279 isOpen={showAddToModal}
280 onClose={() => setShowAddToModal(false)}
281 url={props.cardContent.url}
282 cardContent={props.cardContent}
283 cardId={props.id}
284 note={props.note?.text}
285 isInYourLibrary={props.urlIsInLibrary}
286 urlLibraryCount={props.urlLibraryCount}
287 viaCardId={props.viaCardId}
288 analyticsContext={props.analyticsContext}
289 />
290
291 <AddConnectionModal
292 isOpen={showAddConnectionModal}
293 onClose={() => setShowAddConnectionModal(false)}
294 sourceUrl={props.cardContent.url}
295 targetUrl={props.semblePageUrl}
296 />
297
298 {props.currentCollection && (
299 <RemoveCardFromCollectionModal
300 isOpen={showRemoveFromCollectionModal}
301 onClose={() => setShowRemoveFromCollectionModal(false)}
302 cardId={props.id}
303 collectionId={props.currentCollection.id}
304 />
305 )}
306 <RemoveCardFromLibraryModal
307 isOpen={showRemoveFromLibaryModal}
308 onClose={() => setShowRemoveFromLibraryModal(false)}
309 cardId={props.id}
310 />
311 </>
312 )}
313 </Fragment>
314 );
315}