@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.
Files changed (57) hide show
  1. package/dist/Creator-BFpHsh2u.js +318 -0
  2. package/dist/Creator-BFpHsh2u.js.map +1 -0
  3. package/dist/MediaPlayer-DXz4IBLx.js +313 -0
  4. package/dist/MediaPlayer-DXz4IBLx.js.map +1 -0
  5. package/dist/Visitor-C1Fcrgd6.js +199 -0
  6. package/dist/Visitor-C1Fcrgd6.js.map +1 -0
  7. package/dist/assets/index.css +1 -1
  8. package/dist/index.d.ts +13 -16
  9. package/dist/index.js +27 -35
  10. package/dist/index.js.map +1 -1
  11. package/package.json +1 -2
  12. package/src/components/CustomMessage/CustomMessage.stories.tsx +1 -1
  13. package/src/components/CustomMessage/index.tsx +0 -1
  14. package/src/components/LockedAttachment/LockedAttachment.stories.tsx +143 -49
  15. package/src/components/LockedAttachment/components/Creator.tsx +406 -114
  16. package/src/components/LockedAttachment/components/MediaPlayer.tsx +162 -80
  17. package/src/components/LockedAttachment/components/Visitor.tsx +205 -145
  18. package/src/components/LockedAttachment/index.tsx +7 -7
  19. package/src/components/LockedAttachment/types.ts +1 -5
  20. package/src/components/LockedAttachment/utils/icons.ts +2 -1
  21. package/src/components/LockedAttachment/utils/mimeType.test.ts +29 -7
  22. package/src/components/LockedAttachment/utils/mimeType.ts +3 -1
  23. package/src/types.ts +0 -1
  24. package/dist/Creator-B6M8dB0U.js +0 -87
  25. package/dist/Creator-B6M8dB0U.js.map +0 -1
  26. package/dist/MediaPlayer-DsjlYGGH.js +0 -539
  27. package/dist/MediaPlayer-DsjlYGGH.js.map +0 -1
  28. package/dist/Preview-DqAv16NS.js +0 -87
  29. package/dist/Preview-DqAv16NS.js.map +0 -1
  30. package/dist/Visitor-CpmFZRGO.js +0 -175
  31. package/dist/Visitor-CpmFZRGO.js.map +0 -1
  32. package/dist/dash.all.min-Duv4lvGS.js +0 -18858
  33. package/dist/dash.all.min-Duv4lvGS.js.map +0 -1
  34. package/dist/hls-Bogc7CBn.js +0 -21710
  35. package/dist/hls-Bogc7CBn.js.map +0 -1
  36. package/dist/index-Da-xN4Yq.js +0 -16142
  37. package/dist/index-Da-xN4Yq.js.map +0 -1
  38. package/dist/index-Dj9rqWcU.js +0 -69
  39. package/dist/index-Dj9rqWcU.js.map +0 -1
  40. package/dist/mixin-B6jYfIcp.js +0 -808
  41. package/dist/mixin-B6jYfIcp.js.map +0 -1
  42. package/dist/react-BxlQMOfz.js +0 -419
  43. package/dist/react-BxlQMOfz.js.map +0 -1
  44. package/dist/react-COAP-MIW.js +0 -377
  45. package/dist/react-COAP-MIW.js.map +0 -1
  46. package/dist/react-Cn4WlMcl.js +0 -3108
  47. package/dist/react-Cn4WlMcl.js.map +0 -1
  48. package/dist/react-CwTJArKY.js +0 -459
  49. package/dist/react-CwTJArKY.js.map +0 -1
  50. package/dist/react-DkfS_atT.js +0 -373
  51. package/dist/react-DkfS_atT.js.map +0 -1
  52. package/dist/react-Pea5fum1.js +0 -286
  53. package/dist/react-Pea5fum1.js.map +0 -1
  54. package/dist/react-RiBbsUDd.js +0 -534
  55. package/dist/react-RiBbsUDd.js.map +0 -1
  56. package/dist/react-dS1WBxxz.js +0 -238
  57. 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<HTMLVideoElement>(null)
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 = (e: React.MouseEvent<HTMLDivElement> | React.TouchEvent<HTMLDivElement>) => {
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 ? { aspectRatio: String(videoAspect) } : undefined
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) { onContainerClick(); return }
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 src={poster} alt="" className="absolute inset-0 h-full w-full object-cover" />
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, { className: 'size-12 text-black/20', weight: 'regular' })}
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
- <ReactPlayer
190
- ref={playerRef}
191
- src={source}
192
- poster={poster}
193
- loop={loop}
194
- muted={muted}
195
- playsInline
196
- width="100%"
197
- height="100%"
198
- onLoadStart={() => setBuffering(true)}
199
- onCanPlay={() => setBuffering(false)}
200
- onWaiting={() => setBuffering(true)}
201
- onPlay={() => setManualPlayRequired(false)}
202
- onLoadedMetadata={() => {
203
- const el = playerRef.current
204
- if (el && el.videoWidth && el.videoHeight) {
205
- setVideoAspect(el.videoWidth / el.videoHeight)
206
- }
207
- }}
208
- onEnded={() => {
209
- if (!loop) {
210
- setPlaying(false)
211
- setPlayed(0)
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 className="size-8 animate-spin text-white/80" weight="bold" />
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 pointer-events-none">
248
- <div className="h-1 w-full overflow-hidden rounded-full bg-white/30">
249
- <div className="h-full rounded-full bg-white" style={{ width: `${scrubberPercent}%` }} />
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 && <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">
255
- <button
256
- type="button"
257
- onClick={(e) => { e.stopPropagation(); setPlaying((p) => !p) }}
258
- className="shrink-0 text-white"
259
- aria-label={playing ? 'Pause' : 'Play'}
260
- >
261
- {playing
262
- ? <PauseIcon className="size-5" weight="fill" />
263
- : <PlayIcon className="size-5 translate-x-px" weight="fill" />
264
- }
265
- </button>
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
- 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'}`}
291
- style={{ left: `${scrubberPercent}%` }}
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
- </div>}
376
+ )}
295
377
  </div>
296
378
  )
297
379
  }