@linktr.ee/messaging-react 3.8.1 → 3.9.0-rc-1783407048

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.
Files changed (87) hide show
  1. package/dist/Card-D8yiJ8Uw.cjs +2 -0
  2. package/dist/Card-D8yiJ8Uw.cjs.map +1 -0
  3. package/dist/Card-DSG-wR8t.js +96 -0
  4. package/dist/Card-DSG-wR8t.js.map +1 -0
  5. package/dist/Card-DVfSiq9b.cjs +2 -0
  6. package/dist/Card-DVfSiq9b.cjs.map +1 -0
  7. package/dist/Card-DX9nBx1S.js +221 -0
  8. package/dist/Card-DX9nBx1S.js.map +1 -0
  9. package/dist/Card-iKTcn1wg.cjs +2 -0
  10. package/dist/Card-iKTcn1wg.cjs.map +1 -0
  11. package/dist/Card-qDLtjK6u.js +108 -0
  12. package/dist/Card-qDLtjK6u.js.map +1 -0
  13. package/dist/PurchaseStatusBadge-CJH4S-8Z.js +36 -0
  14. package/dist/PurchaseStatusBadge-CJH4S-8Z.js.map +1 -0
  15. package/dist/PurchaseStatusBadge-UjDzZhLj.cjs +2 -0
  16. package/dist/PurchaseStatusBadge-UjDzZhLj.cjs.map +1 -0
  17. package/dist/TextShell-BiqF2UB0.js +32 -0
  18. package/dist/TextShell-BiqF2UB0.js.map +1 -0
  19. package/dist/TextShell-Bnzh0T75.cjs +2 -0
  20. package/dist/TextShell-Bnzh0T75.cjs.map +1 -0
  21. package/dist/assets/index.css +1 -1
  22. package/dist/{index-BCgRoF_u.js → index-CZTUyiPe.js} +1154 -1113
  23. package/dist/index-CZTUyiPe.js.map +1 -0
  24. package/dist/index-DwUOvMxg.cjs +2 -0
  25. package/dist/index-DwUOvMxg.cjs.map +1 -0
  26. package/dist/index.cjs +1 -1
  27. package/dist/index.d.ts +98 -55
  28. package/dist/index.js +1 -1
  29. package/dist/testing.d.ts +1 -0
  30. package/dist/usePreviewToggle-BG-pfsgP.js +323 -0
  31. package/dist/usePreviewToggle-BG-pfsgP.js.map +1 -0
  32. package/dist/usePreviewToggle-DXQT07ws.cjs +2 -0
  33. package/dist/usePreviewToggle-DXQT07ws.cjs.map +1 -0
  34. package/package.json +2 -2
  35. package/src/components/CustomMessage/CustomMessage.stories.tsx +54 -1
  36. package/src/components/CustomMessage/MessageTag.tsx +8 -0
  37. package/src/components/CustomMessage/index.tsx +61 -22
  38. package/src/components/LockedAttachment/LockedAttachment.stories.tsx +410 -333
  39. package/src/components/LockedAttachment/LockedAttachment.test.tsx +104 -0
  40. package/src/components/LockedAttachment/components/Composer/Card.tsx +97 -120
  41. package/src/components/LockedAttachment/components/Media/Card.tsx +237 -0
  42. package/src/components/LockedAttachment/components/Text/Card.tsx +177 -0
  43. package/src/components/LockedAttachment/components/_shared/CardBody.tsx +3 -3
  44. package/src/components/LockedAttachment/components/_shared/CardThumbnail.tsx +92 -0
  45. package/src/components/LockedAttachment/components/_shared/GalleryThumbnail.tsx +11 -33
  46. package/src/components/LockedAttachment/components/_shared/LoadingDots.tsx +36 -0
  47. package/src/components/LockedAttachment/components/_shared/LockBadge.tsx +21 -11
  48. package/src/components/LockedAttachment/components/_shared/{LockedCardShell.tsx → MediaShell.tsx} +5 -5
  49. package/src/components/LockedAttachment/components/_shared/PreviewToggle.tsx +58 -0
  50. package/src/components/LockedAttachment/components/_shared/PriceLockRow.tsx +21 -0
  51. package/src/components/LockedAttachment/components/_shared/PurchaseStatusBadge.tsx +87 -0
  52. package/src/components/LockedAttachment/components/_shared/{LockedThumbnail.tsx → SingleThumbnail.tsx} +5 -5
  53. package/src/components/LockedAttachment/components/_shared/SkeletonTextLines.tsx +39 -0
  54. package/src/components/LockedAttachment/components/_shared/TextShell.tsx +19 -0
  55. package/src/components/LockedAttachment/components/_shared/usePreviewToggle.test.ts +67 -0
  56. package/src/components/LockedAttachment/components/_shared/usePreviewToggle.ts +89 -0
  57. package/src/components/LockedAttachment/components/_shared/useReceivedSource.ts +83 -0
  58. package/src/components/LockedAttachment/index.tsx +17 -28
  59. package/src/components/LockedAttachment/types.ts +28 -4
  60. package/src/index.ts +2 -5
  61. package/src/stream-custom-data.ts +1 -0
  62. package/src/styles.css +5 -0
  63. package/dist/Card-BRvcFjsq.js +0 -132
  64. package/dist/Card-BRvcFjsq.js.map +0 -1
  65. package/dist/Card-BuXkok0A.cjs +0 -2
  66. package/dist/Card-BuXkok0A.cjs.map +0 -1
  67. package/dist/Card-CvNHv3me.js +0 -107
  68. package/dist/Card-CvNHv3me.js.map +0 -1
  69. package/dist/Card-DOSuQdnk.cjs +0 -2
  70. package/dist/Card-DOSuQdnk.cjs.map +0 -1
  71. package/dist/Card-NnQw99XE.cjs +0 -2
  72. package/dist/Card-NnQw99XE.cjs.map +0 -1
  73. package/dist/Card-jGOcSN1O.js +0 -163
  74. package/dist/Card-jGOcSN1O.js.map +0 -1
  75. package/dist/LockedThumbnail-EsjaAqLR.js +0 -220
  76. package/dist/LockedThumbnail-EsjaAqLR.js.map +0 -1
  77. package/dist/LockedThumbnail-uAauUd5c.cjs +0 -2
  78. package/dist/LockedThumbnail-uAauUd5c.cjs.map +0 -1
  79. package/dist/index-BCgRoF_u.js.map +0 -1
  80. package/dist/index-DRurVQ0-.cjs +0 -2
  81. package/dist/index-DRurVQ0-.cjs.map +0 -1
  82. package/src/components/LockedAttachment/components/Composer/index.ts +0 -2
  83. package/src/components/LockedAttachment/components/Received/Card.tsx +0 -191
  84. package/src/components/LockedAttachment/components/Received/index.ts +0 -2
  85. package/src/components/LockedAttachment/components/Sent/Card.tsx +0 -177
  86. package/src/components/LockedAttachment/components/Sent/index.ts +0 -2
  87. /package/src/components/LockedAttachment/components/{Received → Media}/CardActions.tsx +0 -0
