@linktr.ee/messaging-react 1.31.0-rc-1776677746 → 1.31.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.
Files changed (35) hide show
  1. package/dist/{Creator-DGe3CQ_j.js → Card-C5t3dZ5q.js} +177 -150
  2. package/dist/Card-C5t3dZ5q.js.map +1 -0
  3. package/dist/Card-Cn2va-Qr.js +205 -0
  4. package/dist/Card-Cn2va-Qr.js.map +1 -0
  5. package/dist/index.d.ts +35 -30
  6. package/dist/index.js +951 -956
  7. package/dist/index.js.map +1 -1
  8. package/package.json +1 -1
  9. package/src/components/ChannelView.tsx +24 -36
  10. package/src/components/CustomMessage/CustomMessage.stories.tsx +1 -14
  11. package/src/components/CustomMessage/context.tsx +20 -0
  12. package/src/components/CustomMessage/index.tsx +39 -28
  13. package/src/components/LockedAttachment/LockedAttachment.stories.tsx +8 -13
  14. package/src/components/LockedAttachment/components/Creator/Card.tsx +159 -0
  15. package/src/components/LockedAttachment/components/Creator/CardAudioPreview.tsx +161 -0
  16. package/src/components/LockedAttachment/components/Creator/CardCollapsedThumbnail.tsx +58 -0
  17. package/src/components/LockedAttachment/components/Creator/CardImagePreview.tsx +56 -0
  18. package/src/components/LockedAttachment/components/Creator/CardVideoPreview.tsx +91 -0
  19. package/src/components/LockedAttachment/components/Creator/index.tsx +2 -0
  20. package/src/components/LockedAttachment/components/Visitor/Card.tsx +186 -0
  21. package/src/components/LockedAttachment/components/Visitor/CardActions.tsx +71 -0
  22. package/src/components/LockedAttachment/components/Visitor/CardImagePreview.tsx +39 -0
  23. package/src/components/LockedAttachment/components/Visitor/CardMediaPreview.tsx +36 -0
  24. package/src/components/LockedAttachment/components/Visitor/CardThumbnailPreview.tsx +45 -0
  25. package/src/components/LockedAttachment/components/Visitor/index.ts +2 -0
  26. package/src/components/LockedAttachment/index.tsx +16 -23
  27. package/src/components/LockedAttachment/types.ts +14 -1
  28. package/src/components/MessagingShell/index.tsx +0 -6
  29. package/src/index.ts +4 -1
  30. package/src/types.ts +0 -21
  31. package/dist/Creator-DGe3CQ_j.js.map +0 -1
  32. package/dist/Visitor-DyJTWB2_.js +0 -204
  33. package/dist/Visitor-DyJTWB2_.js.map +0 -1
  34. package/src/components/LockedAttachment/components/Creator.tsx +0 -470
  35. package/src/components/LockedAttachment/components/Visitor.tsx +0 -356
