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 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}