@@ -1,10 +1,162 @@
1
1
  import type { Meta, StoryFn } from '@storybook/react'
2
+ import classNames from 'classnames'
2
3
  import React, { useState } from 'react'
4
+ import { MessageText } from 'stream-chat-react'
3
5
 
4
6
  import type { LockedAttachmentGalleryItem } from './types'
5
7
 
6
8
  import LockedAttachment from '.'
7
9
 
10
+ const meta: Meta = {
11
+ title: 'LockedAttachment',
12
+ parameters: { layout: 'fullscreen' },
13
+ }
14
+ export default meta
15
+
16
+ const MatrixTable: React.FC<{ children: React.ReactNode }> = ({ children }) => (
17
+ <div className="str-chat min-h-screen w-full bg-[#F9F7F4] p-12">
18
+ <table className="border-separate border-spacing-4">
19
+ <thead>
20
+ <tr>
21
+ <th className="w-8 pb-2" />
22
+ <th className="pb-2 text-left text-xs font-medium text-black/40">
23
+ Received (buyer&apos;s view)
24
+ </th>
25
+ <th className="pb-2 text-left text-xs font-medium text-black/40">
26
+ Sent (creator&apos;s view)
27
+ </th>
28
+ </tr>
29
+ </thead>
30
+ <tbody>{children}</tbody>
31
+ </table>
32
+ </div>
33
+ )
34
+
35
+ const MatrixTableRow: React.FC<{
36
+ label: React.ReactNode
37
+ received: React.ReactNode
38
+ sent: React.ReactNode
39
+ }> = ({ label, received, sent }) => (
40
+ <tr>
41
+ <td className="whitespace-nowrap pr-4 align-top text-xs font-medium text-black/40">
42
+ {label}
43
+ </td>
44
+ <td className="align-top">
45
+ <MatrixTableCell isMine={false}>{received}</MatrixTableCell>
46
+ </td>
47
+ <td className="align-top">
48
+ <MatrixTableCell isMine>{sent}</MatrixTableCell>
49
+ </td>
50
+ </tr>
51
+ )
52
+
53
+ /**
54
+ * Mirrors the exact DOM `CustomMessage` produces around a paid TEXT
55
+ * attachment (see `CustomMessage/index.tsx`'s `isTextAttachmentMessage`
56
+ * branch), so the bubble color/radius CSS custom properties (scoped under
57
+ * `.str-chat` in styles.css) resolve the same way they do for the real
58
+ * consumer — no hardcoded fallback colors needed in the cards themselves.
59
+ */
60
+ const MatrixTableCell: React.FC<{
61
+ isMine: boolean
62
+ children: React.ReactNode
63
+ }> = ({ isMine, children }) => (
64
+ <div
65
+ className={classNames(
66
+ 'str-chat__message str-chat__message-simple str-chat__message--regular str-chat__message--received has-no-text',
67
+ isMine
68
+ ? 'str-chat__message--me str-chat__message-simple--me'
69
+ : 'str-chat__message--other'
70
+ )}
71
+ >
72
+ <div className="str-chat__message-inner">
73
+ <div className="str-chat__message-bubble-wrapper">{children}</div>
74
+ </div>
75
+ </div>
76
+ )
77
+
78
+ export const Text: StoryFn = () => {
79
+ const [isUnlocking, setUnlocking] = useState(false)
80
+
81
+ // Matches what `CustomMessage` passes `MessageText` for a revealed paid
82
+ // text attachment (`displayMessage`) — just enough of a `LocalMessage`
83
+ // shape to render the real `str-chat__message-text` markup/CSS.
84
+ const purchasedMessage = {
85
+ text: "Here's the address, see you Thursday at 7!",
86
+ type: 'regular',
87
+ } as Parameters<typeof MessageText>[0]['message']
88
+
89
+ return (
90
+ <MatrixTable>
91
+ <MatrixTableRow
92
+ label="Composer"
93
+ received={null}
94
+ sent={
95
+ <LockedAttachment.Composer
96
+ contentType="text"
97
+ text={purchasedMessage?.text}
98
+ amountText="AU$4.99"
99
+ onDismiss={() => alert('Removed')}
100
+ />
101
+ }
102
+ />
103
+ <MatrixTableRow
104
+ label="Idle"
105
+ received={
106
+ <LockedAttachment.Text
107
+ isMine={false}
108
+ amountText="AU$4.99"
109
+ isUnlocking={isUnlocking}
110
+ onUnlockClick={() => {
111
+ setUnlocking(true)
112
+ setTimeout(() => setUnlocking(false), 1500)
113
+ }}
114
+ />
115
+ }
116
+ sent={
117
+ <LockedAttachment.Text
118
+ isMine
119
+ amountText="AU$4.99"
120
+ paymentStatus="pending"
121
+ />
122
+ }
123
+ />
124
+ <MatrixTableRow
125
+ label="Unlocking"
126
+ received={
127
+ <LockedAttachment.Text
128
+ isMine={false}
129
+ amountText="AU$4.99"
130
+ isUnlocking
131
+ />
132
+ }
133
+ sent={null}
134
+ />
135
+ <MatrixTableRow
136
+ label="Purchased"
137
+ received={
138
+ <LockedAttachment.Text
139
+ isMine={false}
140
+ amountText="AU$4.99"
141
+ paymentStatus="paid"
142
+ >
143
+ <MessageText message={purchasedMessage} />
144
+ </LockedAttachment.Text>
145
+ }
146
+ sent={
147
+ <LockedAttachment.Text
148
+ isMine
149
+ amountText="AU$4.99"
150
+ paymentStatus="paid"
151
+ >
152
+ <MessageText message={purchasedMessage} />
153
+ </LockedAttachment.Text>
154
+ }
155
+ />
156
+ </MatrixTable>
157
+ )
158
+ }
159
+
8
160
  const VIDEO_THUMBNAIL_BLURRED = '/video-thumbnail-blurred.jpg'
