@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.
- package/dist/{Creator-DGe3CQ_j.js → Card-C5t3dZ5q.js} +177 -150
- package/dist/Card-C5t3dZ5q.js.map +1 -0
- package/dist/Card-Cn2va-Qr.js +205 -0
- package/dist/Card-Cn2va-Qr.js.map +1 -0
- package/dist/index.d.ts +35 -30
- package/dist/index.js +951 -956
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/components/ChannelView.tsx +24 -36
- package/src/components/CustomMessage/CustomMessage.stories.tsx +1 -14
- package/src/components/CustomMessage/context.tsx +20 -0
- package/src/components/CustomMessage/index.tsx +39 -28
- package/src/components/LockedAttachment/LockedAttachment.stories.tsx +8 -13
- package/src/components/LockedAttachment/components/Creator/Card.tsx +159 -0
- package/src/components/LockedAttachment/components/Creator/CardAudioPreview.tsx +161 -0
- package/src/components/LockedAttachment/components/Creator/CardCollapsedThumbnail.tsx +58 -0
- package/src/components/LockedAttachment/components/Creator/CardImagePreview.tsx +56 -0
- package/src/components/LockedAttachment/components/Creator/CardVideoPreview.tsx +91 -0
- package/src/components/LockedAttachment/components/Creator/index.tsx +2 -0
- package/src/components/LockedAttachment/components/Visitor/Card.tsx +186 -0
- package/src/components/LockedAttachment/components/Visitor/CardActions.tsx +71 -0
- package/src/components/LockedAttachment/components/Visitor/CardImagePreview.tsx +39 -0
- package/src/components/LockedAttachment/components/Visitor/CardMediaPreview.tsx +36 -0
- package/src/components/LockedAttachment/components/Visitor/CardThumbnailPreview.tsx +45 -0
- package/src/components/LockedAttachment/components/Visitor/index.ts +2 -0
- package/src/components/LockedAttachment/index.tsx +16 -23
- package/src/components/LockedAttachment/types.ts +14 -1
- package/src/components/MessagingShell/index.tsx +0 -6
- package/src/index.ts +4 -1
- package/src/types.ts +0 -21
- package/dist/Creator-DGe3CQ_j.js.map +0 -1
- package/dist/Visitor-DyJTWB2_.js +0 -204
- package/dist/Visitor-DyJTWB2_.js.map +0 -1
- package/src/components/LockedAttachment/components/Creator.tsx +0 -470
- 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
|