This repository has no description
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}