@linktr.ee/messaging-react 3.10.0-rc-1783660962 → 3.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/Card-B-L2MST4.js +107 -0
- package/dist/Card-B-L2MST4.js.map +1 -0
- package/dist/Card-B56QxJW3.cjs +2 -0
- package/dist/Card-B56QxJW3.cjs.map +1 -0
- package/dist/Card-Bqh-BQaX.js +132 -0
- package/dist/Card-Bqh-BQaX.js.map +1 -0
- package/dist/Card-ZCE6Lhfc.cjs +2 -0
- package/dist/Card-ZCE6Lhfc.cjs.map +1 -0
- package/dist/Card-g4JD-2ES.js +163 -0
- package/dist/Card-g4JD-2ES.js.map +1 -0
- package/dist/Card-wac6AMNc.cjs +2 -0
- package/dist/Card-wac6AMNc.cjs.map +1 -0
- package/dist/LockedThumbnail-BAQmoIff.js +220 -0
- package/dist/LockedThumbnail-BAQmoIff.js.map +1 -0
- package/dist/LockedThumbnail-Cr0Gqs3Y.cjs +2 -0
- package/dist/LockedThumbnail-Cr0Gqs3Y.cjs.map +1 -0
- package/dist/assets/index.css +1 -1
- package/dist/index-BPhZyaxT.cjs +2 -0
- package/dist/index-BPhZyaxT.cjs.map +1 -0
- package/dist/{index-DFZrAiLB.js → index-BxzzWK4K.js} +1249 -1276
- package/dist/index-BxzzWK4K.js.map +1 -0
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +101 -164
- package/dist/index.js +1 -1
- package/dist/testing.d.ts +0 -1
- package/package.json +2 -2
- package/src/components/ChannelList/CustomChannelPreview.test.tsx +0 -95
- package/src/components/ChannelList/CustomChannelPreview.tsx +9 -21
- package/src/components/CustomMessage/CustomMessage.stories.tsx +1 -54
- package/src/components/CustomMessage/MessageTag.tsx +0 -8
- package/src/components/CustomMessage/context.tsx +1 -1
- package/src/components/CustomMessage/index.tsx +42 -76
- package/src/components/CustomMessageInput/CustomMessageInput.stories.tsx +3 -5
- package/src/components/LockedAttachment/LockedAttachment.stories.tsx +450 -0
- package/src/components/LockedAttachment/components/Composer/Card.tsx +221 -0
- package/src/components/LockedAttachment/components/Composer/index.ts +2 -0
- package/src/components/LockedAttachment/components/Received/Card.tsx +191 -0
- package/src/components/{CustomMessage/LockedAttachment/components/Media → LockedAttachment/components/Received}/CardActions.tsx +2 -9
- package/src/components/LockedAttachment/components/Received/index.ts +2 -0
- package/src/components/LockedAttachment/components/Sent/Card.tsx +177 -0
- package/src/components/LockedAttachment/components/Sent/index.ts +2 -0
- package/src/components/{CustomMessage/LockedAttachment → LockedAttachment}/components/_shared/CardBody.tsx +7 -8
- package/src/components/{CustomMessage/LockedAttachment → LockedAttachment}/components/_shared/GalleryThumbnail.tsx +39 -31
- package/src/components/LockedAttachment/components/_shared/LockBadge.tsx +39 -0
- package/src/components/{CustomMessage/LockedAttachment/components/_shared/MediaShell.tsx → LockedAttachment/components/_shared/LockedCardShell.tsx} +8 -8
- package/src/components/{CustomMessage/LockedAttachment/components/_shared/SingleThumbnail.tsx → LockedAttachment/components/_shared/LockedThumbnail.tsx} +7 -11
- package/src/components/LockedAttachment/index.tsx +63 -0
- package/src/components/LockedAttachment/types.ts +51 -0
- package/src/index.ts +8 -3
- package/src/stream-custom-data.ts +0 -1
- package/src/styles.css +0 -12
- package/src/types.ts +1 -1
- package/dist/Card-415OfRhw.cjs +0 -2
- package/dist/Card-415OfRhw.cjs.map +0 -1
- package/dist/Card-CyWdUMCB.cjs +0 -2
- package/dist/Card-CyWdUMCB.cjs.map +0 -1
- package/dist/Card-V5d6qhDT.js +0 -694
- package/dist/Card-V5d6qhDT.js.map +0 -1
- package/dist/Card-qS17qnN0.js +0 -103
- package/dist/Card-qS17qnN0.js.map +0 -1
- package/dist/LoadingDots-Ct3WmVlT.js +0 -59
- package/dist/LoadingDots-Ct3WmVlT.js.map +0 -1
- package/dist/LoadingDots-D9GGphgM.cjs +0 -2
- package/dist/LoadingDots-D9GGphgM.cjs.map +0 -1
- package/dist/index-Bx_zjNMR.cjs +0 -2
- package/dist/index-Bx_zjNMR.cjs.map +0 -1
- package/dist/index-DFZrAiLB.js.map +0 -1
- package/src/components/CustomMessage/LockedAttachment/LockedAttachment.stories.tsx +0 -541
- package/src/components/CustomMessage/LockedAttachment/LockedAttachment.test.tsx +0 -117
- package/src/components/CustomMessage/LockedAttachment/components/Media/Card.tsx +0 -358
- package/src/components/CustomMessage/LockedAttachment/components/Media/types.ts +0 -79
- package/src/components/CustomMessage/LockedAttachment/components/Text/Card.tsx +0 -153
- package/src/components/CustomMessage/LockedAttachment/components/Text/types.ts +0 -58
- package/src/components/CustomMessage/LockedAttachment/components/_shared/CardThumbnail.tsx +0 -92
- package/src/components/CustomMessage/LockedAttachment/components/_shared/LoadingDots.tsx +0 -36
- package/src/components/CustomMessage/LockedAttachment/components/_shared/LockBadge.tsx +0 -63
- package/src/components/CustomMessage/LockedAttachment/components/_shared/PreviewToggle.tsx +0 -58
- package/src/components/CustomMessage/LockedAttachment/components/_shared/PriceLockRow.tsx +0 -21
- package/src/components/CustomMessage/LockedAttachment/components/_shared/PurchaseStatusBadge.tsx +0 -74
- package/src/components/CustomMessage/LockedAttachment/components/_shared/SkeletonTextLines.tsx +0 -29
- package/src/components/CustomMessage/LockedAttachment/components/_shared/TextShell.tsx +0 -28
- package/src/components/CustomMessage/LockedAttachment/components/_shared/usePreviewToggle.test.ts +0 -67
- package/src/components/CustomMessage/LockedAttachment/components/_shared/usePreviewToggle.ts +0 -89
- package/src/components/CustomMessage/LockedAttachment/components/_shared/useReceivedSource.ts +0 -83
- package/src/components/CustomMessage/LockedAttachment/index.tsx +0 -105
- package/src/components/CustomMessage/LockedAttachment/types.ts +0 -44
|
@@ -1,358 +0,0 @@
|
|
|
1
|
-
import { PencilSimpleIcon, XIcon } from '@phosphor-icons/react'
|
|
2
|
-
import React from 'react'
|
|
3
|
-
|
|
4
|
-
import { getSourceType } from '../../../../AttachmentCard'
|
|
5
|
-
import CardBody from '../_shared/CardBody'
|
|
6
|
-
import CardThumbnail, {
|
|
7
|
-
galleryBodyIcon,
|
|
8
|
-
isGalleryCard,
|
|
9
|
-
} from '../_shared/CardThumbnail'
|
|
10
|
-
import MediaShell from '../_shared/MediaShell'
|
|
11
|
-
import PreviewToggle from '../_shared/PreviewToggle'
|
|
12
|
-
import PurchaseStatusBadge from '../_shared/PurchaseStatusBadge'
|
|
13
|
-
import { usePreviewToggle } from '../_shared/usePreviewToggle'
|
|
14
|
-
import { useReceivedSource } from '../_shared/useReceivedSource'
|
|
15
|
-
|
|
16
|
-
import ReceivedCardActions from './CardActions'
|
|
17
|
-
import type {
|
|
18
|
-
MediaCardDraftProps,
|
|
19
|
-
MediaCardOtherProps,
|
|
20
|
-
MediaCardOwnProps,
|
|
21
|
-
MediaCardPreviewProps,
|
|
22
|
-
MediaCardProps,
|
|
23
|
-
} from './types'
|
|
24
|
-
|
|
25
|
-
export type { MediaCardProps } from './types'
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* Media card while still drafting — the locked thumbnail (single or gallery)
|
|
29
|
-
* the composer taps to preview the attachment they're about to send, plus
|
|
30
|
-
* the title/price body with optional dismiss X and edit pencil.
|
|
31
|
-
*/
|
|
32
|
-
const DraftMediaCard: React.FC<MediaCardDraftProps> = ({
|
|
33
|
-
title,
|
|
34
|
-
mimeType = 'application/octet-stream',
|
|
35
|
-
thumbnailUrl,
|
|
36
|
-
detail,
|
|
37
|
-
amountText,
|
|
38
|
-
placeholderTitle,
|
|
39
|
-
placeholderAmountText,
|
|
40
|
-
gallery = [],
|
|
41
|
-
onDismiss,
|
|
42
|
-
onPreviewClick,
|
|
43
|
-
onFetchSource,
|
|
44
|
-
onEditClick,
|
|
45
|
-
}) => {
|
|
46
|
-
const isGallery = isGalleryCard(gallery)
|
|
47
|
-
|
|
48
|
-
// Gallery is always previewable in the composer because each item's source
|
|
49
|
-
// is already provided up-front via `gallery[*].sourceUrl`.
|
|
50
|
-
const { source, isPreviewVisible, isLoadingPreview, togglePreview } =
|
|
51
|
-
usePreviewToggle({ isGallery, onFetchSource, onPreviewClick })
|
|
52
|
-
|
|
53
|
-
const showLocked = !isPreviewVisible
|
|
54
|
-
const isBusy = isLoadingPreview
|
|
55
|
-
|
|
56
|
-
const statusBadge = (
|
|
57
|
-
<React.Fragment>
|
|
58
|
-
<span className="text-xs font-medium text-white/55">•</span>
|
|
59
|
-
<span
|
|
60
|
-
className={
|
|
61
|
-
amountText
|
|
62
|
-
? 'text-xs font-medium text-white/55'
|
|
63
|
-
: 'text-xs font-medium text-white/30'
|
|
64
|
-
}
|
|
65
|
-
>
|
|
66
|
-
{amountText || placeholderAmountText}
|
|
67
|
-
</span>
|
|
68
|
-
</React.Fragment>
|
|
69
|
-
)
|
|
70
|
-
|
|
71
|
-
const dismissButton = onDismiss ? (
|
|
72
|
-
<button
|
|
73
|
-
type="button"
|
|
74
|
-
onClick={(e) => {
|
|
75
|
-
// Stop the click from bubbling up into the outer preview-toggle
|
|
76
|
-
// wrapper when both are wired up at the same time.
|
|
77
|
-
e.stopPropagation()
|
|
78
|
-
onDismiss()
|
|
79
|
-
}}
|
|
80
|
-
className="flex size-6 items-center justify-center rounded-full bg-[#121110] text-white"
|
|
81
|
-
aria-label="Dismiss attachment"
|
|
82
|
-
>
|
|
83
|
-
<XIcon className="size-3" weight="bold" />
|
|
84
|
-
</button>
|
|
85
|
-
) : undefined
|
|
86
|
-
|
|
87
|
-
const editButton = onEditClick ? (
|
|
88
|
-
<button
|
|
89
|
-
type="button"
|
|
90
|
-
onClick={onEditClick}
|
|
91
|
-
aria-label="Edit attachment"
|
|
92
|
-
className="flex size-10 items-center justify-center rounded-full bg-white/10 text-white hover:bg-white/15"
|
|
93
|
-
>
|
|
94
|
-
<PencilSimpleIcon className="size-5" weight="regular" />
|
|
95
|
-
</button>
|
|
96
|
-
) : undefined
|
|
97
|
-
|
|
98
|
-
return (
|
|
99
|
-
<MediaShell variant="dark">
|
|
100
|
-
<PreviewToggle
|
|
101
|
-
onToggle={togglePreview}
|
|
102
|
-
isBusy={isBusy}
|
|
103
|
-
isPreviewVisible={!showLocked}
|
|
104
|
-
>
|
|
105
|
-
<CardThumbnail
|
|
106
|
-
variant="dark"
|
|
107
|
-
gallery={gallery}
|
|
108
|
-
title={title}
|
|
109
|
-
showLocked={showLocked}
|
|
110
|
-
mimeType={mimeType}
|
|
111
|
-
thumbnailUrl={thumbnailUrl}
|
|
112
|
-
source={source}
|
|
113
|
-
topRight={dismissButton}
|
|
114
|
-
/>
|
|
115
|
-
</PreviewToggle>
|
|
116
|
-
|
|
117
|
-
<CardBody
|
|
118
|
-
variant="dark"
|
|
119
|
-
title={title}
|
|
120
|
-
placeholderTitle={placeholderTitle}
|
|
121
|
-
mimeType={mimeType}
|
|
122
|
-
detail={detail}
|
|
123
|
-
statusBadge={statusBadge}
|
|
124
|
-
icon={isGallery ? galleryBodyIcon('dark') : undefined}
|
|
125
|
-
trailingAction={editButton}
|
|
126
|
-
/>
|
|
127
|
-
</MediaShell>
|
|
128
|
-
)
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
/**
|
|
132
|
-
* Static mockup of what the recipient will see before purchase — always
|
|
133
|
-
* locked/blurred (the lock badge renders from `showLocked` alone, no toggle
|
|
134
|
-
* wiring needed), no interactivity, no real content revealed.
|
|
135
|
-
*/
|
|
136
|
-
const PreviewMediaCard: React.FC<MediaCardPreviewProps> = ({
|
|
137
|
-
title,
|
|
138
|
-
mimeType = 'application/octet-stream',
|
|
139
|
-
thumbnailUrl,
|
|
140
|
-
detail,
|
|
141
|
-
amountText,
|
|
142
|
-
placeholderTitle,
|
|
143
|
-
placeholderAmountText,
|
|
144
|
-
gallery = [],
|
|
145
|
-
}) => {
|
|
146
|
-
const isGallery = isGalleryCard(gallery)
|
|
147
|
-
|
|
148
|
-
const statusBadge = (
|
|
149
|
-
<React.Fragment>
|
|
150
|
-
<span className="text-xs font-medium text-black/55">•</span>
|
|
151
|
-
<span
|
|
152
|
-
className={
|
|
153
|
-
amountText
|
|
154
|
-
? 'text-xs font-medium text-black/55'
|
|
155
|
-
: 'text-xs font-medium text-black/30'
|
|
156
|
-
}
|
|
157
|
-
>
|
|
158
|
-
{amountText || placeholderAmountText}
|
|
159
|
-
</span>
|
|
160
|
-
</React.Fragment>
|
|
161
|
-
)
|
|
162
|
-
|
|
163
|
-
return (
|
|
164
|
-
<MediaShell variant="light">
|
|
165
|
-
<CardThumbnail
|
|
166
|
-
variant="light"
|
|
167
|
-
gallery={gallery}
|
|
168
|
-
title={title}
|
|
169
|
-
showLocked
|
|
170
|
-
mimeType={mimeType}
|
|
171
|
-
thumbnailUrl={thumbnailUrl}
|
|
172
|
-
/>
|
|
173
|
-
|
|
174
|
-
<CardBody
|
|
175
|
-
variant="light"
|
|
176
|
-
title={title}
|
|
177
|
-
placeholderTitle={placeholderTitle}
|
|
178
|
-
mimeType={mimeType}
|
|
179
|
-
detail={detail}
|
|
180
|
-
statusBadge={statusBadge}
|
|
181
|
-
icon={isGallery ? galleryBodyIcon('light') : undefined}
|
|
182
|
-
/>
|
|
183
|
-
</MediaShell>
|
|
184
|
-
)
|
|
185
|
-
}
|
|
186
|
-
/**
|
|
187
|
-
* The sender's ('mine') view of a paid media attachment: the locked
|
|
188
|
-
* thumbnail (single or gallery) they can tap to preview their own
|
|
189
|
-
* attachment, plus the title/purchase-status body.
|
|
190
|
-
*/
|
|
191
|
-
const OwnMediaCard: React.FC<MediaCardOwnProps> = ({
|
|
192
|
-
title,
|
|
193
|
-
mimeType = 'application/octet-stream',
|
|
194
|
-
thumbnailUrl,
|
|
195
|
-
detail,
|
|
196
|
-
amountText,
|
|
197
|
-
placeholderTitle,
|
|
198
|
-
paymentStatus = 'pending',
|
|
199
|
-
gallery = [],
|
|
200
|
-
onPreviewClick,
|
|
201
|
-
onFetchSource,
|
|
202
|
-
}) => {
|
|
203
|
-
const isGallery = isGalleryCard(gallery)
|
|
204
|
-
|
|
205
|
-
// Gallery is always previewable for the sender because each item's source
|
|
206
|
-
// is already provided up-front via `gallery[*].sourceUrl`.
|
|
207
|
-
const { source, isPreviewVisible, isLoadingPreview, togglePreview } =
|
|
208
|
-
usePreviewToggle({ isGallery, onFetchSource, onPreviewClick })
|
|
209
|
-
|
|
210
|
-
const showLocked = !isPreviewVisible
|
|
211
|
-
const isBusy = isLoadingPreview
|
|
212
|
-
|
|
213
|
-
return (
|
|
214
|
-
<MediaShell variant="dark">
|
|
215
|
-
<PreviewToggle
|
|
216
|
-
onToggle={togglePreview}
|
|
217
|
-
isBusy={isBusy}
|
|
218
|
-
isPreviewVisible={!showLocked}
|
|
219
|
-
>
|
|
220
|
-
<CardThumbnail
|
|
221
|
-
variant="dark"
|
|
222
|
-
gallery={gallery}
|
|
223
|
-
title={title}
|
|
224
|
-
showLocked={showLocked}
|
|
225
|
-
paymentStatus={paymentStatus}
|
|
226
|
-
mimeType={mimeType}
|
|
227
|
-
thumbnailUrl={thumbnailUrl}
|
|
228
|
-
source={source}
|
|
229
|
-
/>
|
|
230
|
-
</PreviewToggle>
|
|
231
|
-
|
|
232
|
-
<CardBody
|
|
233
|
-
variant="dark"
|
|
234
|
-
title={title}
|
|
235
|
-
placeholderTitle={placeholderTitle}
|
|
236
|
-
mimeType={mimeType}
|
|
237
|
-
detail={detail}
|
|
238
|
-
statusBadge={
|
|
239
|
-
<PurchaseStatusBadge
|
|
240
|
-
isMine={true}
|
|
241
|
-
paymentStatus={paymentStatus}
|
|
242
|
-
amountText={amountText}
|
|
243
|
-
/>
|
|
244
|
-
}
|
|
245
|
-
icon={isGallery ? galleryBodyIcon('dark') : undefined}
|
|
246
|
-
/>
|
|
247
|
-
</MediaShell>
|
|
248
|
-
)
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
/**
|
|
252
|
-
* The recipient's ('other') view of a paid media attachment: the locked
|
|
253
|
-
* thumbnail (single or gallery) with an Unlock/Download CTA. The source is
|
|
254
|
-
* revealed by fetching once — on click when already paid, or automatically
|
|
255
|
-
* the first time a paid card scrolls into view.
|
|
256
|
-
*/
|
|
257
|
-
const OtherMediaCard: React.FC<MediaCardOtherProps> = ({
|
|
258
|
-
title,
|
|
259
|
-
amountText,
|
|
260
|
-
thumbnailUrl,
|
|
261
|
-
mimeType = 'application/octet-stream',
|
|
262
|
-
detail,
|
|
263
|
-
gallery = [],
|
|
264
|
-
onUnlockClick,
|
|
265
|
-
onFetchSource,
|
|
266
|
-
onDownloadClick,
|
|
267
|
-
paymentStatus,
|
|
268
|
-
isUnlocking = false,
|
|
269
|
-
}) => {
|
|
270
|
-
const isGallery = isGalleryCard(gallery)
|
|
271
|
-
|
|
272
|
-
const { source, cardRef, handleUnlockClick } = useReceivedSource({
|
|
273
|
-
paymentStatus,
|
|
274
|
-
onFetchSource,
|
|
275
|
-
onUnlockClick,
|
|
276
|
-
})
|
|
277
|
-
|
|
278
|
-
const effectiveSourceUrl = source?.sourceUrl
|
|
279
|
-
const effectiveRedeemUrl = source?.redeemUrl
|
|
280
|
-
|
|
281
|
-
// For gallery, the per-item sources are carried by `gallery` itself, so
|
|
282
|
-
// the lock state is driven by paymentStatus rather than the single
|
|
283
|
-
// `effectiveSourceUrl`. Everything else still respects the source fetch.
|
|
284
|
-
const isLocked = isGallery
|
|
285
|
-
? paymentStatus !== 'paid'
|
|
286
|
-
: effectiveSourceUrl === undefined
|
|
287
|
-
const sourceType = getSourceType(mimeType)
|
|
288
|
-
|
|
289
|
-
// For gallery, ReceivedCardActions falls back to a plain <button>
|
|
290
|
-
// (no anchor) because the gallery has no single sourceUrl to link to —
|
|
291
|
-
// the per-item sources live on `gallery` itself.
|
|
292
|
-
const actionSourceUrl = isGallery ? undefined : effectiveSourceUrl
|
|
293
|
-
|
|
294
|
-
return (
|
|
295
|
-
<MediaShell
|
|
296
|
-
variant="light"
|
|
297
|
-
rootRef={cardRef}
|
|
298
|
-
data-testid="locked-attachment"
|
|
299
|
-
>
|
|
300
|
-
<CardThumbnail
|
|
301
|
-
variant="light"
|
|
302
|
-
gallery={gallery}
|
|
303
|
-
title={title}
|
|
304
|
-
showLocked={isLocked}
|
|
305
|
-
paymentStatus={paymentStatus}
|
|
306
|
-
mimeType={mimeType}
|
|
307
|
-
thumbnailUrl={thumbnailUrl}
|
|
308
|
-
source={source}
|
|
309
|
-
containedImage={!isLocked && sourceType === 'document'}
|
|
310
|
-
/>
|
|
311
|
-
|
|
312
|
-
<CardBody
|
|
313
|
-
variant="light"
|
|
314
|
-
title={title}
|
|
315
|
-
mimeType={mimeType}
|
|
316
|
-
detail={detail}
|
|
317
|
-
statusBadge={
|
|
318
|
-
<PurchaseStatusBadge
|
|
319
|
-
isMine={false}
|
|
320
|
-
paymentStatus={paymentStatus}
|
|
321
|
-
amountText={amountText}
|
|
322
|
-
/>
|
|
323
|
-
}
|
|
324
|
-
icon={isGallery ? galleryBodyIcon('light') : undefined}
|
|
325
|
-
action={
|
|
326
|
-
<ReceivedCardActions
|
|
327
|
-
isLocked={isLocked}
|
|
328
|
-
isUnlocking={isUnlocking}
|
|
329
|
-
sourceUrl={actionSourceUrl}
|
|
330
|
-
redeemUrl={effectiveRedeemUrl}
|
|
331
|
-
onUnlockClicked={handleUnlockClick}
|
|
332
|
-
onDownloadClicked={onDownloadClick}
|
|
333
|
-
/>
|
|
334
|
-
}
|
|
335
|
-
/>
|
|
336
|
-
</MediaShell>
|
|
337
|
-
)
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
/**
|
|
341
|
-
* The card either party sees for a paid media attachment. `isDraft`/`isPreview`
|
|
342
|
-
* select the composer's own drafting view / a static receiver-side mockup;
|
|
343
|
-
* otherwise `isMine` selects the sender's sent view vs. the recipient's
|
|
344
|
-
* unlock/download view.
|
|
345
|
-
*/
|
|
346
|
-
const MediaCard: React.FC<MediaCardProps> = (props) => {
|
|
347
|
-
if (props.isDraft) {
|
|
348
|
-
return <DraftMediaCard {...props} />
|
|
349
|
-
} else if (props.isPreview) {
|
|
350
|
-
return <PreviewMediaCard {...props} />
|
|
351
|
-
} else if (props.isMine) {
|
|
352
|
-
return <OwnMediaCard {...props} />
|
|
353
|
-
} else {
|
|
354
|
-
return <OtherMediaCard {...props} />
|
|
355
|
-
}
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
export default MediaCard
|
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
FetchSourceHandler,
|
|
3
|
-
LockedAttachmentGalleryItem,
|
|
4
|
-
PaymentStatus,
|
|
5
|
-
} from '../../types'
|
|
6
|
-
|
|
7
|
-
interface MediaCardBaseProps {
|
|
8
|
-
/** Attachment title. */
|
|
9
|
-
title?: string
|
|
10
|
-
/** Fallback shown when `title` is unset. */
|
|
11
|
-
placeholderTitle?: string
|
|
12
|
-
/** Drives icon/thumbnail rendering. */
|
|
13
|
-
mimeType?: string
|
|
14
|
-
/** Poster image, locked or unlocked. */
|
|
15
|
-
thumbnailUrl?: string
|
|
16
|
-
/** Secondary description text. */
|
|
17
|
-
detail?: string
|
|
18
|
-
/** Amount label. */
|
|
19
|
-
amountText?: string
|
|
20
|
-
/** 2+ items renders as a carousel instead of a single thumbnail. */
|
|
21
|
-
gallery?: LockedAttachmentGalleryItem[]
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export interface MediaCardDraftProps extends MediaCardBaseProps {
|
|
25
|
-
/** True while composing — shows the real (unredacted) thumbnail. */
|
|
26
|
-
isDraft: true
|
|
27
|
-
isPreview?: false
|
|
28
|
-
/** Fallback amount before a price is set. */
|
|
29
|
-
placeholderAmountText?: string
|
|
30
|
-
/** Lazily fetches the source on first tap; cached and reused after. */
|
|
31
|
-
onFetchSource?: FetchSourceHandler
|
|
32
|
-
/** Fired on first tap to preview. */
|
|
33
|
-
onPreviewClick?: () => void
|
|
34
|
-
/** Removes the draft attachment. */
|
|
35
|
-
onDismiss?: () => void
|
|
36
|
-
/** Renders a pencil button to edit the attachment metadata. */
|
|
37
|
-
onEditClick?: () => void
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
export interface MediaCardPreviewProps extends MediaCardBaseProps {
|
|
41
|
-
/** True to render exactly as the recipient will see it before purchase — always locked/blurred. */
|
|
42
|
-
isDraft?: false
|
|
43
|
-
isPreview: true
|
|
44
|
-
/** Fallback amount before a price is set. */
|
|
45
|
-
placeholderAmountText?: string
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
interface MediaCardSentBaseProps extends MediaCardBaseProps {
|
|
49
|
-
/** False once sent. */
|
|
50
|
-
isDraft?: false
|
|
51
|
-
isPreview?: false
|
|
52
|
-
/** Drives the lock/skeleton/badge state. */
|
|
53
|
-
paymentStatus?: PaymentStatus
|
|
54
|
-
onFetchSource?: FetchSourceHandler
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
export type MediaCardOwnProps = MediaCardSentBaseProps & {
|
|
58
|
-
/** Sender's own view. */
|
|
59
|
-
isMine: true
|
|
60
|
-
/** Fired on first tap to preview own attachment. */
|
|
61
|
-
onPreviewClick?: () => void
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
export type MediaCardOtherProps = MediaCardSentBaseProps & {
|
|
65
|
-
/** Recipient's view. */
|
|
66
|
-
isMine: false
|
|
67
|
-
/** Opens checkout on click. Omit to hide the button. */
|
|
68
|
-
onUnlockClick?: () => void
|
|
69
|
-
/** Omit to hide the Download button. */
|
|
70
|
-
onDownloadClick?: () => void
|
|
71
|
-
/** Spinner on Unlock while checkout runs. */
|
|
72
|
-
isUnlocking?: boolean
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
export type MediaCardProps =
|
|
76
|
-
| MediaCardDraftProps
|
|
77
|
-
| MediaCardPreviewProps
|
|
78
|
-
| MediaCardOwnProps
|
|
79
|
-
| MediaCardOtherProps
|
|
@@ -1,153 +0,0 @@
|
|
|
1
|
-
import { LockSimpleIcon, XIcon } from '@phosphor-icons/react'
|
|
2
|
-
import type React from 'react'
|
|
3
|
-
|
|
4
|
-
import LoadingDots from '../_shared/LoadingDots'
|
|
5
|
-
import PriceLockRow from '../_shared/PriceLockRow'
|
|
6
|
-
import PurchaseStatusBadge from '../_shared/PurchaseStatusBadge'
|
|
7
|
-
import SkeletonTextLines from '../_shared/SkeletonTextLines'
|
|
8
|
-
import TextShell from '../_shared/TextShell'
|
|
9
|
-
|
|
10
|
-
import type {
|
|
11
|
-
TextCardDraftProps,
|
|
12
|
-
TextCardOtherProps,
|
|
13
|
-
TextCardOwnProps,
|
|
14
|
-
TextCardPreviewProps,
|
|
15
|
-
TextCardProps,
|
|
16
|
-
} from './types'
|
|
17
|
-
|
|
18
|
-
export type { TextCardProps } from './types'
|
|
19
|
-
|
|
20
|
-
const DraftTextCard: React.FC<TextCardDraftProps> = ({
|
|
21
|
-
renderedText,
|
|
22
|
-
amountText,
|
|
23
|
-
placeholderAmountText,
|
|
24
|
-
onDismiss,
|
|
25
|
-
}) => (
|
|
26
|
-
<TextShell isMine className="pr-8">
|
|
27
|
-
{renderedText || <SkeletonTextLines variant="dark" />}
|
|
28
|
-
<PriceLockRow>
|
|
29
|
-
<span className={amountText ? 'text-white/55' : 'text-white/30'}>
|
|
30
|
-
{amountText || placeholderAmountText}
|
|
31
|
-
</span>
|
|
32
|
-
</PriceLockRow>
|
|
33
|
-
{onDismiss && (
|
|
34
|
-
<button
|
|
35
|
-
type="button"
|
|
36
|
-
onClick={onDismiss}
|
|
37
|
-
className="absolute right-3 top-3 flex size-6 shrink-0 items-center justify-center rounded-full bg-white/10 text-white hover:bg-white/15"
|
|
38
|
-
aria-label="Dismiss attachment"
|
|
39
|
-
>
|
|
40
|
-
<XIcon className="size-3" weight="bold" />
|
|
41
|
-
</button>
|
|
42
|
-
)}
|
|
43
|
-
</TextShell>
|
|
44
|
-
)
|
|
45
|
-
|
|
46
|
-
const PreviewTextCard: React.FC<TextCardPreviewProps> = ({
|
|
47
|
-
amountText,
|
|
48
|
-
placeholderAmountText,
|
|
49
|
-
}) => (
|
|
50
|
-
<TextShell isMine={false}>
|
|
51
|
-
<SkeletonTextLines variant="light" />
|
|
52
|
-
<PriceLockRow>
|
|
53
|
-
<span className="opacity-55">{amountText || placeholderAmountText}</span>
|
|
54
|
-
</PriceLockRow>
|
|
55
|
-
</TextShell>
|
|
56
|
-
)
|
|
57
|
-
|
|
58
|
-
const OwnTextCard: React.FC<TextCardOwnProps> = ({
|
|
59
|
-
renderedText,
|
|
60
|
-
amountText,
|
|
61
|
-
paymentStatus = 'pending',
|
|
62
|
-
}) => {
|
|
63
|
-
const isPaid = paymentStatus === 'paid'
|
|
64
|
-
|
|
65
|
-
return (
|
|
66
|
-
<TextShell isMine>
|
|
67
|
-
{isPaid && renderedText ? (
|
|
68
|
-
renderedText
|
|
69
|
-
) : (
|
|
70
|
-
<SkeletonTextLines variant="dark" />
|
|
71
|
-
)}
|
|
72
|
-
<PriceLockRow>
|
|
73
|
-
{amountText && <span className="text-white/55">{amountText}</span>}
|
|
74
|
-
<PurchaseStatusBadge isMine paymentStatus={paymentStatus} />
|
|
75
|
-
</PriceLockRow>
|
|
76
|
-
</TextShell>
|
|
77
|
-
)
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
const OtherTextCard: React.FC<TextCardOtherProps> = ({
|
|
81
|
-
renderedText,
|
|
82
|
-
amountText,
|
|
83
|
-
paymentStatus,
|
|
84
|
-
onUnlockClick,
|
|
85
|
-
isUnlocking = false,
|
|
86
|
-
}) => {
|
|
87
|
-
const isPaid = paymentStatus === 'paid'
|
|
88
|
-
|
|
89
|
-
if (isPaid && renderedText) {
|
|
90
|
-
return (
|
|
91
|
-
<TextShell isMine={false}>
|
|
92
|
-
{renderedText}
|
|
93
|
-
<PriceLockRow>
|
|
94
|
-
{amountText != null && (
|
|
95
|
-
<span className="opacity-55">{amountText}</span>
|
|
96
|
-
)}
|
|
97
|
-
<PurchaseStatusBadge isMine={false} paymentStatus="paid" />
|
|
98
|
-
</PriceLockRow>
|
|
99
|
-
</TextShell>
|
|
100
|
-
)
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
const ariaLabel = isUnlocking
|
|
104
|
-
? 'Unlocking message.'
|
|
105
|
-
: 'Unlock this message.'
|
|
106
|
-
|
|
107
|
-
return (
|
|
108
|
-
<TextShell isMine={false}>
|
|
109
|
-
<div className="flex items-center gap-3">
|
|
110
|
-
<div className="flex flex-1 flex-col gap-2">
|
|
111
|
-
<SkeletonTextLines variant="light" />
|
|
112
|
-
<span className="sr-only" aria-live="polite">
|
|
113
|
-
{ariaLabel}
|
|
114
|
-
</span>
|
|
115
|
-
<PriceLockRow>
|
|
116
|
-
{amountText != null && (
|
|
117
|
-
<span className="opacity-55">{amountText}</span>
|
|
118
|
-
)}
|
|
119
|
-
</PriceLockRow>
|
|
120
|
-
</div>
|
|
121
|
-
<button
|
|
122
|
-
type="button"
|
|
123
|
-
onClick={onUnlockClick}
|
|
124
|
-
disabled={isUnlocking}
|
|
125
|
-
aria-label={isUnlocking ? 'Unlocking message' : 'Unlock message'}
|
|
126
|
-
aria-busy={isUnlocking}
|
|
127
|
-
title={isUnlocking ? 'Unlocking message' : 'Unlock message'}
|
|
128
|
-
className="flex size-10 shrink-0 items-center justify-center rounded-full bg-[#121110] text-white hover:bg-[#2a2928]"
|
|
129
|
-
>
|
|
130
|
-
{isUnlocking ? (
|
|
131
|
-
<LoadingDots />
|
|
132
|
-
) : (
|
|
133
|
-
<LockSimpleIcon className="size-4" weight="fill" aria-hidden />
|
|
134
|
-
)}
|
|
135
|
-
</button>
|
|
136
|
-
</div>
|
|
137
|
-
</TextShell>
|
|
138
|
-
)
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
const TextCard: React.FC<TextCardProps> = (props) => {
|
|
142
|
-
if (props.isDraft) {
|
|
143
|
-
return <DraftTextCard {...props} />
|
|
144
|
-
} else if (props.isPreview) {
|
|
145
|
-
return <PreviewTextCard {...props} />
|
|
146
|
-
} else if (props.isMine) {
|
|
147
|
-
return <OwnTextCard {...props} />
|
|
148
|
-
} else {
|
|
149
|
-
return <OtherTextCard {...props} />
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
export default TextCard
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
import type React from 'react'
|
|
2
|
-
|
|
3
|
-
import type { PaymentStatus } from '../../types'
|
|
4
|
-
|
|
5
|
-
interface TextCardBaseProps {
|
|
6
|
-
/** Amount label. */
|
|
7
|
-
amountText?: string
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
export interface TextCardDraftProps extends TextCardBaseProps {
|
|
11
|
-
/** True while composing — shows the real draft text. */
|
|
12
|
-
isDraft: true
|
|
13
|
-
isPreview?: false
|
|
14
|
-
/** Real draft content, not yet redacted. */
|
|
15
|
-
renderedText?: React.ReactNode
|
|
16
|
-
/** Fallback amount before a price is set. */
|
|
17
|
-
placeholderAmountText?: string
|
|
18
|
-
/** Removes the draft attachment. */
|
|
19
|
-
onDismiss?: () => void
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export interface TextCardPreviewProps extends TextCardBaseProps {
|
|
23
|
-
/** True to render exactly as the recipient will see it before purchase — skeleton lines only. */
|
|
24
|
-
isDraft?: false
|
|
25
|
-
isPreview: true
|
|
26
|
-
/** Fallback amount before a price is set. */
|
|
27
|
-
placeholderAmountText?: string
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
interface TextCardSentBaseProps extends TextCardBaseProps {
|
|
31
|
-
/** False once sent. */
|
|
32
|
-
isDraft?: false
|
|
33
|
-
isPreview?: false
|
|
34
|
-
/** Revealed content once paid. */
|
|
35
|
-
renderedText?: React.ReactNode
|
|
36
|
-
/** Drives the lock/skeleton/badge state. */
|
|
37
|
-
paymentStatus?: PaymentStatus
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
export type TextCardOwnProps = TextCardSentBaseProps & {
|
|
41
|
-
/** Sender's own view. */
|
|
42
|
-
isMine: true
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
export type TextCardOtherProps = TextCardSentBaseProps & {
|
|
46
|
-
/** Recipient's view. */
|
|
47
|
-
isMine: false
|
|
48
|
-
/** Spinner on Unlock while checkout runs. */
|
|
49
|
-
isUnlocking?: boolean
|
|
50
|
-
/** Opens checkout on click. Omit to hide the button. */
|
|
51
|
-
onUnlockClick?: () => void
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
export type TextCardProps =
|
|
55
|
-
| TextCardDraftProps
|
|
56
|
-
| TextCardPreviewProps
|
|
57
|
-
| TextCardOwnProps
|
|
58
|
-
| TextCardOtherProps
|