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 / AddCardToModalContent.tsx
5.1 kB 158 lines
1'use client'; 2 3import type { Collection, UrlCard } from '@semble/types'; 4import { Stack } from '@mantine/core'; 5import { notifications } from '@mantine/notifications'; 6import { useState } from 'react'; 7import CollectionSelectorError from '@/features/collections/components/collectionSelector/Error.CollectionSelector'; 8import CollectionSelector from '@/features/collections/components/collectionSelector/CollectionSelector'; 9import useGetCardFromMyLibrary from '@/features/cards/lib/queries/useGetCardFromMyLibrary'; 10import useMyCollections from '@/features/collections/lib/queries/useMyCollections'; 11import useUpdateCardAssociations from '@/features/cards/lib/mutations/useUpdateCardAssociations'; 12import useAddCard from '@/features/cards/lib/mutations/useAddCard'; 13import { track } from '@vercel/analytics'; 14import AddCardActions from '../addCardActions/AddCardActions'; 15import { CardSaveAnalyticsContext } from '@/features/analytics/types'; 16 17interface Props { 18 onClose: () => void; 19 url: string; 20 cardId?: string; 21 note?: string; 22 cardContent?: UrlCard['cardContent']; 23 viaCardId?: string; 24 analyticsContext?: CardSaveAnalyticsContext; 25} 26 27export default function AddCardToModalContent(props: Props) { 28 const cardStatus = useGetCardFromMyLibrary({ url: props.url }); 29 const isMyCard = props?.cardId === cardStatus.data.card?.id; 30 const [note, setNote] = useState(isMyCard ? props.note : ''); 31 const { data, error } = useMyCollections(); 32 33 const addCard = useAddCard(props.analyticsContext); 34 const updateCardAssociations = useUpdateCardAssociations( 35 props.analyticsContext, 36 ); 37 38 if (error) { 39 return <CollectionSelectorError />; 40 } 41 42 const allCollections = 43 data?.pages.flatMap((page) => page.collections ?? []) ?? []; 44 45 const collectionsWithCard = allCollections.filter((c) => 46 cardStatus.data.collections?.some((col) => col.id === c.id), 47 ); 48 49 const [selectedCollections, setSelectedCollections] = 50 useState<Collection[]>(collectionsWithCard); 51 52 const isSaving = addCard.isPending || updateCardAssociations.isPending; 53 54 const handleUpdateCard = (e: React.FormEvent) => { 55 e.preventDefault(); 56 track('add or update existing card'); 57 58 const trimmedNote = note?.trimEnd() === '' ? undefined : note; 59 60 const addedCollections = selectedCollections.filter( 61 (c) => !collectionsWithCard.some((original) => original.id === c.id), 62 ); 63 64 const removedCollections = collectionsWithCard.filter( 65 (c) => !selectedCollections.some((selected) => selected.id === c.id), 66 ); 67 68 const hasNoteChanged = trimmedNote !== props.note; 69 const hasAdded = addedCollections.length > 0; 70 const hasRemoved = removedCollections.length > 0; 71 72 // no change, close modal 73 if (cardStatus.data.card && !hasNoteChanged && !hasAdded && !hasRemoved) { 74 props.onClose(); 75 return; 76 } 77 78 // card not yet in library, add it 79 if (!cardStatus.data.card) { 80 addCard.mutate( 81 { 82 url: props.url, 83 note: trimmedNote, 84 collectionIds: selectedCollections.map((c) => c.id), 85 viaCardId: props.viaCardId, 86 }, 87 { 88 onError: () => { 89 notifications.show({ message: 'Could not add card.' }); 90 }, 91 onSettled: () => { 92 props.onClose(); 93 }, 94 }, 95 ); 96 return; 97 } 98 99 // card already in library, update associations instead 100 const updatedCardPayload: { 101 cardId: string; 102 note?: string; 103 addToCollectionIds?: string[]; 104 removeFromCollectionIds?: string[]; 105 addToLibrary?: boolean; 106 } = { cardId: cardStatus.data.card.id }; 107 108 if (hasNoteChanged) updatedCardPayload.note = trimmedNote; 109 if (hasAdded) 110 updatedCardPayload.addToCollectionIds = addedCollections.map((c) => c.id); 111 if (hasRemoved) 112 updatedCardPayload.removeFromCollectionIds = removedCollections.map( 113 (c) => c.id, 114 ); 115 116 // Track as a card save if we're adding collections or a note (indicates user is saving/organizing the card) 117 updatedCardPayload.addToLibrary = hasAdded || hasNoteChanged; 118 119 updateCardAssociations.mutate( 120 { 121 ...updatedCardPayload, 122 viaCardId: props.viaCardId, 123 }, 124 { 125 onError: () => { 126 notifications.show({ message: 'Could not update card.' }); 127 }, 128 onSettled: () => { 129 props.onClose(); 130 }, 131 }, 132 ); 133 }; 134 135 return ( 136 <Stack> 137 <AddCardActions 138 note={isMyCard ? note : cardStatus.data.card?.note?.text} 139 noteId={cardStatus.data.card?.note?.id} 140 onUpdateNote={setNote} 141 onClose={props.onClose} 142 /> 143 144 <CollectionSelector 145 isOpen={true} 146 onClose={props.onClose} 147 onCancel={() => { 148 props.onClose(); 149 setSelectedCollections(collectionsWithCard); 150 }} 151 onSave={handleUpdateCard} 152 isSaving={isSaving} 153 selectedCollections={selectedCollections} 154 onSelectedCollectionsChange={setSelectedCollections} 155 /> 156 </Stack> 157 ); 158}