@linktr.ee/messaging-react 3.8.1 → 3.9.0-rc-1783406511
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/Card-BG-Mg_ss.js +221 -0
- package/dist/Card-BG-Mg_ss.js.map +1 -0
- package/dist/Card-CmRzkuPw.cjs +2 -0
- package/dist/Card-CmRzkuPw.cjs.map +1 -0
- package/dist/Card-DSG-wR8t.js +96 -0
- package/dist/Card-DSG-wR8t.js.map +1 -0
- package/dist/Card-DVEuTlf_.cjs +2 -0
- package/dist/Card-DVEuTlf_.cjs.map +1 -0
- package/dist/Card-K9ky-G-t.js +108 -0
- package/dist/Card-K9ky-G-t.js.map +1 -0
- package/dist/Card-iKTcn1wg.cjs +2 -0
- package/dist/Card-iKTcn1wg.cjs.map +1 -0
- package/dist/PurchaseStatusBadge-CJH4S-8Z.js +36 -0
- package/dist/PurchaseStatusBadge-CJH4S-8Z.js.map +1 -0
- package/dist/PurchaseStatusBadge-UjDzZhLj.cjs +2 -0
- package/dist/PurchaseStatusBadge-UjDzZhLj.cjs.map +1 -0
- package/dist/TextShell-BiqF2UB0.js +32 -0
- package/dist/TextShell-BiqF2UB0.js.map +1 -0
- package/dist/TextShell-Bnzh0T75.cjs +2 -0
- package/dist/TextShell-Bnzh0T75.cjs.map +1 -0
- package/dist/assets/index.css +1 -1
- package/dist/{index-BCgRoF_u.js → index-DO_dYsIf.js} +1154 -1113
- package/dist/index-DO_dYsIf.js.map +1 -0
- package/dist/index-DpzmKL5U.cjs +2 -0
- package/dist/index-DpzmKL5U.cjs.map +1 -0
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +98 -55
- package/dist/index.js +1 -1
- package/dist/testing.d.ts +1 -0
- package/dist/usePreviewToggle-B5kmPELX.cjs +2 -0
- package/dist/usePreviewToggle-B5kmPELX.cjs.map +1 -0
- package/dist/usePreviewToggle-D27Xw9Xx.js +323 -0
- package/dist/usePreviewToggle-D27Xw9Xx.js.map +1 -0
- package/package.json +2 -2
- package/src/components/CustomMessage/CustomMessage.stories.tsx +54 -1
- package/src/components/CustomMessage/MessageTag.tsx +8 -0
- package/src/components/CustomMessage/index.tsx +61 -22
- package/src/components/LockedAttachment/LockedAttachment.stories.tsx +410 -333
- package/src/components/LockedAttachment/LockedAttachment.test.tsx +104 -0
- package/src/components/LockedAttachment/components/Composer/Card.tsx +97 -120
- package/src/components/LockedAttachment/components/Media/Card.tsx +237 -0
- package/src/components/LockedAttachment/components/Text/Card.tsx +177 -0
- package/src/components/LockedAttachment/components/_shared/CardBody.tsx +3 -3
- package/src/components/LockedAttachment/components/_shared/CardThumbnail.tsx +92 -0
- package/src/components/LockedAttachment/components/_shared/GalleryThumbnail.tsx +11 -33
- package/src/components/LockedAttachment/components/_shared/LoadingDots.tsx +36 -0
- package/src/components/LockedAttachment/components/_shared/LockBadge.tsx +21 -11
- package/src/components/LockedAttachment/components/_shared/{LockedCardShell.tsx → MediaShell.tsx} +5 -5
- package/src/components/LockedAttachment/components/_shared/PreviewToggle.tsx +58 -0
- package/src/components/LockedAttachment/components/_shared/PriceLockRow.tsx +21 -0
- package/src/components/LockedAttachment/components/_shared/PurchaseStatusBadge.tsx +87 -0
- package/src/components/LockedAttachment/components/_shared/{LockedThumbnail.tsx → SingleThumbnail.tsx} +5 -5
- package/src/components/LockedAttachment/components/_shared/SkeletonTextLines.tsx +39 -0
- package/src/components/LockedAttachment/components/_shared/TextShell.tsx +19 -0
- package/src/components/LockedAttachment/components/_shared/usePreviewToggle.test.ts +67 -0
- package/src/components/LockedAttachment/components/_shared/usePreviewToggle.ts +89 -0
- package/src/components/LockedAttachment/components/_shared/useReceivedSource.ts +83 -0
- package/src/components/LockedAttachment/index.tsx +17 -28
- package/src/components/LockedAttachment/types.ts +28 -4
- package/src/index.ts +2 -5
- package/src/stream-custom-data.ts +1 -0
- package/src/styles.css +5 -0
- package/dist/Card-BRvcFjsq.js +0 -132
- package/dist/Card-BRvcFjsq.js.map +0 -1
- package/dist/Card-BuXkok0A.cjs +0 -2
- package/dist/Card-BuXkok0A.cjs.map +0 -1
- package/dist/Card-CvNHv3me.js +0 -107
- package/dist/Card-CvNHv3me.js.map +0 -1
- package/dist/Card-DOSuQdnk.cjs +0 -2
- package/dist/Card-DOSuQdnk.cjs.map +0 -1
- package/dist/Card-NnQw99XE.cjs +0 -2
- package/dist/Card-NnQw99XE.cjs.map +0 -1
- package/dist/Card-jGOcSN1O.js +0 -163
- package/dist/Card-jGOcSN1O.js.map +0 -1
- package/dist/LockedThumbnail-EsjaAqLR.js +0 -220
- package/dist/LockedThumbnail-EsjaAqLR.js.map +0 -1
- package/dist/LockedThumbnail-uAauUd5c.cjs +0 -2
- package/dist/LockedThumbnail-uAauUd5c.cjs.map +0 -1
- package/dist/index-BCgRoF_u.js.map +0 -1
- package/dist/index-DRurVQ0-.cjs +0 -2
- package/dist/index-DRurVQ0-.cjs.map +0 -1
- package/src/components/LockedAttachment/components/Composer/index.ts +0 -2
- package/src/components/LockedAttachment/components/Received/Card.tsx +0 -191
- package/src/components/LockedAttachment/components/Received/index.ts +0 -2
- package/src/components/LockedAttachment/components/Sent/Card.tsx +0 -177
- package/src/components/LockedAttachment/components/Sent/index.ts +0 -2
- /package/src/components/LockedAttachment/components/{Received → Media}/CardActions.tsx +0 -0
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import classNames from 'classnames'
|
|
2
|
+
import React from 'react'
|
|
3
|
+
|
|
4
|
+
export interface SkeletonTextLinesProps {
|
|
5
|
+
/** Matches the bubble it sits in — "dark" for the sender's own bubble. */
|
|
6
|
+
variant: 'light' | 'dark'
|
|
7
|
+
/**
|
|
8
|
+
* Whether the bars pulse. Pass `false` once the outcome is settled (paid,
|
|
9
|
+
* failed, refunded) — a static skeleton reads as "nothing more will
|
|
10
|
+
* change", while a pulsing one implies something is still in flight.
|
|
11
|
+
*/
|
|
12
|
+
animate: boolean
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Two skeleton bars standing in for the redacted text of a paid text-only
|
|
17
|
+
* message — the real content is never sent to the client until purchase, so
|
|
18
|
+
* there's nothing to render yet.
|
|
19
|
+
*
|
|
20
|
+
* Uses explicit black/white (not `bg-current`) because Tailwind doesn't
|
|
21
|
+
* support opacity modifiers on the `current` color — `bg-current/30` silently
|
|
22
|
+
* produces no CSS rule at all.
|
|
23
|
+
*/
|
|
24
|
+
const SkeletonTextLines: React.FC<SkeletonTextLinesProps> = ({
|
|
25
|
+
variant = 'dark',
|
|
26
|
+
animate = true,
|
|
27
|
+
}) => {
|
|
28
|
+
const pulse = animate ? 'animate-pulse motion-reduce:animate-none' : ''
|
|
29
|
+
const colour = variant === 'dark' ? 'bg-white/25' : 'bg-black/15'
|
|
30
|
+
|
|
31
|
+
return (
|
|
32
|
+
<div className="flex flex-col gap-2 str-chat__message-text my-1" aria-hidden>
|
|
33
|
+
<div className={classNames('h-3 w-40 rounded-full', pulse, colour)} />
|
|
34
|
+
<div className={classNames('h-3 w-24 rounded-full', pulse, colour)} />
|
|
35
|
+
</div>
|
|
36
|
+
)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export default SkeletonTextLines
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import classNames from 'classnames'
|
|
2
|
+
import React from 'react'
|
|
3
|
+
|
|
4
|
+
const TextShell: React.FC<React.PropsWithChildren<{ className?: string }>> = ({
|
|
5
|
+
children,
|
|
6
|
+
className,
|
|
7
|
+
}) => (
|
|
8
|
+
<div
|
|
9
|
+
className={classNames(
|
|
10
|
+
'str-chat__message-bubble message-locked-attachment-text',
|
|
11
|
+
'px-4 py-3 flex flex-col gap-1',
|
|
12
|
+
className
|
|
13
|
+
)}
|
|
14
|
+
>
|
|
15
|
+
{children}
|
|
16
|
+
</div>
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
export default TextShell
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { describe, it, expect, vi } from 'vitest'
|
|
2
|
+
|
|
3
|
+
import { act, renderHook } from '../../../../test/utils'
|
|
4
|
+
|
|
5
|
+
import { usePreviewToggle } from './usePreviewToggle'
|
|
6
|
+
|
|
7
|
+
const source = { sourceUrl: 'https://example.test/asset.mp4' }
|
|
8
|
+
|
|
9
|
+
describe('usePreviewToggle', () => {
|
|
10
|
+
it('gallery: reveals without invoking the async source fetcher', async () => {
|
|
11
|
+
const onFetchSource = vi.fn().mockResolvedValue(source)
|
|
12
|
+
const onPreviewClick = vi.fn()
|
|
13
|
+
const { result } = renderHook(() =>
|
|
14
|
+
usePreviewToggle({ isGallery: true, onFetchSource, onPreviewClick })
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
await act(async () => {
|
|
18
|
+
await result.current.handleToggle()
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
expect(result.current.isPreviewVisible).toBe(true)
|
|
22
|
+
expect(onPreviewClick).toHaveBeenCalledTimes(1)
|
|
23
|
+
expect(onFetchSource).not.toHaveBeenCalled()
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
it('first reveal fetches the source, then caches it on subsequent toggles', async () => {
|
|
27
|
+
const onFetchSource = vi.fn().mockResolvedValue(source)
|
|
28
|
+
const { result } = renderHook(() =>
|
|
29
|
+
usePreviewToggle({ isGallery: false, onFetchSource })
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
// First reveal → fetches.
|
|
33
|
+
await act(async () => {
|
|
34
|
+
await result.current.handleToggle()
|
|
35
|
+
})
|
|
36
|
+
expect(result.current.isPreviewVisible).toBe(true)
|
|
37
|
+
expect(result.current.source).toEqual(source)
|
|
38
|
+
expect(onFetchSource).toHaveBeenCalledTimes(1)
|
|
39
|
+
|
|
40
|
+
// Toggle off.
|
|
41
|
+
await act(async () => {
|
|
42
|
+
await result.current.handleToggle()
|
|
43
|
+
})
|
|
44
|
+
expect(result.current.isPreviewVisible).toBe(false)
|
|
45
|
+
|
|
46
|
+
// Toggle back on → reuses the cache, no second fetch.
|
|
47
|
+
await act(async () => {
|
|
48
|
+
await result.current.handleToggle()
|
|
49
|
+
})
|
|
50
|
+
expect(result.current.isPreviewVisible).toBe(true)
|
|
51
|
+
expect(onFetchSource).toHaveBeenCalledTimes(1)
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
it('stays locked when the fetcher resolves without a source', async () => {
|
|
55
|
+
const onFetchSource = vi.fn().mockResolvedValue(undefined)
|
|
56
|
+
const { result } = renderHook(() =>
|
|
57
|
+
usePreviewToggle({ isGallery: false, onFetchSource })
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
await act(async () => {
|
|
61
|
+
await result.current.handleToggle()
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
expect(result.current.isPreviewVisible).toBe(false)
|
|
65
|
+
expect(result.current.source).toBeUndefined()
|
|
66
|
+
})
|
|
67
|
+
})
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { useCallback, useRef, useState } from 'react'
|
|
2
|
+
|
|
3
|
+
import type { LockedAttachmentSource } from '../../types'
|
|
4
|
+
|
|
5
|
+
export interface UsePreviewToggleOptions {
|
|
6
|
+
/** Gallery items carry their own per-item sources on the `gallery` prop already, so toggling skips the async fetch entirely. */
|
|
7
|
+
isGallery: boolean
|
|
8
|
+
/**
|
|
9
|
+
* Lazily loads the underlying source; the resolved value is cached in
|
|
10
|
+
* `source` and reused on subsequent toggles.
|
|
11
|
+
*/
|
|
12
|
+
onFetchSource?: () => Promise<LockedAttachmentSource | void>
|
|
13
|
+
/** Fired on every toggle tap, before any visibility/fetch logic runs. */
|
|
14
|
+
onPreviewClick?: () => void
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface UsePreviewToggleResult {
|
|
18
|
+
source: LockedAttachmentSource | undefined
|
|
19
|
+
isPreviewVisible: boolean
|
|
20
|
+
isLoadingPreview: boolean
|
|
21
|
+
handleToggle: () => Promise<void>
|
|
22
|
+
/**
|
|
23
|
+
* `handleToggle` when the card can actually be previewed (gallery, or a
|
|
24
|
+
* fetch/click handler is wired), else `undefined` — pass straight to
|
|
25
|
+
* `PreviewToggle.onToggle` so a non-previewable card renders no
|
|
26
|
+
* toggle affordance.
|
|
27
|
+
*/
|
|
28
|
+
togglePreview: (() => Promise<void>) | undefined
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Shared preview-toggle state for Composer/Media isMine: flips a locked thumbnail to
|
|
33
|
+
* its unlocked preview, lazily fetching and caching the source on first
|
|
34
|
+
* reveal. Media !isMine doesn't use this — it fetches on scroll-into-view/paid
|
|
35
|
+
* instead of on a toggle tap.
|
|
36
|
+
*/
|
|
37
|
+
export function usePreviewToggle({
|
|
38
|
+
isGallery,
|
|
39
|
+
onFetchSource,
|
|
40
|
+
onPreviewClick,
|
|
41
|
+
}: UsePreviewToggleOptions): UsePreviewToggleResult {
|
|
42
|
+
const [source, setSource] = useState<LockedAttachmentSource | undefined>()
|
|
43
|
+
const [isPreviewVisible, setIsPreviewVisible] = useState(false)
|
|
44
|
+
const [isLoadingPreview, setIsLoadingPreview] = useState(false)
|
|
45
|
+
const fetchingRef = useRef(false)
|
|
46
|
+
|
|
47
|
+
const handleToggle = useCallback(async () => {
|
|
48
|
+
onPreviewClick?.()
|
|
49
|
+
if (isPreviewVisible) {
|
|
50
|
+
setIsPreviewVisible(false)
|
|
51
|
+
return
|
|
52
|
+
}
|
|
53
|
+
if (isGallery) {
|
|
54
|
+
setIsPreviewVisible(true)
|
|
55
|
+
return
|
|
56
|
+
}
|
|
57
|
+
if (source) {
|
|
58
|
+
setIsPreviewVisible(true)
|
|
59
|
+
return
|
|
60
|
+
}
|
|
61
|
+
if (!onFetchSource) return
|
|
62
|
+
if (fetchingRef.current) return
|
|
63
|
+
fetchingRef.current = true
|
|
64
|
+
setIsLoadingPreview(true)
|
|
65
|
+
try {
|
|
66
|
+
const result = await onFetchSource()
|
|
67
|
+
if (result) {
|
|
68
|
+
setSource(result)
|
|
69
|
+
setIsPreviewVisible(true)
|
|
70
|
+
}
|
|
71
|
+
} finally {
|
|
72
|
+
fetchingRef.current = false
|
|
73
|
+
setIsLoadingPreview(false)
|
|
74
|
+
}
|
|
75
|
+
}, [isPreviewVisible, isGallery, source, onPreviewClick, onFetchSource])
|
|
76
|
+
|
|
77
|
+
const togglePreview =
|
|
78
|
+
isGallery || onFetchSource != null || onPreviewClick != null
|
|
79
|
+
? handleToggle
|
|
80
|
+
: undefined
|
|
81
|
+
|
|
82
|
+
return {
|
|
83
|
+
source,
|
|
84
|
+
isPreviewVisible,
|
|
85
|
+
isLoadingPreview,
|
|
86
|
+
handleToggle,
|
|
87
|
+
togglePreview,
|
|
88
|
+
}
|
|
89
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { useCallback, useEffect, useRef, useState } from 'react'
|
|
2
|
+
|
|
3
|
+
import type {
|
|
4
|
+
FetchSourceHandler,
|
|
5
|
+
LockedAttachmentSource,
|
|
6
|
+
PaymentStatus,
|
|
7
|
+
} from '../../types'
|
|
8
|
+
|
|
9
|
+
export interface UseReceivedSourceOptions {
|
|
10
|
+
paymentStatus?: PaymentStatus
|
|
11
|
+
onFetchSource?: FetchSourceHandler
|
|
12
|
+
/** Called on Unlock click while the card is still unpaid (opens checkout). */
|
|
13
|
+
onUnlockClick?: () => void
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface UseReceivedSourceResult {
|
|
17
|
+
source: LockedAttachmentSource | undefined
|
|
18
|
+
/** Attach to the card root so the paid-source auto-fetch can observe it. */
|
|
19
|
+
cardRef: React.RefObject<HTMLDivElement>
|
|
20
|
+
/** Fetches the source when already paid, else defers to `onUnlockClick`. */
|
|
21
|
+
handleUnlockClick: () => void
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Media !isMine's source-reveal logic: unlike Composer/Media isMine's
|
|
26
|
+
* tap-to-toggle (`usePreviewToggle`), the recipient's card reveals its source
|
|
27
|
+
* by fetching once — on click when already paid, or automatically the first
|
|
28
|
+
* time a paid card scrolls fully into view. The fetch is guarded so it runs
|
|
29
|
+
* at most once in flight.
|
|
30
|
+
*/
|
|
31
|
+
export function useReceivedSource({
|
|
32
|
+
paymentStatus,
|
|
33
|
+
onFetchSource,
|
|
34
|
+
onUnlockClick,
|
|
35
|
+
}: UseReceivedSourceOptions): UseReceivedSourceResult {
|
|
36
|
+
const [source, setSource] = useState<LockedAttachmentSource | undefined>()
|
|
37
|
+
|
|
38
|
+
const cardRef = useRef<HTMLDivElement>(null)
|
|
39
|
+
const fetchingRef = useRef(false)
|
|
40
|
+
|
|
41
|
+
const onFetchSourceRef = useRef(onFetchSource)
|
|
42
|
+
onFetchSourceRef.current = onFetchSource
|
|
43
|
+
|
|
44
|
+
const fetchSource = useCallback(async (): Promise<void> => {
|
|
45
|
+
if (fetchingRef.current) return
|
|
46
|
+
fetchingRef.current = true
|
|
47
|
+
try {
|
|
48
|
+
const result = await onFetchSourceRef.current?.()
|
|
49
|
+
if (result) setSource(result)
|
|
50
|
+
} finally {
|
|
51
|
+
fetchingRef.current = false
|
|
52
|
+
}
|
|
53
|
+
}, [])
|
|
54
|
+
|
|
55
|
+
const handleUnlockClick = useCallback(() => {
|
|
56
|
+
if (paymentStatus === 'paid') {
|
|
57
|
+
void fetchSource()
|
|
58
|
+
} else {
|
|
59
|
+
onUnlockClick?.()
|
|
60
|
+
}
|
|
61
|
+
}, [paymentStatus, fetchSource, onUnlockClick])
|
|
62
|
+
|
|
63
|
+
// Auto-fetch the source once the paid card scrolls into view.
|
|
64
|
+
useEffect(() => {
|
|
65
|
+
if (!cardRef.current) return
|
|
66
|
+
if (paymentStatus !== 'paid' || source !== undefined) return
|
|
67
|
+
|
|
68
|
+
const observer = new IntersectionObserver(
|
|
69
|
+
([entry]) => {
|
|
70
|
+
if (entry.isIntersecting) {
|
|
71
|
+
void fetchSource()
|
|
72
|
+
observer.disconnect()
|
|
73
|
+
}
|
|
74
|
+
},
|
|
75
|
+
{ threshold: 1.0 }
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
observer.observe(cardRef.current)
|
|
79
|
+
return () => observer.disconnect()
|
|
80
|
+
}, [paymentStatus, source, fetchSource])
|
|
81
|
+
|
|
82
|
+
return { source, cardRef, handleUnlockClick }
|
|
83
|
+
}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import React, { Suspense } from 'react'
|
|
2
2
|
|
|
3
3
|
import type { ComposerCardProps } from './components/Composer/Card'
|
|
4
|
-
import type {
|
|
5
|
-
import type {
|
|
4
|
+
import type { MediaCardProps } from './components/Media/Card'
|
|
5
|
+
import type { TextCardProps } from './components/Text/Card'
|
|
6
6
|
|
|
7
7
|
const ComposerCardLazy = React.lazy(() => import('./components/Composer/Card'))
|
|
8
|
-
const
|
|
9
|
-
const
|
|
8
|
+
const TextCardLazy = React.lazy(() => import('./components/Text/Card'))
|
|
9
|
+
const MediaCardLazy = React.lazy(() => import('./components/Media/Card'))
|
|
10
10
|
|
|
11
11
|
const LockedAttachmentFallback = () => (
|
|
12
12
|
<div
|
|
@@ -21,42 +21,31 @@ const Composer = (props: ComposerCardProps) => (
|
|
|
21
21
|
</Suspense>
|
|
22
22
|
)
|
|
23
23
|
|
|
24
|
-
const
|
|
24
|
+
const Text = (props: TextCardProps) => (
|
|
25
25
|
<Suspense fallback={<LockedAttachmentFallback />}>
|
|
26
|
-
<
|
|
26
|
+
<TextCardLazy {...props} />
|
|
27
27
|
</Suspense>
|
|
28
28
|
)
|
|
29
29
|
|
|
30
|
-
const
|
|
30
|
+
const Media = (props: MediaCardProps) => (
|
|
31
31
|
<Suspense fallback={<LockedAttachmentFallback />}>
|
|
32
|
-
<
|
|
32
|
+
<MediaCardLazy {...props} />
|
|
33
33
|
</Suspense>
|
|
34
34
|
)
|
|
35
35
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
*/
|
|
42
|
-
const Creator = Sent
|
|
43
|
-
|
|
44
|
-
/** @deprecated Renamed to `LockedAttachment.Received`. */
|
|
45
|
-
const Visitor = Received
|
|
46
|
-
|
|
47
|
-
const LockedAttachment = { Composer, Sent, Received, Creator, Visitor }
|
|
36
|
+
const LockedAttachment = {
|
|
37
|
+
Composer,
|
|
38
|
+
Text,
|
|
39
|
+
Media,
|
|
40
|
+
}
|
|
48
41
|
|
|
49
42
|
export default LockedAttachment
|
|
50
|
-
export
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
* should migrate to `ComposerCardProps`.
|
|
54
|
-
*/
|
|
55
|
-
export type CreatorCardProps = SentCardProps
|
|
56
|
-
/** @deprecated Renamed to `ReceivedCardProps`. */
|
|
57
|
-
export type VisitorCardProps = ReceivedCardProps
|
|
43
|
+
export { default as PurchaseStatusBadge } from './components/_shared/PurchaseStatusBadge'
|
|
44
|
+
export type { PurchaseStatusBadgeProps } from './components/_shared/PurchaseStatusBadge'
|
|
45
|
+
export type { ComposerCardProps, TextCardProps, MediaCardProps }
|
|
58
46
|
export type {
|
|
59
47
|
PaymentStatus,
|
|
48
|
+
FetchSourceHandler,
|
|
60
49
|
LockedAttachmentSource,
|
|
61
50
|
LockedAttachmentGalleryItem,
|
|
62
51
|
LockedAttachmentContextValue,
|
|
@@ -4,19 +4,28 @@ import type { PaymentStatus } from '../../stream-custom-data'
|
|
|
4
4
|
|
|
5
5
|
export interface LockedAttachmentBaseProps {
|
|
6
6
|
title?: string
|
|
7
|
+
/** Dimmed fallback shown in the title slot when `title` is unset (no attachment metadata yet — still drafting). */
|
|
8
|
+
placeholderTitle?: string
|
|
7
9
|
mimeType?: string
|
|
8
10
|
thumbnailUrl?: string
|
|
9
11
|
detail?: string
|
|
10
12
|
amountText?: string
|
|
11
|
-
paymentStatus?: PaymentStatus
|
|
12
13
|
/**
|
|
13
14
|
* When provided with 2+ items, the card renders as a mixed-media carousel
|
|
14
15
|
* (e.g. a couple of photos + a video) instead of a single thumbnail. Each
|
|
15
16
|
* item brings its own thumbnail and optional source so that
|
|
16
|
-
* `LockedAttachment.Composer` / `.
|
|
17
|
-
*
|
|
17
|
+
* `LockedAttachment.Composer` / `.Media` can share the same carousel
|
|
18
|
+
* chrome.
|
|
18
19
|
*/
|
|
19
20
|
gallery?: LockedAttachmentGalleryItem[]
|
|
21
|
+
/**
|
|
22
|
+
* "text" renders the card as a locked/plain chat bubble showing the
|
|
23
|
+
* message's own text (skeleton-blurred until purchased) instead of a media
|
|
24
|
+
* thumbnail card — for a paid text-only message. Media-only props
|
|
25
|
+
* (`mimeType`, `thumbnailUrl`, `title`, `detail`, `gallery`) are ignored in
|
|
26
|
+
* this mode. Defaults to `"media"`.
|
|
27
|
+
*/
|
|
28
|
+
contentType?: 'text' | 'media'
|
|
20
29
|
}
|
|
21
30
|
|
|
22
31
|
export interface LockedAttachmentGalleryItem {
|
|
@@ -39,11 +48,26 @@ export interface LockedAttachmentSource {
|
|
|
39
48
|
|
|
40
49
|
export type { PaymentStatus }
|
|
41
50
|
|
|
51
|
+
/**
|
|
52
|
+
* Lazily fetches the underlying source (URL/thumbnail) for a locked
|
|
53
|
+
* attachment. Returns `void` to signal "no source available" — the card
|
|
54
|
+
* stays locked. Shared by `ComposerCardProps`/`MediaCardProps.onFetchSource`.
|
|
55
|
+
*/
|
|
56
|
+
export type FetchSourceHandler = () => Promise<LockedAttachmentSource | void>
|
|
57
|
+
|
|
42
58
|
export interface LockedAttachmentContextValue {
|
|
43
59
|
isUnlocking: (id: string) => boolean
|
|
44
60
|
onUnlockClick?: (message: LocalMessage, channel: Channel) => void
|
|
45
61
|
onDownloadClick?: (message: LocalMessage, channel: Channel) => void
|
|
46
|
-
|
|
62
|
+
/**
|
|
63
|
+
* Reveal the locked media's underlying source (URL/thumbnail). Not used
|
|
64
|
+
* for a paid text message — the backend writes the real text directly onto
|
|
65
|
+
* the message on purchase and `message.text` alone drives that reveal.
|
|
66
|
+
*/
|
|
67
|
+
onFetchSource?: (
|
|
68
|
+
message: LocalMessage,
|
|
69
|
+
channel: Channel
|
|
70
|
+
) => Promise<LockedAttachmentSource | void>
|
|
47
71
|
}
|
|
48
72
|
|
|
49
73
|
export const defaultLockedAttachmentContextValue: LockedAttachmentContextValue = {
|
package/src/index.ts
CHANGED
|
@@ -70,13 +70,10 @@ export type { AvatarProps } from './components/Avatar'
|
|
|
70
70
|
export type { ActionButtonProps } from './components/ActionButton'
|
|
71
71
|
export type {
|
|
72
72
|
ComposerCardProps,
|
|
73
|
-
|
|
74
|
-
|
|
73
|
+
TextCardProps as LockedAttachmentTextCardProps,
|
|
74
|
+
MediaCardProps as LockedAttachmentMediaCardProps,
|
|
75
75
|
LockedAttachmentGalleryItem,
|
|
76
76
|
LockedAttachmentContextValue,
|
|
77
|
-
// Deprecated aliases preserved for backwards compatibility:
|
|
78
|
-
CreatorCardProps,
|
|
79
|
-
VisitorCardProps,
|
|
80
77
|
} from './components/LockedAttachment'
|
|
81
78
|
export type {
|
|
82
79
|
ComposerCardProps as LinkAttachmentComposerCardProps,
|
package/src/styles.css
CHANGED
package/dist/Card-BRvcFjsq.js
DELETED
|
@@ -1,132 +0,0 @@
|
|
|
1
|
-
import { jsxs as y, jsx as t } from "react/jsx-runtime";
|
|
2
|
-
import { ImagesIcon as A, PencilSimpleIcon as C, XIcon as D } from "@phosphor-icons/react";
|
|
3
|
-
import V, { useState as h, useRef as E, useCallback as K } from "react";
|
|
4
|
-
import { L as P, C as S, G as X, a as q } from "./LockedThumbnail-EsjaAqLR.js";
|
|
5
|
-
const Q = ({
|
|
6
|
-
title: l,
|
|
7
|
-
mimeType: b = "application/octet-stream",
|
|
8
|
-
thumbnailUrl: N,
|
|
9
|
-
detail: I,
|
|
10
|
-
amountText: g,
|
|
11
|
-
placeholderTitle: L = "Attachment title",
|
|
12
|
-
placeholderAmountText: z,
|
|
13
|
-
gallery: r,
|
|
14
|
-
onDismiss: p,
|
|
15
|
-
onPreviewClick: a,
|
|
16
|
-
onFetchSource: n,
|
|
17
|
-
onEditClick: x
|
|
18
|
-
}) => {
|
|
19
|
-
const [c, B] = h(), [u, o] = h(!1), [R, w] = h(!1), d = E(!1), i = ((r == null ? void 0 : r.length) ?? 0) >= 2, j = K(async () => {
|
|
20
|
-
if (a == null || a(), u) {
|
|
21
|
-
o(!1);
|
|
22
|
-
return;
|
|
23
|
-
}
|
|
24
|
-
if (i) {
|
|
25
|
-
o(!0);
|
|
26
|
-
return;
|
|
27
|
-
}
|
|
28
|
-
if (c) {
|
|
29
|
-
o(!0);
|
|
30
|
-
return;
|
|
31
|
-
}
|
|
32
|
-
if (n && !d.current) {
|
|
33
|
-
d.current = !0, w(!0);
|
|
34
|
-
try {
|
|
35
|
-
const e = await n();
|
|
36
|
-
e && (B(e), o(!0));
|
|
37
|
-
} finally {
|
|
38
|
-
d.current = !1, w(!1);
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
}, [u, i, c, a, n]), f = i || n || a ? j : void 0, m = !u, s = R, G = /* @__PURE__ */ y(V.Fragment, { children: [
|
|
42
|
-
/* @__PURE__ */ t("span", { className: "text-xs font-medium text-white/55", children: "•" }),
|
|
43
|
-
/* @__PURE__ */ t(
|
|
44
|
-
"span",
|
|
45
|
-
{
|
|
46
|
-
className: g ? "text-xs font-medium text-white/55" : "text-xs font-medium text-white/30",
|
|
47
|
-
children: g || z
|
|
48
|
-
}
|
|
49
|
-
)
|
|
50
|
-
] }), v = p ? /* @__PURE__ */ t(
|
|
51
|
-
"button",
|
|
52
|
-
{
|
|
53
|
-
type: "button",
|
|
54
|
-
onClick: (e) => {
|
|
55
|
-
e.stopPropagation(), p();
|
|
56
|
-
},
|
|
57
|
-
className: "flex size-6 items-center justify-center rounded-full bg-[#121110] text-white",
|
|
58
|
-
"aria-label": "Dismiss attachment",
|
|
59
|
-
children: /* @__PURE__ */ t(D, { className: "size-3", weight: "bold" })
|
|
60
|
-
}
|
|
61
|
-
) : void 0, T = x ? /* @__PURE__ */ t(
|
|
62
|
-
"button",
|
|
63
|
-
{
|
|
64
|
-
type: "button",
|
|
65
|
-
onClick: x,
|
|
66
|
-
"aria-label": "Edit attachment",
|
|
67
|
-
className: "flex size-10 items-center justify-center rounded-full bg-white/10 text-white hover:bg-white/15",
|
|
68
|
-
children: /* @__PURE__ */ t(C, { className: "size-5", weight: "regular" })
|
|
69
|
-
}
|
|
70
|
-
) : void 0, k = i ? /* @__PURE__ */ t(
|
|
71
|
-
X,
|
|
72
|
-
{
|
|
73
|
-
variant: "dark",
|
|
74
|
-
gallery: r,
|
|
75
|
-
title: l,
|
|
76
|
-
showLocked: m,
|
|
77
|
-
topRight: v
|
|
78
|
-
}
|
|
79
|
-
) : /* @__PURE__ */ t(
|
|
80
|
-
q,
|
|
81
|
-
{
|
|
82
|
-
variant: "dark",
|
|
83
|
-
mimeType: b,
|
|
84
|
-
thumbnailUrl: N,
|
|
85
|
-
title: l,
|
|
86
|
-
source: c,
|
|
87
|
-
showLocked: m,
|
|
88
|
-
topRight: v
|
|
89
|
-
}
|
|
90
|
-
);
|
|
91
|
-
return /* @__PURE__ */ y(P, { variant: "dark", children: [
|
|
92
|
-
f ? (
|
|
93
|
-
// Uses a `<div role="button">` rather than a native `<button>` so the
|
|
94
|
-
// dismiss control rendered inside `thumbnail` (also a `<button>`)
|
|
95
|
-
// doesn't produce invalid nested interactive elements.
|
|
96
|
-
/* @__PURE__ */ t(
|
|
97
|
-
"div",
|
|
98
|
-
{
|
|
99
|
-
role: "button",
|
|
100
|
-
tabIndex: s ? -1 : 0,
|
|
101
|
-
"aria-label": "Toggle preview",
|
|
102
|
-
"aria-busy": s,
|
|
103
|
-
"aria-pressed": !m,
|
|
104
|
-
"aria-disabled": s || void 0,
|
|
105
|
-
onClick: s ? void 0 : f,
|
|
106
|
-
onKeyDown: (e) => {
|
|
107
|
-
s || e.target === e.currentTarget && (e.key === "Enter" || e.key === " ") && (e.preventDefault(), f());
|
|
108
|
-
},
|
|
109
|
-
className: s ? "block w-full text-left" : "block w-full cursor-pointer text-left",
|
|
110
|
-
children: k
|
|
111
|
-
}
|
|
112
|
-
)
|
|
113
|
-
) : k,
|
|
114
|
-
/* @__PURE__ */ t(
|
|
115
|
-
S,
|
|
116
|
-
{
|
|
117
|
-
variant: "dark",
|
|
118
|
-
title: l,
|
|
119
|
-
placeholderTitle: L,
|
|
120
|
-
mimeType: b,
|
|
121
|
-
detail: I,
|
|
122
|
-
statusBadge: G,
|
|
123
|
-
icon: i ? /* @__PURE__ */ t(A, { className: "size-5 shrink-0 text-white/55" }) : void 0,
|
|
124
|
-
trailingAction: T
|
|
125
|
-
}
|
|
126
|
-
)
|
|
127
|
-
] });
|
|
128
|
-
};
|
|
129
|
-
export {
|
|
130
|
-
Q as default
|
|
131
|
-
};
|
|
132
|
-
//# sourceMappingURL=Card-BRvcFjsq.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"Card-BRvcFjsq.js","sources":["../src/components/LockedAttachment/components/Composer/Card.tsx"],"sourcesContent":["import { ImagesIcon, PencilSimpleIcon, XIcon } from '@phosphor-icons/react'\nimport React, { useCallback, useRef, useState } from 'react'\n\nimport type {\n LockedAttachmentBaseProps,\n LockedAttachmentSource,\n} from '../../types'\nimport CardBody from '../_shared/CardBody'\nimport GalleryThumbnail from '../_shared/GalleryThumbnail'\nimport LockedCardShell from '../_shared/LockedCardShell'\nimport LockedThumbnail from '../_shared/LockedThumbnail'\n\nexport interface ComposerCardProps extends LockedAttachmentBaseProps {\n /** Placeholder shown in the title slot before the composer types one. */\n placeholderTitle?: string\n /** Placeholder shown in the amount slot before one is configured. */\n placeholderAmountText?: string\n /**\n * When provided, renders a dismiss X in the thumbnail corner. Called when\n * the composer clicks it to remove the attachment.\n */\n onDismiss?: () => void\n /** Fired the first time the composer taps the thumbnail to preview. */\n onPreviewClick?: () => void\n /**\n * Lazily loads the underlying source so the composer can preview the\n * attachment they're about to send. Called the first time the thumbnail is\n * tapped; the returned source is cached and reused on subsequent toggles.\n */\n onFetchSource?: () => Promise<LockedAttachmentSource | void>\n /**\n * When provided, renders a pencil button in the body bottom-right that the\n * composer can use to edit the attachment metadata (e.g. open the price /\n * gallery editor). Matches the Composer \"Button\" instance in Figma.\n */\n onEditClick?: () => void\n}\n\n/**\n * The card the composer sees while drafting a paid attachment.\n * Matches the Composer column of the messaging design system in Figma.\n */\nconst ComposerCard: React.FC<ComposerCardProps> = ({\n title,\n mimeType = 'application/octet-stream',\n thumbnailUrl,\n detail,\n amountText,\n placeholderTitle = 'Attachment title',\n placeholderAmountText,\n gallery,\n onDismiss,\n onPreviewClick,\n onFetchSource,\n onEditClick,\n}) => {\n const [source, setSource] = useState<LockedAttachmentSource | undefined>()\n const [isPreviewVisible, setIsPreviewVisible] = useState(false)\n const [isLoadingPreview, setIsLoadingPreview] = useState(false)\n const fetchingRef = useRef(false)\n\n const isGallery = (gallery?.length ?? 0) >= 2\n\n const handleToggle = useCallback(async () => {\n onPreviewClick?.()\n if (isPreviewVisible) {\n setIsPreviewVisible(false)\n return\n }\n // Gallery items carry their own per-item sources on the `gallery` prop, so\n // we just flip visibility — no async source fetch is needed.\n if (isGallery) {\n setIsPreviewVisible(true)\n return\n }\n if (source) {\n setIsPreviewVisible(true)\n return\n }\n if (!onFetchSource) return\n if (fetchingRef.current) return\n fetchingRef.current = true\n setIsLoadingPreview(true)\n try {\n const result = await onFetchSource()\n if (result) {\n setSource(result)\n setIsPreviewVisible(true)\n }\n } finally {\n fetchingRef.current = false\n setIsLoadingPreview(false)\n }\n }, [isPreviewVisible, isGallery, source, onPreviewClick, onFetchSource])\n\n // Gallery is always previewable in the composer because each item's source\n // is already provided up-front via `gallery[*].sourceUrl`.\n const togglePreview =\n isGallery || onFetchSource || onPreviewClick ? handleToggle : undefined\n const showLocked = !isPreviewVisible\n const isBusy = isLoadingPreview\n\n const statusBadge = (\n <React.Fragment>\n <span className=\"text-xs font-medium text-white/55\">•</span>\n <span\n className={\n amountText\n ? 'text-xs font-medium text-white/55'\n : 'text-xs font-medium text-white/30'\n }\n >\n {amountText || placeholderAmountText}\n </span>\n </React.Fragment>\n )\n\n const dismissButton = onDismiss ? (\n <button\n type=\"button\"\n onClick={(e) => {\n // Stop the click from bubbling up into the outer preview-toggle\n // wrapper when both are wired up at the same time.\n e.stopPropagation()\n onDismiss()\n }}\n className=\"flex size-6 items-center justify-center rounded-full bg-[#121110] text-white\"\n aria-label=\"Dismiss attachment\"\n >\n <XIcon className=\"size-3\" weight=\"bold\" />\n </button>\n ) : undefined\n\n const editButton = onEditClick ? (\n <button\n type=\"button\"\n onClick={onEditClick}\n aria-label=\"Edit attachment\"\n className=\"flex size-10 items-center justify-center rounded-full bg-white/10 text-white hover:bg-white/15\"\n >\n <PencilSimpleIcon className=\"size-5\" weight=\"regular\" />\n </button>\n ) : undefined\n\n const thumbnail = isGallery ? (\n <GalleryThumbnail\n variant=\"dark\"\n gallery={gallery!}\n title={title}\n showLocked={showLocked}\n topRight={dismissButton}\n />\n ) : (\n <LockedThumbnail\n variant=\"dark\"\n mimeType={mimeType}\n thumbnailUrl={thumbnailUrl}\n title={title}\n source={source}\n showLocked={showLocked}\n topRight={dismissButton}\n />\n )\n\n return (\n <LockedCardShell variant=\"dark\">\n {togglePreview ? (\n // Uses a `<div role=\"button\">` rather than a native `<button>` so the\n // dismiss control rendered inside `thumbnail` (also a `<button>`)\n // doesn't produce invalid nested interactive elements.\n <div\n role=\"button\"\n tabIndex={isBusy ? -1 : 0}\n aria-label=\"Toggle preview\"\n aria-busy={isBusy}\n aria-pressed={!showLocked}\n aria-disabled={isBusy || undefined}\n onClick={isBusy ? undefined : togglePreview}\n onKeyDown={(e) => {\n if (isBusy) return\n // Only handle keys that originate on the wrapper itself, not on\n // inner interactive elements (dismiss X, carousel arrows). Without\n // this guard, pressing Enter/Space on a focused child would bubble\n // up and toggle the preview in addition to activating the child.\n if (e.target !== e.currentTarget) return\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault()\n void togglePreview()\n }\n }}\n className={\n !isBusy\n ? 'block w-full cursor-pointer text-left'\n : 'block w-full text-left'\n }\n >\n {thumbnail}\n </div>\n ) : (\n thumbnail\n )}\n\n <CardBody\n variant=\"dark\"\n title={title}\n placeholderTitle={placeholderTitle}\n mimeType={mimeType}\n detail={detail}\n statusBadge={statusBadge}\n icon={\n isGallery ? (\n <ImagesIcon className=\"size-5 shrink-0 text-white/55\" />\n ) : undefined\n }\n trailingAction={editButton}\n />\n </LockedCardShell>\n )\n}\n\nexport default ComposerCard\n"],"names":["ComposerCard","title","mimeType","thumbnailUrl","detail","amountText","placeholderTitle","placeholderAmountText","gallery","onDismiss","onPreviewClick","onFetchSource","onEditClick","source","setSource","useState","isPreviewVisible","setIsPreviewVisible","isLoadingPreview","setIsLoadingPreview","fetchingRef","useRef","isGallery","handleToggle","useCallback","result","togglePreview","showLocked","isBusy","statusBadge","jsxs","React","jsx","dismissButton","XIcon","editButton","PencilSimpleIcon","thumbnail","GalleryThumbnail","LockedThumbnail","LockedCardShell","CardBody","ImagesIcon"],"mappings":"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"}
|
package/dist/Card-BuXkok0A.cjs
DELETED
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("react/jsx-runtime"),d=require("@phosphor-icons/react"),t=require("react");require("classnames");const T=require("./index-DRurVQ0-.cjs"),j=require("./LockedThumbnail-uAauUd5c.cjs"),p="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",U=({isLocked:i,sourceUrl:u,redeemUrl:g,onUnlockClicked:l,onDownloadClicked:o,isUnlocking:r=!1})=>{if(i&&l!=null)return e.jsx("button",{type:"button",onClick:l,disabled:r,className:p,children:r?e.jsx("span",{className:"size-4 animate-spin rounded-full border-2 border-white/30 border-t-white"}):e.jsxs(t.Fragment,{children:[e.jsx(d.LockSimpleIcon,{className:"size-4",weight:"fill"}),"Unlock"]})});if(!i&&o!=null){const c=g??u;return c!=null?e.jsxs("a",{href:c,target:"_blank",rel:"noopener noreferrer",onClick:o,className:`${p} !text-white`,children:[e.jsx(d.DownloadSimpleIcon,{className:"size-4",weight:"bold"}),"Download"]}):e.jsxs("button",{type:"button",onClick:o,className:p,children:[e.jsx(d.DownloadSimpleIcon,{className:"size-4",weight:"bold"}),"Download"]})}return null},B=({title:i,amountText:u,thumbnailUrl:g,mimeType:l="application/octet-stream",detail:o,gallery:r,onUnlockClick:c,onFetchSource:N,onDownloadClick:I,paymentStatus:n,isUnlocking:C=!1})=>{const f=((r==null?void 0:r.length)??0)>=2,[s,q]=t.useState(),v=t.useRef(null),w=t.useRef(!1),x=t.useRef(N);x.current=N;const k=s==null?void 0:s.sourceUrl,z=s==null?void 0:s.redeemUrl,h=t.useCallback(async()=>{var a;if(!w.current){w.current=!0;try{const b=await((a=x.current)==null?void 0:a.call(x));b&&q(b)}finally{w.current=!1}}},[]),L=t.useCallback(()=>{n==="paid"?h():c==null||c()},[n,h,c]);t.useEffect(()=>{if(!v.current||n!=="paid"||s!==void 0)return;const a=new IntersectionObserver(([b])=>{b.isIntersecting&&(h(),a.disconnect())},{threshold:1});return a.observe(v.current),()=>a.disconnect()},[n,s,h]);const m=f?n!=="paid":k===void 0,R=T.getSourceType(l),D=n==="paid"?e.jsxs(t.Fragment,{children:[e.jsx("span",{className:"text-xs font-medium text-black/55",children:"•"}),e.jsx("span",{className:"text-xs font-medium text-[#008236]",children:"Purchased"}),e.jsx(d.CheckCircleIcon,{className:"size-4 text-[#008236]",weight:"bold"})]}):u!=null?e.jsxs(t.Fragment,{children:[e.jsx("span",{className:"text-xs font-medium text-black/55",children:"•"}),e.jsx("span",{className:"text-xs font-medium text-black/55",children:u})]}):null,S=f?void 0:k;return e.jsxs(j.LockedCardShell,{variant:"light",rootRef:v,"data-testid":"locked-attachment",children:[f?e.jsx(j.GalleryThumbnail,{variant:"light",gallery:r,title:i,showLocked:m,paymentStatus:n}):e.jsx(j.LockedThumbnail,{variant:"light",mimeType:l,thumbnailUrl:g,title:i,source:s,showLocked:m,paymentStatus:n,containedImage:!m&&(R==="image"||R==="document")}),e.jsx(j.CardBody,{variant:"light",title:i,mimeType:l,detail:o,statusBadge:D,icon:f?e.jsx(d.ImagesIcon,{className:"size-5 shrink-0 text-black/55"}):void 0,action:e.jsx(U,{isLocked:m,isUnlocking:C,sourceUrl:S,redeemUrl:z,onUnlockClicked:L,onDownloadClicked:I})})]})};exports.default=B;
|
|
2
|
-
//# sourceMappingURL=Card-BuXkok0A.cjs.map
|