9
161
  const VIDEO_THUMBNAIL = '/video-thumbnail.jpg'
10
162
  const VIDEO_SOURCE = '/video-source.mp4'
@@ -21,12 +173,6 @@ const AUDIO_THUMBNAIL_BLURRED = '/audio-thumbnail-blurred.jpg'
21
173
  const AUDIO_THUMBNAIL = '/audio-thumbnail.jpg'
22
174
  const AUDIO_SOURCE = '/audio-source.mp3'
23
175
 
24
- const meta: Meta = {
25
- title: 'LockedAttachment',
26
- parameters: { layout: 'fullscreen' },
27
- }
28
- export default meta
29
-
30
176
  const VARIANTS = [
31
177
  {
32
178
  label: 'Video',
@@ -106,159 +252,166 @@ const GALLERY_ITEMS: LockedAttachmentGalleryItem[] = [
106
252
  const GALLERY_DETAIL = '1 video, 3 photos'
107
253
  const GALLERY_TITLE = 'Workout Guide'
108
254
 
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>
255
+ const MediaMatrix: React.FC<{ variant: (typeof VARIANTS)[number] }> = ({
256
+ variant,
257
+ }) => {
258
+ const [isPaid, setPaid] = useState(false)
259
+ const [isUnlocking, setUnlocking] = useState(false)
260
+ const {
261
+ title,
262
+ mimeType,
263
+ detail,
264
+ thumbnailUrl,
265
+ thumbnailUnlockedUrl,
266
+ sourceUrl,
267
+ } = variant
268
+
269
+ const fetchSource = async () => ({
270
+ sourceUrl,
271
+ thumbnailUrl: thumbnailUnlockedUrl,
272
+ })
273
+
274
+ return (
275
+ <MatrixTable>
276
+ <MatrixTableRow
277
+ label="Preview"
278
+ received={null}
279
+ sent={
280
+ <LockedAttachment.Composer
281
+ placeholderTitle="Attachment title"
282
+ placeholderAmountText="AU$0.00"
283
+ thumbnailUrl={thumbnailUrl}
284
+ mimeType={mimeType}
285
+ detail={detail}
286
+ onFetchSource={fetchSource}
287
+ />
288
+ }
289
+ />
290
+ <MatrixTableRow
291
+ label="Composer"
292
+ received={null}
293
+ sent={
294
+ <LockedAttachment.Composer
295
+ title={title}
296
+ thumbnailUrl={thumbnailUrl}
297
+ mimeType={mimeType}
298
+ detail={detail}
299
+ amountText="AU$9.99"
300
+ onDismiss={() => alert('Dismissed')}
301
+ />
302
+ }
303
+ />
304
+ <MatrixTableRow
305
+ label="Idle"
306
+ received={
307
+ <LockedAttachment.Media
308
+ isMine={false}
309
+ title={title}
310
+ thumbnailUrl={thumbnailUrl}
311
+ mimeType={mimeType}
312
+ detail={detail}
313
+ amountText="AU$9.99"
314
+ paymentStatus={isPaid ? 'paid' : undefined}
315
+ isUnlocking={isUnlocking}
316
+ onUnlockClick={() => {
317
+ setUnlocking(true)
318
+ setTimeout(() => {
319
+ setUnlocking(false)
320
+ setPaid(true)
321
+ }, 1500)
322
+ }}
323
+ onDownloadClick={() => alert('Download clicked')}
324
+ onFetchSource={fetchSource}
325
+ />
326
+ }
327
+ sent={
328
+ <LockedAttachment.Media
329
+ isMine
330
+ title={title}
331
+ thumbnailUrl={thumbnailUrl}
332
+ mimeType={mimeType}
333
+ detail={detail}
334
+ amountText="AU$9.99"
335
+ onFetchSource={fetchSource}
336
+ />
337
+ }
338
+ />
339
+ <MatrixTableRow
340
+ label="Unlocking"
341
+ received={
342
+ <LockedAttachment.Media
343
+ isMine={false}
344
+ title={title}
345
+ thumbnailUrl={thumbnailUrl}
346
+ mimeType={mimeType}
347
+ detail={detail}
348
+ amountText="AU$9.99"
349
+ isUnlocking
350
+ onDownloadClick={() => alert('Download clicked')}
351
+ onFetchSource={fetchSource}
352
+ />
353
+ }
354
+ // Sender never unlocks their own message — no distinct state to show.
355
+ sent={null}
356
+ />
357
+ <MatrixTableRow
358
+ label="Purchased"
359
+ received={
360
+ <LockedAttachment.Media
361
+ isMine={false}
362
+ title={title}
363
+ thumbnailUrl={thumbnailUrl}
364
+ mimeType={mimeType}
365
+ detail={detail}
366
+ amountText="AU$9.99"
367
+ paymentStatus="paid"
368
+ onUnlockClick={() => alert('Unlock clicked')}
369
+ onDownloadClick={() => alert('Download clicked')}
370
+ onFetchSource={fetchSource}
371
+ />
372
+ }
373
+ sent={
374
+ <LockedAttachment.Media
375
+ isMine
376
+ title={title}
377
+ thumbnailUrl={thumbnailUrl}
378
+ mimeType={mimeType}
379
+ detail={detail}
380
+ amountText="AU$9.99"
381
+ paymentStatus="paid"
382
+ onFetchSource={fetchSource}
383
+ />
384
+ }
385
+ />
386
+ </MatrixTable>
387
+ )
388
+ }
389
+
390
+ const variantByLabel = (label: string) =>
391
+ VARIANTS.find((v) => v.label === label)!
392
+
393
+ export const Video: StoryFn = () => (
394
+ <MediaMatrix variant={variantByLabel('Video')} />
113
395
  )
396
+ // LockedAttachment.Media (!isMine) autoplays the video preview once paid; even with
397
+ // our SMIL/CSS animation freeze the <video> element advances frames
398
+ // independently and produces a non-deterministic snapshot. Skip in Chromatic.
399
+ Video.parameters = { chromatic: { disableSnapshot: true } }
114
400
 
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>
401
+ export const Audio: StoryFn = () => (
402
+ <MediaMatrix variant={variantByLabel('Audio')} />
133
403
  )
134
404
 
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>
405
+ export const Image: StoryFn = () => (
406
+ <MediaMatrix variant={variantByLabel('Image')} />
189
407
  )
190
408
 
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>
409
+ export const Document: StoryFn = () => (
410
+ <MediaMatrix variant={variantByLabel('Document')} />
411
+ )
412
+
413
+ export const Unknown: StoryFn = () => (
414
+ <MediaMatrix variant={variantByLabel('Unknown')} />
262
415
  )
263
416
 
264
417
  export const Gallery: StoryFn = () => {
@@ -266,185 +419,109 @@ export const Gallery: StoryFn = () => {
266
419
  const [isUnlocking, setUnlocking] = useState(false)
267
420
 
268
421
  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>
422
+ <MatrixTable>
423
+ <MatrixTableRow
424
+ label="Preview"
425
+ received={null}
426
+ sent={
427
+ <LockedAttachment.Composer
428
+ detail={GALLERY_DETAIL}
429
+ gallery={GALLERY_ITEMS}
430
+ />
431
+ }
432
+ />
433
+ <MatrixTableRow
434
+ label="Composer"
435
+ received={null}
436
+ sent={
437
+ <LockedAttachment.Composer
438
+ title={GALLERY_TITLE}
439
+ detail={GALLERY_DETAIL}
440
+ amountText="AU$50.00"
441
+ gallery={GALLERY_ITEMS}
442
+ onDismiss={() => alert('Dismissed gallery')}
443
+ onEditClick={() => alert('Edit gallery')}
444
+ />
445
+ }
446
+ />
447
+ <MatrixTableRow
448
+ label="Idle"
449
+ received={
450
+ <LockedAttachment.Media
451
+ isMine={false}
452
+ title={GALLERY_TITLE}
453
+ detail={GALLERY_DETAIL}
454
+ amountText="AU$50.00"
455
+ gallery={GALLERY_ITEMS}
456
+ paymentStatus={isPaid ? 'paid' : undefined}
457
+ isUnlocking={isUnlocking}
458
+ onUnlockClick={() => {
459
+ setUnlocking(true)
460
+ setTimeout(() => {
461
+ setUnlocking(false)
462
+ setPaid(true)
463
+ }, 1500)
464
+ }}
465
+ onDownloadClick={() => alert('Download gallery')}
466
+ />
467
+ }
468
+ sent={
469
+ <LockedAttachment.Media
470
+ isMine
471
+ title={GALLERY_TITLE}
472
+ detail={GALLERY_DETAIL}
473
+ amountText="AU$50.00"
474
+ gallery={GALLERY_ITEMS}
475
+ />
476
+ }
477
+ />
478
+ <MatrixTableRow
479
+ label={
480
+ <>
481
+ Unlocking
482
+ <br />
483
+ (pre-purchase)
484
+ </>
485
+ }
486
+ received={
487
+ <LockedAttachment.Media
488
+ isMine={false}
489
+ title={GALLERY_TITLE}
490
+ detail={GALLERY_DETAIL}
491
+ amountText="AU$50.00"
492
+ gallery={GALLERY_ITEMS}
493
+ isUnlocking
494
+ onDownloadClick={() => alert('Download gallery')}
495
+ />
496
+ }
497
+ // Sender never unlocks their own message — no distinct state to show.
498
+ sent={null}
499
+ />
500
+ <MatrixTableRow
501
+ label="Purchased"
502
+ received={
503
+ <LockedAttachment.Media
504
+ isMine={false}
505
+ title={GALLERY_TITLE}
506
+ detail={GALLERY_DETAIL}
507
+ amountText="AU$50.00"
508
+ paymentStatus="paid"
509
+ gallery={GALLERY_ITEMS}
510
+ onUnlockClick={() => alert('Unlock clicked')}
511
+ onDownloadClick={() => alert('Download gallery')}
512
+ />
513
+ }
514
+ sent={
515
+ <LockedAttachment.Media
516
+ isMine
517
+ title={GALLERY_TITLE}
518
+ detail={GALLERY_DETAIL}
519
+ amountText="AU$50.00"
520
+ paymentStatus="paid"
521
+ gallery={GALLERY_ITEMS}
522
+ />
523
+ }
524
+ />
525
+ </MatrixTable>
361
526
  )
362
527
  }
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 } }