@linktr.ee/messaging-react 3.10.0 → 3.11.0-rc-1783756377

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 (88) hide show
  1. package/dist/Card-415OfRhw.cjs +2 -0
  2. package/dist/Card-415OfRhw.cjs.map +1 -0
  3. package/dist/Card-BnbNynLQ.js +694 -0
  4. package/dist/Card-BnbNynLQ.js.map +1 -0
  5. package/dist/Card-UnwKmYzt.cjs +2 -0
  6. package/dist/Card-UnwKmYzt.cjs.map +1 -0
  7. package/dist/Card-qS17qnN0.js +103 -0
  8. package/dist/Card-qS17qnN0.js.map +1 -0
  9. package/dist/LoadingDots-Ct3WmVlT.js +59 -0
  10. package/dist/LoadingDots-Ct3WmVlT.js.map +1 -0
  11. package/dist/LoadingDots-D9GGphgM.cjs +2 -0
  12. package/dist/LoadingDots-D9GGphgM.cjs.map +1 -0
  13. package/dist/assets/index.css +1 -1
  14. package/dist/index-CyHyftBi.cjs +2 -0
  15. package/dist/index-CyHyftBi.cjs.map +1 -0
  16. package/dist/{index-BxzzWK4K.js → index-DJXjS2H9.js} +1397 -1358
  17. package/dist/index-DJXjS2H9.js.map +1 -0
  18. package/dist/index.cjs +1 -1
  19. package/dist/index.d.ts +196 -101
  20. package/dist/index.js +1 -1
  21. package/dist/testing.d.ts +1 -0
  22. package/package.json +2 -2
  23. package/src/components/ChannelList/CustomChannelPreview.test.tsx +95 -0
  24. package/src/components/ChannelList/CustomChannelPreview.tsx +21 -9
  25. package/src/components/ChannelList/index.test.tsx +46 -0
  26. package/src/components/ChannelList/index.tsx +12 -0
  27. package/src/components/CustomMessage/CustomMessage.stories.tsx +54 -1
  28. package/src/components/CustomMessage/LockedAttachment/LockedAttachment.stories.tsx +541 -0
  29. package/src/components/CustomMessage/LockedAttachment/LockedAttachment.test.tsx +117 -0
  30. package/src/components/CustomMessage/LockedAttachment/components/Media/Card.tsx +358 -0
  31. package/src/components/{LockedAttachment/components/Received → CustomMessage/LockedAttachment/components/Media}/CardActions.tsx +9 -2
  32. package/src/components/CustomMessage/LockedAttachment/components/Media/types.ts +79 -0
  33. package/src/components/CustomMessage/LockedAttachment/components/Text/Card.tsx +153 -0
  34. package/src/components/CustomMessage/LockedAttachment/components/Text/types.ts +58 -0
  35. package/src/components/{LockedAttachment → CustomMessage/LockedAttachment}/components/_shared/CardBody.tsx +8 -7
  36. package/src/components/CustomMessage/LockedAttachment/components/_shared/CardThumbnail.tsx +92 -0
  37. package/src/components/{LockedAttachment → CustomMessage/LockedAttachment}/components/_shared/GalleryThumbnail.tsx +31 -39
  38. package/src/components/CustomMessage/LockedAttachment/components/_shared/LoadingDots.tsx +36 -0
  39. package/src/components/CustomMessage/LockedAttachment/components/_shared/LockBadge.tsx +63 -0
  40. package/src/components/{LockedAttachment/components/_shared/LockedCardShell.tsx → CustomMessage/LockedAttachment/components/_shared/MediaShell.tsx} +8 -8
  41. package/src/components/CustomMessage/LockedAttachment/components/_shared/PreviewToggle.tsx +58 -0
  42. package/src/components/CustomMessage/LockedAttachment/components/_shared/PriceLockRow.tsx +21 -0
  43. package/src/components/CustomMessage/LockedAttachment/components/_shared/PurchaseStatusBadge.tsx +74 -0
  44. package/src/components/{LockedAttachment/components/_shared/LockedThumbnail.tsx → CustomMessage/LockedAttachment/components/_shared/SingleThumbnail.tsx} +11 -7
  45. package/src/components/CustomMessage/LockedAttachment/components/_shared/SkeletonTextLines.tsx +29 -0
  46. package/src/components/CustomMessage/LockedAttachment/components/_shared/TextShell.tsx +28 -0
  47. package/src/components/CustomMessage/LockedAttachment/components/_shared/usePreviewToggle.test.ts +67 -0
  48. package/src/components/CustomMessage/LockedAttachment/components/_shared/usePreviewToggle.ts +89 -0
  49. package/src/components/CustomMessage/LockedAttachment/components/_shared/useReceivedSource.ts +83 -0
  50. package/src/components/CustomMessage/LockedAttachment/index.tsx +105 -0
  51. package/src/components/CustomMessage/LockedAttachment/types.ts +44 -0
  52. package/src/components/CustomMessage/MessageTag.tsx +8 -0
  53. package/src/components/CustomMessage/context.tsx +1 -1
  54. package/src/components/CustomMessage/index.tsx +76 -42
  55. package/src/components/CustomMessageInput/CustomMessageInput.stories.tsx +5 -3
  56. package/src/index.ts +3 -8
  57. package/src/stream-custom-data.ts +1 -0
  58. package/src/styles.css +12 -0
  59. package/src/types.ts +35 -2
  60. package/dist/Card-B-L2MST4.js +0 -107
  61. package/dist/Card-B-L2MST4.js.map +0 -1
  62. package/dist/Card-B56QxJW3.cjs +0 -2
  63. package/dist/Card-B56QxJW3.cjs.map +0 -1
  64. package/dist/Card-Bqh-BQaX.js +0 -132
  65. package/dist/Card-Bqh-BQaX.js.map +0 -1
  66. package/dist/Card-ZCE6Lhfc.cjs +0 -2
  67. package/dist/Card-ZCE6Lhfc.cjs.map +0 -1
  68. package/dist/Card-g4JD-2ES.js +0 -163
  69. package/dist/Card-g4JD-2ES.js.map +0 -1
  70. package/dist/Card-wac6AMNc.cjs +0 -2
  71. package/dist/Card-wac6AMNc.cjs.map +0 -1
  72. package/dist/LockedThumbnail-BAQmoIff.js +0 -220
  73. package/dist/LockedThumbnail-BAQmoIff.js.map +0 -1
  74. package/dist/LockedThumbnail-Cr0Gqs3Y.cjs +0 -2
  75. package/dist/LockedThumbnail-Cr0Gqs3Y.cjs.map +0 -1
  76. package/dist/index-BPhZyaxT.cjs +0 -2
  77. package/dist/index-BPhZyaxT.cjs.map +0 -1
  78. package/dist/index-BxzzWK4K.js.map +0 -1
  79. package/src/components/LockedAttachment/LockedAttachment.stories.tsx +0 -450
  80. package/src/components/LockedAttachment/components/Composer/Card.tsx +0 -221
  81. package/src/components/LockedAttachment/components/Composer/index.ts +0 -2
  82. package/src/components/LockedAttachment/components/Received/Card.tsx +0 -191
  83. package/src/components/LockedAttachment/components/Received/index.ts +0 -2
  84. package/src/components/LockedAttachment/components/Sent/Card.tsx +0 -177
  85. package/src/components/LockedAttachment/components/Sent/index.ts +0 -2
  86. package/src/components/LockedAttachment/components/_shared/LockBadge.tsx +0 -39
  87. package/src/components/LockedAttachment/index.tsx +0 -63
  88. package/src/components/LockedAttachment/types.ts +0 -51
