@linktr.ee/messaging-react 1.33.3 → 1.35.0-rc-1777516745

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. package/dist/Card-BXKUE7JN.js +195 -0
  2. package/dist/Card-BXKUE7JN.js.map +1 -0
  3. package/dist/Card-J-rj8Q6w.js +167 -0
  4. package/dist/Card-J-rj8Q6w.js.map +1 -0
  5. package/dist/assets/index.css +1 -1
  6. package/dist/{index-Ydi1pTAi.js → index-B3H4GLek.js} +1067 -987
  7. package/dist/index-B3H4GLek.js.map +1 -0
  8. package/dist/index.d.ts +5 -2
  9. package/dist/index.js +1 -1
  10. package/package.json +1 -1
  11. package/src/components/Avatar/Avatar.stories.tsx +29 -0
  12. package/src/components/Avatar/index.tsx +43 -20
  13. package/src/components/ChannelView.stories.tsx +45 -1
  14. package/src/components/ChannelView.tsx +9 -5
  15. package/src/components/CustomMessage/CustomMessage.stories.tsx +75 -15
  16. package/src/components/CustomMessage/MessageTag.stories.tsx +1 -1
  17. package/src/components/CustomMessage/index.tsx +8 -5
  18. package/src/components/CustomTypingIndicator/CustomTypingIndicator.stories.tsx +114 -0
  19. package/src/components/CustomTypingIndicator/index.tsx +101 -0
  20. package/src/components/LockedAttachment/LockedAttachment.stories.tsx +94 -66
  21. package/src/components/LockedAttachment/components/Creator/Card.tsx +55 -23
  22. package/src/components/LockedAttachment/components/Creator/CardThumbnail.tsx +2 -2
  23. package/src/components/LockedAttachment/components/Visitor/Card.tsx +9 -16
  24. package/src/components/LockedAttachment/components/Visitor/CardActions.tsx +3 -12
  25. package/src/components/LockedAttachment/components/Visitor/CardThumbnail.tsx +2 -2
  26. package/src/components/MediaMessage/MediaMessage.stories.tsx +77 -71
  27. package/src/components/MediaMessage/index.tsx +1 -1
  28. package/src/styles.css +27 -5
  29. package/dist/Card-BfA8wq8O.js +0 -181
  30. package/dist/Card-BfA8wq8O.js.map +0 -1
  31. package/dist/Card-Bpud_enW.js +0 -177
  32. package/dist/Card-Bpud_enW.js.map +0 -1
  33. package/dist/index-Ydi1pTAi.js.map +0 -1
@@ -5,31 +5,39 @@ import type { LocalMessage } from 'stream-chat'
5
5
  import { MediaMessage } from '.'
6
6
 
7
7
  const meta: Meta<typeof MediaMessage> = {
8
- title: 'Components/MediaMessage',
8
+ title: 'MediaMessage',
9
9
  component: MediaMessage,
10
10
  parameters: {
11
- layout: 'centered',
11
+ layout: 'fullscreen',
12
12
  },
13
13
  }
14
14
  export default meta
15
15
 
16
- // ---------------------------------------------------------------------------
17
- // Shared helpers
18
- // ---------------------------------------------------------------------------
16
+ type MessageAttachment = NonNullable<LocalMessage['attachments']>[number]
19
17
 
20
- const SENDER = { id: 'user-other', name: 'Jane Creator', image: 'https://i.pravatar.cc/40?img=5' }
21
- const ME = { id: 'user-me', name: 'Me' }
18
+ const base = (overrides: Partial<LocalMessage> = {}): LocalMessage => {
19
+ const defaultMessage: LocalMessage = {
20
+ id: 'msg-1',
21
+ text: '',
22
+ type: 'regular',
23
+ created_at: new Date(),
24
+ updated_at: new Date(),
25
+ deleted_at: null,
26
+ pinned_at: null,
27
+ status: 'received',
28
+ attachments: [],
29
+ }
22
30
 
23
- const base = (overrides: Partial<LocalMessage> = {}): LocalMessage => ({
24
- id: 'msg-1',
25
- text: '',
26
- type: 'regular',
27
- created_at: new Date(),
28
- updated_at: new Date(),
29
- user: SENDER,
30
- attachments: [],
31
- ...overrides,
32
- })
31
+ return {
32
+ ...defaultMessage,
33
+ ...overrides,
34
+ created_at: overrides.created_at ?? defaultMessage.created_at,
35
+ updated_at: overrides.updated_at ?? defaultMessage.updated_at,
36
+ deleted_at: overrides.deleted_at ?? defaultMessage.deleted_at,
37
+ pinned_at: overrides.pinned_at ?? defaultMessage.pinned_at,
38
+ status: overrides.status ?? defaultMessage.status,
39
+ }
40
+ }
33
41
 
