@linktr.ee/messaging-react 1.28.0-rc-1776233688 → 1.28.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.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Visitor-C4WqnN8H.js","sources":["../src/components/LockedAttachment/components/Visitor.tsx"],"sourcesContent":["import {\n CheckCircleIcon,\n DownloadSimpleIcon,\n LockOpenIcon,\n LockSimpleIcon,\n} from '@phosphor-icons/react'\nimport React, { useEffect, useState } from 'react'\n\nimport { isDevBuild } from '../../../utils/isDevBuild'\nimport type {\n LockedAttachmentBaseProps,\n LockedAttachmentSource,\n PaymentStatus,\n} from '../types'\nimport { renderTypeIcon } from '../utils/icons'\nimport { getSourceType } from '../utils/mimeType'\n\nimport MediaPlayer from './MediaPlayer'\n\nexport interface VisitorCardProps extends LockedAttachmentBaseProps {\n /**\n * Called when the visitor clicks Unlock. Return the resolved source URL.\n * The component manages loading state and sets source internally on resolution.\n * Omit to hide the Unlock button.\n */\n onUnlock?: () => Promise<LockedAttachmentSource>\n /**\n * Called when the visitor clicks Download on an unlocked card.\n * May return a Promise — if so, the component shows a loader until it resolves.\n */\n onDownload?: () => void | Promise<void>\n}\n\nconst getLockIcon = (paymentStatus?: PaymentStatus): React.ElementType =>\n paymentStatus === 'paid' ? LockOpenIcon : LockSimpleIcon\n\n\ninterface LockOverlayProps {\n icon: React.ElementType\n loading?: boolean\n}\n\nconst LockOverlay: React.FC<LockOverlayProps> = (props) => {\n const { icon: Icon, loading } = props\n return (\n <div className=\"absolute inset-0 bg-black/30\">\n <div className=\"absolute left-3 top-3 flex size-8 items-center justify-center rounded-full bg-black/60\">\n {loading ? (\n <span className=\"flex items-center gap-[2px]\">\n <span className=\"size-1 rounded-full bg-white animate-bounce [animation-delay:-0.3s]\" />\n <span className=\"size-1 rounded-full bg-white animate-bounce [animation-delay:-0.15s]\" />\n <span className=\"size-1 rounded-full bg-white animate-bounce\" />\n </span>\n ) : (\n <Icon className=\"size-4 text-white\" weight=\"fill\" />\n )}\n </div>\n </div>\n )\n}\n\ninterface LockedPreviewProps {\n thumbnail?: string\n mimeType: string\n LockIcon: React.ElementType\n loading?: boolean\n}\n\nconst LockedPreview: React.FC<LockedPreviewProps> = (props) => {\n const { thumbnail, mimeType, LockIcon, loading } = props\n return (\n <div className=\"relative aspect-video overflow-hidden bg-black/5\">\n {thumbnail ? (\n <img\n src={thumbnail}\n alt=\"\"\n className=\"absolute inset-0 h-full w-full object-cover\"\n />\n ) : (\n <div className=\"absolute inset-0 flex items-center justify-center\">\n {renderTypeIcon(mimeType, {\n className: 'size-12 text-black/20',\n weight: 'regular',\n })}\n </div>\n )}\n <LockOverlay icon={LockIcon} loading={loading} />\n </div>\n )\n}\n\n\ninterface ImagePreviewProps {\n source?: string\n thumbnail?: string\n mimeType: string\n title?: string\n paymentStatus?: PaymentStatus\n isLocked: boolean\n loading?: boolean\n}\n\nconst ImagePreview: React.FC<ImagePreviewProps> = (props) => {\n const { source, thumbnail, mimeType, title, paymentStatus, isLocked, loading } = props\n const [sourceReady, setSourceReady] = useState(false)\n\n useEffect(() => {\n setSourceReady(false)\n }, [source])\n\n if (isLocked) {\n return (\n <LockedPreview\n thumbnail={thumbnail}\n mimeType={mimeType}\n LockIcon={getLockIcon(paymentStatus)}\n loading={loading}\n />\n )\n }\n\n return (\n <div className=\"relative overflow-hidden bg-black/5\">\n <img\n src={source}\n alt={title}\n className={`block w-full transition-opacity duration-300 ${sourceReady ? 'opacity-100' : 'opacity-0'}`}\n onLoad={() => setSourceReady(true)}\n />\n </div>\n )\n}\n\ninterface DocumentPreviewProps {\n thumbnail?: string\n mimeType: string\n paymentStatus?: PaymentStatus\n isLocked: boolean\n loading?: boolean\n}\n\nconst DocumentPreview: React.FC<DocumentPreviewProps> = (props) => {\n const { thumbnail, mimeType, paymentStatus, isLocked, loading } = props\n return (\n <div className=\"relative aspect-video overflow-hidden bg-black/5\">\n {thumbnail ? (\n <img\n src={thumbnail}\n alt=\"\"\n className=\"absolute inset-0 h-full w-full object-cover\"\n />\n ) : (\n <div className=\"absolute inset-0 flex items-center justify-center\">\n {renderTypeIcon(mimeType, {\n className: 'size-12 text-black/20',\n weight: 'regular',\n })}\n </div>\n )}\n {isLocked && <LockOverlay icon={getLockIcon(paymentStatus)} loading={loading} />}\n </div>\n )\n}\n\ninterface MediaPreviewProps {\n source?: string\n thumbnail?: string\n mimeType: string\n paymentStatus?: PaymentStatus\n isLocked: boolean\n loading?: boolean\n}\n\nconst MediaPreview: React.FC<MediaPreviewProps> = (props) => {\n const { source, thumbnail, mimeType, paymentStatus, isLocked, loading } = props\n if (isLocked) {\n return (\n <LockedPreview\n thumbnail={thumbnail}\n mimeType={mimeType}\n LockIcon={getLockIcon(paymentStatus)}\n loading={loading}\n />\n )\n }\n return <MediaPlayer source={source!} mimeType={mimeType} poster={thumbnail} />\n}\n\n\nconst LoadingDots = () => (\n <span className=\"flex items-center gap-1\">\n <span className=\"size-1 rounded-full bg-white animate-bounce [animation-delay:-0.3s]\" />\n <span className=\"size-1 rounded-full bg-white animate-bounce [animation-delay:-0.15s]\" />\n <span className=\"size-1 rounded-full bg-white animate-bounce\" />\n </span>\n)\n\ninterface CardActionsProps {\n isLocked: boolean\n unlockLoading: boolean\n downloadLoading: boolean\n paymentStatus?: PaymentStatus\n source?: string\n onUnlock?: () => void\n onDownload?: () => void\n}\n\nconst CardActions: React.FC<CardActionsProps> = (props) => {\n const { isLocked, unlockLoading, downloadLoading, paymentStatus, source, onUnlock, onDownload } = props\n const LockIcon = getLockIcon(paymentStatus)\n\n if (isLocked && onUnlock) {\n return (\n <button\n type=\"button\"\n onClick={onUnlock}\n disabled={unlockLoading}\n 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 disabled:opacity-70\"\n >\n {unlockLoading ? (\n <LoadingDots />\n ) : (\n <>\n {paymentStatus === 'paid' ? (\n <LockOpenIcon className=\"size-4\" weight=\"fill\" />\n ) : (\n <LockIcon className=\"size-4\" weight=\"fill\" />\n )}\n {paymentStatus === 'paid' ? 'Open' : 'Unlock'}\n </>\n )}\n </button>\n )\n }\n\n if (!isLocked && onDownload && source) {\n return (\n <button\n type=\"button\"\n onClick={onDownload}\n disabled={downloadLoading}\n 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 disabled:opacity-70\"\n >\n {downloadLoading ? (\n <LoadingDots />\n ) : (\n <>\n <DownloadSimpleIcon className=\"size-4\" weight=\"bold\" />\n Download\n </>\n )}\n </button>\n )\n }\n\n return null\n}\n\n\nconst VisitorCard: React.FC<VisitorCardProps> = (props) => {\n const {\n title,\n amountText,\n thumbnail,\n source: sourceProp,\n mimeType = 'application/octet-stream',\n detail,\n onUnlock,\n onDownload,\n paymentStatus,\n } = props\n const [source, setSource] = useState(sourceProp)\n const [unlockLoading, setUnlockLoading] = useState(false)\n const [downloadLoading, setDownloadLoading] = useState(false)\n\n useEffect(() => {\n if (sourceProp !== undefined) setSource(sourceProp)\n }, [sourceProp])\n\n const isLocked = source === undefined\n const sourceType = getSourceType(mimeType)\n\n const handleUnlock = async () => {\n if (!onUnlock) return\n setUnlockLoading(true)\n try {\n const result = await onUnlock()\n setSource(result.source)\n } catch (err) {\n if (isDevBuild()) console.debug('[LockedAttachment] onUnlock failed', err)\n } finally {\n setUnlockLoading(false)\n }\n }\n\n const handleDownload = async () => {\n if (!onDownload) return\n setDownloadLoading(true)\n try {\n await onDownload()\n } catch (err) {\n if (isDevBuild()) console.debug('[LockedAttachment] onDownload failed', err)\n } finally {\n setDownloadLoading(false)\n }\n }\n\n let mediaPreview: React.ReactNode\n if (sourceType === 'image') {\n mediaPreview = (\n <ImagePreview\n source={source}\n thumbnail={thumbnail}\n mimeType={mimeType}\n title={title}\n paymentStatus={paymentStatus}\n isLocked={isLocked}\n loading={unlockLoading}\n />\n )\n } else if (sourceType === 'document') {\n mediaPreview = (\n <DocumentPreview\n thumbnail={thumbnail}\n mimeType={mimeType}\n paymentStatus={paymentStatus}\n isLocked={isLocked}\n loading={unlockLoading}\n />\n )\n } else {\n mediaPreview = (\n <MediaPreview\n source={source}\n thumbnail={thumbnail}\n mimeType={mimeType}\n paymentStatus={paymentStatus}\n isLocked={isLocked}\n loading={unlockLoading}\n />\n )\n }\n\n return (\n <div className=\"w-[280px] select-none overflow-hidden rounded-3xl bg-white shadow-card\">\n {mediaPreview}\n <div className=\"px-4 pb-3 pt-3\">\n <p className=\"mb-1.5 truncate text-base font-medium text-black\">\n {title}\n </p>\n <div className=\"flex items-center gap-1\">\n {renderTypeIcon(mimeType, {\n className: 'size-5 shrink-0 text-black/55',\n weight: 'regular',\n })}\n {detail && (\n <span className=\"text-xs font-medium text-black/55\">{detail}</span>\n )}\n {paymentStatus === 'paid' ? (\n <>\n <span className=\"text-xs font-medium text-black/55\">•</span>\n <span className=\"text-xs font-medium text-[#008236]\">\n Purchased\n </span>\n <CheckCircleIcon\n className=\"size-4 text-[#008236]\"\n weight=\"bold\"\n />\n </>\n ) : (\n amountText && (\n <>\n <span className=\"text-xs font-medium text-black/55\">•</span>\n <span className=\"text-xs font-medium text-black/55\">\n {amountText}\n </span>\n </>\n )\n )}\n </div>\n <CardActions\n isLocked={isLocked}\n unlockLoading={unlockLoading}\n downloadLoading={downloadLoading}\n paymentStatus={paymentStatus}\n source={source}\n onUnlock={onUnlock ? handleUnlock : undefined}\n onDownload={onDownload ? handleDownload : undefined}\n />\n </div>\n </div>\n )\n}\n\nexport default VisitorCard\n"],"names":["getLockIcon","paymentStatus","LockOpenIcon","LockSimpleIcon","LockOverlay","props","Icon","loading","jsx","jsxs","LockedPreview","thumbnail","mimeType","LockIcon","ImagePreview","source","title","isLocked","sourceReady","setSourceReady","useState","useEffect","DocumentPreview","MediaPreview","MediaPlayer","LoadingDots","CardActions","unlockLoading","downloadLoading","onUnlock","onDownload","Fragment","DownloadSimpleIcon","VisitorCard","amountText","sourceProp","detail","setSource","setUnlockLoading","setDownloadLoading","sourceType","getSourceType","handleUnlock","result","handleDownload","mediaPreview","renderTypeIcon","CheckCircleIcon"],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -526,8 +526,9 @@ declare interface VisitorCardProps extends LockedAttachmentBaseProps {
526
526
  onUnlock?: () => Promise<LockedAttachmentSource>;
527
527
  /**
528
528
  * Called when the visitor clicks Download on an unlocked card.
529
+ * May return a Promise — if so, the component shows a loader until it resolves.
529
530
  */
530
- onDownload?: () => void;
531
+ onDownload?: () => void | Promise<void>;
531
532
  }
532
533
 
533
534
  export declare type VoteSelection = 'up' | 'down' | null;
package/dist/index.js CHANGED
@@ -821,7 +821,7 @@ function vs(t) {
821
821
  }, [s, n == null ? void 0 : n.userID, t.id, r]);
