This repository has no description
0

Configure Feed

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

semble / src / webapp / features / cards / components / urlCardActions / UrlCardActions.tsx
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}