@linktr.ee/messaging-react 1.27.0 → 1.28.0-rc-1776231821
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-BFpHsh2u.js +318 -0
- package/dist/Creator-BFpHsh2u.js.map +1 -0
- package/dist/MediaPlayer-DXz4IBLx.js +313 -0
- package/dist/MediaPlayer-DXz4IBLx.js.map +1 -0
- package/dist/Visitor-C1Fcrgd6.js +199 -0
- package/dist/Visitor-C1Fcrgd6.js.map +1 -0
- package/dist/assets/index.css +1 -1
- package/dist/index.d.ts +13 -16
- package/dist/index.js +27 -35
- package/dist/index.js.map +1 -1
- package/package.json +1 -2
- package/src/components/CustomMessage/CustomMessage.stories.tsx +1 -1
- package/src/components/CustomMessage/index.tsx +0 -1
- package/src/components/LockedAttachment/LockedAttachment.stories.tsx +143 -49
- package/src/components/LockedAttachment/components/Creator.tsx +406 -114
- package/src/components/LockedAttachment/components/MediaPlayer.tsx +162 -80
- package/src/components/LockedAttachment/components/Visitor.tsx +205 -145
- package/src/components/LockedAttachment/index.tsx +7 -7
- package/src/components/LockedAttachment/types.ts +1 -5
- package/src/components/LockedAttachment/utils/icons.ts +2 -1
- package/src/components/LockedAttachment/utils/mimeType.test.ts +29 -7
- package/src/components/LockedAttachment/utils/mimeType.ts +3 -1
- package/src/types.ts +0 -1
- package/dist/Creator-B6M8dB0U.js +0 -87
- package/dist/Creator-B6M8dB0U.js.map +0 -1
- package/dist/MediaPlayer-DsjlYGGH.js +0 -539
- package/dist/MediaPlayer-DsjlYGGH.js.map +0 -1
- package/dist/Preview-DqAv16NS.js +0 -87
- package/dist/Preview-DqAv16NS.js.map +0 -1
- package/dist/Visitor-CpmFZRGO.js +0 -175
- package/dist/Visitor-CpmFZRGO.js.map +0 -1
- package/dist/dash.all.min-Duv4lvGS.js +0 -18858
- package/dist/dash.all.min-Duv4lvGS.js.map +0 -1
- package/dist/hls-Bogc7CBn.js +0 -21710
- package/dist/hls-Bogc7CBn.js.map +0 -1
- package/dist/index-Da-xN4Yq.js +0 -16142
- package/dist/index-Da-xN4Yq.js.map +0 -1
- package/dist/index-Dj9rqWcU.js +0 -69
- package/dist/index-Dj9rqWcU.js.map +0 -1
- package/dist/mixin-B6jYfIcp.js +0 -808
- package/dist/mixin-B6jYfIcp.js.map +0 -1
- package/dist/react-BxlQMOfz.js +0 -419
- package/dist/react-BxlQMOfz.js.map +0 -1
- package/dist/react-COAP-MIW.js +0 -377
- package/dist/react-COAP-MIW.js.map +0 -1
- package/dist/react-Cn4WlMcl.js +0 -3108
- package/dist/react-Cn4WlMcl.js.map +0 -1
- package/dist/react-CwTJArKY.js +0 -459
- package/dist/react-CwTJArKY.js.map +0 -1
- package/dist/react-DkfS_atT.js +0 -373
- package/dist/react-DkfS_atT.js.map +0 -1
- package/dist/react-Pea5fum1.js +0 -286
- package/dist/react-Pea5fum1.js.map +0 -1
- package/dist/react-RiBbsUDd.js +0 -534
- package/dist/react-RiBbsUDd.js.map +0 -1
- package/dist/react-dS1WBxxz.js +0 -238
- package/dist/react-dS1WBxxz.js.map +0 -1
|
@@ -1,10 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
CircleNotchIcon,
|
|
3
|
-
PauseIcon,
|
|
4
|
-
PlayIcon,
|
|
5
|
-
} from '@phosphor-icons/react'
|
|
1
|
+
import { CircleNotchIcon, PauseIcon, PlayIcon } from '@phosphor-icons/react'
|
|
6
2
|
import React, { useCallback, useEffect, useRef, useState } from 'react'
|
|
7
|
-
import ReactPlayer from 'react-player'
|
|
8
3
|
|
|
9
4
|
import { isDevBuild } from '../../../utils/isDevBuild'
|
|
10
5
|
import { renderTypeIcon } from '../utils/icons'
|
|
@@ -27,6 +22,8 @@ export interface MediaPlayerProps {
|
|
|
27
22
|
mimeType: string
|
|
28
23
|
poster?: string
|
|
29
24
|
autoPlay?: boolean
|
|
25
|
+
/** Controlled playing state. When provided, syncs to internal play/pause. */
|
|
26
|
+
playing?: boolean
|
|
30
27
|
loop?: boolean
|
|
31
28
|
controls?: boolean
|
|
32
29
|
showProgress?: boolean
|
|
@@ -40,6 +37,7 @@ const MediaPlayer: React.FC<MediaPlayerProps> = ({
|
|
|
40
37
|
mimeType,
|
|
41
38
|
poster,
|
|
42
39
|
autoPlay = false,
|
|
40
|
+
playing: playingProp,
|
|
43
41
|
loop = false,
|
|
44
42
|
controls = true,
|
|
45
43
|
showProgress = false,
|
|
@@ -48,6 +46,18 @@ const MediaPlayer: React.FC<MediaPlayerProps> = ({
|
|
|
48
46
|
}) => {
|
|
49
47
|
const sourceType = getSourceType(mimeType)
|
|
50
48
|
const [playing, setPlaying] = useState(autoPlay)
|
|
49
|
+
|
|
50
|
+
// Sync controlled playing prop to internal state
|
|
51
|
+
const prevPlayingPropRef = useRef(playingProp)
|
|
52
|
+
useEffect(() => {
|
|
53
|
+
if (
|
|
54
|
+
playingProp !== undefined &&
|
|
55
|
+
playingProp !== prevPlayingPropRef.current
|
|
56
|
+
) {
|
|
57
|
+
prevPlayingPropRef.current = playingProp
|
|
58
|
+
setPlaying(playingProp)
|
|
59
|
+
}
|
|
60
|
+
}, [playingProp])
|
|
51
61
|
const [played, setPlayed] = useState(0)
|
|
52
62
|
const [seeking, setSeeking] = useState(false)
|
|
53
63
|
const [scrubberHovered, setScrubberHovered] = useState(false)
|
|
@@ -55,7 +65,7 @@ const MediaPlayer: React.FC<MediaPlayerProps> = ({
|
|
|
55
65
|
const [buffering, setBuffering] = useState(false)
|
|
56
66
|
/** Set when autoplay/play() was rejected so user can start via gesture (no controls UI). */
|
|
57
67
|
const [manualPlayRequired, setManualPlayRequired] = useState(false)
|
|
58
|
-
const playerRef = useRef<
|
|
68
|
+
const playerRef = useRef<HTMLMediaElement>(null)
|
|
59
69
|
const trackRef = useRef<HTMLDivElement>(null)
|
|
60
70
|
const rafRef = useRef<number | null>(null)
|
|
61
71
|
|
|
@@ -123,7 +133,9 @@ const MediaPlayer: React.FC<MediaPlayerProps> = ({
|
|
|
123
133
|
if (el && el.duration) el.currentTime = fraction * el.duration
|
|
124
134
|
}, [])
|
|
125
135
|
|
|
126
|
-
const handleTrackPointerDown = (
|
|
136
|
+
const handleTrackPointerDown = (
|
|
137
|
+
e: React.MouseEvent<HTMLDivElement> | React.TouchEvent<HTMLDivElement>
|
|
138
|
+
) => {
|
|
127
139
|
e.stopPropagation()
|
|
128
140
|
setSeeking(true)
|
|
129
141
|
const fraction = getFraction(e)
|
|
@@ -151,7 +163,9 @@ const MediaPlayer: React.FC<MediaPlayerProps> = ({
|
|
|
151
163
|
}, [seeking, getFraction, seekTo])
|
|
152
164
|
|
|
153
165
|
// Use natural aspect ratio once metadata loads, fall back to 16:9 before then.
|
|
154
|
-
const aspectStyle = videoAspect
|
|
166
|
+
const aspectStyle = videoAspect
|
|
167
|
+
? { aspectRatio: String(videoAspect) }
|
|
168
|
+
: undefined
|
|
155
169
|
const aspectClass = !videoAspect ? ' aspect-video' : ''
|
|
156
170
|
const scrubberPercent = Math.round(played * 100)
|
|
157
171
|
|
|
@@ -163,7 +177,10 @@ const MediaPlayer: React.FC<MediaPlayerProps> = ({
|
|
|
163
177
|
style={aspectStyle}
|
|
164
178
|
onClick={() => {
|
|
165
179
|
if (manualPlayRequired) return
|
|
166
|
-
if (onContainerClick) {
|
|
180
|
+
if (onContainerClick) {
|
|
181
|
+
onContainerClick()
|
|
182
|
+
return
|
|
183
|
+
}
|
|
167
184
|
if (controls) setPlaying((p) => !p)
|
|
168
185
|
}}
|
|
169
186
|
onKeyDown={(e) => {
|
|
@@ -178,45 +195,78 @@ const MediaPlayer: React.FC<MediaPlayerProps> = ({
|
|
|
178
195
|
}}
|
|
179
196
|
>
|
|
180
197
|
{poster && (
|
|
181
|
-
<img
|
|
198
|
+
<img
|
|
199
|
+
src={poster}
|
|
200
|
+
alt=""
|
|
201
|
+
className="absolute inset-0 h-full w-full object-cover"
|
|
202
|
+
/>
|
|
182
203
|
)}
|
|
183
204
|
{!poster && (
|
|
184
205
|
<div className="absolute inset-0 flex items-center justify-center">
|
|
185
|
-
{renderTypeIcon(mimeType, {
|
|
206
|
+
{renderTypeIcon(mimeType, {
|
|
207
|
+
className: 'size-12 text-black/20',
|
|
208
|
+
weight: 'regular',
|
|
209
|
+
})}
|
|
186
210
|
</div>
|
|
187
211
|
)}
|
|
188
212
|
<div className="absolute inset-0">
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
|
|
213
|
+
{sourceType === 'audio' ? (
|
|
214
|
+
<audio
|
|
215
|
+
ref={playerRef as React.RefObject<HTMLAudioElement>}
|
|
216
|
+
src={source}
|
|
217
|
+
loop={loop}
|
|
218
|
+
muted={muted}
|
|
219
|
+
style={{ width: '100%', height: '100%' }}
|
|
220
|
+
onLoadStart={() => setBuffering(true)}
|
|
221
|
+
onCanPlay={() => setBuffering(false)}
|
|
222
|
+
onWaiting={() => setBuffering(true)}
|
|
223
|
+
onPlay={() => setManualPlayRequired(false)}
|
|
224
|
+
onEnded={() => {
|
|
225
|
+
if (!loop) {
|
|
226
|
+
setPlaying(false)
|
|
227
|
+
setPlayed(0)
|
|
228
|
+
}
|
|
229
|
+
}}
|
|
230
|
+
>
|
|
231
|
+
<track kind="captions" />
|
|
232
|
+
</audio>
|
|
233
|
+
) : (
|
|
234
|
+
<video
|
|
235
|
+
ref={playerRef as React.RefObject<HTMLVideoElement>}
|
|
236
|
+
src={source}
|
|
237
|
+
poster={poster}
|
|
238
|
+
loop={loop}
|
|
239
|
+
muted={muted}
|
|
240
|
+
playsInline
|
|
241
|
+
style={{ width: '100%', height: '100%' }}
|
|
242
|
+
onLoadStart={() => setBuffering(true)}
|
|
243
|
+
onCanPlay={() => setBuffering(false)}
|
|
244
|
+
onWaiting={() => setBuffering(true)}
|
|
245
|
+
onPlay={() => setManualPlayRequired(false)}
|
|
246
|
+
onLoadedMetadata={() => {
|
|
247
|
+
const el = playerRef.current
|
|
248
|
+
if (el instanceof HTMLVideoElement && el.videoWidth && el.videoHeight) {
|
|
249
|
+
setVideoAspect(el.videoWidth / el.videoHeight)
|
|
250
|
+
}
|
|
251
|
+
}}
|
|
252
|
+
onEnded={() => {
|
|
253
|
+
if (!loop) {
|
|
254
|
+
setPlaying(false)
|
|
255
|
+
setPlayed(0)
|
|
256
|
+
}
|
|
257
|
+
}}
|
|
258
|
+
>
|
|
259
|
+
<track kind="captions" />
|
|
260
|
+
</video>
|
|
261
|
+
)}
|
|
215
262
|
</div>
|
|
216
263
|
|
|
217
264
|
{buffering && !manualPlayRequired && (
|
|
218
265
|
<div className="absolute inset-0 z-10 flex items-center justify-center">
|
|
219
|
-
<CircleNotchIcon
|
|
266
|
+
<CircleNotchIcon
|
|
267
|
+
className="size-8 animate-spin text-white/80"
|
|
268
|
+
weight="bold"
|
|
269
|
+
/>
|
|
220
270
|
</div>
|
|
221
271
|
)}
|
|
222
272
|
|
|
@@ -244,54 +294,86 @@ const MediaPlayer: React.FC<MediaPlayerProps> = ({
|
|
|
244
294
|
)}
|
|
245
295
|
|
|
246
296
|
{showProgress && !controls && (
|
|
247
|
-
<div className="absolute inset-x-0 bottom-0 px-3 pb-2.5 pt-6 bg-gradient-to-t from-black/40 to-transparent
|
|
248
|
-
<div
|
|
249
|
-
|
|
297
|
+
<div className="absolute inset-x-0 bottom-0 px-3 pb-2.5 pt-6 bg-gradient-to-t from-black/40 to-transparent">
|
|
298
|
+
<div
|
|
299
|
+
role="slider"
|
|
300
|
+
aria-label="Playback position"
|
|
301
|
+
aria-valuenow={scrubberPercent}
|
|
302
|
+
aria-valuemin={0}
|
|
303
|
+
aria-valuemax={100}
|
|
304
|
+
tabIndex={0}
|
|
305
|
+
ref={trackRef}
|
|
306
|
+
className="relative flex h-4 w-full cursor-pointer items-center"
|
|
307
|
+
onMouseDown={handleTrackPointerDown}
|
|
308
|
+
onTouchStart={handleTrackPointerDown}
|
|
309
|
+
onClick={(e) => e.stopPropagation()}
|
|
310
|
+
onKeyDown={(e) => {
|
|
311
|
+
if (e.key === 'ArrowRight') seekTo(Math.min(1, played + 0.05))
|
|
312
|
+
if (e.key === 'ArrowLeft') seekTo(Math.max(0, played - 0.05))
|
|
313
|
+
}}
|
|
314
|
+
>
|
|
315
|
+
<div className="w-full overflow-hidden rounded-full bg-white/30 h-1">
|
|
316
|
+
<div
|
|
317
|
+
className="h-full rounded-full bg-white"
|
|
318
|
+
style={{ width: `${scrubberPercent}%` }}
|
|
319
|
+
/>
|
|
320
|
+
</div>
|
|
250
321
|
</div>
|
|
251
322
|
</div>
|
|
252
323
|
)}
|
|
253
324
|
|
|
254
|
-
{controls &&
|
|
255
|
-
<
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
325
|
+
{controls && (
|
|
326
|
+
<div className="absolute inset-x-0 bottom-0 flex items-center gap-2 bg-gradient-to-t from-black/60 to-transparent px-3 pb-2.5 pt-6 transition-all duration-200">
|
|
327
|
+
<button
|
|
328
|
+
type="button"
|
|
329
|
+
onClick={(e) => {
|
|
330
|
+
e.stopPropagation()
|
|
331
|
+
setPlaying((p) => !p)
|
|
332
|
+
}}
|
|
333
|
+
className="shrink-0 text-white"
|
|
334
|
+
aria-label={playing ? 'Pause' : 'Play'}
|
|
335
|
+
>
|
|
336
|
+
{playing ? (
|
|
337
|
+
<PauseIcon className="size-5" weight="fill" />
|
|
338
|
+
) : (
|
|
339
|
+
<PlayIcon className="size-5 translate-x-px" weight="fill" />
|
|
340
|
+
)}
|
|
341
|
+
</button>
|
|
266
342
|
|
|
267
|
-
<div
|
|
268
|
-
role="slider"
|
|
269
|
-
aria-label="Playback position"
|
|
270
|
-
aria-valuenow={scrubberPercent}
|
|
271
|
-
aria-valuemin={0}
|
|
272
|
-
aria-valuemax={100}
|
|
273
|
-
tabIndex={0}
|
|
274
|
-
ref={trackRef}
|
|
275
|
-
className="relative flex h-4 w-full cursor-pointer items-center"
|
|
276
|
-
onMouseDown={handleTrackPointerDown}
|
|
277
|
-
onTouchStart={handleTrackPointerDown}
|
|
278
|
-
onClick={(e) => e.stopPropagation()}
|
|
279
|
-
onMouseEnter={() => setScrubberHovered(true)}
|
|
280
|
-
onMouseLeave={() => setScrubberHovered(false)}
|
|
281
|
-
onKeyDown={(e) => {
|
|
282
|
-
if (e.key === 'ArrowRight') seekTo(Math.min(1, played + 0.05))
|
|
283
|
-
if (e.key === 'ArrowLeft') seekTo(Math.max(0, played - 0.05))
|
|
284
|
-
}}
|
|
285
|
-
>
|
|
286
|
-
<div className={`w-full overflow-hidden rounded-full bg-white/30 transition-all duration-200 ${scrubberHovered || seeking ? 'h-1.5' : 'h-1'}`}>
|
|
287
|
-
<div className="h-full rounded-full bg-white" style={{ width: `${scrubberPercent}%` }} />
|
|
288
|
-
</div>
|
|
289
343
|
<div
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
344
|
+
role="slider"
|
|
345
|
+
aria-label="Playback position"
|
|
346
|
+
aria-valuenow={scrubberPercent}
|
|
347
|
+
aria-valuemin={0}
|
|
348
|
+
aria-valuemax={100}
|
|
349
|
+
tabIndex={0}
|
|
350
|
+
ref={trackRef}
|
|
351
|
+
className="relative flex h-4 w-full cursor-pointer items-center"
|
|
352
|
+
onMouseDown={handleTrackPointerDown}
|
|
353
|
+
onTouchStart={handleTrackPointerDown}
|
|
354
|
+
onClick={(e) => e.stopPropagation()}
|
|
355
|
+
onMouseEnter={() => setScrubberHovered(true)}
|
|
356
|
+
onMouseLeave={() => setScrubberHovered(false)}
|
|
357
|
+
onKeyDown={(e) => {
|
|
358
|
+
if (e.key === 'ArrowRight') seekTo(Math.min(1, played + 0.05))
|
|
359
|
+
if (e.key === 'ArrowLeft') seekTo(Math.max(0, played - 0.05))
|
|
360
|
+
}}
|
|
361
|
+
>
|
|
362
|
+
<div
|
|
363
|
+
className={`w-full overflow-hidden rounded-full bg-white/30 transition-all duration-200 ${scrubberHovered || seeking ? 'h-1.5' : 'h-1'}`}
|
|
364
|
+
>
|
|
365
|
+
<div
|
|
366
|
+
className="h-full rounded-full bg-white"
|
|
367
|
+
style={{ width: `${scrubberPercent}%` }}
|
|
368
|
+
/>
|
|
369
|
+
</div>
|
|
370
|
+
<div
|
|
371
|
+
className={`absolute size-3 -translate-x-1/2 rounded-full bg-white shadow transition-[opacity,transform] duration-200 ${scrubberHovered || seeking ? 'scale-100 opacity-100' : 'scale-0 opacity-0'}`}
|
|
372
|
+
style={{ left: `${scrubberPercent}%` }}
|
|
373
|
+
/>
|
|
374
|
+
</div>
|
|
293
375
|
</div>
|
|
294
|
-
|
|
376
|
+
)}
|
|
295
377
|
</div>
|
|
296
378
|
)
|
|
297
379
|
}
|