@@ -32,17 +32,17 @@ import {
32
32
  import { useMessageVote } from '../../hooks/useMessageVote'
33
33
  import { getMessageDisplayText } from '../../utils/getMessageDisplayText'
34
34
  import { Avatar } from '../Avatar'
35
- import LockedAttachment from '../LockedAttachment'
36
35
  import { isLinkAttachment } from '../MediaMessage'
37
- import {
38
- bubbleGroupPositionFromStream as messageAttachmentGroupPositionFromStream,
39
- } from '../MessageAttachment'
36
+ import { bubbleGroupPositionFromStream as messageAttachmentGroupPositionFromStream } from '../MessageAttachment'
40
37
 
41
38
  import { useCustomMessage } from './context'
39
+ import LockedAttachment from './LockedAttachment'
42
40
  import {
43
41
  MessageTag,
44
42
  isAttachmentMessage,
45
43
  isChatbotMessage,
44
+ isMediaAttachmentMessage,
45
+ isTextAttachmentMessage,
46
46
  isTipOnlyMessage,
47
47
  } from './MessageTag'
48
48
  import { MessageVoteButtons } from './MessageVoteButtons'
@@ -145,13 +145,12 @@ const CustomMessageWithContext = (props: CustomMessageWithContextProps) => {
145
145
  (total, segment) => total + segment.attachments.length,
146
146
  0
147
147
  ) === (finalAttachments?.length ?? 0)
148
- const streamAttachmentGroupPosition = messageAttachmentGroupPositionFromStream(
149
- {
148
+ const streamAttachmentGroupPosition =
149
+ messageAttachmentGroupPositionFromStream({
150
150
  endOfGroup,
151
151
  firstOfGroup,
152
152
  groupedByUser,
153
- }
154
- )
153
+ })
155
154
 
156
155
  if (isDateSeparatorMessage(message)) {
157
156
  return null
@@ -226,6 +225,26 @@ const CustomMessageWithContext = (props: CustomMessageWithContextProps) => {
226
225
  !isAIGenerated &&
227
226
  !isChatbot
228
227
 
228
+ const sharedLockedTextProps = {
229
+ contentType: 'text' as const,
230
+ amountText: message.metadata?.amount_text,
231
+ paymentStatus: message.metadata?.payment_status,
232
+ renderedText: message.text && (
233
+ <MessageText message={displayMessage} renderText={renderText} />
234
+ ),
235
+ }
236
+ const sharedLockedMediaProps = {
237
+ contentType: 'media' as const,
238
+ title: message.metadata?.attachment_title,
239
+ mimeType: message.metadata?.attachment_mime_type,
240
+ thumbnailUrl: message.metadata?.attachment_thumbnail,
241
+ amountText: message.metadata?.amount_text,
242
+ detail: message.metadata?.attachment_detail,
243
+ paymentStatus: message.metadata?.payment_status,
244
+ onFetchSource: onFetchSource
245
+ ? () => onFetchSource(message, channel)
246
+ : undefined,
247
+ }
229
248
  return (
230
249
  <>
231
250
  {editing && (
@@ -276,43 +295,61 @@ const CustomMessageWithContext = (props: CustomMessageWithContextProps) => {
276
295
  marginInlineStart: 0,
277
296
  }}
278
297
  >
279
- {isAttachment ? (
298
+ {isTipOnly ? (
299
+ <MessageTag message={message} standalone />
300
+ ) : isTextAttachmentMessage(message) ? (
280
301
  <div className="str-chat__message-bubble-wrapper">
281
- {isMine ? (
282
- <div className="flex items-center gap-2">
283
- {MessageActions && <MessageActions />}
284
- <LockedAttachment.Sent
285
- title={message.metadata?.attachment_title}
286
- mimeType={message.metadata?.attachment_mime_type}
287
- thumbnailUrl={message.metadata?.attachment_thumbnail}
288
- amountText={message.metadata?.amount_text}
289
- detail={message.metadata?.attachment_detail}
290
- paymentStatus={message.metadata?.payment_status}
291
- onPreviewClick={() => onUnlockClick?.(message, channel)}
292
- onFetchSource={async () =>
293
- await onFetchSource?.(message, channel)
294
- }
302
+ <div className="flex items-center gap-2">
303
+ {isMine && MessageActions && <MessageActions />}
304
+ {isMine ? (
305
+ <LockedAttachment
306
+ {...sharedLockedTextProps}
307
+ isMine={true}
295
308
  />
296
- </div>
297
- ) : (
298
- <div className="flex items-center gap-2">
299
- <LockedAttachment.Received
300
- title={message.metadata?.attachment_title}
301
- mimeType={message.metadata?.attachment_mime_type}
302
- thumbnailUrl={message.metadata?.attachment_thumbnail}
303
- amountText={message.metadata?.amount_text}
304
- detail={message.metadata?.attachment_detail}
305
- paymentStatus={message.metadata?.payment_status}
309
+ ) : (
310
+ <LockedAttachment
311
+ {...sharedLockedTextProps}
312
+ isMine={false}
306
313
  isUnlocking={isUnlocking(message.id)}
307
314
  onUnlockClick={() => onUnlockClick?.(message, channel)}
308
- onFetchSource={async () =>
309
- await onFetchSource?.(message, channel)
315
+ />
316
+ )}
317
+ {!isMine && MessageActions && <MessageActions />}
318
+ </div>
319
+ </div>
320
+ ) : isMediaAttachmentMessage(message) ? (
321
+ <div className="str-chat__message-bubble-wrapper">
322
+ <div className="flex items-center gap-2">
323
+ {isMine && MessageActions && <MessageActions />}
324
+ {isMine ? (
325
+ <LockedAttachment
326
+ {...sharedLockedMediaProps}
327
+ isMine={true}
328
+ onPreviewClick={() =>
329
+ onUnlockClick
330
+ ? onUnlockClick(message, channel)
331
+ : undefined
310
332
  }
311
- onDownloadClick={() => onDownloadClick?.(message, channel)}
312
333
  />
313
- {MessageActions && <MessageActions />}
314
- </div>
315
- )}
334
+ ) : (
335
+ <LockedAttachment
336
+ {...sharedLockedMediaProps}
337
+ isMine={false}
338
+ isUnlocking={isUnlocking(message.id)}
339
+ onUnlockClick={() =>
340
+ onUnlockClick
341
+ ? onUnlockClick(message, channel)
342
+ : undefined
343
+ }
344
+ onDownloadClick={() =>
345
+ onDownloadClick
346
+ ? onDownloadClick(message, channel)
347
+ : undefined
348
+ }
349
+ />
350
+ )}
351
+ {!isMine && MessageActions && <MessageActions />}
352
+ </div>
316
353
  {message.text && (
317
354
  <div className="str-chat__message-bubble">
318
355
  <MessageText
@@ -322,9 +359,6 @@ const CustomMessageWithContext = (props: CustomMessageWithContextProps) => {
322
359
  </div>
323
360
  )}
324
361
  </div>
325
- ) : isTipOnly ? (
326
- /* Tip-only messages render as a standalone bubble */
327
- <MessageTag message={message} standalone />
328
362
  ) : canRenderAttachmentsInToolkit ? (
329
363
  <StreamAttachmentMessage
330
364
  groupPosition={streamAttachmentGroupPosition}
@@ -5,7 +5,7 @@ import { QueryChannelAPIResponse, StreamChat } from 'stream-chat'
5
5
  import { Channel, Chat, type SendButtonProps } from 'stream-chat-react'
6
6
 
7
7
  import { createMockStreamChatClient } from '../../testing/createMockStreamChatClient'
8
- import LockedAttachment from '../LockedAttachment'
8
+ import LockedAttachment from '../CustomMessage/LockedAttachment'
9
9
 
10
10
  import { CustomMessageInput } from '.'
11
11
 
@@ -229,7 +229,9 @@ WithAttachments.parameters = {
229
229
  export const WithPaidAttachment: StoryFn<WrapperProps> = (args) => <Wrapper {...args} />
230
230
  WithPaidAttachment.args = {
231
231
  attachmentPreviewList: () => (
232
- <LockedAttachment.Composer
232
+ <LockedAttachment
233
+ isPreview
234
+ contentType="media"
233
235
  title="Exclusive Content"
234
236
  mimeType="image/jpeg"
235
237
  thumbnailUrl="https://picsum.photos/seed/paid/320/180"
@@ -266,7 +268,7 @@ WithPaidAttachment.parameters = {
266
268
  docs: {
267
269
  description: {
268
270
  story:
269
- "Message input with a paid attachment staged inside the input bubble. Uses `Channel`'s `AttachmentPreviewList` override to render a `LockedAttachment.Composer` card at the top of the input. `onDismiss` removes it; `onEditClick` reopens the paid content wizard.",
271
+ "Message input with a paid attachment staged inside the input bubble. Uses `Channel`'s `AttachmentPreviewList` override to render a `LockedAttachment isPreview` card at the top of the input. `onDismiss` removes it; `onEditClick` reopens the paid content wizard.",
270
272
  },
271
273
  },
272
274
  }
package/src/index.ts CHANGED
@@ -6,7 +6,7 @@ export { MessagingShell } from './components/MessagingShell'
6
6
  export { ChannelList } from './components/ChannelList'
7
7
  export { ChannelView } from './components/ChannelView'
8
8
  export { default as ActionButton } from './components/ActionButton'
9
- export { default as LockedAttachment } from './components/LockedAttachment'
9
+ export { default as LockedAttachment } from './components/CustomMessage/LockedAttachment'
10
10
  export { default as LinkAttachment } from './components/LinkAttachment'
11
11
  export { default as MessageAttachment } from './components/MessageAttachment'
12
12
  export { Avatar } from './components/Avatar'
@@ -76,15 +76,10 @@ export type {
76
76
  } from './components/MessageBubble'
77
77
  export type { ActionButtonProps } from './components/ActionButton'
78
78
  export type {
79
- ComposerCardProps,
80
- SentCardProps,
81
- ReceivedCardProps,
79
+ LockedAttachmentProps,
82
80
  LockedAttachmentGalleryItem,
83
81
  LockedAttachmentContextValue,
84
- // Deprecated aliases preserved for backwards compatibility:
85
- CreatorCardProps,
86
- VisitorCardProps,
87
- } from './components/LockedAttachment'
82
+ } from './components/CustomMessage/LockedAttachment'
88
83
  export type {
89
84
  ComposerCardProps as LinkAttachmentComposerCardProps,
90
85
  SentCardProps as LinkAttachmentSentCardProps,
@@ -41,6 +41,7 @@ export interface MessageMetadata {
41
41
  amount_text?: string
42
42
  payment_status?: PaymentStatus
43
43
  attachment_title?: string
44
+ attachment_content_type?: 'text' | 'media'
44
45
  attachment_mime_type?: string
45
46
  attachment_thumbnail?: string
46
47
  attachment_detail?: string
package/src/styles.css CHANGED
@@ -831,3 +831,15 @@
831
831
  color: #016e1a;
832
832
  background-color: #dbf0e0;
833
833
  }
834
+
835
+ /** Remove padding from the text inside the locked attachment text shell,
836
+ and inherit TextShell's explicit colour — stream-chat-react's own
837
+ `MessageText` sets its own (black) colour that only gets overridden by
838
+ `.str-chat`'s CSS vars, which the Composer preview doesn't have. */
839
+ .message-locked-attachment-text .str-chat__message-text {
840
+ padding: 0rem !important;
841
+ }
842
+
843
+ .message-locked-attachment-text .str-chat__message-text-inner {
844
+ color: inherit;
845
+ }
package/src/types.ts CHANGED
@@ -2,12 +2,13 @@ import type {
2
2
  MessagingUser,
3
3
  StreamChatServiceConfig,
4
4
  } from '@linktr.ee/messaging-core'
5
- import type { ComponentType } from 'react'
5
+ import type { ComponentType, Dispatch, SetStateAction } from 'react'
6
6
  import type {
7
7
  Channel,
8
8
  ChannelFilters,
9
9
  ChannelMemberResponse,
10
10
  ChannelSort,
11
+ Event,
11
12
  LocalMessage,
12
13
  SendMessageAPIResponse,
13
14
  StreamChat,
@@ -20,7 +21,7 @@ import type {
20
21
 
21
22
  import type { MessageMetadata } from './stream-custom-data'
22
23
 
23
- export type { LockedAttachmentSource } from './components/LockedAttachment'
24
+ export type { LockedAttachmentSource } from './components/CustomMessage/LockedAttachment'
24
25
 
25
26
  /**
26
27
  * Callback invoked when a user clicks a link inside the channel view.
@@ -122,8 +123,40 @@ export interface ChannelListProps {
122
123
  * Falls back to message.text when no matching translation exists.
123
124
  */
124
125
  viewerLanguage?: string
126
+ /**
127
+ * Lock the visible channel order so live events don't promote a channel to
128
+ * the top. Forwarded to Stream's `<ChannelList>`. Note `lockChannelOrder`
129
+ * alone doesn't cover `notification.message_new` or `channel.visible`; pair it
130
+ * with the event handlers below to fully neutralize promotion.
131
+ */
132
+ lockChannelOrder?: boolean
133
+ /**
134
+ * Live channel-list event handlers forwarded verbatim to Stream's
135
+ * `<ChannelList>`. Left undefined, Stream applies its default "move the
136
+ * changed channel to the top" behaviour.
137
+ *
138
+ * Hosts that order by a custom channel field (`last_conversation_at` for the
139
+ * inbox, `priority_score` for Spotlight) use these to neutralize promotion on
140
+ * new-message / visibility events and re-sort on `channel.updated` instead.
141
+ * Each receives Stream's `setChannels` dispatcher and the triggering event.
142
+ * See MES-1214 / MES-1219.
143
+ */
144
+ onMessageNewHandler?: ChannelListReorderHandler
145
+ onMessageNew?: ChannelListReorderHandler
146
+ onChannelVisible?: ChannelListReorderHandler
147
+ onAddedToChannel?: ChannelListReorderHandler
148
+ onChannelUpdated?: ChannelListReorderHandler
125
149
  }
126
150
 
151
+ /**
152
+ * Signature of Stream's `<ChannelList>` live-event handlers: mutate the loaded
153
+ * channel array via `setChannels` in response to a websocket `event`.
154
+ */
155
+ export type ChannelListReorderHandler = (
156
+ setChannels: Dispatch<SetStateAction<Channel[]>>,
157
+ event: Event
158
+ ) => void
159
+
127
160
  /**
128
161
  * ChannelView component props
129
162
  */
@@ -1,107 +0,0 @@
1
- import { jsxs as x, jsx as e } from "react/jsx-runtime";
2
- import { CheckCircleIcon as T, ImagesIcon as j } from "@phosphor-icons/react";
3
- import k, { useState as b, useRef as z, useCallback as C } from "react";
4
- import { L as D, C as V, G as A, a as E } from "./LockedThumbnail-BAQmoIff.js";
5
- const M = ({
6
- title: o,
7
- mimeType: p = "application/octet-stream",
8
- thumbnailUrl: N,
9
- detail: I,
10
- amountText: g,
11
- placeholderTitle: L = "Attachment title",
12
- paymentStatus: c,
13
- gallery: i,
14
- onPreviewClick: a,
15
- onFetchSource: n
16
- }) => {
17
- const [d, y] = b(), [u, l] = b(!1), [B, v] = b(!1), f = z(!1), r = ((i == null ? void 0 : i.length) ?? 0) >= 2, G = C(async () => {
18
- if (a == null || a(), u) {
19
- l(!1);
20
- return;
21
- }
22
- if (r) {
23
- l(!0);
24
- return;
25
- }
26
- if (d) {
27
- l(!0);
28
- return;
29
- }
30
- if (n && !f.current) {
31
- f.current = !0, v(!0);
32
- try {
33
- const t = await n();
34
- t && (y(t), l(!0));
35
- } finally {
36
- f.current = !1, v(!1);
37
- }
38
- }
39
- }, [u, r, d, a, n]), m = r || n || a ? G : void 0, h = !u, s = B, R = c === "paid" ? /* @__PURE__ */ x(k.Fragment, { children: [
40
- /* @__PURE__ */ e("span", { className: "text-xs font-medium text-white/55", children: "•" }),
41
- /* @__PURE__ */ e("span", { className: "text-xs font-medium text-[#34c759]", children: "Sold" }),
42
- /* @__PURE__ */ e(T, { className: "size-4 text-[#34c759]", weight: "bold" })
43
- ] }) : g ? /* @__PURE__ */ x(k.Fragment, { children: [
44
- /* @__PURE__ */ e("span", { className: "text-xs font-medium text-white/55", children: "•" }),
45
- /* @__PURE__ */ e("span", { className: "text-xs font-medium text-white/55", children: g })
46
- ] }) : null, w = r ? /* @__PURE__ */ e(
47
- A,
48
- {
49
- variant: "dark",
50
- gallery: i,
51
- title: o,
52
- showLocked: h,
53
- paymentStatus: c
54
- }
55
- ) : /* @__PURE__ */ e(
56
- E,
57
- {
58
- variant: "dark",
59
- mimeType: p,
60
- thumbnailUrl: N,
61
- title: o,
62
- source: d,
63
- showLocked: h,
64
- paymentStatus: c
65
- }
66
- );
67
- return /* @__PURE__ */ x(D, { variant: "dark", children: [
68
- m ? (
69
- // Uses a `<div role="button">` rather than a native `<button>` so the
70
- // gallery's carousel arrow `<button>`s rendered inside `thumbnail`
71
- // don't produce invalid nested interactive elements.
72
- /* @__PURE__ */ e(
73
- "div",
74
- {
75
- role: "button",
76
- tabIndex: s ? -1 : 0,
77
- "aria-label": "Toggle preview",
78
- "aria-busy": s,
79
- "aria-pressed": !h,
80
- "aria-disabled": s || void 0,
81
- onClick: s ? void 0 : m,
82
- onKeyDown: (t) => {
83
- s || t.target === t.currentTarget && (t.key === "Enter" || t.key === " ") && (t.preventDefault(), m());
84
- },
85
- className: s ? "block w-full text-left" : "block w-full cursor-pointer text-left",
86
- children: w
87
- }
88
- )
89
- ) : w,
90
- /* @__PURE__ */ e(
91
- V,
92
- {
93
- variant: "dark",
94
- title: o,
95
- placeholderTitle: L,
96
- mimeType: p,
97
- detail: I,
98
- statusBadge: R,
99
- icon: r ? /* @__PURE__ */ e(j, { className: "size-5 shrink-0 text-white/55" }) : void 0
100
- }
101
- )
102
- ] });
103
- };
104
- export {
105
- M as default
106
- };
107
- //# sourceMappingURL=Card-B-L2MST4.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"Card-B-L2MST4.js","sources":["../src/components/LockedAttachment/components/Sent/Card.tsx"],"sourcesContent":["import { CheckCircleIcon, ImagesIcon } 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 SentCardProps extends LockedAttachmentBaseProps {\n /** Placeholder shown in the title slot when no title is set. */\n placeholderTitle?: string\n /** Fired the first time the sender taps the thumbnail to preview their own attachment. */\n onPreviewClick?: () => void\n /**\n * Lazily loads the underlying source so the sender can preview the attachment.\n * Called the first time the thumbnail is tapped; the returned source is cached\n * and reused on subsequent toggles.\n */\n onFetchSource?: () => Promise<LockedAttachmentSource | void>\n}\n\n/**\n * The card the sender sees in chat after a paid attachment has been posted.\n * Matches the Sent column of the messaging design system in Figma.\n */\nconst SentCard: React.FC<SentCardProps> = ({\n title,\n mimeType = 'application/octet-stream',\n thumbnailUrl,\n detail,\n amountText,\n placeholderTitle = 'Attachment title',\n paymentStatus,\n gallery,\n onPreviewClick,\n onFetchSource,\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 for the sender 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 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-[#34c759]\">Sold</span>\n <CheckCircleIcon className=\"size-4 text-[#34c759]\" weight=\"bold\" />\n </React.Fragment>\n ) : amountText ? (\n <React.Fragment>\n <span className=\"text-xs font-medium text-white/55\">&bull;</span>\n <span className=\"text-xs font-medium text-white/55\">{amountText}</span>\n </React.Fragment>\n ) : null\n\n const thumbnail = isGallery ? (\n <GalleryThumbnail\n variant=\"dark\"\n gallery={gallery!}\n title={title}\n showLocked={showLocked}\n paymentStatus={paymentStatus}\n />\n ) : (\n <LockedThumbnail\n variant=\"dark\"\n mimeType={mimeType}\n thumbnailUrl={thumbnailUrl}\n title={title}\n source={source}\n showLocked={showLocked}\n paymentStatus={paymentStatus}\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 // gallery's carousel arrow `<button>`s rendered inside `thumbnail`\n // don'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 (gallery 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 />\n </LockedCardShell>\n )\n}\n\nexport default SentCard\n"],"names":["SentCard","title","mimeType","thumbnailUrl","detail","amountText","placeholderTitle","paymentStatus","gallery","onPreviewClick","onFetchSource","source","setSource","useState","isPreviewVisible","setIsPreviewVisible","isLoadingPreview","setIsLoadingPreview","fetchingRef","useRef","isGallery","handleToggle","useCallback","result","togglePreview","showLocked","isBusy","statusBadge","jsxs","React","jsx","CheckCircleIcon","thumbnail","GalleryThumbnail","LockedThumbnail","LockedCardShell","e","CardBody","ImagesIcon"],"mappings":";;;;AA6BA,MAAMA,IAAoC,CAAC;AAAA,EACzC,OAAAC;AAAA,EACA,UAAAC,IAAW;AAAA,EACX,cAAAC;AAAA,EACA,QAAAC;AAAA,EACA,YAAAC;AAAA,EACA,kBAAAC,IAAmB;AAAA,EACnB,eAAAC;AAAA,EACA,SAAAC;AAAA,EACA,gBAAAC;AAAA,EACA,eAAAC;AACF,MAAM;AACJ,QAAM,CAACC,GAAQC,CAAS,IAAIC,EAAA,GACtB,CAACC,GAAkBC,CAAmB,IAAIF,EAAS,EAAK,GACxD,CAACG,GAAkBC,CAAmB,IAAIJ,EAAS,EAAK,GACxDK,IAAcC,EAAO,EAAK,GAE1BC,MAAaZ,KAAA,gBAAAA,EAAS,WAAU,MAAM,GAEtCa,IAAeC,EAAY,YAAY;AAE3C,QADAb,KAAA,QAAAA,KACIK,GAAkB;AACpB,MAAAC,EAAoB,EAAK;AACzB;AAAA,IACF;AAGA,QAAIK,GAAW;AACb,MAAAL,EAAoB,EAAI;AACxB;AAAA,IACF;AACA,QAAIJ,GAAQ;AACV,MAAAI,EAAoB,EAAI;AACxB;AAAA,IACF;AACA,QAAKL,KACD,CAAAQ,EAAY,SAChB;AAAA,MAAAA,EAAY,UAAU,IACtBD,EAAoB,EAAI;AACxB,UAAI;AACF,cAAMM,IAAS,MAAMb,EAAA;AACrB,QAAIa,MACFX,EAAUW,CAAM,GAChBR,EAAoB,EAAI;AAAA,MAE5B,UAAA;AACE,QAAAG,EAAY,UAAU,IACtBD,EAAoB,EAAK;AAAA,MAC3B;AAAA;AAAA,EACF,GAAG,CAACH,GAAkBM,GAAWT,GAAQF,GAAgBC,CAAa,CAAC,GAIjEc,IACJJ,KAAaV,KAAiBD,IAAiBY,IAAe,QAC1DI,IAAa,CAACX,GACdY,IAASV,GAETW,IACJpB,MAAkB,SAChB,gBAAAqB,EAACC,EAAM,UAAN,EACC,UAAA;AAAA,IAAA,gBAAAC,EAAC,QAAA,EAAK,WAAU,qCAAoC,UAAA,KAAM;AAAA,IAC1D,gBAAAA,EAAC,QAAA,EAAK,WAAU,sCAAqC,UAAA,QAAI;AAAA,IACzD,gBAAAA,EAACC,GAAA,EAAgB,WAAU,yBAAwB,QAAO,OAAA,CAAO;AAAA,EAAA,EAAA,CACnE,IACE1B,IACF,gBAAAuB,EAACC,EAAM,UAAN,EACC,UAAA;AAAA,IAAA,gBAAAC,EAAC,QAAA,EAAK,WAAU,qCAAoC,UAAA,KAAM;AAAA,IAC1D,gBAAAA,EAAC,QAAA,EAAK,WAAU,qCAAqC,UAAAzB,EAAA,CAAW;AAAA,EAAA,EAAA,CAClE,IACE,MAEA2B,IAAYZ,IAChB,gBAAAU;AAAA,IAACG;AAAA,IAAA;AAAA,MACC,SAAQ;AAAA,MACR,SAAAzB;AAAA,MACA,OAAAP;AAAA,MACA,YAAAwB;AAAA,MACA,eAAAlB;AAAA,IAAA;AAAA,EAAA,IAGF,gBAAAuB;AAAA,IAACI;AAAA,IAAA;AAAA,MACC,SAAQ;AAAA,MACR,UAAAhC;AAAA,MACA,cAAAC;AAAA,MACA,OAAAF;AAAA,MACA,QAAAU;AAAA,MACA,YAAAc;AAAA,MACA,eAAAlB;AAAA,IAAA;AAAA,EAAA;AAIJ,SACE,gBAAAqB,EAACO,GAAA,EAAgB,SAAQ,QACtB,UAAA;AAAA,IAAAX;AAAA;AAAA;AAAA;AAAA,MAIC,gBAAAM;AAAA,QAAC;AAAA,QAAA;AAAA,UACC,MAAK;AAAA,UACL,UAAUJ,IAAS,KAAK;AAAA,UACxB,cAAW;AAAA,UACX,aAAWA;AAAA,UACX,gBAAc,CAACD;AAAA,UACf,iBAAeC,KAAU;AAAA,UACzB,SAASA,IAAS,SAAYF;AAAA,UAC9B,WAAW,CAACY,MAAM;AAChB,YAAIV,KAKAU,EAAE,WAAWA,EAAE,kBACfA,EAAE,QAAQ,WAAWA,EAAE,QAAQ,SACjCA,EAAE,eAAA,GACGZ,EAAA;AAAA,UAET;AAAA,UACA,WACGE,IAEG,2BADA;AAAA,UAIL,UAAAM;AAAA,QAAA;AAAA,MAAA;AAAA,QAGHA;AAAA,IAGF,gBAAAF;AAAA,MAACO;AAAA,MAAA;AAAA,QACC,SAAQ;AAAA,QACR,OAAApC;AAAA,QACA,kBAAAK;AAAA,QACA,UAAAJ;AAAA,QACA,QAAAE;AAAA,QACA,aAAAuB;AAAA,QACA,MACEP,IACE,gBAAAU,EAACQ,GAAA,EAAW,WAAU,iCAAgC,IACpD;AAAA,MAAA;AAAA,IAAA;AAAA,EAER,GACF;AAEJ;"}
@@ -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-BPhZyaxT.cjs"),j=require("./LockedThumbnail-Cr0Gqs3Y.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-B56QxJW3.cjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"Card-B56QxJW3.cjs","sources":["../src/components/LockedAttachment/components/Received/CardActions.tsx","../src/components/LockedAttachment/components/Received/Card.tsx"],"sourcesContent":["import { DownloadSimpleIcon, LockSimpleIcon } from '@phosphor-icons/react'\nimport React from 'react'\n\nexport interface ReceivedCardActionsProps {\n /** Whether the card is currently locked. Drives which CTA we render. */\n isLocked: boolean\n /**\n * When unlocked, the URL used as the Download link's `href` (opens in a new\n * tab). When omitted, the Download CTA falls back to a plain `<button>`\n * that just calls `onDownloadClicked` — useful for galleries where the\n * single-source URL doesn't exist.\n */\n sourceUrl?: string\n /** Optional alternate href that takes precedence over `sourceUrl`. */\n redeemUrl?: string\n onUnlockClicked?: () => void\n onDownloadClicked?: () => void\n isUnlocking?: boolean\n}\n\nconst buttonClass =\n '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'\n\n/**\n * Renders the primary CTA below the body of a Received card.\n * - Locked + onUnlockClicked → \"Unlock\" button\n * - Unlocked + onDownloadClicked + (redeemUrl || sourceUrl) → \"Download\" link\n * - Unlocked + onDownloadClicked + no URL → \"Download\" button (e.g. gallery)\n * Otherwise renders nothing.\n */\nconst ReceivedCardActions: React.FC<ReceivedCardActionsProps> = ({\n isLocked,\n sourceUrl,\n redeemUrl,\n onUnlockClicked,\n onDownloadClicked,\n isUnlocking = false,\n}) => {\n if (isLocked && onUnlockClicked != null) {\n return (\n <button\n type=\"button\"\n onClick={onUnlockClicked}\n disabled={isUnlocking}\n className={buttonClass}\n >\n {isUnlocking ? (\n <span className=\"size-4 animate-spin rounded-full border-2 border-white/30 border-t-white\" />\n ) : (\n <React.Fragment>\n <LockSimpleIcon className=\"size-4\" weight=\"fill\" />\n Unlock\n </React.Fragment>\n )}\n </button>\n )\n }\n\n if (!isLocked && onDownloadClicked != null) {\n const href = redeemUrl ?? sourceUrl\n if (href != null) {\n return (\n <a\n href={href}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n onClick={onDownloadClicked}\n className={`${buttonClass} !text-white`}\n >\n <DownloadSimpleIcon className=\"size-4\" weight=\"bold\" />\n Download\n </a>\n )\n }\n\n return (\n <button\n type=\"button\"\n onClick={onDownloadClicked}\n className={buttonClass}\n >\n <DownloadSimpleIcon className=\"size-4\" weight=\"bold\" />\n Download\n </button>\n )\n }\n\n return null\n}\n\nexport default ReceivedCardActions\n","import { CheckCircleIcon, ImagesIcon } from '@phosphor-icons/react'\nimport React, { useCallback, useEffect, useRef, useState } from 'react'\n\nimport { getSourceType } from '../../../AttachmentCard'\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\nimport ReceivedCardActions from './CardActions'\n\nexport interface ReceivedCardProps extends LockedAttachmentBaseProps {\n /**\n * Called when the recipient clicks Unlock on an unpaid attachment.\n * Use this to open a checkout flow. Omit to hide the Unlock button.\n */\n onUnlockClick?: () => void\n /**\n * Called to fetch the attachment source — fired automatically when\n * `paymentStatus` transitions to `'paid'`, or immediately on click when\n * `paymentStatus` is already `'paid'`. Return a `LockedAttachmentSource`\n * to unlock the card.\n */\n onFetchSource?: () => Promise<LockedAttachmentSource | void>\n /**\n * Called when the recipient clicks Download on an unlocked card.\n * Omit to hide the Download button.\n */\n onDownloadClick?: () => void\n /**\n * When true, shows a loading spinner on the Unlock button.\n * Driven by the LockedAttachmentContext (e.g. checkout in progress).\n */\n isUnlocking?: boolean\n}\n\n/**\n * The card the recipient sees in chat for a paid attachment.\n * Matches the Received column of the messaging design system in Figma:\n * locked → blurred thumbnail + Unlock CTA, unlocked → clear image + Download CTA.\n */\nconst ReceivedCard: React.FC<ReceivedCardProps> = ({\n title,\n amountText,\n thumbnailUrl,\n mimeType = 'application/octet-stream',\n detail,\n gallery,\n onUnlockClick,\n onFetchSource,\n onDownloadClick,\n paymentStatus,\n isUnlocking = false,\n}) => {\n const isGallery = (gallery?.length ?? 0) >= 2\n const [source, setSource] = useState<LockedAttachmentSource | undefined>()\n\n const cardRef = useRef<HTMLDivElement>(null)\n const fetchingRef = useRef(false)\n\n const onFetchSourceRef = useRef(onFetchSource)\n onFetchSourceRef.current = onFetchSource\n\n const effectiveSourceUrl = source?.sourceUrl\n const effectiveRedeemUrl = source?.redeemUrl\n\n const fetchSource = useCallback(async (): Promise<void> => {\n if (fetchingRef.current) return\n fetchingRef.current = true\n try {\n const result = await onFetchSourceRef.current?.()\n if (result) setSource(result)\n } finally {\n fetchingRef.current = false\n }\n }, [])\n\n const handleUnlockClick = useCallback(() => {\n if (paymentStatus === 'paid') {\n void fetchSource()\n } else {\n onUnlockClick?.()\n }\n }, [paymentStatus, fetchSource, onUnlockClick])\n\n // Auto-fetch the source once the paid card scrolls into view.\n useEffect(() => {\n if (!cardRef.current) return\n if (paymentStatus !== 'paid' || source !== undefined) return\n\n const observer = new IntersectionObserver(\n ([entry]) => {\n if (entry.isIntersecting) {\n void fetchSource()\n observer.disconnect()\n }\n },\n { threshold: 1.0 }\n )\n\n observer.observe(cardRef.current)\n return () => observer.disconnect()\n }, [paymentStatus, source, fetchSource])\n\n // For gallery, the per-item sources are carried by `gallery` itself, so\n // the lock state is driven by paymentStatus rather than the single\n // `effectiveSourceUrl`. Everything else still respects the source fetch.\n const isLocked = isGallery\n ? paymentStatus !== 'paid'\n : effectiveSourceUrl === undefined\n const sourceType = getSourceType(mimeType)\n\n const statusBadge =\n paymentStatus === 'paid' ? (\n <React.Fragment>\n <span className=\"text-xs font-medium text-black/55\">&bull;</span>\n <span className=\"text-xs font-medium text-[#008236]\">Purchased</span>\n <CheckCircleIcon className=\"size-4 text-[#008236]\" weight=\"bold\" />\n </React.Fragment>\n ) : amountText != null ? (\n <React.Fragment>\n <span className=\"text-xs font-medium text-black/55\">&bull;</span>\n <span className=\"text-xs font-medium text-black/55\">{amountText}</span>\n </React.Fragment>\n ) : null\n\n // For gallery, ReceivedCardActions falls back to a plain <button>\n // (no anchor) because the gallery has no single sourceUrl to link to —\n // the per-item sources live on `gallery` itself.\n const actionSourceUrl = isGallery ? undefined : effectiveSourceUrl\n\n return (\n <LockedCardShell\n variant=\"light\"\n rootRef={cardRef}\n data-testid=\"locked-attachment\"\n >\n {isGallery ? (\n <GalleryThumbnail\n variant=\"light\"\n gallery={gallery!}\n title={title}\n showLocked={isLocked}\n paymentStatus={paymentStatus}\n />\n ) : (\n <LockedThumbnail\n variant=\"light\"\n mimeType={mimeType}\n thumbnailUrl={thumbnailUrl}\n title={title}\n source={source}\n showLocked={isLocked}\n paymentStatus={paymentStatus}\n containedImage={\n !isLocked && (sourceType === 'image' || sourceType === 'document')\n }\n />\n )}\n\n <CardBody\n variant=\"light\"\n title={title}\n mimeType={mimeType}\n detail={detail}\n statusBadge={statusBadge}\n icon={\n isGallery ? (\n <ImagesIcon className=\"size-5 shrink-0 text-black/55\" />\n ) : undefined\n }\n action={\n <ReceivedCardActions\n isLocked={isLocked}\n isUnlocking={isUnlocking}\n sourceUrl={actionSourceUrl}\n redeemUrl={effectiveRedeemUrl}\n onUnlockClicked={handleUnlockClick}\n onDownloadClicked={onDownloadClick}\n />\n }\n />\n </LockedCardShell>\n )\n}\n\nexport default ReceivedCard\n"],"names":["buttonClass","ReceivedCardActions","isLocked","sourceUrl","redeemUrl","onUnlockClicked","onDownloadClicked","isUnlocking","jsx","jsxs","React","LockSimpleIcon","href","DownloadSimpleIcon","ReceivedCard","title","amountText","thumbnailUrl","mimeType","detail","gallery","onUnlockClick","onFetchSource","onDownloadClick","paymentStatus","isGallery","source","setSource","useState","cardRef","useRef","fetchingRef","onFetchSourceRef","effectiveSourceUrl","effectiveRedeemUrl","fetchSource","useCallback","result","_a","handleUnlockClick","useEffect","observer","entry","sourceType","getSourceType","statusBadge","CheckCircleIcon","actionSourceUrl","LockedCardShell","GalleryThumbnail","LockedThumbnail","CardBody","ImagesIcon"],"mappings":"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"}
@@ -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-BAQmoIff.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-Bqh-BQaX.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"Card-Bqh-BQaX.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\">&bull;</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":";;;;AA0CA,MAAMA,IAA4C,CAAC;AAAA,EACjD,OAAAC;AAAA,EACA,UAAAC,IAAW;AAAA,EACX,cAAAC;AAAA,EACA,QAAAC;AAAA,EACA,YAAAC;AAAA,EACA,kBAAAC,IAAmB;AAAA,EACnB,uBAAAC;AAAA,EACA,SAAAC;AAAA,EACA,WAAAC;AAAA,EACA,gBAAAC;AAAA,EACA,eAAAC;AAAA,EACA,aAAAC;AACF,MAAM;AACJ,QAAM,CAACC,GAAQC,CAAS,IAAIC,EAAA,GACtB,CAACC,GAAkBC,CAAmB,IAAIF,EAAS,EAAK,GACxD,CAACG,GAAkBC,CAAmB,IAAIJ,EAAS,EAAK,GACxDK,IAAcC,EAAO,EAAK,GAE1BC,MAAad,KAAA,gBAAAA,EAAS,WAAU,MAAM,GAEtCe,IAAeC,EAAY,YAAY;AAE3C,QADAd,KAAA,QAAAA,KACIM,GAAkB;AACpB,MAAAC,EAAoB,EAAK;AACzB;AAAA,IACF;AAGA,QAAIK,GAAW;AACb,MAAAL,EAAoB,EAAI;AACxB;AAAA,IACF;AACA,QAAIJ,GAAQ;AACV,MAAAI,EAAoB,EAAI;AACxB;AAAA,IACF;AACA,QAAKN,KACD,CAAAS,EAAY,SAChB;AAAA,MAAAA,EAAY,UAAU,IACtBD,EAAoB,EAAI;AACxB,UAAI;AACF,cAAMM,IAAS,MAAMd,EAAA;AACrB,QAAIc,MACFX,EAAUW,CAAM,GAChBR,EAAoB,EAAI;AAAA,MAE5B,UAAA;AACE,QAAAG,EAAY,UAAU,IACtBD,EAAoB,EAAK;AAAA,MAC3B;AAAA;AAAA,EACF,GAAG,CAACH,GAAkBM,GAAWT,GAAQH,GAAgBC,CAAa,CAAC,GAIjEe,IACJJ,KAAaX,KAAiBD,IAAiBa,IAAe,QAC1DI,IAAa,CAACX,GACdY,IAASV,GAETW,IACJ,gBAAAC,EAACC,EAAM,UAAN,EACC,UAAA;AAAA,IAAA,gBAAAC,EAAC,QAAA,EAAK,WAAU,qCAAoC,UAAA,KAAM;AAAA,IAC1D,gBAAAA;AAAA,MAAC;AAAA,MAAA;AAAA,QACC,WACE3B,IACI,sCACA;AAAA,QAGL,UAAAA,KAAcE;AAAA,MAAA;AAAA,IAAA;AAAA,EACjB,GACF,GAGI0B,IAAgBxB,IACpB,gBAAAuB;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,MAAK;AAAA,MACL,SAAS,CAAC,MAAM;AAGd,UAAE,gBAAA,GACFvB,EAAA;AAAA,MACF;AAAA,MACA,WAAU;AAAA,MACV,cAAW;AAAA,MAEX,UAAA,gBAAAuB,EAACE,GAAA,EAAM,WAAU,UAAS,QAAO,OAAA,CAAO;AAAA,IAAA;AAAA,EAAA,IAExC,QAEEC,IAAavB,IACjB,gBAAAoB;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,MAAK;AAAA,MACL,SAASpB;AAAA,MACT,cAAW;AAAA,MACX,WAAU;AAAA,MAEV,UAAA,gBAAAoB,EAACI,GAAA,EAAiB,WAAU,UAAS,QAAO,UAAA,CAAU;AAAA,IAAA;AAAA,EAAA,IAEtD,QAEEC,IAAYf,IAChB,gBAAAU;AAAA,IAACM;AAAA,IAAA;AAAA,MACC,SAAQ;AAAA,MACR,SAAA9B;AAAA,MACA,OAAAP;AAAA,MACA,YAAA0B;AAAA,MACA,UAAUM;AAAA,IAAA;AAAA,EAAA,IAGZ,gBAAAD;AAAA,IAACO;AAAA,IAAA;AAAA,MACC,SAAQ;AAAA,MACR,UAAArC;AAAA,MACA,cAAAC;AAAA,MACA,OAAAF;AAAA,MACA,QAAAY;AAAA,MACA,YAAAc;AAAA,MACA,UAAUM;AAAA,IAAA;AAAA,EAAA;AAId,SACE,gBAAAH,EAACU,GAAA,EAAgB,SAAQ,QACtB,UAAA;AAAA,IAAAd;AAAA;AAAA;AAAA;AAAA,MAIC,gBAAAM;AAAA,QAAC;AAAA,QAAA;AAAA,UACC,MAAK;AAAA,UACL,UAAUJ,IAAS,KAAK;AAAA,UACxB,cAAW;AAAA,UACX,aAAWA;AAAA,UACX,gBAAc,CAACD;AAAA,UACf,iBAAeC,KAAU;AAAA,UACzB,SAASA,IAAS,SAAYF;AAAA,UAC9B,WAAW,CAAC,MAAM;AAChB,YAAIE,KAKA,EAAE,WAAW,EAAE,kBACf,EAAE,QAAQ,WAAW,EAAE,QAAQ,SACjC,EAAE,eAAA,GACGF,EAAA;AAAA,UAET;AAAA,UACA,WACGE,IAEG,2BADA;AAAA,UAIL,UAAAS;AAAA,QAAA;AAAA,MAAA;AAAA,QAGHA;AAAA,IAGF,gBAAAL;AAAA,MAACS;AAAA,MAAA;AAAA,QACC,SAAQ;AAAA,QACR,OAAAxC;AAAA,QACA,kBAAAK;AAAA,QACA,UAAAJ;AAAA,QACA,QAAAE;AAAA,QACA,aAAAyB;AAAA,QACA,MACEP,IACE,gBAAAU,EAACU,GAAA,EAAW,WAAU,iCAAgC,IACpD;AAAA,QAEN,gBAAgBP;AAAA,MAAA;AAAA,IAAA;AAAA,EAClB,GACF;AAEJ;"}