822
822
  return { selected: r, voteUp: l, voteDown: d };
823
823
  }
824
- const ws = F.lazy(() => import("./Creator-BJ6UBWsR.js")), _s = F.lazy(() => import("./Visitor-C1Fcrgd6.js")), Je = () => /* @__PURE__ */ e(
824
+ const ws = F.lazy(() => import("./Creator-VyMyIk2b.js")), _s = F.lazy(() => import("./Visitor-C4WqnN8H.js")), Je = () => /* @__PURE__ */ e(
825
825
  "div",
826
826
  {
827
827
  className: "w-[280px] min-h-[200px] animate-pulse rounded-3xl bg-black/[0.06] shadow-[0px_0px_0px_1px_rgba(0,0,0,0.04),0px_1px_2px_0px_rgba(0,0,0,0.04)]",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@linktr.ee/messaging-react",
3
- "version": "1.28.0-rc-1776233688",
3
+ "version": "1.28.0",
4
4
  "description": "React messaging components built on messaging-core for web applications",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -1,7 +1,7 @@
1
1
  import type { Meta, StoryFn } from '@storybook/react'
2
- import React from 'react'
2
+ import React, { useEffect, useState } from 'react'
3
3
 
4
- import LockedAttachment, { type VisitorCardProps } from '.'
4
+ import LockedAttachment, { type CreatorCardProps, type VisitorCardProps } from '.'
5
5
 
6
6
  const VIDEO_THUMBNAIL_BLURRED = '/video-thumbnail-blurred.jpg'
7
7
  const VIDEO_SOURCE = '/video-source.mp4'
@@ -26,6 +26,25 @@ type InteractiveProps = Omit<VisitorCardProps, 'onUnlock' | 'onDownload'> & {
26
26
  unlockedSource: string
27
27
  }
28
28
 
29
+ /** Simulates a slow network by withholding the source for 2s, then loading via blob URL. */
30
+ type SlowCreatorPreviewProps = Omit<CreatorCardProps, 'isPreview'> & { sourcePath: string }
31
+ const SlowCreatorPreview = ({ sourcePath, ...props }: SlowCreatorPreviewProps) => {
32
+ const [source, setSource] = useState<string | undefined>(undefined)
33
+ useEffect(() => {
34
+ setSource(undefined)
35
+ let cancelled = false
36
+ const load = async () => {
37
+ await new Promise((resolve) => setTimeout(resolve, 2000))
38
+ const res = await fetch(sourcePath)
39
+ const blob = await res.blob()
40
+ if (!cancelled) setSource(URL.createObjectURL(blob))
41
+ }
42
+ void load()
43
+ return () => { cancelled = true }
44
+ }, [sourcePath])
45
+ return <LockedAttachment isCreator isPreview source={source} {...props} />
46
+ }
47
+
29
48
  const Interactive = ({ unlockedSource, ...props }: InteractiveProps) => (
30
49
  <LockedAttachment
31
50
  {...props}
@@ -192,7 +211,7 @@ export const Visitor: StoryFn = () => (
192
211
  detail={detail}
193
212
  amountText="AU$9.99"
194
213
  paymentStatus="paid"
195
- onDownload={() => {}}
214
+ onDownload={() => new Promise((resolve) => setTimeout(resolve, 2000))}
196
215
  />
197
216
  </td>
198
217
  ))}
@@ -211,11 +230,9 @@ export const Creator: StoryFn = () => (
211
230
  </td>
212
231
  {VARIANTS.map(({ mimeType, detail, thumbnail, source }) => (
213
232
  <td key={mimeType} className="align-top">
214
- <LockedAttachment
215
- isCreator
216
- isPreview
233
+ <SlowCreatorPreview
217
234
  thumbnail={thumbnail}
218
- source={source}
235
+ sourcePath={source}
219
236
  mimeType={mimeType}
220
237
  detail={detail}
221
238
  placeholderAmountText="A$0.00"
@@ -131,9 +131,12 @@ const AudioPreview: React.FC<AudioPreviewProps> = (props) => {
131
131
  }
132
132
  }, [playing, seeking])
133
133
 
134
+ const [audioReady, setAudioReady] = useState(false)
135
+
134
136
  useEffect(() => {
135
137
  setPlaying(false)
136
138
  setPlayed(0)
139
+ setAudioReady(false)
137
140
  }, [source])
138
141
 
139
142
  const getFraction = useCallback(
@@ -187,6 +190,7 @@ const AudioPreview: React.FC<AudioPreviewProps> = (props) => {
187
190
  ref={audioRef}
188
191
  src={source}
189
192
  loop
193
+ onCanPlay={() => setAudioReady(true)}
190
194
  onEnded={() => {
191
195
  setPlaying(false)
192
196
  setPlayed(0)
@@ -198,10 +202,10 @@ const AudioPreview: React.FC<AudioPreviewProps> = (props) => {
198
202
  <CollapsedThumbnail
199
203
  thumbnail={thumbnail}
200
204
  mimeType={mimeType}
201
- overlayIcon={source ? (playing ? PauseIcon : PlayIcon) : undefined}
202
- onClick={source ? toggle : undefined}
205
+ overlayIcon={source && audioReady ? (playing ? PauseIcon : PlayIcon) : undefined}
206
+ onClick={source && audioReady ? toggle : undefined}
203
207
  />
204
- {source && (
208
+ {source && audioReady && (
205
209
  <div className="absolute inset-x-0 bottom-0 px-3 pb-2.5 pt-6 bg-gradient-to-t from-black/40 to-transparent">
206
210
  <div
207
211
  ref={trackRef}
@@ -63,6 +63,8 @@ const MediaPlayer: React.FC<MediaPlayerProps> = ({
63
63
  const [scrubberHovered, setScrubberHovered] = useState(false)
64
64
  const [videoAspect, setVideoAspect] = useState<number | null>(null)
65
65
  const [buffering, setBuffering] = useState(false)
66
+ /** True until the first canPlay fires for the current source — hides controls/spinner behind poster. */
67
+ const [initialLoad, setInitialLoad] = useState(true)
66
68
  /** Set when autoplay/play() was rejected so user can start via gesture (no controls UI). */
67
69
  const [manualPlayRequired, setManualPlayRequired] = useState(false)
68
70
  const playerRef = useRef<HTMLMediaElement>(null)
@@ -71,6 +73,7 @@ const MediaPlayer: React.FC<MediaPlayerProps> = ({
71
73
 
72
74
  useEffect(() => {
73
75
  setManualPlayRequired(false)
76
+ setInitialLoad(true)
74
77
  }, [source])
75
78
 
76
79
  useEffect(() => {
@@ -209,6 +212,11 @@ const MediaPlayer: React.FC<MediaPlayerProps> = ({
209
212
  })}
210
213
  </div>
211
214
  )}
215
+ {initialLoad && poster && (
216
+ <div className="absolute inset-0 z-20">
217
+ <img src={poster} alt="" className="h-full w-full object-cover" />
218
+ </div>
219
+ )}
212
220
  <div className="absolute inset-0">
213
221
  {sourceType === 'audio' ? (
214
222
  <audio
@@ -218,7 +226,7 @@ const MediaPlayer: React.FC<MediaPlayerProps> = ({
218
226
  muted={muted}
219
227
  style={{ width: '100%', height: '100%' }}
220
228
  onLoadStart={() => setBuffering(true)}
221
- onCanPlay={() => setBuffering(false)}
229
+ onCanPlay={() => { setBuffering(false); setInitialLoad(false) }}
222
230
  onWaiting={() => setBuffering(true)}
223
231
  onPlay={() => setManualPlayRequired(false)}
224
232
  onEnded={() => {
@@ -240,7 +248,7 @@ const MediaPlayer: React.FC<MediaPlayerProps> = ({
240
248
  playsInline
241
249
  style={{ width: '100%', height: '100%' }}
242
250
  onLoadStart={() => setBuffering(true)}
243
- onCanPlay={() => setBuffering(false)}
251
+ onCanPlay={() => { setBuffering(false); setInitialLoad(false) }}
244
252
  onWaiting={() => setBuffering(true)}
245
253
  onPlay={() => setManualPlayRequired(false)}
246
254
  onLoadedMetadata={() => {
@@ -24,10 +24,11 @@ export interface VisitorCardProps extends LockedAttachmentBaseProps {
24
24
  * Omit to hide the Unlock button.
25
25
  */
26
26
  onUnlock?: () => Promise<LockedAttachmentSource>
27
- /**
27
+ /**
28
28
  * Called when the visitor clicks Download on an unlocked card.
29
+ * May return a Promise — if so, the component shows a loader until it resolves.
29
30
  */
30
- onDownload?: () => void
31
+ onDownload?: () => void | Promise<void>
31
32
  }
32
33
 
33
34
  const getLockIcon = (paymentStatus?: PaymentStatus): React.ElementType =>
@@ -36,14 +37,23 @@ const getLockIcon = (paymentStatus?: PaymentStatus): React.ElementType =>
36
37
 
37
38
  interface LockOverlayProps {
38
39
  icon: React.ElementType
40
+ loading?: boolean
39
41
  }
40
42
 
41
43
  const LockOverlay: React.FC<LockOverlayProps> = (props) => {
42
- const { icon: Icon } = props
44
+ const { icon: Icon, loading } = props
43
45
  return (
44
46
  <div className="absolute inset-0 bg-black/30">
45
47
  <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" />
48
+ {loading ? (
49
+ <span className="flex items-center gap-[2px]">
50
+ <span className="size-1 rounded-full bg-white animate-bounce [animation-delay:-0.3s]" />
51
+ <span className="size-1 rounded-full bg-white animate-bounce [animation-delay:-0.15s]" />
52
+ <span className="size-1 rounded-full bg-white animate-bounce" />
53
+ </span>
54
+ ) : (
55
+ <Icon className="size-4 text-white" weight="fill" />
56
+ )}
47
57
  </div>
48
58
  </div>
49
59
  )
@@ -53,10 +63,11 @@ interface LockedPreviewProps {
53
63
  thumbnail?: string
54
64
  mimeType: string
55
65
  LockIcon: React.ElementType
66
+ loading?: boolean
56
67
  }
57
68
 
58
69
  const LockedPreview: React.FC<LockedPreviewProps> = (props) => {
59
- const { thumbnail, mimeType, LockIcon } = props
70
+ const { thumbnail, mimeType, LockIcon, loading } = props
60
71
  return (
61
72
  <div className="relative aspect-video overflow-hidden bg-black/5">
62
73
  {thumbnail ? (
@@ -73,7 +84,7 @@ const LockedPreview: React.FC<LockedPreviewProps> = (props) => {
73
84
  })}
74
85
  </div>
75
86
  )}
76
- <LockOverlay icon={LockIcon} />
87
+ <LockOverlay icon={LockIcon} loading={loading} />
77
88
  </div>
78
89
  )
79
90
  }
@@ -86,10 +97,11 @@ interface ImagePreviewProps {
86
97
  title?: string
87
98
  paymentStatus?: PaymentStatus
88
99
  isLocked: boolean
100
+ loading?: boolean
89
101
  }
90
102
 
91
103
  const ImagePreview: React.FC<ImagePreviewProps> = (props) => {
92
- const { source, thumbnail, mimeType, title, paymentStatus, isLocked } = props
104
+ const { source, thumbnail, mimeType, title, paymentStatus, isLocked, loading } = props
93
105
  const [sourceReady, setSourceReady] = useState(false)
94
106
 
95
107
  useEffect(() => {
@@ -102,6 +114,7 @@ const ImagePreview: React.FC<ImagePreviewProps> = (props) => {
102
114
  thumbnail={thumbnail}
103
115
  mimeType={mimeType}
104
116
  LockIcon={getLockIcon(paymentStatus)}
117
+ loading={loading}
105
118
  />
106
119
  )
107
120
  }
@@ -123,10 +136,11 @@ interface DocumentPreviewProps {
123
136
  mimeType: string
124
137
  paymentStatus?: PaymentStatus
125
138
  isLocked: boolean
139
+ loading?: boolean
126
140
  }
127
141
 
128
142
  const DocumentPreview: React.FC<DocumentPreviewProps> = (props) => {
129
- const { thumbnail, mimeType, paymentStatus, isLocked } = props
143
+ const { thumbnail, mimeType, paymentStatus, isLocked, loading } = props
130
144
  return (
131
145
  <div className="relative aspect-video overflow-hidden bg-black/5">
132
146
  {thumbnail ? (
@@ -143,7 +157,7 @@ const DocumentPreview: React.FC<DocumentPreviewProps> = (props) => {
143
157
  })}
144
158
  </div>
145
159
  )}
146
- {isLocked && <LockOverlay icon={getLockIcon(paymentStatus)} />}
160
+ {isLocked && <LockOverlay icon={getLockIcon(paymentStatus)} loading={loading} />}
147
161
  </div>
148
162
  )
149
163
  }
@@ -154,16 +168,18 @@ interface MediaPreviewProps {
154
168
  mimeType: string
155
169
  paymentStatus?: PaymentStatus
156
170
  isLocked: boolean
171
+ loading?: boolean
157
172
  }
158
173
 
159
174
  const MediaPreview: React.FC<MediaPreviewProps> = (props) => {
160
- const { source, thumbnail, mimeType, paymentStatus, isLocked } = props
175
+ const { source, thumbnail, mimeType, paymentStatus, isLocked, loading } = props
161
176
  if (isLocked) {
162
177
  return (
163
178
  <LockedPreview
164
179
  thumbnail={thumbnail}
165
180
  mimeType={mimeType}
166
181
  LockIcon={getLockIcon(paymentStatus)}
182
+ loading={loading}
167
183
  />
168
184
  )
169
185
  }
@@ -171,9 +187,18 @@ const MediaPreview: React.FC<MediaPreviewProps> = (props) => {
171
187
  }
172
188
 
173
189
 
190
+ const LoadingDots = () => (
191
+ <span className="flex items-center gap-1">
192
+ <span className="size-1 rounded-full bg-white animate-bounce [animation-delay:-0.3s]" />
193
+ <span className="size-1 rounded-full bg-white animate-bounce [animation-delay:-0.15s]" />
194
+ <span className="size-1 rounded-full bg-white animate-bounce" />
195
+ </span>
196
+ )
197
+
174
198
  interface CardActionsProps {
175
199
  isLocked: boolean
176
- loading: boolean
200
+ unlockLoading: boolean
201
+ downloadLoading: boolean
177
202
  paymentStatus?: PaymentStatus
178
203
  source?: string
179
204
  onUnlock?: () => void
@@ -181,8 +206,7 @@ interface CardActionsProps {
181
206
  }
182
207
 
183
208
  const CardActions: React.FC<CardActionsProps> = (props) => {
184
- const { isLocked, loading, paymentStatus, source, onUnlock, onDownload } =
185
- props
209
+ const { isLocked, unlockLoading, downloadLoading, paymentStatus, source, onUnlock, onDownload } = props
186
210
  const LockIcon = getLockIcon(paymentStatus)
187
211
 
188
212
  if (isLocked && onUnlock) {
@@ -190,15 +214,11 @@ const CardActions: React.FC<CardActionsProps> = (props) => {
190
214
  <button
191
215
  type="button"
192
216
  onClick={onUnlock}
193
- disabled={loading}
217
+ disabled={unlockLoading}
194
218
  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 disabled:opacity-70"
195
219
  >
196
- {loading ? (
197
- <span className="flex items-center gap-1">
198
- <span className="size-1 rounded-full bg-white animate-bounce [animation-delay:-0.3s]" />
199
- <span className="size-1 rounded-full bg-white animate-bounce [animation-delay:-0.15s]" />
200
- <span className="size-1 rounded-full bg-white animate-bounce" />
201
- </span>
220
+ {unlockLoading ? (
221
+ <LoadingDots />
202
222
  ) : (
203
223
  <>
204
224
  {paymentStatus === 'paid' ? (
@@ -215,16 +235,21 @@ const CardActions: React.FC<CardActionsProps> = (props) => {
215
235
 
216
236
  if (!isLocked && onDownload && source) {
217
237
  return (
218
- <a
219
- href={source}
220
- target="_blank"
221
- rel="noopener noreferrer"
238
+ <button
239
+ type="button"
222
240
  onClick={onDownload}
223
- 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"
241
+ disabled={downloadLoading}
242
+ 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 disabled:opacity-70"
224
243
  >
225
- <DownloadSimpleIcon className="size-4" weight="bold" />
226
- Download
227
- </a>
244
+ {downloadLoading ? (
245
+ <LoadingDots />
246
+ ) : (
247
+ <>
248
+ <DownloadSimpleIcon className="size-4" weight="bold" />
249
+ Download
250
+ </>
251
+ )}
252
+ </button>
228
253
  )
229
254
  }
230
255
 
@@ -245,7 +270,8 @@ const VisitorCard: React.FC<VisitorCardProps> = (props) => {
245
270
  paymentStatus,
246
271
  } = props
247
272
  const [source, setSource] = useState(sourceProp)
248
- const [loading, setLoading] = useState(false)
273
+ const [unlockLoading, setUnlockLoading] = useState(false)
274
+ const [downloadLoading, setDownloadLoading] = useState(false)
249
275
 
250
276
  useEffect(() => {
251
277
  if (sourceProp !== undefined) setSource(sourceProp)
@@ -256,14 +282,26 @@ const VisitorCard: React.FC<VisitorCardProps> = (props) => {
256
282
 
257
283
  const handleUnlock = async () => {
258
284
  if (!onUnlock) return
259
- setLoading(true)
285
+ setUnlockLoading(true)
260
286
  try {
261
287
  const result = await onUnlock()
262
288
  setSource(result.source)
263
289
  } catch (err) {
264
290
  if (isDevBuild()) console.debug('[LockedAttachment] onUnlock failed', err)
265
291
  } finally {
266
- setLoading(false)
292
+ setUnlockLoading(false)
293
+ }
294
+ }
295
+
296
+ const handleDownload = async () => {
297
+ if (!onDownload) return
298
+ setDownloadLoading(true)
299
+ try {
300
+ await onDownload()
301
+ } catch (err) {
302
+ if (isDevBuild()) console.debug('[LockedAttachment] onDownload failed', err)
303
+ } finally {
304
+ setDownloadLoading(false)
267
305
  }
268
306
  }
269
307
 
@@ -277,6 +315,7 @@ const VisitorCard: React.FC<VisitorCardProps> = (props) => {
277
315
  title={title}
278
316
  paymentStatus={paymentStatus}
279
317
  isLocked={isLocked}
318
+ loading={unlockLoading}
280
319
  />
281
320
  )
282
321
  } else if (sourceType === 'document') {
@@ -286,6 +325,7 @@ const VisitorCard: React.FC<VisitorCardProps> = (props) => {
286
325
  mimeType={mimeType}
287
326
  paymentStatus={paymentStatus}
288
327
  isLocked={isLocked}
328
+ loading={unlockLoading}
289
329
  />
290
330
  )
291
331
  } else {
@@ -296,6 +336,7 @@ const VisitorCard: React.FC<VisitorCardProps> = (props) => {
296
336
  mimeType={mimeType}
297
337
  paymentStatus={paymentStatus}
298
338
  isLocked={isLocked}
339
+ loading={unlockLoading}
299
340
  />
300
341
  )
301
342
  }
@@ -339,11 +380,12 @@ const VisitorCard: React.FC<VisitorCardProps> = (props) => {
339
380
  </div>
340
381
  <CardActions
341
382
  isLocked={isLocked}
342
- loading={loading}
383
+ unlockLoading={unlockLoading}
384
+ downloadLoading={downloadLoading}
343
385
  paymentStatus={paymentStatus}
344
386
  source={source}
345
387
  onUnlock={onUnlock ? handleUnlock : undefined}
346
- onDownload={onDownload}
388
+ onDownload={onDownload ? handleDownload : undefined}
347
389
  />
348
390
  </div>
349
391
  </div>
@@ -1 +0,0 @@
1
- {"version":3,"file":"Creator-BJ6UBWsR.js","sources":["../src/components/LockedAttachment/components/Creator.tsx"],"sourcesContent":["import {\n CheckCircleIcon,\n EyeIcon,\n EyeSlashIcon,\n LockIcon,\n LockOpenIcon,\n PauseIcon,\n PlayIcon,\n XIcon,\n} from '@phosphor-icons/react'\nimport classNames from 'classnames'\nimport React, { useCallback, useEffect, useRef, useState } from 'react'\n\nimport type { LockedAttachmentBaseProps } from '../types'\nimport { renderTypeIcon } from '../utils/icons'\nimport { getSourceType } from '../utils/mimeType'\n\nimport MediaPlayer from './MediaPlayer'\n\nexport interface CreatorCardProps extends LockedAttachmentBaseProps {\n isPreview?: boolean\n placeholderTitle?: string\n placeholderAmountText?: string\n onDismiss?: () => void\n}\n\ninterface CloseButtonProps {\n onClose: () => void\n}\n\nconst CloseButton: React.FC<CloseButtonProps> = (props) => {\n const { onClose } = props\n return (\n <button\n type=\"button\"\n onClick={onClose}\n className=\"absolute left-3 top-3 z-40 flex size-8 items-center justify-center rounded-full bg-black/60 text-white\"\n aria-label=\"Close preview\"\n >\n <EyeIcon className=\"size-4\" weight=\"fill\" />\n </button>\n )\n}\n\ninterface CollapsedThumbnailProps {\n thumbnail?: string\n mimeType: string\n overlayIcon?: React.ElementType\n darkOverlay?: boolean\n onClick?: () => void\n}\n\nconst CollapsedThumbnail: React.FC<CollapsedThumbnailProps> = (props) => {\n const { thumbnail, mimeType, overlayIcon: OverlayIcon, darkOverlay, onClick } = props\n return (\n <button\n type=\"button\"\n disabled={!onClick}\n className={classNames(\n 'relative aspect-video block w-full overflow-hidden border-0 bg-black/5 p-0 text-left appearance-none',\n { 'cursor-pointer': !!onClick, 'cursor-default': !onClick }\n )}\n onClick={onClick}\n aria-label={OverlayIcon ? 'Toggle preview' : undefined}\n >\n {thumbnail ? (\n <img\n src={thumbnail}\n alt=\"\"\n className=\"absolute inset-0 h-full w-full object-cover\"\n />\n ) : (\n <div className=\"absolute inset-0 flex items-center justify-center\">\n {renderTypeIcon(mimeType, {\n className: 'size-12 text-black/20',\n weight: 'regular',\n })}\n </div>\n )}\n {darkOverlay && (\n <div className=\"pointer-events-none absolute inset-0 bg-black/30\" />\n )}\n {OverlayIcon && (\n <div className=\"pointer-events-none absolute left-3 top-3 flex size-8 items-center justify-center rounded-full bg-black/60 text-white\">\n <OverlayIcon className=\"size-4\" weight=\"fill\" />\n </div>\n )}\n </button>\n )\n}\n\n\ninterface AudioPreviewProps {\n source?: string\n thumbnail?: string\n mimeType: string\n}\n\nconst AudioPreview: React.FC<AudioPreviewProps> = (props) => {\n const { source, thumbnail, mimeType } = props\n const [playing, setPlaying] = useState(false)\n const [played, setPlayed] = useState(0)\n const [seeking, setSeeking] = useState(false)\n const audioRef = useRef<HTMLAudioElement>(null)\n const trackRef = useRef<HTMLDivElement>(null)\n const rafRef = useRef<number | null>(null)\n\n useEffect(() => {\n const el = audioRef.current\n if (!el) return\n if (playing) {\n void el.play().catch(() => setPlaying(false))\n } else {\n el.pause()\n }\n }, [playing])\n\n useEffect(() => {\n if (!playing) {\n if (rafRef.current !== null) cancelAnimationFrame(rafRef.current)\n return\n }\n const tick = () => {\n const el = audioRef.current\n if (el && el.duration && !seeking) setPlayed(el.currentTime / el.duration)\n rafRef.current = requestAnimationFrame(tick)\n }\n rafRef.current = requestAnimationFrame(tick)\n return () => {\n if (rafRef.current !== null) cancelAnimationFrame(rafRef.current)\n }\n }, [playing, seeking])\n\n useEffect(() => {\n setPlaying(false)\n setPlayed(0)\n }, [source])\n\n const getFraction = useCallback(\n (e: MouseEvent | TouchEvent | React.MouseEvent | React.TouchEvent) => {\n const track = trackRef.current\n if (!track) return 0\n const clientX =\n 'touches' in e\n ? (e.touches[0]?.clientX ?? e.changedTouches[0]?.clientX ?? 0)\n : e.clientX\n const rect = track.getBoundingClientRect()\n return Math.max(0, Math.min(1, (clientX - rect.left) / rect.width))\n },\n []\n )\n\n const seekTo = useCallback((fraction: number) => {\n const el = audioRef.current\n if (el && el.duration) el.currentTime = fraction * el.duration\n }, [])\n\n useEffect(() => {\n if (!seeking) return\n const onMove = (e: MouseEvent | TouchEvent) => {\n const f = getFraction(e)\n setPlayed(f)\n seekTo(f)\n }\n const onUp = (e: MouseEvent | TouchEvent) => {\n setSeeking(false)\n seekTo(getFraction(e))\n }\n window.addEventListener('mousemove', onMove)\n window.addEventListener('mouseup', onUp)\n window.addEventListener('touchmove', onMove, { passive: true })\n window.addEventListener('touchend', onUp)\n return () => {\n window.removeEventListener('mousemove', onMove)\n window.removeEventListener('mouseup', onUp)\n window.removeEventListener('touchmove', onMove)\n window.removeEventListener('touchend', onUp)\n }\n }, [seeking, getFraction, seekTo])\n\n const toggle = useCallback(() => setPlaying((p) => !p), [])\n\n return (\n <div className=\"relative\">\n {source && (\n <audio\n ref={audioRef}\n src={source}\n loop\n onEnded={() => {\n setPlaying(false)\n setPlayed(0)\n }}\n >\n <track kind=\"captions\" />\n </audio>\n )}\n <CollapsedThumbnail\n thumbnail={thumbnail}\n mimeType={mimeType}\n overlayIcon={source ? (playing ? PauseIcon : PlayIcon) : undefined}\n onClick={source ? toggle : undefined}\n />\n {source && (\n <div className=\"absolute inset-x-0 bottom-0 px-3 pb-2.5 pt-6 bg-gradient-to-t from-black/40 to-transparent\">\n <div\n ref={trackRef}\n role=\"slider\"\n aria-label=\"Playback position\"\n aria-valuenow={Math.round(played * 100)}\n aria-valuemin={0}\n aria-valuemax={100}\n tabIndex={0}\n className=\"relative flex h-4 w-full cursor-pointer items-center\"\n onMouseDown={(e) => {\n e.stopPropagation()\n setSeeking(true)\n const f = getFraction(e)\n setPlayed(f)\n seekTo(f)\n }}\n onTouchStart={(e) => {\n e.stopPropagation()\n setSeeking(true)\n const f = getFraction(e)\n setPlayed(f)\n seekTo(f)\n }}\n onClick={(e) => e.stopPropagation()}\n onKeyDown={(e) => {\n if (e.key === 'ArrowRight') seekTo(Math.min(1, played + 0.05))\n if (e.key === 'ArrowLeft') seekTo(Math.max(0, played - 0.05))\n }}\n >\n <div className=\"w-full overflow-hidden rounded-full bg-white/30 h-1\">\n <div\n className=\"h-full rounded-full bg-white\"\n style={{ width: `${Math.round(played * 100)}%` }}\n />\n </div>\n </div>\n </div>\n )}\n </div>\n )\n}\n\ninterface VideoPreviewProps {\n source?: string\n thumbnail?: string\n mimeType: string\n}\n\nconst VideoPreview: React.FC<VideoPreviewProps> = (props) => {\n const { source, thumbnail, mimeType } = props\n const [expanded, setExpanded] = useState(false)\n const collapse = () => setExpanded(false)\n\n useEffect(() => {\n setExpanded(false)\n }, [source])\n\n if (!source) {\n return <CollapsedThumbnail thumbnail={thumbnail} mimeType={mimeType} />\n }\n\n return (\n <div\n className={classNames('relative overflow-hidden', {\n 'aspect-video': !expanded,\n })}\n >\n <MediaPlayer\n source={source}\n mimeType={mimeType}\n poster={thumbnail}\n playing={expanded}\n loop\n controls={false}\n showProgress\n onContainerClick={collapse}\n muted\n />\n {!expanded && (\n <button\n type=\"button\"\n className=\"absolute inset-0 block cursor-pointer border-0 p-0 text-left appearance-none\"\n onClick={() => setExpanded(true)}\n aria-label=\"Expand video preview\"\n >\n {thumbnail ? (\n <img\n src={thumbnail}\n alt=\"\"\n className=\"absolute inset-0 h-full w-full object-cover\"\n />\n ) : (\n <div className=\"absolute inset-0 flex items-center justify-center\">\n {renderTypeIcon(mimeType, {\n className: 'size-12 text-black/20',\n weight: 'regular',\n })}\n </div>\n )}\n <div className=\"pointer-events-none absolute left-3 top-3 flex size-8 items-center justify-center rounded-full bg-black/60 text-white\">\n <EyeSlashIcon className=\"size-4\" weight=\"fill\" />\n </div>\n </button>\n )}\n {expanded && <CloseButton onClose={collapse} />}\n </div>\n )\n}\n\ninterface ImagePreviewProps {\n source?: string\n thumbnail?: string\n mimeType: string\n title?: string\n}\n\nconst ImagePreview: React.FC<ImagePreviewProps> = (props) => {\n const { source, thumbnail, mimeType, title } = props\n const [expanded, setExpanded] = useState(false)\n const collapse = () => setExpanded(false)\n\n useEffect(() => {\n setExpanded(false)\n }, [source])\n\n if (expanded && source) {\n return (\n <div className=\"relative\">\n <button\n type=\"button\"\n className=\"block w-full cursor-pointer border-0 p-0 text-left appearance-none\"\n onClick={collapse}\n aria-label=\"Close preview\"\n >\n <img src={source} alt={title ?? ''} className=\"block w-full\" />\n </button>\n <CloseButton onClose={collapse} />\n </div>\n )\n }\n\n return (\n <CollapsedThumbnail\n thumbnail={thumbnail}\n mimeType={mimeType}\n overlayIcon={source ? EyeSlashIcon : undefined}\n onClick={source ? () => setExpanded(true) : undefined}\n />\n )\n}\n\n\nconst CreatorCard: React.FC<CreatorCardProps> = (props) => {\n const {\n title,\n mimeType = 'application/octet-stream',\n thumbnail,\n source,\n detail,\n amountText,\n placeholderTitle = 'Attachment title',\n placeholderAmountText,\n paymentStatus,\n onDismiss,\n isPreview = false,\n } = props\n const sourceType = getSourceType(mimeType)\n const displayAmountText = amountText ?? placeholderAmountText\n const isPlaceholderAmount = !amountText && !!placeholderAmountText\n\n let mediaPreview: React.ReactNode\n if (isPreview && sourceType === 'audio') {\n mediaPreview = (\n <AudioPreview source={source} thumbnail={thumbnail} mimeType={mimeType} />\n )\n } else if (isPreview && sourceType === 'video') {\n mediaPreview = (\n <VideoPreview source={source} thumbnail={thumbnail} mimeType={mimeType} />\n )\n } else if (isPreview && sourceType === 'image') {\n mediaPreview = (\n <ImagePreview\n source={source}\n thumbnail={thumbnail}\n mimeType={mimeType}\n title={title}\n />\n )\n } else {\n const lockedOverlayIcon = onDismiss\n ? undefined\n : paymentStatus === 'paid'\n ? LockOpenIcon\n : LockIcon\n mediaPreview = (\n <CollapsedThumbnail\n thumbnail={thumbnail}\n mimeType={mimeType}\n overlayIcon={lockedOverlayIcon}\n darkOverlay\n />\n )\n }\n\n return (\n <div className=\"relative w-[280px] select-none overflow-hidden rounded-3xl bg-white shadow-card\">\n {onDismiss && (\n <button\n type=\"button\"\n onClick={onDismiss}\n className=\"absolute right-3 top-3 z-50 flex size-8 items-center justify-center rounded-full bg-black/60 text-white\"\n aria-label=\"Dismiss attachment\"\n >\n <XIcon className=\"size-4\" weight=\"bold\" />\n </button>\n )}\n {mediaPreview}\n <div className=\"px-4 pb-3 pt-3\">\n <p\n className={classNames('mb-1.5 truncate text-base font-medium', {\n 'text-black/30': !title,\n 'text-black': !!title,\n })}\n >\n {title || placeholderTitle}\n </p>\n <div className=\"flex items-center gap-1\">\n {renderTypeIcon(mimeType, {\n className: 'size-5 shrink-0 text-black/55',\n weight: 'regular',\n })}\n {detail && (\n <span className=\"text-xs font-medium text-black/55\">{detail}</span>\n )}\n {paymentStatus === 'paid' ? (\n <>\n <span className=\"text-xs font-medium text-black/55\">•</span>\n <span className=\"text-xs font-medium text-[#008236]\">\n Purchased\n </span>\n <CheckCircleIcon\n className=\"size-4 text-[#008236]\"\n weight=\"bold\"\n />\n </>\n ) : (\n displayAmountText && (\n <>\n <span\n className={classNames('text-xs font-medium', {\n 'text-black/30': isPlaceholderAmount,\n 'text-black/55': !isPlaceholderAmount,\n })}\n >\n •\n </span>\n <span\n className={classNames('text-xs font-medium', {\n 'text-black/30': isPlaceholderAmount,\n 'text-black/55': !isPlaceholderAmount,\n })}\n >\n {displayAmountText}\n </span>\n </>\n )\n )}\n </div>\n </div>\n </div>\n )\n}\n\nexport default CreatorCard\n"],"names":["CloseButton","props","onClose","jsx","EyeIcon","CollapsedThumbnail","thumbnail","mimeType","OverlayIcon","darkOverlay","onClick","jsxs","classNames","AudioPreview","source","playing","setPlaying","useState","played","setPlayed","seeking","setSeeking","audioRef","useRef","trackRef","rafRef","useEffect","el","tick","getFraction","useCallback","e","track","clientX","_a","_b","rect","seekTo","fraction","onMove","f","onUp","toggle","p","PauseIcon","PlayIcon","VideoPreview","expanded","setExpanded","collapse","MediaPlayer","EyeSlashIcon","ImagePreview","title","CreatorCard","detail","amountText","placeholderTitle","placeholderAmountText","paymentStatus","onDismiss","isPreview","sourceType","getSourceType","displayAmountText","isPlaceholderAmount","mediaPreview","LockOpenIcon","LockIcon","XIcon","renderTypeIcon","Fragment","CheckCircleIcon"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"MediaPlayer-DXz4IBLx.js","sources":["../src/components/LockedAttachment/utils/mimeType.ts","../src/components/LockedAttachment/utils/icons.ts","../src/components/LockedAttachment/components/MediaPlayer.tsx"],"sourcesContent":["export type AttachmentSourceType = 'image' | 'audio' | 'video' | 'document'\n\nexport type DocumentIconType =\n | 'pdf'\n | 'doc'\n | 'xls'\n | 'csv'\n | 'ppt'\n | 'zip'\n | 'text'\n | 'markdown'\n | 'generic'\n\nconst DOCUMENT_ICON_PATTERNS: Array<[RegExp, DocumentIconType]> = [\n [/pdf/, 'pdf'],\n [/wordprocessingml|msword|\\.doc/, 'doc'],\n [/spreadsheetml|ms-excel|\\.xls/, 'xls'],\n [/csv/, 'csv'],\n [/presentationml|ms-powerpoint|\\.ppt/, 'ppt'],\n [/zip|x-rar|x-7z|x-tar|x-gzip/, 'zip'],\n [/plain|rtf/, 'text'],\n [/markdown/, 'markdown'],\n]\n\nexport function getSourceType(mimeType: string): AttachmentSourceType {\n if (mimeType.startsWith('video/')) return 'video'\n if (mimeType.startsWith('audio/')) return 'audio'\n if (mimeType.startsWith('image/')) return 'image'\n return 'document'\n}\n\nexport function getDocumentIconType(mimeType: string): DocumentIconType {\n const match = DOCUMENT_ICON_PATTERNS.find(([pattern]) =>\n pattern.test(mimeType)\n )\n return match ? match[1] : 'generic'\n}\n","import {\n FileIcon,\n FileCsvIcon,\n FileDocIcon,\n FileMdIcon,\n FilePdfIcon,\n FilePptIcon,\n FileTextIcon,\n FileXlsIcon,\n FileZipIcon,\n ImageIcon,\n SpeakerHighIcon,\n VideoCameraIcon,\n IconProps,\n} from '@phosphor-icons/react'\nimport React from 'react'\n\nimport { getDocumentIconType, getSourceType } from './mimeType'\nimport type { AttachmentSourceType } from './mimeType'\n\nexport const MEDIA_TYPE_ICON: Record<AttachmentSourceType, React.ElementType> =\n {\n video: VideoCameraIcon,\n audio: SpeakerHighIcon,\n image: ImageIcon,\n document: FileIcon,\n }\n\nconst DOCUMENT_ICON_COMPONENT = {\n pdf: FilePdfIcon,\n doc: FileDocIcon,\n xls: FileXlsIcon,\n csv: FileCsvIcon,\n ppt: FilePptIcon,\n zip: FileZipIcon,\n text: FileTextIcon,\n markdown: FileMdIcon,\n generic: FileIcon,\n} as const\n\nexport function getTypeIcon(mimeType: string): React.ElementType {\n const sourceType = getSourceType(mimeType)\n if (sourceType !== 'document') return MEDIA_TYPE_ICON[sourceType]\n return DOCUMENT_ICON_COMPONENT[getDocumentIconType(mimeType)]\n}\n\n/** Use instead of `<TypeIcon />` where TypeIcon = getTypeIcon(mime) to satisfy react-hooks/static-components. */\nexport function renderTypeIcon(\n mimeType: string,\n props: IconProps\n): React.ReactElement {\n return React.createElement(getTypeIcon(mimeType), props)\n}\n","import { CircleNotchIcon, PauseIcon, PlayIcon } from '@phosphor-icons/react'\nimport React, { useCallback, useEffect, useRef, useState } from 'react'\n\nimport { isDevBuild } from '../../../utils/isDevBuild'\nimport { renderTypeIcon } from '../utils/icons'\nimport { getSourceType, type AttachmentSourceType } from '../utils/mimeType'\n\nconst getPlayerBg = (sourceType: AttachmentSourceType, poster?: string) =>\n sourceType === 'audio' && !poster ? 'bg-black/5' : 'bg-black'\n\nfunction getClientXFromEvent(\n e: MouseEvent | TouchEvent | React.MouseEvent | React.TouchEvent\n): number {\n if ('touches' in e) {\n return e.touches[0]?.clientX ?? e.changedTouches[0]?.clientX ?? 0\n }\n return e.clientX\n}\n\nexport interface MediaPlayerProps {\n source: string\n mimeType: string\n poster?: string\n autoPlay?: boolean\n /** Controlled playing state. When provided, syncs to internal play/pause. */\n playing?: boolean\n loop?: boolean\n controls?: boolean\n showProgress?: boolean\n onContainerClick?: () => void\n /** When true, requests muted playback (helps autoplay policies on video). */\n muted?: boolean\n}\n\nconst MediaPlayer: React.FC<MediaPlayerProps> = ({\n source,\n mimeType,\n poster,\n autoPlay = false,\n playing: playingProp,\n loop = false,\n controls = true,\n showProgress = false,\n onContainerClick,\n muted = false,\n}) => {\n const sourceType = getSourceType(mimeType)\n const [playing, setPlaying] = useState(autoPlay)\n\n // Sync controlled playing prop to internal state\n const prevPlayingPropRef = useRef(playingProp)\n useEffect(() => {\n if (\n playingProp !== undefined &&\n playingProp !== prevPlayingPropRef.current\n ) {\n prevPlayingPropRef.current = playingProp\n setPlaying(playingProp)\n }\n }, [playingProp])\n const [played, setPlayed] = useState(0)\n const [seeking, setSeeking] = useState(false)\n const [scrubberHovered, setScrubberHovered] = useState(false)\n const [videoAspect, setVideoAspect] = useState<number | null>(null)\n const [buffering, setBuffering] = useState(false)\n /** Set when autoplay/play() was rejected so user can start via gesture (no controls UI). */\n const [manualPlayRequired, setManualPlayRequired] = useState(false)\n const playerRef = useRef<HTMLMediaElement>(null)\n const trackRef = useRef<HTMLDivElement>(null)\n const rafRef = useRef<number | null>(null)\n\n useEffect(() => {\n setManualPlayRequired(false)\n }, [source])\n\n useEffect(() => {\n if (!playing) {\n if (rafRef.current !== null) {\n cancelAnimationFrame(rafRef.current)\n rafRef.current = null\n }\n return\n }\n const tick = () => {\n const el = playerRef.current\n if (el && el.duration && !seeking) setPlayed(el.currentTime / el.duration)\n rafRef.current = requestAnimationFrame(tick)\n }\n rafRef.current = requestAnimationFrame(tick)\n return () => {\n if (rafRef.current !== null) cancelAnimationFrame(rafRef.current)\n }\n }, [playing, seeking])\n\n // ReactPlayer v3 uses native HTML media elements and does not support a\n // declarative `playing` prop — playback must be driven imperatively.\n useEffect(() => {\n const el = playerRef.current\n if (!el) return\n if (playing) {\n void el.play().catch((err) => {\n setPlaying(false)\n setManualPlayRequired(true)\n if (isDevBuild()) {\n console.debug('[MediaPlayer] play() failed', err)\n }\n })\n } else {\n el.pause()\n }\n }, [playing])\n\n const startPlaybackFromGesture = useCallback(() => {\n setManualPlayRequired(false)\n setPlaying(true)\n }, [])\n\n const getFraction = useCallback(\n (e: MouseEvent | TouchEvent | React.MouseEvent | React.TouchEvent) => {\n const track = trackRef.current\n if (!track) return 0\n const rect = track.getBoundingClientRect()\n return Math.max(\n 0,\n Math.min(1, (getClientXFromEvent(e) - rect.left) / rect.width)\n )\n },\n []\n )\n\n const seekTo = useCallback((fraction: number) => {\n const el = playerRef.current\n if (el && el.duration) el.currentTime = fraction * el.duration\n }, [])\n\n const handleTrackPointerDown = (\n e: React.MouseEvent<HTMLDivElement> | React.TouchEvent<HTMLDivElement>\n ) => {\n e.stopPropagation()\n setSeeking(true)\n const fraction = getFraction(e)\n setPlayed(fraction)\n seekTo(fraction)\n }\n\n useEffect(() => {\n if (!seeking) return\n const onMove = (e: MouseEvent | TouchEvent) => setPlayed(getFraction(e))\n const onUp = (e: MouseEvent | TouchEvent) => {\n setSeeking(false)\n seekTo(getFraction(e))\n }\n window.addEventListener('mousemove', onMove)\n window.addEventListener('mouseup', onUp)\n window.addEventListener('touchmove', onMove, { passive: true })\n window.addEventListener('touchend', onUp)\n return () => {\n window.removeEventListener('mousemove', onMove)\n window.removeEventListener('mouseup', onUp)\n window.removeEventListener('touchmove', onMove)\n window.removeEventListener('touchend', onUp)\n }\n }, [seeking, getFraction, seekTo])\n\n // Use natural aspect ratio once metadata loads, fall back to 16:9 before then.\n const aspectStyle = videoAspect\n ? { aspectRatio: String(videoAspect) }\n : undefined\n const aspectClass = !videoAspect ? ' aspect-video' : ''\n const scrubberPercent = Math.round(played * 100)\n\n return (\n <div\n role=\"button\"\n tabIndex={0}\n className={`relative cursor-pointer overflow-hidden ${getPlayerBg(sourceType, poster)}${aspectClass}`}\n style={aspectStyle}\n onClick={() => {\n if (manualPlayRequired) return\n if (onContainerClick) {\n onContainerClick()\n return\n }\n if (controls) setPlaying((p) => !p)\n }}\n onKeyDown={(e) => {\n if (e.key !== 'Enter' && e.key !== ' ') return\n e.preventDefault()\n if (manualPlayRequired) return\n if (onContainerClick) {\n onContainerClick()\n return\n }\n if (controls) setPlaying((p) => !p)\n }}\n >\n {poster && (\n <img\n src={poster}\n alt=\"\"\n className=\"absolute inset-0 h-full w-full object-cover\"\n />\n )}\n {!poster && (\n <div className=\"absolute inset-0 flex items-center justify-center\">\n {renderTypeIcon(mimeType, {\n className: 'size-12 text-black/20',\n weight: 'regular',\n })}\n </div>\n )}\n <div className=\"absolute inset-0\">\n {sourceType === 'audio' ? (\n <audio\n ref={playerRef as React.RefObject<HTMLAudioElement>}\n src={source}\n loop={loop}\n muted={muted}\n style={{ width: '100%', height: '100%' }}\n onLoadStart={() => setBuffering(true)}\n onCanPlay={() => setBuffering(false)}\n onWaiting={() => setBuffering(true)}\n onPlay={() => setManualPlayRequired(false)}\n onEnded={() => {\n if (!loop) {\n setPlaying(false)\n setPlayed(0)\n }\n }}\n >\n <track kind=\"captions\" />\n </audio>\n ) : (\n <video\n ref={playerRef as React.RefObject<HTMLVideoElement>}\n src={source}\n poster={poster}\n loop={loop}\n muted={muted}\n playsInline\n style={{ width: '100%', height: '100%' }}\n onLoadStart={() => setBuffering(true)}\n onCanPlay={() => setBuffering(false)}\n onWaiting={() => setBuffering(true)}\n onPlay={() => setManualPlayRequired(false)}\n onLoadedMetadata={() => {\n const el = playerRef.current\n if (el instanceof HTMLVideoElement && el.videoWidth && el.videoHeight) {\n setVideoAspect(el.videoWidth / el.videoHeight)\n }\n }}\n onEnded={() => {\n if (!loop) {\n setPlaying(false)\n setPlayed(0)\n }\n }}\n >\n <track kind=\"captions\" />\n </video>\n )}\n </div>\n\n {buffering && !manualPlayRequired && (\n <div className=\"absolute inset-0 z-10 flex items-center justify-center\">\n <CircleNotchIcon\n className=\"size-8 animate-spin text-white/80\"\n weight=\"bold\"\n />\n </div>\n )}\n\n {manualPlayRequired && !controls && (\n <div\n className=\"absolute inset-0 z-30 flex cursor-pointer items-center justify-center bg-black/35\"\n role=\"button\"\n tabIndex={0}\n aria-label=\"Play preview\"\n onClick={(e) => {\n e.stopPropagation()\n startPlaybackFromGesture()\n }}\n onKeyDown={(e) => {\n if (e.key !== 'Enter' && e.key !== ' ') return\n e.preventDefault()\n e.stopPropagation()\n startPlaybackFromGesture()\n }}\n >\n <span className=\"flex size-16 items-center justify-center rounded-full bg-white/20 text-white backdrop-blur-sm\">\n <PlayIcon className=\"size-9 translate-x-0.5\" weight=\"fill\" />\n </span>\n </div>\n )}\n\n {showProgress && !controls && (\n <div className=\"absolute inset-x-0 bottom-0 px-3 pb-2.5 pt-6 bg-gradient-to-t from-black/40 to-transparent\">\n <div\n role=\"slider\"\n aria-label=\"Playback position\"\n aria-valuenow={scrubberPercent}\n aria-valuemin={0}\n aria-valuemax={100}\n tabIndex={0}\n ref={trackRef}\n className=\"relative flex h-4 w-full cursor-pointer items-center\"\n onMouseDown={handleTrackPointerDown}\n onTouchStart={handleTrackPointerDown}\n onClick={(e) => e.stopPropagation()}\n onKeyDown={(e) => {\n if (e.key === 'ArrowRight') seekTo(Math.min(1, played + 0.05))\n if (e.key === 'ArrowLeft') seekTo(Math.max(0, played - 0.05))\n }}\n >\n <div className=\"w-full overflow-hidden rounded-full bg-white/30 h-1\">\n <div\n className=\"h-full rounded-full bg-white\"\n style={{ width: `${scrubberPercent}%` }}\n />\n </div>\n </div>\n </div>\n )}\n\n {controls && (\n <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\">\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation()\n setPlaying((p) => !p)\n }}\n className=\"shrink-0 text-white\"\n aria-label={playing ? 'Pause' : 'Play'}\n >\n {playing ? (\n <PauseIcon className=\"size-5\" weight=\"fill\" />\n ) : (\n <PlayIcon className=\"size-5 translate-x-px\" weight=\"fill\" />\n )}\n </button>\n\n <div\n role=\"slider\"\n aria-label=\"Playback position\"\n aria-valuenow={scrubberPercent}\n aria-valuemin={0}\n aria-valuemax={100}\n tabIndex={0}\n ref={trackRef}\n className=\"relative flex h-4 w-full cursor-pointer items-center\"\n onMouseDown={handleTrackPointerDown}\n onTouchStart={handleTrackPointerDown}\n onClick={(e) => e.stopPropagation()}\n onMouseEnter={() => setScrubberHovered(true)}\n onMouseLeave={() => setScrubberHovered(false)}\n onKeyDown={(e) => {\n if (e.key === 'ArrowRight') seekTo(Math.min(1, played + 0.05))\n if (e.key === 'ArrowLeft') seekTo(Math.max(0, played - 0.05))\n }}\n >\n <div\n className={`w-full overflow-hidden rounded-full bg-white/30 transition-all duration-200 ${scrubberHovered || seeking ? 'h-1.5' : 'h-1'}`}\n >\n <div\n className=\"h-full rounded-full bg-white\"\n style={{ width: `${scrubberPercent}%` }}\n />\n </div>\n <div\n 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'}`}\n style={{ left: `${scrubberPercent}%` }}\n />\n </div>\n </div>\n )}\n </div>\n )\n}\n\nexport default MediaPlayer\n"],"names":["DOCUMENT_ICON_PATTERNS","getSourceType","mimeType","getDocumentIconType","match","pattern","MEDIA_TYPE_ICON","VideoCameraIcon","SpeakerHighIcon","ImageIcon","FileIcon","DOCUMENT_ICON_COMPONENT","FilePdfIcon","FileDocIcon","FileXlsIcon","FileCsvIcon","FilePptIcon","FileZipIcon","FileTextIcon","FileMdIcon","getTypeIcon","sourceType","renderTypeIcon","props","React","getPlayerBg","poster","getClientXFromEvent","e","_a","_b","MediaPlayer","source","autoPlay","playingProp","loop","controls","showProgress","onContainerClick","muted","playing","setPlaying","useState","prevPlayingPropRef","useRef","useEffect","played","setPlayed","seeking","setSeeking","scrubberHovered","setScrubberHovered","videoAspect","setVideoAspect","buffering","setBuffering","manualPlayRequired","setManualPlayRequired","playerRef","trackRef","rafRef","tick","el","err","startPlaybackFromGesture","useCallback","getFraction","track","rect","seekTo","fraction","handleTrackPointerDown","onMove","onUp","aspectStyle","aspectClass","scrubberPercent","jsxs","p","jsx","CircleNotchIcon","PlayIcon","PauseIcon"],"mappings":"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"}