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 / addCardToModal / AddCardToModal.tsx
4.1 kB 136 lines
1import type { UrlCard } from '@semble/types'; 2import { DEFAULT_OVERLAY_PROPS } from '@/styles/overlays'; 3import { Modal, Stack, Text } from '@mantine/core'; 4import { Suspense } from 'react'; 5import CollectionSelectorSkeleton from '@/features/collections/components/collectionSelector/Skeleton.CollectionSelector'; 6import AddCardToModalContent from './AddCardToModalContent'; 7import CardToBeAddedPreview from '../cardToBeAddedPreview/CardToBeAddedPreview'; 8import { CardSaveAnalyticsContext } from '@/features/analytics/types'; 9import useAddCard from '@/features/cards/lib/mutations/useAddCard'; 10import useUpdateCardAssociations from '@/features/cards/lib/mutations/useUpdateCardAssociations'; 11import { notifications } from '@mantine/notifications'; 12import { track } from '@vercel/analytics'; 13 14interface Props { 15 isOpen: boolean; 16 onClose: () => void; 17 url: string; 18 cardId?: string; 19 note?: string; 20 isInYourLibrary?: boolean; 21 urlLibraryCount: number; 22 viaCardId?: string; 23 cardContent?: UrlCard['cardContent']; 24 analyticsContext?: CardSaveAnalyticsContext; 25} 26 27export default function AddCardToModal(props: Props) { 28 const addCard = useAddCard(props.analyticsContext); 29 const updateCardAssociations = useUpdateCardAssociations( 30 props.analyticsContext, 31 ); 32 33 const count = props.urlLibraryCount ?? 0; 34 35 const subtitle = (() => { 36 if (count === 0) return 'Not saved by anyone yet'; 37 38 if (props.isInYourLibrary) { 39 if (count === 1) return 'Saved by you'; 40 return `Saved by you and ${count - 1} other${count - 1 > 1 ? 's' : ''}`; 41 } else { 42 if (count === 1) return 'Saved by 1 person'; 43 return `Saved by ${count} people`; 44 } 45 })(); 46 47 const handleSubmit = (data: { 48 isAddingNewCard: boolean; 49 cardData?: { 50 url: string; 51 note?: string; 52 collectionIds: string[]; 53 viaCardId?: string; 54 }; 55 updateData?: { 56 cardId: string; 57 note?: string; 58 addToCollectionIds?: string[]; 59 removeFromCollectionIds?: string[]; 60 addToLibrary?: boolean; 61 viaCardId?: string; 62 }; 63 }) => { 64 track('add or update existing card'); 65 66 if (data.isAddingNewCard && data.cardData) { 67 const notificationId = `add-card-${Date.now()}`; 68 notifications.show({ 69 id: notificationId, 70 loading: true, 71 title: 'Adding card...', 72 message: 'Please wait', 73 position: 'top-center', 74 autoClose: false, 75 withCloseButton: false, 76 }); 77 78 props.onClose(); 79 80 addCard.mutate({ ...data.cardData, notificationId }); 81 } else if (!data.isAddingNewCard && data.updateData) { 82 const notificationId = `update-card-${Date.now()}`; 83 notifications.show({ 84 id: notificationId, 85 loading: true, 86 title: 'Updating card...', 87 message: 'Please wait', 88 position: 'top-center', 89 autoClose: false, 90 withCloseButton: false, 91 }); 92 93 props.onClose(); 94 95 updateCardAssociations.mutate({ ...data.updateData, notificationId }); 96 } 97 }; 98 99 return ( 100 <Modal 101 opened={props.isOpen} 102 onClose={props.onClose} 103 title={ 104 <Stack gap={0}> 105 <Text fw={600}>Add or update {props.cardId ? 'card' : 'link'}</Text> 106 <Text c="gray" fw={500}> 107 {subtitle} 108 </Text> 109 </Stack> 110 } 111 overlayProps={DEFAULT_OVERLAY_PROPS} 112 centered 113 onClick={(e) => e.stopPropagation()} 114 > 115 <Stack justify="space-between" gap={'xs'}> 116 <CardToBeAddedPreview 117 url={props.url} 118 title={props.cardContent?.title} 119 imageUrl={props.cardContent?.imageUrl} 120 /> 121 <Suspense fallback={<CollectionSelectorSkeleton />}> 122 <AddCardToModalContent 123 onClose={props.onClose} 124 onSubmit={handleSubmit} 125 url={props.url} 126 cardId={props.cardId} 127 cardContent={props.cardContent} 128 note={props.note} 129 viaCardId={props.viaCardId} 130 isSaving={addCard.isPending || updateCardAssociations.isPending} 131 /> 132 </Suspense> 133 </Stack> 134 </Modal> 135 ); 136}