@linktr.ee/messaging-react 3.10.0 → 3.11.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-415OfRhw.cjs +2 -0
- package/dist/Card-415OfRhw.cjs.map +1 -0
- package/dist/Card-CyWdUMCB.cjs +2 -0
- package/dist/Card-CyWdUMCB.cjs.map +1 -0
- package/dist/Card-V5d6qhDT.js +694 -0
- package/dist/Card-V5d6qhDT.js.map +1 -0
- package/dist/Card-qS17qnN0.js +103 -0
- package/dist/Card-qS17qnN0.js.map +1 -0
- package/dist/LoadingDots-Ct3WmVlT.js +59 -0
- package/dist/LoadingDots-Ct3WmVlT.js.map +1 -0
- package/dist/LoadingDots-D9GGphgM.cjs +2 -0
- package/dist/LoadingDots-D9GGphgM.cjs.map +1 -0
- package/dist/assets/index.css +1 -1
- package/dist/index-Bx_zjNMR.cjs +2 -0
- package/dist/index-Bx_zjNMR.cjs.map +1 -0
- package/dist/{index-BxzzWK4K.js → index-DFZrAiLB.js} +1276 -1249
- package/dist/index-DFZrAiLB.js.map +1 -0
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +164 -101
- package/dist/index.js +1 -1
- package/dist/testing.d.ts +1 -0
- package/package.json +1 -1
- package/src/components/ChannelList/CustomChannelPreview.test.tsx +95 -0
- package/src/components/ChannelList/CustomChannelPreview.tsx +21 -9
- package/src/components/CustomMessage/CustomMessage.stories.tsx +54 -1
- package/src/components/CustomMessage/LockedAttachment/LockedAttachment.stories.tsx +541 -0
- package/src/components/CustomMessage/LockedAttachment/LockedAttachment.test.tsx +117 -0
- package/src/components/CustomMessage/LockedAttachment/components/Media/Card.tsx +358 -0
- package/src/components/{LockedAttachment/components/Received → CustomMessage/LockedAttachment/components/Media}/CardActions.tsx +9 -2
- package/src/components/CustomMessage/LockedAttachment/components/Media/types.ts +79 -0
- package/src/components/CustomMessage/LockedAttachment/components/Text/Card.tsx +153 -0
- package/src/components/CustomMessage/LockedAttachment/components/Text/types.ts +58 -0
- package/src/components/{LockedAttachment → CustomMessage/LockedAttachment}/components/_shared/CardBody.tsx +8 -7
- package/src/components/CustomMessage/LockedAttachment/components/_shared/CardThumbnail.tsx +92 -0
- package/src/components/{LockedAttachment → CustomMessage/LockedAttachment}/components/_shared/GalleryThumbnail.tsx +31 -39
- package/src/components/CustomMessage/LockedAttachment/components/_shared/LoadingDots.tsx +36 -0
- package/src/components/CustomMessage/LockedAttachment/components/_shared/LockBadge.tsx +63 -0
- package/src/components/{LockedAttachment/components/_shared/LockedCardShell.tsx → CustomMessage/LockedAttachment/components/_shared/MediaShell.tsx} +8 -8
- package/src/components/CustomMessage/LockedAttachment/components/_shared/PreviewToggle.tsx +58 -0
- package/src/components/CustomMessage/LockedAttachment/components/_shared/PriceLockRow.tsx +21 -0
- package/src/components/CustomMessage/LockedAttachment/components/_shared/PurchaseStatusBadge.tsx +74 -0
- package/src/components/{LockedAttachment/components/_shared/LockedThumbnail.tsx → CustomMessage/LockedAttachment/components/_shared/SingleThumbnail.tsx} +11 -7
- package/src/components/CustomMessage/LockedAttachment/components/_shared/SkeletonTextLines.tsx +29 -0
- package/src/components/CustomMessage/LockedAttachment/components/_shared/TextShell.tsx +28 -0
- package/src/components/CustomMessage/LockedAttachment/components/_shared/usePreviewToggle.test.ts +67 -0
- package/src/components/CustomMessage/LockedAttachment/components/_shared/usePreviewToggle.ts +89 -0
- package/src/components/CustomMessage/LockedAttachment/components/_shared/useReceivedSource.ts +83 -0
- package/src/components/CustomMessage/LockedAttachment/index.tsx +105 -0
- package/src/components/CustomMessage/LockedAttachment/types.ts +44 -0
- package/src/components/CustomMessage/MessageTag.tsx +8 -0
- package/src/components/CustomMessage/context.tsx +1 -1
- package/src/components/CustomMessage/index.tsx +76 -42
- package/src/components/CustomMessageInput/CustomMessageInput.stories.tsx +5 -3
- package/src/index.ts +3 -8
- package/src/stream-custom-data.ts +1 -0
- package/src/styles.css +12 -0
- package/src/types.ts +1 -1
- package/dist/Card-B-L2MST4.js +0 -107
- package/dist/Card-B-L2MST4.js.map +0 -1
- package/dist/Card-B56QxJW3.cjs +0 -2
- package/dist/Card-B56QxJW3.cjs.map +0 -1
- package/dist/Card-Bqh-BQaX.js +0 -132
- package/dist/Card-Bqh-BQaX.js.map +0 -1
- package/dist/Card-ZCE6Lhfc.cjs +0 -2
- package/dist/Card-ZCE6Lhfc.cjs.map +0 -1
- package/dist/Card-g4JD-2ES.js +0 -163
- package/dist/Card-g4JD-2ES.js.map +0 -1
- package/dist/Card-wac6AMNc.cjs +0 -2
- package/dist/Card-wac6AMNc.cjs.map +0 -1
- package/dist/LockedThumbnail-BAQmoIff.js +0 -220
- package/dist/LockedThumbnail-BAQmoIff.js.map +0 -1
- package/dist/LockedThumbnail-Cr0Gqs3Y.cjs +0 -2
- package/dist/LockedThumbnail-Cr0Gqs3Y.cjs.map +0 -1
- package/dist/index-BPhZyaxT.cjs +0 -2
- package/dist/index-BPhZyaxT.cjs.map +0 -1
- package/dist/index-BxzzWK4K.js.map +0 -1
- package/src/components/LockedAttachment/LockedAttachment.stories.tsx +0 -450
- package/src/components/LockedAttachment/components/Composer/Card.tsx +0 -221
- package/src/components/LockedAttachment/components/Composer/index.ts +0 -2
- package/src/components/LockedAttachment/components/Received/Card.tsx +0 -191
- package/src/components/LockedAttachment/components/Received/index.ts +0 -2
- package/src/components/LockedAttachment/components/Sent/Card.tsx +0 -177
- package/src/components/LockedAttachment/components/Sent/index.ts +0 -2
- package/src/components/LockedAttachment/components/_shared/LockBadge.tsx +0 -39
- package/src/components/LockedAttachment/index.tsx +0 -63
- package/src/components/LockedAttachment/types.ts +0 -51
|
@@ -1,450 +0,0 @@
|
|
|
1
|
-
import type { Meta, StoryFn } from '@storybook/react'
|
|
2
|
-
import React, { useState } from 'react'
|
|
3
|
-
|
|
4
|
-
import type { LockedAttachmentGalleryItem } from './types'
|
|
5
|
-
|
|
6
|
-
import LockedAttachment from '.'
|
|
7
|
-
|
|
8
|
-
const VIDEO_THUMBNAIL_BLURRED = '/video-thumbnail-blurred.jpg'
|
|
9
|
-
const VIDEO_THUMBNAIL = '/video-thumbnail.jpg'
|
|
10
|
-
const VIDEO_SOURCE = '/video-source.mp4'
|
|
11
|
-
|
|
12
|
-
const IMAGE_THUMBNAIL_BLURRED = '/image-thumbnail-blurred.jpg'
|
|
13
|
-
const IMAGE_THUMBNAIL = '/image-thumbnail.jpg'
|
|
14
|
-
const IMAGE_SOURCE = '/image-source.jpg'
|
|
15
|
-
|
|
16
|
-
const DOCUMENT_THUMBNAIL_BLURRED = '/document-thumbnail-blurred.jpg'
|
|
17
|
-
const DOCUMENT_THUMBNAIL = '/document-thumbnail.jpg'
|
|
18
|
-
const DOCUMENT_SOURCE = '/document-source.pdf'
|
|
19
|
-
|
|
20
|
-
const AUDIO_THUMBNAIL_BLURRED = '/audio-thumbnail-blurred.jpg'
|
|
21
|
-
const AUDIO_THUMBNAIL = '/audio-thumbnail.jpg'
|
|
22
|
-
const AUDIO_SOURCE = '/audio-source.mp3'
|
|
23
|
-
|
|
24
|
-
const meta: Meta = {
|
|
25
|
-
title: 'LockedAttachment',
|
|
26
|
-
parameters: { layout: 'fullscreen' },
|
|
27
|
-
}
|
|
28
|
-
export default meta
|
|
29
|
-
|
|
30
|
-
const VARIANTS = [
|
|
31
|
-
{
|
|
32
|
-
label: 'Video',
|
|
33
|
-
title: "Alicia's Workout Plan",
|
|
34
|
-
mimeType: 'video/mp4',
|
|
35
|
-
detail: '1:20',
|
|
36
|
-
thumbnailUrl: VIDEO_THUMBNAIL_BLURRED,
|
|
37
|
-
thumbnailUnlockedUrl: VIDEO_THUMBNAIL,
|
|
38
|
-
sourceUrl: VIDEO_SOURCE,
|
|
39
|
-
},
|
|
40
|
-
{
|
|
41
|
-
label: 'Audio',
|
|
42
|
-
title: 'Morning Meditation',
|
|
43
|
-
mimeType: 'audio/mpeg',
|
|
44
|
-
detail: '4:35',
|
|
45
|
-
thumbnailUrl: AUDIO_THUMBNAIL_BLURRED,
|
|
46
|
-
thumbnailUnlockedUrl: AUDIO_THUMBNAIL,
|
|
47
|
-
sourceUrl: AUDIO_SOURCE,
|
|
48
|
-
},
|
|
49
|
-
{
|
|
50
|
-
label: 'Image',
|
|
51
|
-
title: 'Picture of my cat',
|
|
52
|
-
mimeType: 'image/jpeg',
|
|
53
|
-
detail: '3.2 MB',
|
|
54
|
-
thumbnailUrl: IMAGE_THUMBNAIL_BLURRED,
|
|
55
|
-
thumbnailUnlockedUrl: IMAGE_THUMBNAIL,
|
|
56
|
-
sourceUrl: IMAGE_SOURCE,
|
|
57
|
-
},
|
|
58
|
-
{
|
|
59
|
-
label: 'Document',
|
|
60
|
-
title: 'Strength Training Guide',
|
|
61
|
-
mimeType: 'application/zip',
|
|
62
|
-
detail: '14 files',
|
|
63
|
-
thumbnailUrl: DOCUMENT_THUMBNAIL_BLURRED,
|
|
64
|
-
thumbnailUnlockedUrl: DOCUMENT_THUMBNAIL,
|
|
65
|
-
sourceUrl: DOCUMENT_SOURCE,
|
|
66
|
-
},
|
|
67
|
-
{
|
|
68
|
-
label: 'Unknown',
|
|
69
|
-
title: 'Unknown Attachment',
|
|
70
|
-
mimeType: 'application/octet-stream',
|
|
71
|
-
detail: '2.4 MB',
|
|
72
|
-
thumbnailUrl: undefined,
|
|
73
|
-
thumbnailUnlockedUrl: undefined,
|
|
74
|
-
sourceUrl: DOCUMENT_SOURCE,
|
|
75
|
-
},
|
|
76
|
-
]
|
|
77
|
-
|
|
78
|
-
/**
|
|
79
|
-
* A mixed-media collection (a video + 3 photos) used by the Gallery stories
|
|
80
|
-
* below. Each `LockedAttachment.*` card accepts a `gallery` prop and renders
|
|
81
|
-
* a carousel of these items when 2+ items are provided.
|
|
82
|
-
*/
|
|
83
|
-
const GALLERY_ITEMS: LockedAttachmentGalleryItem[] = [
|
|
84
|
-
{
|
|
85
|
-
mimeType: 'video/mp4',
|
|
86
|
-
thumbnailUrl: VIDEO_THUMBNAIL_BLURRED,
|
|
87
|
-
sourceUrl: VIDEO_SOURCE,
|
|
88
|
-
},
|
|
89
|
-
{
|
|
90
|
-
mimeType: 'image/jpeg',
|
|
91
|
-
thumbnailUrl: IMAGE_THUMBNAIL_BLURRED,
|
|
92
|
-
sourceUrl: IMAGE_SOURCE,
|
|
93
|
-
},
|
|
94
|
-
{
|
|
95
|
-
mimeType: 'image/jpeg',
|
|
96
|
-
thumbnailUrl: DOCUMENT_THUMBNAIL_BLURRED,
|
|
97
|
-
sourceUrl: DOCUMENT_SOURCE,
|
|
98
|
-
},
|
|
99
|
-
{
|
|
100
|
-
mimeType: 'image/jpeg',
|
|
101
|
-
thumbnailUrl: AUDIO_THUMBNAIL_BLURRED,
|
|
102
|
-
sourceUrl: AUDIO_SOURCE,
|
|
103
|
-
},
|
|
104
|
-
]
|
|
105
|
-
|
|
106
|
-
const GALLERY_DETAIL = '1 video, 3 photos'
|
|
107
|
-
const GALLERY_TITLE = 'Workout Guide'
|
|
108
|
-
|
|
109
|
-
const Table = ({ children }: { children: React.ReactNode }) => (
|
|
110
|
-
<div className="min-h-screen w-full p-12 bg-[#F9F7F4]">
|
|
111
|
-
<table className="border-separate border-spacing-4">{children}</table>
|
|
112
|
-
</div>
|
|
113
|
-
)
|
|
114
|
-
|
|
115
|
-
const TableHead = ({
|
|
116
|
-
variants,
|
|
117
|
-
}: {
|
|
118
|
-
variants: { label: string; mimeType: string }[]
|
|
119
|
-
}) => (
|
|
120
|
-
<thead>
|
|
121
|
-
<tr>
|
|
122
|
-
<th className="text-left text-xs font-medium text-black/40 pb-2" />
|
|
123
|
-
{variants.map(({ label, mimeType }) => (
|
|
124
|
-
<th
|
|
125
|
-
key={mimeType}
|
|
126
|
-
className="text-left text-xs font-medium text-black/40 pb-2"
|
|
127
|
-
>
|
|
128
|
-
{label}
|
|
129
|
-
</th>
|
|
130
|
-
))}
|
|
131
|
-
</tr>
|
|
132
|
-
</thead>
|
|
133
|
-
)
|
|
134
|
-
|
|
135
|
-
export const Composer: StoryFn = () => (
|
|
136
|
-
<Table>
|
|
137
|
-
<TableHead variants={VARIANTS} />
|
|
138
|
-
<tbody>
|
|
139
|
-
<tr>
|
|
140
|
-
<td className="text-xs text-right font-medium text-black/40 pr-4 align-top pt-2">
|
|
141
|
-
Preview
|
|
142
|
-
</td>
|
|
143
|
-
{VARIANTS.map(
|
|
144
|
-
({
|
|
145
|
-
mimeType,
|
|
146
|
-
detail,
|
|
147
|
-
thumbnailUrl,
|
|
148
|
-
thumbnailUnlockedUrl,
|
|
149
|
-
sourceUrl,
|
|
150
|
-
}) => (
|
|
151
|
-
<td key={mimeType} className="align-top">
|
|
152
|
-
<LockedAttachment.Composer
|
|
153
|
-
placeholderTitle="Attachment title"
|
|
154
|
-
placeholderAmountText="AU$0.00"
|
|
155
|
-
thumbnailUrl={thumbnailUrl}
|
|
156
|
-
mimeType={mimeType}
|
|
157
|
-
detail={detail}
|
|
158
|
-
onFetchSource={async () => {
|
|
159
|
-
await new Promise((resolve) => setTimeout(resolve, 1500))
|
|
160
|
-
return {
|
|
161
|
-
sourceUrl: sourceUrl,
|
|
162
|
-
thumbnailUrl: thumbnailUnlockedUrl,
|
|
163
|
-
}
|
|
164
|
-
}}
|
|
165
|
-
/>
|
|
166
|
-
</td>
|
|
167
|
-
)
|
|
168
|
-
)}
|
|
169
|
-
</tr>
|
|
170
|
-
<tr>
|
|
171
|
-
<td className="text-xs text-right font-medium text-black/40 pr-4 align-top pt-2">
|
|
172
|
-
Pending
|
|
173
|
-
</td>
|
|
174
|
-
{VARIANTS.map(({ title, mimeType, detail, thumbnailUrl }) => (
|
|
175
|
-
<td key={mimeType} className="align-top">
|
|
176
|
-
<LockedAttachment.Composer
|
|
177
|
-
title={title}
|
|
178
|
-
thumbnailUrl={thumbnailUrl}
|
|
179
|
-
mimeType={mimeType}
|
|
180
|
-
detail={detail}
|
|
181
|
-
amountText="AU$9.99"
|
|
182
|
-
onDismiss={() => alert('Dismissed')}
|
|
183
|
-
/>
|
|
184
|
-
</td>
|
|
185
|
-
))}
|
|
186
|
-
</tr>
|
|
187
|
-
</tbody>
|
|
188
|
-
</Table>
|
|
189
|
-
)
|
|
190
|
-
|
|
191
|
-
export const Sent: StoryFn = () => (
|
|
192
|
-
<Table>
|
|
193
|
-
<TableHead variants={VARIANTS} />
|
|
194
|
-
<tbody>
|
|
195
|
-
<tr>
|
|
196
|
-
<td className="text-xs text-right font-medium text-black/40 pr-4 align-top pt-2">
|
|
197
|
-
Sent
|
|
198
|
-
</td>
|
|
199
|
-
{VARIANTS.map(
|
|
200
|
-
({
|
|
201
|
-
title,
|
|
202
|
-
mimeType,
|
|
203
|
-
detail,
|
|
204
|
-
thumbnailUrl,
|
|
205
|
-
thumbnailUnlockedUrl,
|
|
206
|
-
sourceUrl,
|
|
207
|
-
}) => (
|
|
208
|
-
<td key={mimeType} className="align-top">
|
|
209
|
-
<LockedAttachment.Sent
|
|
210
|
-
title={title}
|
|
211
|
-
thumbnailUrl={thumbnailUrl}
|
|
212
|
-
mimeType={mimeType}
|
|
213
|
-
detail={detail}
|
|
214
|
-
amountText="AU$9.99"
|
|
215
|
-
onFetchSource={async () => {
|
|
216
|
-
await new Promise((resolve) => setTimeout(resolve, 1500))
|
|
217
|
-
return {
|
|
218
|
-
sourceUrl: sourceUrl,
|
|
219
|
-
thumbnailUrl: thumbnailUnlockedUrl,
|
|
220
|
-
}
|
|
221
|
-
}}
|
|
222
|
-
/>
|
|
223
|
-
</td>
|
|
224
|
-
)
|
|
225
|
-
)}
|
|
226
|
-
</tr>
|
|
227
|
-
<tr>
|
|
228
|
-
<td className="text-xs text-right font-medium text-black/40 pr-4 align-top pt-2">
|
|
229
|
-
Sold
|
|
230
|
-
</td>
|
|
231
|
-
{VARIANTS.map(
|
|
232
|
-
({
|
|
233
|
-
title,
|
|
234
|
-
mimeType,
|
|
235
|
-
detail,
|
|
236
|
-
thumbnailUrl,
|
|
237
|
-
thumbnailUnlockedUrl,
|
|
238
|
-
sourceUrl,
|
|
239
|
-
}) => (
|
|
240
|
-
<td key={mimeType} className="align-top">
|
|
241
|
-
<LockedAttachment.Sent
|
|
242
|
-
title={title}
|
|
243
|
-
thumbnailUrl={thumbnailUrl}
|
|
244
|
-
mimeType={mimeType}
|
|
245
|
-
detail={detail}
|
|
246
|
-
amountText="AU$9.99"
|
|
247
|
-
paymentStatus="paid"
|
|
248
|
-
onFetchSource={async () => {
|
|
249
|
-
await new Promise((resolve) => setTimeout(resolve, 1500))
|
|
250
|
-
return {
|
|
251
|
-
sourceUrl: sourceUrl,
|
|
252
|
-
thumbnailUrl: thumbnailUnlockedUrl,
|
|
253
|
-
}
|
|
254
|
-
}}
|
|
255
|
-
/>
|
|
256
|
-
</td>
|
|
257
|
-
)
|
|
258
|
-
)}
|
|
259
|
-
</tr>
|
|
260
|
-
</tbody>
|
|
261
|
-
</Table>
|
|
262
|
-
)
|
|
263
|
-
|
|
264
|
-
export const Gallery: StoryFn = () => {
|
|
265
|
-
const [isPaid, setPaid] = useState(false)
|
|
266
|
-
const [isUnlocking, setUnlocking] = useState(false)
|
|
267
|
-
|
|
268
|
-
return (
|
|
269
|
-
<Table>
|
|
270
|
-
<thead>
|
|
271
|
-
<tr>
|
|
272
|
-
<th className="text-left text-xs font-medium text-black/40 pb-2" />
|
|
273
|
-
<th className="text-left text-xs font-medium text-black/40 pb-2">
|
|
274
|
-
Composer
|
|
275
|
-
</th>
|
|
276
|
-
<th className="text-left text-xs font-medium text-black/40 pb-2">
|
|
277
|
-
Sent
|
|
278
|
-
</th>
|
|
279
|
-
<th className="text-left text-xs font-medium text-black/40 pb-2">
|
|
280
|
-
Received
|
|
281
|
-
</th>
|
|
282
|
-
</tr>
|
|
283
|
-
</thead>
|
|
284
|
-
<tbody>
|
|
285
|
-
<tr>
|
|
286
|
-
<td className="text-xs text-right font-medium text-black/40 pr-4 align-top pt-2">
|
|
287
|
-
Locked
|
|
288
|
-
</td>
|
|
289
|
-
<td className="align-top">
|
|
290
|
-
<LockedAttachment.Composer
|
|
291
|
-
title={GALLERY_TITLE}
|
|
292
|
-
detail={GALLERY_DETAIL}
|
|
293
|
-
amountText="AU$50.00"
|
|
294
|
-
gallery={GALLERY_ITEMS}
|
|
295
|
-
onDismiss={() => alert('Dismissed gallery')}
|
|
296
|
-
onEditClick={() => alert('Edit gallery')}
|
|
297
|
-
/>
|
|
298
|
-
</td>
|
|
299
|
-
<td className="align-top">
|
|
300
|
-
<LockedAttachment.Sent
|
|
301
|
-
title={GALLERY_TITLE}
|
|
302
|
-
detail={GALLERY_DETAIL}
|
|
303
|
-
amountText="AU$50.00"
|
|
304
|
-
gallery={GALLERY_ITEMS}
|
|
305
|
-
/>
|
|
306
|
-
</td>
|
|
307
|
-
<td className="align-top">
|
|
308
|
-
<LockedAttachment.Received
|
|
309
|
-
title={GALLERY_TITLE}
|
|
310
|
-
detail={GALLERY_DETAIL}
|
|
311
|
-
amountText="AU$50.00"
|
|
312
|
-
gallery={GALLERY_ITEMS}
|
|
313
|
-
paymentStatus={isPaid ? 'paid' : undefined}
|
|
314
|
-
isUnlocking={isUnlocking}
|
|
315
|
-
onUnlockClick={() => {
|
|
316
|
-
setUnlocking(true)
|
|
317
|
-
setTimeout(() => {
|
|
318
|
-
setUnlocking(false)
|
|
319
|
-
setPaid(true)
|
|
320
|
-
}, 1500)
|
|
321
|
-
}}
|
|
322
|
-
onDownloadClick={() => alert('Download gallery')}
|
|
323
|
-
/>
|
|
324
|
-
</td>
|
|
325
|
-
</tr>
|
|
326
|
-
<tr>
|
|
327
|
-
<td className="text-xs text-right font-medium text-black/40 pr-4 align-top pt-2">
|
|
328
|
-
Sold / Purchased
|
|
329
|
-
</td>
|
|
330
|
-
<td className="align-top">
|
|
331
|
-
<LockedAttachment.Composer
|
|
332
|
-
title={GALLERY_TITLE}
|
|
333
|
-
detail={GALLERY_DETAIL}
|
|
334
|
-
amountText="AU$50.00"
|
|
335
|
-
gallery={GALLERY_ITEMS}
|
|
336
|
-
onEditClick={() => alert('Edit gallery')}
|
|
337
|
-
/>
|
|
338
|
-
</td>
|
|
339
|
-
<td className="align-top">
|
|
340
|
-
<LockedAttachment.Sent
|
|
341
|
-
title={GALLERY_TITLE}
|
|
342
|
-
detail={GALLERY_DETAIL}
|
|
343
|
-
amountText="AU$50.00"
|
|
344
|
-
paymentStatus="paid"
|
|
345
|
-
gallery={GALLERY_ITEMS}
|
|
346
|
-
/>
|
|
347
|
-
</td>
|
|
348
|
-
<td className="align-top">
|
|
349
|
-
<LockedAttachment.Received
|
|
350
|
-
title={GALLERY_TITLE}
|
|
351
|
-
detail={GALLERY_DETAIL}
|
|
352
|
-
amountText="AU$50.00"
|
|
353
|
-
paymentStatus="paid"
|
|
354
|
-
gallery={GALLERY_ITEMS}
|
|
355
|
-
onDownloadClick={() => alert('Download gallery')}
|
|
356
|
-
/>
|
|
357
|
-
</td>
|
|
358
|
-
</tr>
|
|
359
|
-
</tbody>
|
|
360
|
-
</Table>
|
|
361
|
-
)
|
|
362
|
-
}
|
|
363
|
-
|
|
364
|
-
export const Received: StoryFn = () => {
|
|
365
|
-
const [isPaid, setPaid] = useState<string | undefined>()
|
|
366
|
-
const [isUnlocking, setUnlocking] = useState<string | undefined>()
|
|
367
|
-
|
|
368
|
-
return (
|
|
369
|
-
<Table>
|
|
370
|
-
<TableHead variants={VARIANTS} />
|
|
371
|
-
<tbody>
|
|
372
|
-
<tr>
|
|
373
|
-
<td className="text-xs text-right font-medium text-black/40 pr-4 align-top pt-2">
|
|
374
|
-
Locked
|
|
375
|
-
</td>
|
|
376
|
-
{VARIANTS.map(
|
|
377
|
-
({
|
|
378
|
-
title,
|
|
379
|
-
mimeType,
|
|
380
|
-
detail,
|
|
381
|
-
thumbnailUrl,
|
|
382
|
-
thumbnailUnlockedUrl,
|
|
383
|
-
sourceUrl,
|
|
384
|
-
}) => (
|
|
385
|
-
<td key={mimeType} className="align-top">
|
|
386
|
-
<LockedAttachment.Received
|
|
387
|
-
title={title}
|
|
388
|
-
thumbnailUrl={thumbnailUrl}
|
|
389
|
-
mimeType={mimeType}
|
|
390
|
-
detail={detail}
|
|
391
|
-
paymentStatus={isPaid === mimeType ? 'paid' : undefined}
|
|
392
|
-
amountText="AU$9.99"
|
|
393
|
-
isUnlocking={isUnlocking === mimeType}
|
|
394
|
-
onUnlockClick={() => {
|
|
395
|
-
setUnlocking(mimeType)
|
|
396
|
-
setTimeout(() => {
|
|
397
|
-
setUnlocking(undefined)
|
|
398
|
-
setPaid(mimeType)
|
|
399
|
-
}, 1500)
|
|
400
|
-
}}
|
|
401
|
-
onDownloadClick={() => alert('Download clicked')}
|
|
402
|
-
onFetchSource={async () => ({
|
|
403
|
-
sourceUrl: sourceUrl,
|
|
404
|
-
thumbnailUrl: thumbnailUnlockedUrl,
|
|
405
|
-
})}
|
|
406
|
-
/>
|
|
407
|
-
</td>
|
|
408
|
-
)
|
|
409
|
-
)}
|
|
410
|
-
</tr>
|
|
411
|
-
<tr>
|
|
412
|
-
<td className="text-xs text-right font-medium text-black/40 pr-4 align-top pt-2">
|
|
413
|
-
Unlocked
|
|
414
|
-
</td>
|
|
415
|
-
{VARIANTS.map(
|
|
416
|
-
({
|
|
417
|
-
title,
|
|
418
|
-
mimeType,
|
|
419
|
-
detail,
|
|
420
|
-
thumbnailUrl,
|
|
421
|
-
thumbnailUnlockedUrl,
|
|
422
|
-
sourceUrl,
|
|
423
|
-
}) => (
|
|
424
|
-
<td key={mimeType} className="align-top">
|
|
425
|
-
<LockedAttachment.Received
|
|
426
|
-
title={title}
|
|
427
|
-
thumbnailUrl={thumbnailUrl}
|
|
428
|
-
mimeType={mimeType}
|
|
429
|
-
detail={detail}
|
|
430
|
-
amountText="AU$9.99"
|
|
431
|
-
paymentStatus="paid"
|
|
432
|
-
onUnlockClick={() => alert('Unlock clicked')}
|
|
433
|
-
onDownloadClick={() => alert('Download clicked')}
|
|
434
|
-
onFetchSource={async () => ({
|
|
435
|
-
sourceUrl: sourceUrl,
|
|
436
|
-
thumbnailUrl: thumbnailUnlockedUrl,
|
|
437
|
-
})}
|
|
438
|
-
/>
|
|
439
|
-
</td>
|
|
440
|
-
)
|
|
441
|
-
)}
|
|
442
|
-
</tr>
|
|
443
|
-
</tbody>
|
|
444
|
-
</Table>
|
|
445
|
-
)
|
|
446
|
-
}
|
|
447
|
-
// LockedAttachment.Received autoplays the video preview once paid; even with
|
|
448
|
-
// our SMIL/CSS animation freeze the <video> element advances frames
|
|
449
|
-
// independently and produces a non-deterministic snapshot. Skip in Chromatic.
|
|
450
|
-
Received.parameters = { chromatic: { disableSnapshot: true } }
|
|
@@ -1,221 +0,0 @@
|
|
|
1
|
-
import { ImagesIcon, PencilSimpleIcon, XIcon } from '@phosphor-icons/react'
|
|
2
|
-
import React, { useCallback, useRef, useState } from 'react'
|
|
3
|
-
|
|
4
|
-
import type {
|
|
5
|
-
LockedAttachmentBaseProps,
|
|
6
|
-
LockedAttachmentSource,
|
|
7
|
-
} from '../../types'
|
|
8
|
-
import CardBody from '../_shared/CardBody'
|
|
9
|
-
import GalleryThumbnail from '../_shared/GalleryThumbnail'
|
|
10
|
-
import LockedCardShell from '../_shared/LockedCardShell'
|
|
11
|
-
import LockedThumbnail from '../_shared/LockedThumbnail'
|
|
12
|
-
|
|
13
|
-
export interface ComposerCardProps extends LockedAttachmentBaseProps {
|
|
14
|
-
/** Placeholder shown in the title slot before the composer types one. */
|
|
15
|
-
placeholderTitle?: string
|
|
16
|
-
/** Placeholder shown in the amount slot before one is configured. */
|
|
17
|
-
placeholderAmountText?: string
|
|
18
|
-
/**
|
|
19
|
-
* When provided, renders a dismiss X in the thumbnail corner. Called when
|
|
20
|
-
* the composer clicks it to remove the attachment.
|
|
21
|
-
*/
|
|
22
|
-
onDismiss?: () => void
|
|
23
|
-
/** Fired the first time the composer taps the thumbnail to preview. */
|
|
24
|
-
onPreviewClick?: () => void
|
|
25
|
-
/**
|
|
26
|
-
* Lazily loads the underlying source so the composer can preview the
|
|
27
|
-
* attachment they're about to send. Called the first time the thumbnail is
|
|
28
|
-
* tapped; the returned source is cached and reused on subsequent toggles.
|
|
29
|
-
*/
|
|
30
|
-
onFetchSource?: () => Promise<LockedAttachmentSource | void>
|
|
31
|
-
/**
|
|
32
|
-
* When provided, renders a pencil button in the body bottom-right that the
|
|
33
|
-
* composer can use to edit the attachment metadata (e.g. open the price /
|
|
34
|
-
* gallery editor). Matches the Composer "Button" instance in Figma.
|
|
35
|
-
*/
|
|
36
|
-
onEditClick?: () => void
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
/**
|
|
40
|
-
* The card the composer sees while drafting a paid attachment.
|
|
41
|
-
* Matches the Composer column of the messaging design system in Figma.
|
|
42
|
-
*/
|
|
43
|
-
const ComposerCard: React.FC<ComposerCardProps> = ({
|
|
44
|
-
title,
|
|
45
|
-
mimeType = 'application/octet-stream',
|
|
46
|
-
thumbnailUrl,
|
|
47
|
-
detail,
|
|
48
|
-
amountText,
|
|
49
|
-
placeholderTitle = 'Attachment title',
|
|
50
|
-
placeholderAmountText,
|
|
51
|
-
gallery,
|
|
52
|
-
onDismiss,
|
|
53
|
-
onPreviewClick,
|
|
54
|
-
onFetchSource,
|
|
55
|
-
onEditClick,
|
|
56
|
-
}) => {
|
|
57
|
-
const [source, setSource] = useState<LockedAttachmentSource | undefined>()
|
|
58
|
-
const [isPreviewVisible, setIsPreviewVisible] = useState(false)
|
|
59
|
-
const [isLoadingPreview, setIsLoadingPreview] = useState(false)
|
|
60
|
-
const fetchingRef = useRef(false)
|
|
61
|
-
|
|
62
|
-
const isGallery = (gallery?.length ?? 0) >= 2
|
|
63
|
-
|
|
64
|
-
const handleToggle = useCallback(async () => {
|
|
65
|
-
onPreviewClick?.()
|
|
66
|
-
if (isPreviewVisible) {
|
|
67
|
-
setIsPreviewVisible(false)
|
|
68
|
-
return
|
|
69
|
-
}
|
|
70
|
-
// Gallery items carry their own per-item sources on the `gallery` prop, so
|
|
71
|
-
// we just flip visibility — no async source fetch is needed.
|
|
72
|
-
if (isGallery) {
|
|
73
|
-
setIsPreviewVisible(true)
|
|
74
|
-
return
|
|
75
|
-
}
|
|
76
|
-
if (source) {
|
|
77
|
-
setIsPreviewVisible(true)
|
|
78
|
-
return
|
|
79
|
-
}
|
|
80
|
-
if (!onFetchSource) return
|
|
81
|
-
if (fetchingRef.current) return
|
|
82
|
-
fetchingRef.current = true
|
|
83
|
-
setIsLoadingPreview(true)
|
|
84
|
-
try {
|
|
85
|
-
const result = await onFetchSource()
|
|
86
|
-
if (result) {
|
|
87
|
-
setSource(result)
|
|
88
|
-
setIsPreviewVisible(true)
|
|
89
|
-
}
|
|
90
|
-
} finally {
|
|
91
|
-
fetchingRef.current = false
|
|
92
|
-
setIsLoadingPreview(false)
|
|
93
|
-
}
|
|
94
|
-
}, [isPreviewVisible, isGallery, source, onPreviewClick, onFetchSource])
|
|
95
|
-
|
|
96
|
-
// Gallery is always previewable in the composer because each item's source
|
|
97
|
-
// is already provided up-front via `gallery[*].sourceUrl`.
|
|
98
|
-
const togglePreview =
|
|
99
|
-
isGallery || onFetchSource || onPreviewClick ? handleToggle : undefined
|
|
100
|
-
const showLocked = !isPreviewVisible
|
|
101
|
-
const isBusy = isLoadingPreview
|
|
102
|
-
|
|
103
|
-
const statusBadge = (
|
|
104
|
-
<React.Fragment>
|
|
105
|
-
<span className="text-xs font-medium text-white/55">•</span>
|
|
106
|
-
<span
|
|
107
|
-
className={
|
|
108
|
-
amountText
|
|
109
|
-
? 'text-xs font-medium text-white/55'
|
|
110
|
-
: 'text-xs font-medium text-white/30'
|
|
111
|
-
}
|
|
112
|
-
>
|
|
113
|
-
{amountText || placeholderAmountText}
|
|
114
|
-
</span>
|
|
115
|
-
</React.Fragment>
|
|
116
|
-
)
|
|
117
|
-
|
|
118
|
-
const dismissButton = onDismiss ? (
|
|
119
|
-
<button
|
|
120
|
-
type="button"
|
|
121
|
-
onClick={(e) => {
|
|
122
|
-
// Stop the click from bubbling up into the outer preview-toggle
|
|
123
|
-
// wrapper when both are wired up at the same time.
|
|
124
|
-
e.stopPropagation()
|
|
125
|
-
onDismiss()
|
|
126
|
-
}}
|
|
127
|
-
className="flex size-6 items-center justify-center rounded-full bg-[#121110] text-white"
|
|
128
|
-
aria-label="Dismiss attachment"
|
|
129
|
-
>
|
|
130
|
-
<XIcon className="size-3" weight="bold" />
|
|
131
|
-
</button>
|
|
132
|
-
) : undefined
|
|
133
|
-
|
|
134
|
-
const editButton = onEditClick ? (
|
|
135
|
-
<button
|
|
136
|
-
type="button"
|
|
137
|
-
onClick={onEditClick}
|
|
138
|
-
aria-label="Edit attachment"
|
|
139
|
-
className="flex size-10 items-center justify-center rounded-full bg-white/10 text-white hover:bg-white/15"
|
|
140
|
-
>
|
|
141
|
-
<PencilSimpleIcon className="size-5" weight="regular" />
|
|
142
|
-
</button>
|
|
143
|
-
) : undefined
|
|
144
|
-
|
|
145
|
-
const thumbnail = isGallery ? (
|
|
146
|
-
<GalleryThumbnail
|
|
147
|
-
variant="dark"
|
|
148
|
-
gallery={gallery!}
|
|
149
|
-
title={title}
|
|
150
|
-
showLocked={showLocked}
|
|
151
|
-
topRight={dismissButton}
|
|
152
|
-
/>
|
|
153
|
-
) : (
|
|
154
|
-
<LockedThumbnail
|
|
155
|
-
variant="dark"
|
|
156
|
-
mimeType={mimeType}
|
|
157
|
-
thumbnailUrl={thumbnailUrl}
|
|
158
|
-
title={title}
|
|
159
|
-
source={source}
|
|
160
|
-
showLocked={showLocked}
|
|
161
|
-
topRight={dismissButton}
|
|
162
|
-
/>
|
|
163
|
-
)
|
|
164
|
-
|
|
165
|
-
return (
|
|
166
|
-
<LockedCardShell variant="dark">
|
|
167
|
-
{togglePreview ? (
|
|
168
|
-
// Uses a `<div role="button">` rather than a native `<button>` so the
|
|
169
|
-
// dismiss control rendered inside `thumbnail` (also a `<button>`)
|
|
170
|
-
// doesn't produce invalid nested interactive elements.
|
|
171
|
-
<div
|
|
172
|
-
role="button"
|
|
173
|
-
tabIndex={isBusy ? -1 : 0}
|
|
174
|
-
aria-label="Toggle preview"
|
|
175
|
-
aria-busy={isBusy}
|
|
176
|
-
aria-pressed={!showLocked}
|
|
177
|
-
aria-disabled={isBusy || undefined}
|
|
178
|
-
onClick={isBusy ? undefined : togglePreview}
|
|
179
|
-
onKeyDown={(e) => {
|
|
180
|
-
if (isBusy) return
|
|
181
|
-
// Only handle keys that originate on the wrapper itself, not on
|
|
182
|
-
// inner interactive elements (dismiss X, carousel arrows). Without
|
|
183
|
-
// this guard, pressing Enter/Space on a focused child would bubble
|
|
184
|
-
// up and toggle the preview in addition to activating the child.
|
|
185
|
-
if (e.target !== e.currentTarget) return
|
|
186
|
-
if (e.key === 'Enter' || e.key === ' ') {
|
|
187
|
-
e.preventDefault()
|
|
188
|
-
void togglePreview()
|
|
189
|
-
}
|
|
190
|
-
}}
|
|
191
|
-
className={
|
|
192
|
-
!isBusy
|
|
193
|
-
? 'block w-full cursor-pointer text-left'
|
|
194
|
-
: 'block w-full text-left'
|
|
195
|
-
}
|
|
196
|
-
>
|
|
197
|
-
{thumbnail}
|
|
198
|
-
</div>
|
|
199
|
-
) : (
|
|
200
|
-
thumbnail
|
|
201
|
-
)}
|
|
202
|
-
|
|
203
|
-
<CardBody
|
|
204
|
-
variant="dark"
|
|
205
|
-
title={title}
|
|
206
|
-
placeholderTitle={placeholderTitle}
|
|
207
|
-
mimeType={mimeType}
|
|
208
|
-
detail={detail}
|
|
209
|
-
statusBadge={statusBadge}
|
|
210
|
-
icon={
|
|
211
|
-
isGallery ? (
|
|
212
|
-
<ImagesIcon className="size-5 shrink-0 text-white/55" />
|
|
213
|
-
) : undefined
|
|
214
|
-
}
|
|
215
|
-
trailingAction={editButton}
|
|
216
|
-
/>
|
|
217
|
-
</LockedCardShell>
|
|
218
|
-
)
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
export default ComposerCard
|