@linktr.ee/messaging-react 1.37.0 → 1.38.0-rc-1777617124
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-DwgUtqsA.js +127 -0
- package/dist/Card-DwgUtqsA.js.map +1 -0
- package/dist/Card-RgHsp9x1.js +138 -0
- package/dist/Card-RgHsp9x1.js.map +1 -0
- package/dist/{index-DOsC03ZN.js → index-B_4pciGp.js} +1411 -1358
- package/dist/index-B_4pciGp.js.map +1 -0
- package/dist/index.d.ts +21 -1
- package/dist/index.js +15 -13
- package/package.json +1 -1
- package/src/components/{LockedAttachment/components → AttachmentCard}/MediaPlayer.tsx +4 -3
- package/src/components/AttachmentCard/Thumbnail.tsx +150 -0
- package/src/components/AttachmentCard/index.tsx +114 -0
- package/src/components/LockedAttachment/components/Creator/Card.tsx +123 -113
- package/src/components/LockedAttachment/components/Visitor/Card.tsx +43 -42
- package/src/components/LockedAttachment/components/Visitor/LockBadge.tsx +12 -0
- package/src/components/MediaMessage/MediaMessage.stories.tsx +45 -4
- package/src/components/MediaMessage/MediaMessage.test.tsx +151 -153
- package/src/components/MediaMessage/index.tsx +239 -349
- package/src/index.ts +7 -3
- package/dist/Card-BHrnmHeu.js +0 -167
- package/dist/Card-BHrnmHeu.js.map +0 -1
- package/dist/Card-D4vEgqWt.js +0 -195
- package/dist/Card-D4vEgqWt.js.map +0 -1
- package/dist/index-DOsC03ZN.js.map +0 -1
- package/src/components/LockedAttachment/components/Creator/CardThumbnail.tsx +0 -114
- package/src/components/LockedAttachment/components/Visitor/CardThumbnail.tsx +0 -81
- /package/src/components/{LockedAttachment → AttachmentCard}/utils/icons.ts +0 -0
- /package/src/components/{LockedAttachment → AttachmentCard}/utils/mimeType.test.ts +0 -0
- /package/src/components/{LockedAttachment → AttachmentCard}/utils/mimeType.ts +0 -0
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { Attachment } from 'stream-chat';
|
|
1
2
|
import { Channel } from 'stream-chat';
|
|
2
3
|
import { ChannelFilters } from 'stream-chat';
|
|
3
4
|
import { ChannelListProps as ChannelListProps_2 } from 'stream-chat-react';
|
|
@@ -315,13 +316,28 @@ export declare interface LockedAttachmentSource {
|
|
|
315
316
|
thumbnailUrl?: string;
|
|
316
317
|
}
|
|
317
318
|
|
|
318
|
-
export declare const MediaMessage: default_2.FC<MediaMessageProps
|
|
319
|
+
export declare const MediaMessage: default_2.FC<MediaMessageProps> & {
|
|
320
|
+
Creator: default_2.FC<{
|
|
321
|
+
message: LocalMessage;
|
|
322
|
+
}>;
|
|
323
|
+
Visitor: default_2.FC<{
|
|
324
|
+
message: LocalMessage;
|
|
325
|
+
}>;
|
|
326
|
+
};
|
|
319
327
|
|
|
320
328
|
export declare interface MediaMessageProps {
|
|
321
329
|
message: LocalMessage;
|
|
322
330
|
isMyMessage?: boolean;
|
|
323
331
|
}
|
|
324
332
|
|
|
333
|
+
export declare interface MediaMessageResolved {
|
|
334
|
+
resolvedUrl: string;
|
|
335
|
+
resolvedType: string;
|
|
336
|
+
title?: string;
|
|
337
|
+
fileSize?: number;
|
|
338
|
+
thumbnailUrl?: string;
|
|
339
|
+
}
|
|
340
|
+
|
|
325
341
|
declare type MessageCustomType = 'MESSAGE_TIP' | 'MESSAGE_PAID' | 'MESSAGE_CHATBOT' | 'MESSAGE_ATTACHMENT' | AgeSafetySystemType | DmAgentSystemType;
|
|
326
342
|
|
|
327
343
|
export declare interface MessageMetadata {
|
|
@@ -505,6 +521,10 @@ export declare interface ParticipantSource {
|
|
|
505
521
|
*/
|
|
506
522
|
declare type PaymentStatus = 'pending' | 'paid' | 'failed' | 'refunded';
|
|
507
523
|
|
|
524
|
+
export declare function resolveLinkAttachment(message: LocalMessage): Attachment | undefined;
|
|
525
|
+
|
|
526
|
+
export declare function resolveMediaFromMessage(message: LocalMessage): MediaMessageResolved | null;
|
|
527
|
+
|
|
508
528
|
export declare function useCustomMessage<K extends keyof CustomMessageRegistry>(key: K): CustomMessageRegistry[K];
|
|
509
529
|
|
|
510
530
|
/**
|
package/dist/index.js
CHANGED
|
@@ -1,23 +1,25 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { b as e, c as t, C as i, d as n, e as o, f as r, F as g, g as M, L as m, M as l, h as u, i as c, j as h, P as d, k as v, r as C, l as L, u as P, m as k, n as p, o as A } from "./index-B_4pciGp.js";
|
|
2
2
|
export {
|
|
3
3
|
e as ActionButton,
|
|
4
4
|
t as Avatar,
|
|
5
5
|
i as ChannelEmptyState,
|
|
6
6
|
n as ChannelList,
|
|
7
7
|
o as ChannelView,
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
8
|
+
r as CustomMessageProvider,
|
|
9
|
+
g as FaqList,
|
|
10
|
+
M as FaqListItem,
|
|
11
|
+
m as LockedAttachment,
|
|
12
|
+
l as MediaMessage,
|
|
13
|
+
u as MessageVoteButtons,
|
|
14
|
+
c as MessagingProvider,
|
|
15
15
|
h as MessagingShell,
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
L as
|
|
16
|
+
d as ParticipantPicker,
|
|
17
|
+
v as formatRelativeTime,
|
|
18
|
+
C as resolveLinkAttachment,
|
|
19
|
+
L as resolveMediaFromMessage,
|
|
20
|
+
P as useCustomMessage,
|
|
21
|
+
k as useMessageVote,
|
|
20
22
|
p as useMessaging,
|
|
21
|
-
|
|
23
|
+
A as useParticipants
|
|
22
24
|
};
|
|
23
25
|
//# sourceMappingURL=index.js.map
|
package/package.json
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { CircleNotchIcon, PauseIcon, PlayIcon } from '@phosphor-icons/react'
|
|
2
2
|
import React, { useCallback, useEffect, useRef, useState } from 'react'
|
|
3
3
|
|
|
4
|
-
import { isDevBuild } from '
|
|
5
|
-
|
|
6
|
-
import {
|
|
4
|
+
import { isDevBuild } from '../../utils/isDevBuild'
|
|
5
|
+
|
|
6
|
+
import { renderTypeIcon } from './utils/icons'
|
|
7
|
+
import { getSourceType } from './utils/mimeType'
|
|
7
8
|
|
|
8
9
|
type TouchEventUnion =
|
|
9
10
|
| MouseEvent
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import React, { useState } from 'react'
|
|
2
|
+
|
|
3
|
+
import MediaPlayer, { type MediaPlayerProps } from './MediaPlayer'
|
|
4
|
+
import { renderTypeIcon } from './utils/icons'
|
|
5
|
+
import { getSourceType } from './utils/mimeType'
|
|
6
|
+
|
|
7
|
+
export type AttachmentThumbnailVariant = 'light' | 'dark'
|
|
8
|
+
|
|
9
|
+
export interface AttachmentThumbnailProps {
|
|
10
|
+
mimeType: string
|
|
11
|
+
sourceUrl?: string
|
|
12
|
+
thumbnailUrl?: string
|
|
13
|
+
title?: string
|
|
14
|
+
variant: AttachmentThumbnailVariant
|
|
15
|
+
/** Extra props passed to MediaPlayer when source is video or audio. */
|
|
16
|
+
mediaPlayerProps?: Partial<
|
|
17
|
+
Pick<
|
|
18
|
+
MediaPlayerProps,
|
|
19
|
+
'autoPlay' | 'loop' | 'muted' | 'controls' | 'onContainerClick'
|
|
20
|
+
>
|
|
21
|
+
>
|
|
22
|
+
/**
|
|
23
|
+
* When true (Visitor unlocked image/document), use aspect-video + object-contain fade-in.
|
|
24
|
+
*/
|
|
25
|
+
containedImage?: boolean
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const placeholderIconClass = (variant: AttachmentThumbnailVariant) =>
|
|
29
|
+
variant === 'dark' ? 'size-12 text-white/20' : 'size-12 text-black/20'
|
|
30
|
+
|
|
31
|
+
const posterShellClass = (variant: AttachmentThumbnailVariant) =>
|
|
32
|
+
variant === 'dark'
|
|
33
|
+
? 'aspect-video overflow-hidden bg-white/10'
|
|
34
|
+
: 'aspect-video overflow-hidden bg-black/5'
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Renders the media preview area for attachment cards (LockedAttachment, MediaMessage).
|
|
38
|
+
* Overlays (dim, lock, eye toggle) are composed by the parent.
|
|
39
|
+
*/
|
|
40
|
+
const AttachmentThumbnail: React.FC<AttachmentThumbnailProps> = ({
|
|
41
|
+
mimeType,
|
|
42
|
+
sourceUrl,
|
|
43
|
+
thumbnailUrl,
|
|
44
|
+
title,
|
|
45
|
+
variant,
|
|
46
|
+
mediaPlayerProps,
|
|
47
|
+
containedImage = false,
|
|
48
|
+
}) => {
|
|
49
|
+
const sourceType = getSourceType(mimeType)
|
|
50
|
+
const [sourceReady, setSourceReady] = useState(false)
|
|
51
|
+
|
|
52
|
+
if (sourceUrl && (sourceType === 'video' || sourceType === 'audio')) {
|
|
53
|
+
return (
|
|
54
|
+
<MediaPlayer
|
|
55
|
+
source={sourceUrl}
|
|
56
|
+
mimeType={mimeType}
|
|
57
|
+
poster={thumbnailUrl}
|
|
58
|
+
controls
|
|
59
|
+
{...mediaPlayerProps}
|
|
60
|
+
/>
|
|
61
|
+
)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (sourceUrl && sourceType === 'image') {
|
|
65
|
+
if (containedImage) {
|
|
66
|
+
return (
|
|
67
|
+
<div className="relative aspect-video overflow-hidden bg-black/5">
|
|
68
|
+
<img
|
|
69
|
+
src={sourceUrl}
|
|
70
|
+
alt={title ?? ''}
|
|
71
|
+
className={`absolute inset-0 h-full w-full object-contain transition-opacity duration-300 ${sourceReady ? 'opacity-100' : 'opacity-0'}`}
|
|
72
|
+
draggable={false}
|
|
73
|
+
onLoad={() => setSourceReady(true)}
|
|
74
|
+
/>
|
|
75
|
+
</div>
|
|
76
|
+
)
|
|
77
|
+
}
|
|
78
|
+
return (
|
|
79
|
+
<img
|
|
80
|
+
src={sourceUrl}
|
|
81
|
+
alt={title ?? ''}
|
|
82
|
+
className="block w-full"
|
|
83
|
+
draggable={false}
|
|
84
|
+
/>
|
|
85
|
+
)
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (sourceUrl && sourceType === 'document') {
|
|
89
|
+
if (thumbnailUrl) {
|
|
90
|
+
if (containedImage) {
|
|
91
|
+
return (
|
|
92
|
+
<div className="relative aspect-video overflow-hidden bg-black/5">
|
|
93
|
+
<img
|
|
94
|
+
src={thumbnailUrl}
|
|
95
|
+
alt={title ?? ''}
|
|
96
|
+
className={`absolute inset-0 h-full w-full object-contain transition-opacity duration-300 ${sourceReady ? 'opacity-100' : 'opacity-0'}`}
|
|
97
|
+
draggable={false}
|
|
98
|
+
onLoad={() => setSourceReady(true)}
|
|
99
|
+
/>
|
|
100
|
+
</div>
|
|
101
|
+
)
|
|
102
|
+
}
|
|
103
|
+
return (
|
|
104
|
+
<img
|
|
105
|
+
src={thumbnailUrl}
|
|
106
|
+
alt=""
|
|
107
|
+
className="block w-full"
|
|
108
|
+
draggable={false}
|
|
109
|
+
/>
|
|
110
|
+
)
|
|
111
|
+
}
|
|
112
|
+
return (
|
|
113
|
+
<div
|
|
114
|
+
className={`flex aspect-video w-full items-center justify-center ${variant === 'dark' ? 'bg-white/10' : 'bg-black/5'}`}
|
|
115
|
+
>
|
|
116
|
+
{renderTypeIcon(mimeType, {
|
|
117
|
+
className: placeholderIconClass(variant),
|
|
118
|
+
weight: 'regular',
|
|
119
|
+
})}
|
|
120
|
+
</div>
|
|
121
|
+
)
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Poster-only or empty (no sourceUrl)
|
|
125
|
+
if (thumbnailUrl) {
|
|
126
|
+
return (
|
|
127
|
+
<div className={`relative ${posterShellClass(variant)}`}>
|
|
128
|
+
<img
|
|
129
|
+
src={thumbnailUrl}
|
|
130
|
+
alt={title ?? ''}
|
|
131
|
+
draggable={false}
|
|
132
|
+
className="absolute inset-0 h-full w-full object-cover"
|
|
133
|
+
/>
|
|
134
|
+
</div>
|
|
135
|
+
)
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
return (
|
|
139
|
+
<div
|
|
140
|
+
className={`flex aspect-video w-full items-center justify-center ${variant === 'dark' ? 'bg-white/10' : 'bg-black/5'}`}
|
|
141
|
+
>
|
|
142
|
+
{renderTypeIcon(mimeType, {
|
|
143
|
+
className: placeholderIconClass(variant),
|
|
144
|
+
weight: 'regular',
|
|
145
|
+
})}
|
|
146
|
+
</div>
|
|
147
|
+
)
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
export default AttachmentThumbnail
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import classNames from 'classnames'
|
|
2
|
+
import React from 'react'
|
|
3
|
+
|
|
4
|
+
import { renderTypeIcon } from './utils/icons'
|
|
5
|
+
|
|
6
|
+
export { default as AttachmentThumbnail } from './Thumbnail'
|
|
7
|
+
export type {
|
|
8
|
+
AttachmentThumbnailProps,
|
|
9
|
+
AttachmentThumbnailVariant,
|
|
10
|
+
} from './Thumbnail'
|
|
11
|
+
export { default as MediaPlayer } from './MediaPlayer'
|
|
12
|
+
export type { MediaPlayerProps } from './MediaPlayer'
|
|
13
|
+
export { renderTypeIcon, getTypeIcon, MEDIA_TYPE_ICON } from './utils/icons'
|
|
14
|
+
export {
|
|
15
|
+
getSourceType,
|
|
16
|
+
getDocumentIconType,
|
|
17
|
+
type AttachmentSourceType,
|
|
18
|
+
type DocumentIconType,
|
|
19
|
+
} from './utils/mimeType'
|
|
20
|
+
|
|
21
|
+
export interface AttachmentCardProps {
|
|
22
|
+
variant: 'light' | 'dark'
|
|
23
|
+
thumbnail: React.ReactNode
|
|
24
|
+
title?: string
|
|
25
|
+
placeholderTitle?: string
|
|
26
|
+
mimeType: string
|
|
27
|
+
detail?: string
|
|
28
|
+
statusBadge?: React.ReactNode
|
|
29
|
+
action?: React.ReactNode
|
|
30
|
+
topLeft?: React.ReactNode
|
|
31
|
+
topRight?: React.ReactNode
|
|
32
|
+
rootRef?: React.Ref<HTMLDivElement>
|
|
33
|
+
'data-testid'?: string
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const AttachmentCard: React.FC<AttachmentCardProps> = ({
|
|
37
|
+
variant,
|
|
38
|
+
thumbnail,
|
|
39
|
+
title,
|
|
40
|
+
placeholderTitle = 'Attachment title',
|
|
41
|
+
mimeType,
|
|
42
|
+
detail,
|
|
43
|
+
statusBadge,
|
|
44
|
+
action,
|
|
45
|
+
topLeft,
|
|
46
|
+
topRight,
|
|
47
|
+
rootRef,
|
|
48
|
+
'data-testid': dataTestId,
|
|
49
|
+
}) => {
|
|
50
|
+
const isDark = variant === 'dark'
|
|
51
|
+
const displayTitle = isDark ? (title ?? placeholderTitle) : (title ?? '')
|
|
52
|
+
const titleDimmed = isDark && !title
|
|
53
|
+
|
|
54
|
+
return (
|
|
55
|
+
<div
|
|
56
|
+
ref={rootRef}
|
|
57
|
+
data-testid={dataTestId}
|
|
58
|
+
className={classNames(
|
|
59
|
+
'relative w-[280px] select-none overflow-hidden rounded-[24px] shadow-[0_0_0_1px_rgba(0,0,0,0.04),0_4px_8px_rgba(0,0,0,0.06)]',
|
|
60
|
+
isDark ? 'bg-[#121110]' : 'bg-white'
|
|
61
|
+
)}
|
|
62
|
+
>
|
|
63
|
+
{topLeft ? (
|
|
64
|
+
<div className="pointer-events-auto absolute left-3 top-3 z-50">{topLeft}</div>
|
|
65
|
+
) : null}
|
|
66
|
+
{topRight ? (
|
|
67
|
+
<div className="pointer-events-auto absolute right-3 top-3 z-50">{topRight}</div>
|
|
68
|
+
) : null}
|
|
69
|
+
|
|
70
|
+
{thumbnail}
|
|
71
|
+
|
|
72
|
+
<div className="px-4 pb-3 pt-3">
|
|
73
|
+
{displayTitle.trim() !== '' && (
|
|
74
|
+
<p
|
|
75
|
+
className={classNames('mb-0.5 truncate text-base font-medium', {
|
|
76
|
+
'text-black': !isDark,
|
|
77
|
+
'text-white/30': isDark && titleDimmed,
|
|
78
|
+
'text-white': isDark && !titleDimmed,
|
|
79
|
+
})}
|
|
80
|
+
>
|
|
81
|
+
{displayTitle}
|
|
82
|
+
</p>
|
|
83
|
+
)}
|
|
84
|
+
|
|
85
|
+
<div className="flex flex-wrap items-center gap-1">
|
|
86
|
+
{renderTypeIcon(mimeType, {
|
|
87
|
+
className: classNames(
|
|
88
|
+
'size-5 shrink-0',
|
|
89
|
+
isDark ? 'text-white/55' : 'text-black/55'
|
|
90
|
+
),
|
|
91
|
+
weight: 'regular',
|
|
92
|
+
})}
|
|
93
|
+
|
|
94
|
+
{detail != null && detail !== '' && (
|
|
95
|
+
<span
|
|
96
|
+
className={classNames(
|
|
97
|
+
'text-xs font-medium',
|
|
98
|
+
isDark ? 'text-white/55' : 'text-black/55'
|
|
99
|
+
)}
|
|
100
|
+
>
|
|
101
|
+
{detail}
|
|
102
|
+
</span>
|
|
103
|
+
)}
|
|
104
|
+
|
|
105
|
+
{statusBadge}
|
|
106
|
+
</div>
|
|
107
|
+
|
|
108
|
+
{action}
|
|
109
|
+
</div>
|
|
110
|
+
</div>
|
|
111
|
+
)
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export default AttachmentCard
|
|
@@ -9,14 +9,12 @@ import {
|
|
|
9
9
|
import classNames from 'classnames'
|
|
10
10
|
import React, { useCallback, useRef, useState } from 'react'
|
|
11
11
|
|
|
12
|
+
import AttachmentCard, { AttachmentThumbnail } from '../../../AttachmentCard'
|
|
12
13
|
import type {
|
|
13
14
|
LockedAttachmentBaseProps,
|
|
14
15
|
LockedAttachmentSource,
|
|
15
16
|
PaymentStatus,
|
|
16
17
|
} from '../../types'
|
|
17
|
-
import { renderTypeIcon } from '../../utils/icons'
|
|
18
|
-
|
|
19
|
-
import CardThumbnail from './CardThumbnail'
|
|
20
18
|
|
|
21
19
|
export interface CreatorCardProps extends LockedAttachmentBaseProps {
|
|
22
20
|
placeholderTitle?: string
|
|
@@ -27,6 +25,65 @@ export interface CreatorCardProps extends LockedAttachmentBaseProps {
|
|
|
27
25
|
onFetchSource?: () => Promise<LockedAttachmentSource | void>
|
|
28
26
|
}
|
|
29
27
|
|
|
28
|
+
function headerSlots(props: {
|
|
29
|
+
onDismiss?: () => void
|
|
30
|
+
onToggle?: () => void
|
|
31
|
+
isExpanded?: boolean
|
|
32
|
+
paymentStatus?: PaymentStatus
|
|
33
|
+
isLoading?: boolean
|
|
34
|
+
}): { topLeft?: React.ReactNode; topRight?: React.ReactNode } {
|
|
35
|
+
const { onDismiss, onToggle, isExpanded, paymentStatus, isLoading } = props
|
|
36
|
+
|
|
37
|
+
if (onDismiss) {
|
|
38
|
+
return {
|
|
39
|
+
topLeft: undefined,
|
|
40
|
+
topRight: (
|
|
41
|
+
<button
|
|
42
|
+
type="button"
|
|
43
|
+
onClick={onDismiss}
|
|
44
|
+
className="flex size-8 items-center justify-center rounded-full bg-black/60 text-white"
|
|
45
|
+
aria-label="Dismiss attachment"
|
|
46
|
+
>
|
|
47
|
+
<XIcon className="size-4" weight="bold" />
|
|
48
|
+
</button>
|
|
49
|
+
),
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (onToggle) {
|
|
54
|
+
const Icon = isExpanded ? EyeIcon : EyeSlashIcon
|
|
55
|
+
return {
|
|
56
|
+
topLeft: (
|
|
57
|
+
<button
|
|
58
|
+
type="button"
|
|
59
|
+
onClick={onToggle}
|
|
60
|
+
className="flex size-8 items-center justify-center rounded-full bg-black/60 text-white"
|
|
61
|
+
aria-label={isExpanded ? 'Hide preview' : 'Show preview'}
|
|
62
|
+
aria-pressed={isExpanded}
|
|
63
|
+
>
|
|
64
|
+
<Icon className="size-4" weight="fill" />
|
|
65
|
+
</button>
|
|
66
|
+
),
|
|
67
|
+
topRight: undefined,
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const Icon = paymentStatus === 'paid' ? LockOpenIcon : LockIcon
|
|
72
|
+
|
|
73
|
+
return {
|
|
74
|
+
topLeft: (
|
|
75
|
+
<div className="flex size-8 items-center justify-center rounded-full bg-black/60 text-white">
|
|
76
|
+
{isLoading ? (
|
|
77
|
+
<span className="size-4 animate-spin rounded-full border-2 border-white/30 border-t-white" />
|
|
78
|
+
) : (
|
|
79
|
+
<Icon className="size-4" weight="fill" />
|
|
80
|
+
)}
|
|
81
|
+
</div>
|
|
82
|
+
),
|
|
83
|
+
topRight: undefined,
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
30
87
|
const CreatorCard: React.FC<CreatorCardProps> = ({
|
|
31
88
|
title,
|
|
32
89
|
mimeType = 'application/octet-stream',
|
|
@@ -78,122 +135,75 @@ const CreatorCard: React.FC<CreatorCardProps> = ({
|
|
|
78
135
|
|
|
79
136
|
const toggleHandler = onFetchSource || onPreviewClick ? handleToggle : undefined
|
|
80
137
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
<
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
mimeType={mimeType}
|
|
96
|
-
onToggle={isBusy ? undefined : toggleHandler}
|
|
97
|
-
/>
|
|
98
|
-
|
|
99
|
-
<div className="px-4 pb-3 pt-3">
|
|
100
|
-
<p
|
|
101
|
-
className={classNames('mb-0.5 truncate text-base font-medium', {
|
|
102
|
-
'text-white/30': !title,
|
|
103
|
-
'text-white': !!title,
|
|
138
|
+
const statusBadge =
|
|
139
|
+
paymentStatus === 'paid' ? (
|
|
140
|
+
<React.Fragment>
|
|
141
|
+
<span className="text-xs font-medium text-white/55">•</span>
|
|
142
|
+
<span className="text-xs font-medium text-[#34c759]">Sold</span>
|
|
143
|
+
<CheckCircleIcon className="size-4 text-[#34c759]" weight="bold" />
|
|
144
|
+
</React.Fragment>
|
|
145
|
+
) : (
|
|
146
|
+
<React.Fragment>
|
|
147
|
+
<span className="text-xs font-medium text-white/55">•</span>
|
|
148
|
+
<span
|
|
149
|
+
className={classNames('text-xs font-medium', {
|
|
150
|
+
'text-white/30': !amountText,
|
|
151
|
+
'text-white/55': !!amountText,
|
|
104
152
|
})}
|
|
105
153
|
>
|
|
106
|
-
{
|
|
107
|
-
</
|
|
108
|
-
|
|
109
|
-
<div className="flex items-center gap-1">
|
|
110
|
-
{renderTypeIcon(mimeType, {
|
|
111
|
-
className: 'size-5 shrink-0 text-white/55',
|
|
112
|
-
weight: 'regular',
|
|
113
|
-
})}
|
|
114
|
-
|
|
115
|
-
{detail && (
|
|
116
|
-
<span className="text-xs font-medium text-white/55">{detail}</span>
|
|
117
|
-
)}
|
|
118
|
-
|
|
119
|
-
{paymentStatus === 'paid' ? (
|
|
120
|
-
<React.Fragment>
|
|
121
|
-
<span className="text-xs font-medium text-white/55">•</span>
|
|
122
|
-
<span className="text-xs font-medium text-[#34c759]">Sold</span>
|
|
123
|
-
<CheckCircleIcon className="size-4 text-[#34c759]" weight="bold" />
|
|
124
|
-
</React.Fragment>
|
|
125
|
-
) : (
|
|
126
|
-
<React.Fragment>
|
|
127
|
-
<span className="text-xs font-medium text-white/55">•</span>
|
|
128
|
-
<span
|
|
129
|
-
className={classNames('text-xs font-medium', {
|
|
130
|
-
'text-white/30': !amountText,
|
|
131
|
-
'text-white/55': !!amountText,
|
|
132
|
-
})}
|
|
133
|
-
>
|
|
134
|
-
{amountText || placeholderAmountText}
|
|
135
|
-
</span>
|
|
136
|
-
</React.Fragment>
|
|
137
|
-
)}
|
|
138
|
-
</div>
|
|
139
|
-
</div>
|
|
140
|
-
</div>
|
|
141
|
-
)
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
interface CardHeaderProps {
|
|
145
|
-
onDismiss?: () => void
|
|
146
|
-
onToggle?: () => void
|
|
147
|
-
isExpanded?: boolean
|
|
148
|
-
paymentStatus?: PaymentStatus
|
|
149
|
-
isLoading?: boolean
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
const CardHeader: React.FC<CardHeaderProps> = ({
|
|
153
|
-
onDismiss,
|
|
154
|
-
onToggle,
|
|
155
|
-
isExpanded,
|
|
156
|
-
paymentStatus,
|
|
157
|
-
isLoading,
|
|
158
|
-
}) => {
|
|
159
|
-
if (onDismiss) {
|
|
160
|
-
return (
|
|
161
|
-
<button
|
|
162
|
-
type="button"
|
|
163
|
-
onClick={onDismiss}
|
|
164
|
-
className="absolute top-3 z-50 flex size-8 items-center justify-center rounded-full bg-black/60 text-white right-3"
|
|
165
|
-
aria-label="Dismiss attachment"
|
|
166
|
-
>
|
|
167
|
-
<XIcon className="size-4" weight="bold" />
|
|
168
|
-
</button>
|
|
169
|
-
)
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
if (onToggle) {
|
|
173
|
-
const Icon = isExpanded ? EyeIcon : EyeSlashIcon
|
|
174
|
-
return (
|
|
175
|
-
<button
|
|
176
|
-
type="button"
|
|
177
|
-
onClick={onToggle}
|
|
178
|
-
className="absolute top-3 z-50 flex size-8 items-center justify-center rounded-full bg-black/60 text-white left-3"
|
|
179
|
-
aria-label={isExpanded ? 'Hide preview' : 'Show preview'}
|
|
180
|
-
aria-pressed={isExpanded}
|
|
181
|
-
>
|
|
182
|
-
<Icon className="size-4" weight="fill" />
|
|
183
|
-
</button>
|
|
154
|
+
{amountText || placeholderAmountText}
|
|
155
|
+
</span>
|
|
156
|
+
</React.Fragment>
|
|
184
157
|
)
|
|
185
|
-
}
|
|
186
158
|
|
|
187
|
-
const
|
|
159
|
+
const { topLeft, topRight } = headerSlots({
|
|
160
|
+
onDismiss,
|
|
161
|
+
onToggle: isBusy ? undefined : toggleHandler,
|
|
162
|
+
isExpanded: isPreviewVisible,
|
|
163
|
+
paymentStatus,
|
|
164
|
+
isLoading: isBusy,
|
|
165
|
+
})
|
|
188
166
|
|
|
189
167
|
return (
|
|
190
|
-
<
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
168
|
+
<AttachmentCard
|
|
169
|
+
variant="dark"
|
|
170
|
+
title={title}
|
|
171
|
+
placeholderTitle={placeholderTitle}
|
|
172
|
+
mimeType={mimeType}
|
|
173
|
+
detail={detail}
|
|
174
|
+
statusBadge={statusBadge}
|
|
175
|
+
topLeft={topLeft}
|
|
176
|
+
topRight={topRight}
|
|
177
|
+
thumbnail={
|
|
178
|
+
<button
|
|
179
|
+
type="button"
|
|
180
|
+
disabled={!toggleHandler || isBusy}
|
|
181
|
+
className={classNames(
|
|
182
|
+
'relative block w-full overflow-hidden border-0 bg-white/10 p-0 text-left appearance-none',
|
|
183
|
+
{ 'cursor-pointer': !!toggleHandler && !isBusy, 'cursor-default': !toggleHandler || isBusy }
|
|
184
|
+
)}
|
|
185
|
+
onClick={isBusy ? undefined : toggleHandler}
|
|
186
|
+
aria-label={toggleHandler ? 'Toggle preview' : undefined}
|
|
187
|
+
aria-busy={isBusy}
|
|
188
|
+
>
|
|
189
|
+
<AttachmentThumbnail
|
|
190
|
+
mimeType={mimeType}
|
|
191
|
+
sourceUrl={effectiveSourceUrl}
|
|
192
|
+
thumbnailUrl={effectiveThumbnailUrl}
|
|
193
|
+
title={title}
|
|
194
|
+
variant="dark"
|
|
195
|
+
mediaPlayerProps={
|
|
196
|
+
effectiveSourceUrl
|
|
197
|
+
? { autoPlay: true, loop: true, controls: true, muted: false }
|
|
198
|
+
: undefined
|
|
199
|
+
}
|
|
200
|
+
/>
|
|
201
|
+
{!isPreviewVisible && (
|
|
202
|
+
<div className="pointer-events-none absolute inset-0 bg-black/30" />
|
|
203
|
+
)}
|
|
204
|
+
</button>
|
|
205
|
+
}
|
|
206
|
+
/>
|
|
197
207
|
)
|
|
198
208
|
}
|
|
199
209
|
|