@@ -1,356 +0,0 @@
1
- import {
2
- CheckCircleIcon,
3
- DownloadSimpleIcon,
4
- LockOpenIcon,
5
- LockSimpleIcon,
6
- } from '@phosphor-icons/react'
7
- import React, { useCallback, useEffect, useState } from 'react'
8
-
9
- import type { LockedAttachmentBaseProps, LockedAttachmentSource, PaymentStatus } from '../types'
10
- import { renderTypeIcon } from '../utils/icons'
11
- import { getSourceType } from '../utils/mimeType'
12
-
13
- import MediaPlayer from './MediaPlayer'
14
-
15
- export interface VisitorCardProps extends LockedAttachmentBaseProps {
16
- /**
17
- * Called when the visitor clicks Unlock. Omit to hide the Unlock button.
18
- */
19
- onUnlockClick?: () => void
20
- /**
21
- * Called when the visitor clicks Download on an unlocked card.
22
- * Omit to hide the Download button.
23
- */
24
- onDownloadClick?: () => void
25
- /**
26
- * Returns current unlock state — sourceUrl (for playback) and redeemUrl (for download).
27
- * Bound to the message by the host before being passed down,
28
- * so this component receives no Stream Chat types.
29
- */
30
- onUnlocked?: () => LockedAttachmentSource
31
- }
32
-
33
- const getLockIcon = (paymentStatus?: PaymentStatus): React.ElementType => {
34
- return paymentStatus === 'paid' ? LockOpenIcon : LockSimpleIcon
35
- }
36
-
37
- interface LockOverlayProps {
38
- icon: React.ElementType
39
- }
40
-
41
- const LockOverlay: React.FC<LockOverlayProps> = (props) => {
42
- const { icon: Icon } = props
43
- return (
44
- <div className="absolute inset-0 bg-black/30">
45
- <div className="absolute left-3 top-3 flex size-8 items-center justify-center rounded-full bg-black/60">
46
- <Icon className="size-4 text-white" weight="fill" />
47
- </div>
48
- </div>
49
- )
50
- }
51
-
52
- interface LockedPreviewProps {
53
- thumbnailUrl?: string
54
- mimeType: string
55
- LockIcon: React.ElementType
56
- }
57
-
58
- const LockedPreview: React.FC<LockedPreviewProps> = (props) => {
59
- const { thumbnailUrl, mimeType, LockIcon } = props
60
- return (
61
- <div className="relative aspect-video overflow-hidden bg-black/5">
62
- {thumbnailUrl ? (
63
- <img
64
- src={thumbnailUrl}
65
- alt=""
66
- className="absolute inset-0 h-full w-full object-cover"
67
- />
68
- ) : (
69
- <div className="absolute inset-0 flex items-center justify-center">
70
- {renderTypeIcon(mimeType, {
71
- className: 'size-12 text-black/20',
72
- weight: 'regular',
73
- })}
74
- </div>
75
- )}
76
- <LockOverlay icon={LockIcon} />
77
- </div>
78
- )
79
- }
80
-
81
- interface ImagePreviewProps {
82
- sourceUrl?: string
83
- thumbnailUrl?: string
84
- mimeType: string
85
- title?: string
86
- paymentStatus?: PaymentStatus
87
- isLocked: boolean
88
- }
89
-
90
- const ImagePreview: React.FC<ImagePreviewProps> = (props) => {
91
- const { sourceUrl, thumbnailUrl, mimeType, title, paymentStatus, isLocked } = props
92
- const [sourceReady, setSourceReady] = useState(false)
93
-
94
- if (isLocked) {
95
- return (
96
- <LockedPreview
97
- thumbnailUrl={thumbnailUrl}
98
- mimeType={mimeType}
99
- LockIcon={getLockIcon(paymentStatus)}
100
- />
101
- )
102
- }
103
-
104
- return (
105
- <div className="relative overflow-hidden bg-black/5">
106
- <img
107
- src={sourceUrl}
108
- alt={title}
109
- className={`block w-full transition-opacity duration-300 ${sourceReady ? 'opacity-100' : 'opacity-0'}`}
110
- onLoad={() => setSourceReady(true)}
111
- />
112
- </div>
113
- )
114
- }
115
-
116
- interface DocumentPreviewProps {
117
- thumbnailUrl?: string
118
- mimeType: string
119
- paymentStatus?: PaymentStatus
120
- isLocked: boolean
121
- }
122
-
123
- const DocumentPreview: React.FC<DocumentPreviewProps> = (props) => {
124
- const { thumbnailUrl, mimeType, paymentStatus, isLocked } = props
125
- return (
126
- <div className="relative aspect-video overflow-hidden bg-black/5">
127
- {thumbnailUrl ? (
128
- <img
129
- src={thumbnailUrl}
130
- alt=""
131
- className="absolute inset-0 h-full w-full object-cover"
132
- />
133
- ) : (
134
- <div className="absolute inset-0 flex items-center justify-center">
135
- {renderTypeIcon(mimeType, {
136
- className: 'size-12 text-black/20',
137
- weight: 'regular',
138
- })}
139
- </div>
140
- )}
141
- {isLocked && <LockOverlay icon={getLockIcon(paymentStatus)} />}
142
- </div>
143
- )
144
- }
145
-
146
- interface MediaPreviewProps {
147
- sourceUrl?: string
148
- thumbnailUrl?: string
149
- mimeType: string
150
- paymentStatus?: PaymentStatus
151
- isLocked: boolean
152
- }
153
-
154
- const MediaPreview: React.FC<MediaPreviewProps> = (props) => {
155
- const { sourceUrl, thumbnailUrl, mimeType, paymentStatus, isLocked } = props
156
- if (isLocked) {
157
- return (
158
- <LockedPreview
159
- thumbnailUrl={thumbnailUrl}
160
- mimeType={mimeType}
161
- LockIcon={getLockIcon(paymentStatus)}
162
- />
163
- )
164
- }
165
- return (
166
- <MediaPlayer
167
- source={sourceUrl!}
168
- mimeType={mimeType}
169
- poster={thumbnailUrl}
170
- />
171
- )
172
- }
173
-
174
- const LoadingDots = () => (
175
- <span className="flex items-center gap-1">
176
- <span className="size-1 rounded-full bg-white animate-bounce [animation-delay:-0.3s]" />
177
- <span className="size-1 rounded-full bg-white animate-bounce [animation-delay:-0.15s]" />
178
- <span className="size-1 rounded-full bg-white animate-bounce" />
179
- </span>
180
- )
181
-
182
- interface CardActionsProps {
183
- isLocked: boolean
184
- isUnlocking?: boolean
185
- sourceUrl?: string
186
- redeemUrl?: string
187
- onUnlockClicked?: () => void
188
- onDownloadClicked?: () => void
189
- }
190
-
191
- const CardActions: React.FC<CardActionsProps> = (props) => {
192
- const {
193
- isLocked,
194
- isUnlocking,
195
- sourceUrl,
196
- redeemUrl,
197
- onUnlockClicked,
198
- onDownloadClicked,
199
- } = props
200
-
201
- if (isLocked && onUnlockClicked) {
202
- return (
203
- <button
204
- type="button"
205
- onClick={onUnlockClicked}
206
- disabled={isUnlocking}
207
- className="mt-3 inline-flex h-10 w-full items-center justify-center gap-2 rounded-full bg-[#121110] px-4 text-sm font-medium leading-none text-white hover:bg-[#2a2928] disabled:opacity-70"
208
- >
209
- {isUnlocking ? (
210
- <LoadingDots />
211
- ) : (
212
- <>
213
- <LockSimpleIcon className="size-4" weight="fill" />
214
- Unlock
215
- </>
216
- )}
217
- </button>
218
- )
219
- }
220
-
221
- if (!isLocked && onDownloadClicked && sourceUrl) {
222
- return (
223
- <a
224
- href={redeemUrl ?? sourceUrl}
225
- target="_blank"
226
- rel="noopener noreferrer"
227
- onClick={onDownloadClicked}
228
- className="mt-3 inline-flex h-10 w-full items-center justify-center gap-2 rounded-full bg-[#121110] px-4 text-sm font-medium leading-none !text-white hover:bg-[#2a2928]"
229
- >
230
- <DownloadSimpleIcon className="size-4" weight="bold" />
231
- Download
232
- </a>
233
- )
234
- }
235
-
236
- return null
237
- }
238
-
239
- const VisitorCard: React.FC<VisitorCardProps> = (props) => {
240
- const {
241
- title,
242
- amountText,
243
- thumbnailUrl,
244
- mimeType = 'application/octet-stream',
245
- detail,
246
- onUnlocked,
247
- onUnlockClick,
248
- onDownloadClick,
249
- paymentStatus,
250
- } = props
251
-
252
- const { sourceUrl, redeemUrl } = onUnlocked?.() ?? {}
253
- const [isUnlocking, setUnlocking] = useState(false)
254
-
255
- const isLocked = sourceUrl === undefined
256
- const sourceType = getSourceType(mimeType)
257
-
258
- useEffect(() => {
259
- if (sourceUrl !== undefined) {
260
- setUnlocking(false)
261
- }
262
- }, [sourceUrl])
263
-
264
- const onUnlockClicked = useCallback(() => {
265
- if (onUnlockClick) {
266
- setUnlocking(true)
267
- onUnlockClick()
268
- }
269
- }, [onUnlockClick])
270
-
271
- let mediaPreview: React.ReactNode
272
- if (sourceType === 'image') {
273
- mediaPreview = (
274
- <ImagePreview
275
- key={sourceUrl}
276
- sourceUrl={sourceUrl}
277
- thumbnailUrl={thumbnailUrl}
278
- mimeType={mimeType}
279
- title={title}
280
- paymentStatus={paymentStatus}
281
- isLocked={isLocked}
282
- />
283
- )
284
- } else if (sourceType === 'document') {
285
- mediaPreview = (
286
- <DocumentPreview
287
- thumbnailUrl={thumbnailUrl}
288
- mimeType={mimeType}
289
- paymentStatus={paymentStatus}
290
- isLocked={isLocked}
291
- />
292
- )
293
- } else {
294
- mediaPreview = (
295
- <MediaPreview
296
- key={sourceUrl}
297
- sourceUrl={sourceUrl}
298
- thumbnailUrl={thumbnailUrl}
299
- mimeType={mimeType}
300
- paymentStatus={paymentStatus}
301
- isLocked={isLocked}
302
- />
303
- )
304
- }
305
-
306
- return (
307
- <div className="w-[280px] select-none overflow-hidden rounded-[24px] bg-white shadow-[0_0_0_1px_rgba(0,0,0,0.04),0_4px_8px_rgba(0,0,0,0.06)]">
308
- {mediaPreview}
309
- <div className="px-4 pb-3 pt-3">
310
- <p className="mb-1.5 truncate text-base font-medium text-black">
311
- {title}
312
- </p>
313
- <div className="flex items-center gap-1">
314
- {renderTypeIcon(mimeType, {
315
- className: 'size-5 shrink-0 text-black/55',
316
- weight: 'regular',
317
- })}
318
- {detail && (
319
- <span className="text-xs font-medium text-black/55">{detail}</span>
320
- )}
321
- {paymentStatus === 'paid' ? (
322
- <React.Fragment>
323
- <span className="text-xs font-medium text-black/55">•</span>
324
- <span className="text-xs font-medium text-[#008236]">
325
- Purchased
326
- </span>
327
- <CheckCircleIcon
328
- className="size-4 text-[#008236]"
329
- weight="bold"
330
- />
331
- </React.Fragment>
332
- ) : (
333
- amountText && (
334
- <React.Fragment>
335
- <span className="text-xs font-medium text-black/55">•</span>
336
- <span className="text-xs font-medium text-black/55">
337
- {amountText}
338
- </span>
339
- </React.Fragment>
340
- )
341
- )}
342
- </div>
343
- <CardActions
344
- isLocked={isLocked}
345
- isUnlocking={isUnlocking}
346
- sourceUrl={sourceUrl}
347
- redeemUrl={redeemUrl}
348
- onUnlockClicked={onUnlockClicked}
349
- onDownloadClicked={onDownloadClick}
350
- />
351
- </div>
352
- </div>
353
- )
354
- }
355
-
356
- export default VisitorCard