34
42
  const VARIANTS = [
35
43
  {
@@ -46,7 +54,8 @@ const VARIANTS = [
46
54
  label: 'Audio',
47
55
  attachment: {
48
56
  type: 'audio',
49
- asset_url: 'https://www.soundhelix.com/examples/mp3/SoundHelix-Song-1.mp3',
57
+ asset_url:
58
+ 'https://www.soundhelix.com/examples/mp3/SoundHelix-Song-1.mp3',
50
59
  mime_type: 'audio/mpeg',
51
60
  title: 'Morning Meditation',
52
61
  file_size: 6_900_000,
@@ -121,21 +130,23 @@ const LINK_VARIANTS = [
121
130
  },
122
131
  ] as const
123
132
 
124
- // ---------------------------------------------------------------------------
125
- // Layout primitives
126
- // ---------------------------------------------------------------------------
127
-
128
- const GridTable: React.FC<{ children: React.ReactNode }> = ({ children }) => (
129
- <div className="p-12 bg-[#F9F7F4]">
133
+ const Table: React.FC<{ children: React.ReactNode }> = ({ children }) => (
134
+ <div className="min-h-screen w-full p-12 bg-[#F9F7F4]">
130
135
  <table className="border-separate border-spacing-4">{children}</table>
131
136
  </div>
132
137
  )
133
138
 
134
- const GridHead: React.FC<{ labels: readonly string[] }> = ({ labels }) => (
139
+ const TableHead: React.FC<{ variants: readonly { label: string }[] }> = ({
140
+ variants,
141
+ }) => (
135
142
  <thead>
136
143
  <tr>
137
- {labels.map((label) => (
138
- <th key={label} className="text-left text-xs font-medium text-black/40 pb-2">
144
+ <th className="text-left text-xs font-medium text-black/40 pb-2" />
145
+ {variants.map(({ label }) => (
146
+ <th
147
+ key={label}
148
+ className="text-left text-xs font-medium text-black/40 pb-2"
149
+ >
139
150
  {label}
140
151
  </th>
141
152
  ))}
@@ -143,91 +154,86 @@ const GridHead: React.FC<{ labels: readonly string[] }> = ({ labels }) => (
143
154
  </thead>
144
155
  )
145
156
 
146
- // ---------------------------------------------------------------------------
147
- // Stories
148
- // ---------------------------------------------------------------------------
149
-
150
- export const Received: StoryFn = () => (
151
- <GridTable>
152
- <GridHead labels={VARIANTS.map((v) => v.label)} />
157
+ export const Attachment: StoryFn = () => (
158
+ <Table>
159
+ <TableHead variants={VARIANTS} />
153
160
  <tbody>
154
161
  <tr>
162
+ <td className="text-xs text-right font-medium text-black/40 pr-4 align-top pt-2">
163
+ Received
164
+ </td>
155
165
  {VARIANTS.map(({ label, attachment }) => (
156
166
  <td key={label} className="align-top">
157
167
  <MediaMessage
158
168
  isMyMessage={false}
159
- message={base({ user: SENDER, attachments: [attachment as LocalMessage['attachments'][number]] })}
169
+ message={base({
170
+ attachments: [
171
+ attachment as MessageAttachment,
172
+ ],
173
+ })}
160
174
  />
161
175
  </td>
162
176
  ))}
163
177
  </tr>
164
- </tbody>
165
- </GridTable>
166
- )
167
- Received.parameters = {
168
- docs: {
169
- description: {
170
- story: 'Received messages — left-aligned with avatar, light gray card background.',
171
- },
172
- },
173
- }
174
-
175
- export const Sent: StoryFn = () => (
176
- <GridTable>
177
- <GridHead labels={VARIANTS.map((v) => v.label)} />
178
- <tbody>
179
178
  <tr>
179
+ <td className="text-xs text-right font-medium text-black/40 pr-4 align-top pt-2">
180
+ Sent
181
+ </td>
180
182
  {VARIANTS.map(({ label, attachment }) => (
181
183
  <td key={label} className="align-top">
182
184
  <MediaMessage
183
185
  isMyMessage={true}
184
- message={base({ user: ME, attachments: [attachment as LocalMessage['attachments'][number]] })}
186
+ message={base({
187
+ attachments: [
188
+ attachment as MessageAttachment,
189
+ ],
190
+ })}
185
191
  />
186
192
  </td>
187
193
  ))}
188
194
  </tr>
189
195
  </tbody>
190
- </GridTable>
196
+ </Table>
191
197
  )
192
- Sent.parameters = {
193
- docs: {
194
- description: {
195
- story: 'Sent messages — right-aligned, no avatar, black card background.',
196
- },
197
- },
198
- }
199
198
 
200
199
  export const Links: StoryFn = () => (
201
- <GridTable>
202
- <GridHead labels={LINK_VARIANTS.map((v) => v.label)} />
200
+ <Table>
201
+ <TableHead variants={LINK_VARIANTS} />
203
202
  <tbody>
204
203
  <tr>
204
+ <td className="text-xs text-right font-medium text-black/40 pr-4 align-top pt-2">
205
+ Received
206
+ </td>
205
207
  {LINK_VARIANTS.map(({ label, attachment }) => (
206
208
  <td key={label} className="align-top">
207
209
  <MediaMessage
208
210
  isMyMessage={false}
209
- message={base({ user: SENDER, attachments: [attachment as LocalMessage['attachments'][number]] })}
211
+ message={base({
212
+ attachments: [
213
+ attachment as MessageAttachment,
214
+ ],
215
+ })}
210
216
  />
211
217
  </td>
212
218
  ))}
213
219
  </tr>
214
220
  <tr>
221
+ <td className="text-xs text-right font-medium text-black/40 pr-4 align-top pt-2">
222
+ Sent
223
+ </td>
215
224
  {LINK_VARIANTS.map(({ label, attachment }) => (
216
225
  <td key={label} className="align-top">
217
226
  <MediaMessage
218
227
  isMyMessage={true}
219
- message={base({ user: ME, attachments: [attachment as LocalMessage['attachments'][number]] })}
228
+ message={base({
229
+ attachments: [
230
+ attachment as MessageAttachment,
231
+ ],
232
+ })}
220
233
  />
221
234
  </td>
222
235
  ))}
223
236
  </tr>
224
237
  </tbody>
225
- </GridTable>
238
+ </Table>
226
239
  )
227
- Links.parameters = {
228
- docs: {
229
- description: {
230
- story: 'Link preview cards — top row received, bottom row sent. Shows thumbnail, title, description, and URL with image fallback.',
231
- },
232
- },
233
- }
@@ -113,7 +113,7 @@ const MediaMeta: React.FC<{
113
113
  <div className="flex items-start gap-2 px-4 pb-3 pt-3">
114
114
  <div className="min-w-0 flex-1">
115
115
  {title && (
116
- <p className={`mb-1.5 truncate text-base font-medium ${primaryText(isMyMessage)}`}>
116
+ <p className={`truncate text-base font-medium ${primaryText(isMyMessage)}`}>
117
117
  {title}
118
118
  </p>
119
119
  )}
package/src/styles.css CHANGED
@@ -152,14 +152,37 @@
152
152
  background: transparent;
153
153
  }
154
154
 
155
- /* Wrapper to ensure tag stays below bubble */
156
- .str-chat__message-bubble-wrapper {
155
+ .str-chat__li .str-chat__message--me {
156
+ position: relative;
157
+ }
158
+
159
+ .str-chat__li .str-chat__message-inner {
160
+ grid-column-gap: 0;
161
+ }
162
+
163
+ .str-chat__li .str-chat__message--me .str-chat__message-bubble-wrapper {
164
+ display: flex;
165
+ flex-direction: column;
166
+ align-items: flex-end;
167
+ gap: 2px;
168
+ }
169
+
170
+ .str-chat__li .str-chat__message--other {
171
+ position: relative;
172
+ grid-template-columns: 40px 1fr;
173
+ }
174
+
175
+ .str-chat__li .str-chat__message--other .str-chat__message-bubble-wrapper {
157
176
  display: flex;
158
177
  flex-direction: column;
178
+ align-items: flex-start;
179
+ gap: 2px;
159
180
  }
160
181
 
161
- .str-chat__typing-indicator {
162
- inset-inline: auto;
182
+ .str-chat__li .str-chat__message--other .str-chat__avatar {
183
+ position: absolute;
184
+ left: 0;
185
+ bottom: 0;
163
186
  }
164
187
 
165
188
  /* Channel list load-more button overrides */
@@ -186,7 +209,6 @@
186
209
  display: inline-flex;
187
210
  align-items: center;
188
211
  gap: 4px;
189
- margin-top: 4px;
190
212
  padding: 0;
191
213
  font-size: 12px;
192
214
  font-weight: 500;
@@ -1,181 +0,0 @@
1
- import { jsxs as i, jsx as e } from "react/jsx-runtime";
2
- import { CheckCircleIcon as N, XIcon as v, EyeIcon as k, EyeSlashIcon as z, LockOpenIcon as I, LockIcon as y } from "@phosphor-icons/react";
3
- import d from "classnames";
4
- import h, { useState as j, useCallback as _ } from "react";
5
- import { r as b, g as C, M as S } from "./index-Ydi1pTAi.js";
6
- const M = ({
7
- title: a,
8
- sourceUrl: l,
9
- thumbnailUrl: t,
10
- mimeType: s,
11
- onToggle: r
12
- }) => {
13
- const n = r && l && t;
14
- return /* @__PURE__ */ i(
15
- "button",
16
- {
17
- type: "button",
18
- disabled: !r,
19
- className: d(
20
- "relative block w-full overflow-hidden border-0 bg-black/5 p-0 text-left appearance-none",
21
- { "cursor-pointer": !!r, "cursor-default": !r }
22
- ),
23
- onClick: r,
24
- "aria-label": r ? "Toggle preview" : void 0,
25
- children: [
26
- n ? /* @__PURE__ */ e(
27
- E,
28
- {
29
- sourceUrl: l,
30
- thumbnailUrl: t,
31
- mimeType: s
32
- }
33
- ) : t ? /* @__PURE__ */ e("div", { className: "aspect-video overflow-hidden", children: /* @__PURE__ */ e(
34
- "img",
35
- {
36
- src: t,
37
- alt: a,
38
- draggable: !1,
39
- className: "absolute inset-0 h-full w-full object-cover"
40
- }
41
- ) }) : /* @__PURE__ */ e("div", { className: "aspect-video flex items-center justify-center", children: b(s, {
42
- className: "size-12 text-black/20",
43
- weight: "regular"
44
- }) }),
45
- !n && /* @__PURE__ */ e("div", { className: "pointer-events-none absolute inset-0 bg-black/30" })
46
- ]
47
- }
48
- );
49
- }, E = ({
50
- sourceUrl: a,
51
- thumbnailUrl: l,
52
- mimeType: t
53
- }) => {
54
- const s = C(t);
55
- return s === "video" || s === "audio" ? /* @__PURE__ */ e(
56
- S,
57
- {
58
- mimeType: t,
59
- source: a,
60
- poster: l,
61
- autoPlay: !0,
62
- loop: !0,
63
- controls: !0,
64
- muted: !1
65
- }
66
- ) : s === "image" ? /* @__PURE__ */ e("img", { src: a, alt: "", className: "block w-full", draggable: !1 }) : s === "document" ? /* @__PURE__ */ e(
67
- "img",
68
- {
69
- src: l,
70
- alt: "",
71
- className: "block w-full",
72
- draggable: !1
73
- }
74
- ) : null;
75
- }, X = ({
76
- title: a,
77
- mimeType: l = "application/octet-stream",
78
- thumbnailUrl: t,
79
- detail: s,
80
- amountText: r,
81
- placeholderTitle: n = "Attachment title",
82
- placeholderAmountText: p,
83
- paymentStatus: u,
84
- onDismiss: x,
85
- onPreviewClick: o
86
- }) => {
87
- const [c, m] = j(), g = c == null ? void 0 : c.sourceUrl, w = (c == null ? void 0 : c.thumbnailUrl) ?? t, f = _(() => {
88
- c ? m(void 0) : o && m(o());
89
- }, [c, o]);
90
- return /* @__PURE__ */ i("div", { className: "relative w-[280px] select-none overflow-hidden rounded-[24px] bg-white shadow-[0_0_0_1px_rgba(0,0,0,0.04),0_4px_8px_rgba(0,0,0,0.06)]", children: [
91
- /* @__PURE__ */ e(
92
- F,
93
- {
94
- onDismiss: x,
95
- onToggle: o ? f : void 0,
96
- isExpanded: !!c,
97
- paymentStatus: u
98
- }
99
- ),
100
- /* @__PURE__ */ e(
101
- M,
102
- {
103
- title: a,
104
- sourceUrl: g,
105
- thumbnailUrl: w,
106
- mimeType: l,
107
- onToggle: o ? f : void 0
108
- }
109
- ),
110
- /* @__PURE__ */ i("div", { className: "px-4 pb-3 pt-3 bg-black", children: [
111
- /* @__PURE__ */ e(
112
- "p",
113
- {
114
- className: d("mb-1.5 truncate text-base font-medium", {
115
- "text-white/30": !a,
116
- "text-white": !!a
117
- }),
118
- children: a || n
119
- }
120
- ),
121
- /* @__PURE__ */ i("div", { className: "flex items-center gap-1", children: [
122
- b(l, {
123
- className: "size-5 shrink-0 text-white/55",
124
- weight: "regular"
125
- }),
126
- s && /* @__PURE__ */ e("span", { className: "text-xs font-medium text-white/55", children: s }),
127
- u === "paid" ? /* @__PURE__ */ i(h.Fragment, { children: [
128
- /* @__PURE__ */ e("span", { className: "text-xs font-medium text-white/55", children: "•" }),
129
- /* @__PURE__ */ e("span", { className: "text-xs font-medium text-[#4ade80]", children: "Sold" }),
130
- /* @__PURE__ */ e(
131
- N,
132
- {
133
- className: "size-4 text-[#4ade80]",
134
- weight: "bold"
135
- }
136
- )
137
- ] }) : /* @__PURE__ */ i(h.Fragment, { children: [
138
- /* @__PURE__ */ e("span", { className: "text-xs font-medium text-white/55", children: "•" }),
139
- /* @__PURE__ */ e(
140
- "span",
141
- {
142
- className: d("text-xs font-medium", {
143
- "text-white/30": !r,
144
- "text-white/55": !!r
145
- }),
146
- children: r || p
147
- }
148
- )
149
- ] })
150
- ] })
151
- ] })
152
- ] });
153
- }, F = ({
154
- onDismiss: a,
155
- onToggle: l,
156
- isExpanded: t,
157
- paymentStatus: s
158
- }) => a ? /* @__PURE__ */ e(
159
- "button",
160
- {
161
- type: "button",
162
- onClick: a,
163
- className: "absolute top-3 z-50 flex size-8 items-center justify-center rounded-full bg-black/60 text-white right-3",
164
- "aria-label": "Dismiss attachment",
165
- children: /* @__PURE__ */ e(v, { className: "size-4", weight: "bold" })
166
- }
167
- ) : l ? /* @__PURE__ */ e(
168
- "button",
169
- {
170
- type: "button",
171
- onClick: l,
172
- className: "absolute top-3 z-50 flex size-8 items-center justify-center rounded-full bg-black/60 text-white left-3",
173
- "aria-label": t ? "Hide preview" : "Show preview",
174
- "aria-pressed": t,
175
- children: /* @__PURE__ */ e(t ? k : z, { className: "size-4", weight: "fill" })
176
- }
177
- ) : /* @__PURE__ */ e("div", { className: "absolute top-3 z-50 flex size-8 items-center justify-center rounded-full bg-black/60 text-white left-3", children: /* @__PURE__ */ e(s === "paid" ? I : y, { className: "size-4", weight: "fill" }) });
178
- export {
179
- X as default
180
- };
181
- //# sourceMappingURL=Card-BfA8wq8O.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"Card-BfA8wq8O.js","sources":["../src/components/LockedAttachment/components/Creator/CardThumbnail.tsx","../src/components/LockedAttachment/components/Creator/Card.tsx"],"sourcesContent":["import classNames from 'classnames'\nimport React from 'react'\n\nimport { renderTypeIcon } from '../../utils/icons'\nimport { getSourceType } from '../../utils/mimeType'\nimport MediaPlayer from '../MediaPlayer'\n\ninterface CardThumbnailProps {\n title?: string\n sourceUrl?: string\n thumbnailUrl?: string\n mimeType: string\n onToggle?: () => void\n}\n\nconst CardThumbnail: React.FC<CardThumbnailProps> = ({\n title,\n sourceUrl,\n thumbnailUrl,\n mimeType,\n onToggle,\n}) => {\n const isExpanded = onToggle && sourceUrl && thumbnailUrl\n\n return (\n <button\n type=\"button\"\n disabled={!onToggle}\n className={classNames(\n 'relative block w-full overflow-hidden border-0 bg-black/5 p-0 text-left appearance-none',\n { 'cursor-pointer': !!onToggle, 'cursor-default': !onToggle }\n )}\n onClick={onToggle}\n aria-label={onToggle ? 'Toggle preview' : undefined}\n >\n {isExpanded ? (\n <ThumbnailMedia\n sourceUrl={sourceUrl}\n thumbnailUrl={thumbnailUrl}\n mimeType={mimeType}\n />\n ) : thumbnailUrl ? (\n <div className=\"aspect-video overflow-hidden\">\n <img\n src={thumbnailUrl}\n alt={title}\n draggable={false}\n className=\"absolute inset-0 h-full w-full object-cover\"\n />\n </div>\n ) : (\n <div className=\"aspect-video flex items-center justify-center\">\n {renderTypeIcon(mimeType, {\n className: 'size-12 text-black/20',\n weight: 'regular',\n })}\n </div>\n )}\n\n {!isExpanded && (\n <div className=\"pointer-events-none absolute inset-0 bg-black/30\" />\n )}\n </button>\n )\n}\n\ninterface ThumbnailMediaProps {\n sourceUrl: string\n thumbnailUrl: string\n mimeType: string\n}\n\nconst ThumbnailMedia: React.FC<ThumbnailMediaProps> = ({\n sourceUrl,\n thumbnailUrl,\n mimeType,\n}) => {\n const sourceType = getSourceType(mimeType)\n\n if (sourceType === 'video' || sourceType === 'audio') {\n return (\n <MediaPlayer\n mimeType={mimeType}\n source={sourceUrl}\n poster={thumbnailUrl}\n autoPlay={true}\n loop={true}\n controls={true}\n muted={false}\n />\n )\n }\n\n if (sourceType === 'image') {\n return (\n <img src={sourceUrl} alt=\"\" className=\"block w-full\" draggable={false} />\n )\n }\n\n if (sourceType === 'document') {\n return (\n <img\n src={thumbnailUrl}\n alt=\"\"\n className=\"block w-full\"\n draggable={false}\n />\n )\n }\n\n return null\n}\n\nexport default CardThumbnail\n","import {\n CheckCircleIcon,\n EyeIcon,\n EyeSlashIcon,\n LockIcon,\n LockOpenIcon,\n XIcon,\n} from '@phosphor-icons/react'\nimport classNames from 'classnames'\nimport React, { useCallback, useState } from 'react'\n\nimport type {\n LockedAttachmentBaseProps,\n LockedAttachmentSource,\n PaymentStatus,\n} from '../../types'\nimport { renderTypeIcon } from '../../utils/icons'\n\nimport CardThumbnail from './CardThumbnail'\n\nexport interface CreatorCardProps extends LockedAttachmentBaseProps {\n placeholderTitle?: string\n placeholderAmountText?: string\n onDismiss?: () => void\n onPreviewClick?: () => LockedAttachmentSource\n}\n\nconst CreatorCard: React.FC<CreatorCardProps> = ({\n title,\n mimeType = 'application/octet-stream',\n thumbnailUrl,\n detail,\n amountText,\n placeholderTitle = 'Attachment title',\n placeholderAmountText,\n paymentStatus,\n onDismiss,\n onPreviewClick,\n}) => {\n const [source, setSource] = useState<LockedAttachmentSource | undefined>()\n\n const effectiveSourceUrl = source?.sourceUrl\n const effectiveThumbnailUrl = source?.thumbnailUrl ?? thumbnailUrl\n\n const handleToggle = useCallback(() => {\n if (source) {\n setSource(undefined)\n } else if (onPreviewClick) {\n setSource(onPreviewClick())\n }\n }, [source, onPreviewClick])\n\n return (\n <div className=\"relative w-[280px] select-none overflow-hidden rounded-[24px] bg-white shadow-[0_0_0_1px_rgba(0,0,0,0.04),0_4px_8px_rgba(0,0,0,0.06)]\">\n <CardHeader\n onDismiss={onDismiss}\n onToggle={onPreviewClick ? handleToggle : undefined}\n isExpanded={!!source}\n paymentStatus={paymentStatus}\n />\n\n <CardThumbnail\n title={title}\n sourceUrl={effectiveSourceUrl}\n thumbnailUrl={effectiveThumbnailUrl}\n mimeType={mimeType}\n onToggle={onPreviewClick ? handleToggle : undefined}\n />\n\n <div className=\"px-4 pb-3 pt-3 bg-black\">\n <p\n className={classNames('mb-1.5 truncate text-base font-medium', {\n 'text-white/30': !title,\n 'text-white': !!title,\n })}\n >\n {title || placeholderTitle}\n </p>\n\n <div className=\"flex items-center gap-1\">\n {renderTypeIcon(mimeType, {\n className: 'size-5 shrink-0 text-white/55',\n weight: 'regular',\n })}\n\n {detail && (\n <span className=\"text-xs font-medium text-white/55\">{detail}</span>\n )}\n\n {paymentStatus === 'paid' ? (\n <React.Fragment>\n <span className=\"text-xs font-medium text-white/55\">&bull;</span>\n <span className=\"text-xs font-medium text-[#4ade80]\">Sold</span>\n <CheckCircleIcon\n className=\"size-4 text-[#4ade80]\"\n weight=\"bold\"\n />\n </React.Fragment>\n ) : (\n <React.Fragment>\n <span className=\"text-xs font-medium text-white/55\">&bull;</span>\n <span\n className={classNames('text-xs font-medium', {\n 'text-white/30': !amountText,\n 'text-white/55': !!amountText,\n })}\n >\n {amountText || placeholderAmountText}\n </span>\n </React.Fragment>\n )}\n </div>\n </div>\n </div>\n )\n}\n\ninterface CardHeaderProps {\n onDismiss?: () => void\n onToggle?: () => void\n isExpanded?: boolean\n paymentStatus?: PaymentStatus\n}\n\nconst CardHeader: React.FC<CardHeaderProps> = ({\n onDismiss,\n onToggle,\n isExpanded,\n paymentStatus,\n}) => {\n if (onDismiss) {\n return (\n <button\n type=\"button\"\n onClick={onDismiss}\n className=\"absolute top-3 z-50 flex size-8 items-center justify-center rounded-full bg-black/60 text-white right-3\"\n aria-label=\"Dismiss attachment\"\n >\n <XIcon className=\"size-4\" weight=\"bold\" />\n </button>\n )\n }\n\n if (onToggle) {\n const Icon = isExpanded ? EyeIcon : EyeSlashIcon\n return (\n <button\n type=\"button\"\n onClick={onToggle}\n className=\"absolute top-3 z-50 flex size-8 items-center justify-center rounded-full bg-black/60 text-white left-3\"\n aria-label={isExpanded ? 'Hide preview' : 'Show preview'}\n aria-pressed={isExpanded}\n >\n <Icon className=\"size-4\" weight=\"fill\" />\n </button>\n )\n }\n\n const Icon = paymentStatus === 'paid' ? LockOpenIcon : LockIcon\n\n return (\n <div className=\"absolute top-3 z-50 flex size-8 items-center justify-center rounded-full bg-black/60 text-white left-3\">\n <Icon className=\"size-4\" weight=\"fill\" />\n </div>\n )\n}\n\nexport default CreatorCard\n"],"names":["CardThumbnail","title","sourceUrl","thumbnailUrl","mimeType","onToggle","isExpanded","jsxs","classNames","jsx","ThumbnailMedia","sourceType","getSourceType","MediaPlayer","CreatorCard","detail","amountText","placeholderTitle","placeholderAmountText","paymentStatus","onDismiss","onPreviewClick","source","setSource","useState","effectiveSourceUrl","effectiveThumbnailUrl","handleToggle","useCallback","CardHeader","renderTypeIcon","React","CheckCircleIcon","XIcon","EyeIcon","EyeSlashIcon","LockOpenIcon","LockIcon"],"mappings":"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"}
@@ -1,177 +0,0 @@
1
- import { jsx as e, jsxs as i } from "react/jsx-runtime";
2
- import { LockSimpleIcon as k, DownloadSimpleIcon as R, LockOpenIcon as U, CheckCircleIcon as D } from "@phosphor-icons/react";
3
- import w, { useState as y, useRef as x, useCallback as N, useEffect as M } from "react";
4
- import { g as O, M as P, r as z } from "./index-Ydi1pTAi.js";
5
- const A = (d) => {
6
- const {
7
- isUnlocking: l = !1,
8
- sourceUrl: n,
9
- redeemUrl: r,
10
- onUnlockClicked: c,
11
- onDownloadClicked: a
12
- } = d, o = n === void 0;
13
- return o && c != null ? /* @__PURE__ */ e(
14
- "button",
15
- {
16
- type: "button",
17
- onClick: c,
18
- disabled: l,
19
- className: "mt-3 inline-flex h-10 w-full items-center justify-center gap-2 rounded-full bg-white px-4 text-sm font-medium leading-none text-[#121110] hover:bg-white/90 disabled:opacity-70",
20
- children: l ? /* @__PURE__ */ e(E, {}) : /* @__PURE__ */ i(w.Fragment, { children: [
21
- /* @__PURE__ */ e(k, { className: "size-4", weight: "fill" }),
22
- "Unlock"
23
- ] })
24
- }
25
- ) : !o && a != null && n != null ? /* @__PURE__ */ i(
26
- "a",
27
- {
28
- href: r ?? n,
29
- target: "_blank",
30
- rel: "noopener noreferrer",
31
- onClick: a,
32
- className: "mt-3 inline-flex h-10 w-full items-center justify-center gap-2 rounded-full bg-white px-4 text-sm font-medium leading-none !text-[#121110] hover:bg-white/90",
33
- children: [
34
- /* @__PURE__ */ e(R, { className: "size-4", weight: "bold" }),
35
- "Download"
36
- ]
37
- }
38
- ) : null;
39
- }, E = () => /* @__PURE__ */ i("span", { className: "flex items-center gap-1", children: [
40
- /* @__PURE__ */ e("span", { className: "size-1 rounded-full bg-white animate-bounce [animation-delay:-0.3s]" }),
41
- /* @__PURE__ */ e("span", { className: "size-1 rounded-full bg-white animate-bounce [animation-delay:-0.15s]" }),
42
- /* @__PURE__ */ e("span", { className: "size-1 rounded-full bg-white animate-bounce" })
43
- ] }), F = ({
44
- title: d,
45
- sourceUrl: l,
46
- thumbnailUrl: n,
47
- mimeType: r,
48
- paymentStatus: c
49
- }) => {
50
- const [a, o] = y(!1), f = l === void 0, s = O(r);
51
- return f ? /* @__PURE__ */ i("div", { className: "relative aspect-video overflow-hidden bg-black/5", children: [
52
- n != null ? /* @__PURE__ */ e(
53
- "img",
54
- {
55
- src: n,
56
- alt: "",
57
- className: "absolute inset-0 h-full w-full object-cover",
58
- draggable: !1
59
- }
60
- ) : /* @__PURE__ */ e("div", { className: "absolute inset-0 flex items-center justify-center", children: z(r, {
61
- className: "size-12 text-black/20",
62
- weight: "regular"
63
- }) }),
64
- f && /* @__PURE__ */ e("div", { className: "absolute inset-0 bg-black/30", children: /* @__PURE__ */ e("div", { className: "absolute left-3 top-3 flex size-8 items-center justify-center rounded-full bg-black/60 text-white", children: c === "paid" ? /* @__PURE__ */ e(U, {}) : /* @__PURE__ */ e(k, {}) }) })
65
- ] }) : s === "audio" || s === "video" ? /* @__PURE__ */ e(
66
- P,
67
- {
68
- source: l,
69
- poster: n,
70
- mimeType: r
71
- }
72
- ) : /* @__PURE__ */ e("div", { className: "relative overflow-hidden bg-black/5", children: /* @__PURE__ */ e(
73
- "img",
74
- {
75
- src: s === "document" ? n : l,
76
- alt: d,
77
- className: `block w-full transition-opacity duration-300 ${a ? "opacity-100" : "opacity-0"}`,
78
- draggable: !1,
79
- onLoad: () => o(!0)
80
- }
81
- ) });
82
- }, q = ({
83
- title: d,
84
- amountText: l,
85
- thumbnailUrl: n,
86
- mimeType: r = "application/octet-stream",
87
- detail: c,
88
- onUnlockClick: a,
89
- onFetchSource: o,
90
- onDownloadClick: f,
91
- paymentStatus: s,
92
- isUnlocking: C = !1
93
- }) => {
94
- const [t, _] = y(), g = x(null), p = x(!1), m = x(o);
95
- m.current = o;
96
- const v = t == null ? void 0 : t.sourceUrl, j = (t == null ? void 0 : t.thumbnailUrl) ?? n, I = t == null ? void 0 : t.redeemUrl, h = N(async () => {
97
- var u;
98
- if (!p.current) {
99
- p.current = !0;
100
- try {
101
- const b = await ((u = m.current) == null ? void 0 : u.call(m));
102
- b && _(b);
103
- } finally {
104
- p.current = !1;
105
- }
106
- }
107
- }, []), L = N(() => {
108
- s === "paid" ? h() : a == null || a();
109
- }, [s, h, a]);
110
- return M(() => {
111
- if (!g.current || s !== "paid" || t !== void 0) return;
112
- const u = new IntersectionObserver(
113
- ([b]) => {
114
- b.isIntersecting && (h(), u.disconnect());
115
- },
116
- { threshold: 1 }
117
- );
118
- return u.observe(g.current), () => u.disconnect();
119
- }, [s, t, h]), /* @__PURE__ */ i(
120
- "div",
121
- {
122
- ref: g,
123
- "data-testid": "locked-attachment",
124
- className: "w-[280px] select-none overflow-hidden rounded-[24px] bg-white shadow-[0_0_0_1px_rgba(0,0,0,0.04),0_4px_8px_rgba(0,0,0,0.06)]",
125
- children: [
126
- /* @__PURE__ */ e(
127
- F,
128
- {
129
- title: d,
130
- sourceUrl: v,
131
- thumbnailUrl: j,
132
- mimeType: r,
133
- paymentStatus: s
134
- }
135
- ),
136
- /* @__PURE__ */ i("div", { className: "px-4 pb-3 pt-3 bg-black", children: [
137
- /* @__PURE__ */ e("p", { className: "mb-1.5 truncate text-base font-medium text-white", children: d }),
138
- /* @__PURE__ */ i("div", { className: "flex items-center gap-1", children: [
139
- z(r, {
140
- className: "size-5 shrink-0 text-white/55",
141
- weight: "regular"
142
- }),
143
- c && /* @__PURE__ */ e("span", { className: "text-xs font-medium text-white/55", children: c }),
144
- s === "paid" ? /* @__PURE__ */ i(w.Fragment, { children: [
145
- /* @__PURE__ */ e("span", { className: "text-xs font-medium text-white/55", children: "•" }),
146
- /* @__PURE__ */ e("span", { className: "text-xs font-medium text-[#4ade80]", children: "Purchased" }),
147
- /* @__PURE__ */ e(
148
- D,
149
- {
150
- className: "size-4 text-[#4ade80]",
151
- weight: "bold"
152
- }
153
- )
154
- ] }) : l != null ? /* @__PURE__ */ i(w.Fragment, { children: [
155
- /* @__PURE__ */ e("span", { className: "text-xs font-medium text-white/55", children: "•" }),
156
- /* @__PURE__ */ e("span", { className: "text-xs font-medium text-white/55", children: l })
157
- ] }) : null
158
- ] }),
159
- /* @__PURE__ */ e(
160
- A,
161
- {
162
- isUnlocking: C,
163
- sourceUrl: v,
164
- redeemUrl: I,
165
- onUnlockClicked: L,
166
- onDownloadClicked: f
167
- }
168
- )
169
- ] })
170
- ]
171
- }
172
- );
173
- };
174
- export {
175
- q as default
176
- };
177
- //# sourceMappingURL=Card-Bpud_enW.js.map