@linktr.ee/messaging-react 2.2.0 → 2.2.2-rc-1779314025

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 (36) hide show
  1. package/dist/{Card-EKxCn56j.js → Card-CAliTKdt.js} +3 -3
  2. package/dist/{Card-EKxCn56j.js.map → Card-CAliTKdt.js.map} +1 -1
  3. package/dist/Card-CRMpOj0f.cjs +2 -0
  4. package/dist/Card-CRMpOj0f.cjs.map +1 -0
  5. package/dist/{Card-ChR37pLZ.js → Card-Ca5PnHml.js} +2 -2
  6. package/dist/{Card-ChR37pLZ.js.map → Card-Ca5PnHml.js.map} +1 -1
  7. package/dist/{Card-BdTueeyk.js → Card-Dz2v3fXf.js} +2 -2
  8. package/dist/{Card-BdTueeyk.js.map → Card-Dz2v3fXf.js.map} +1 -1
  9. package/dist/Card-b41LWND_.cjs +2 -0
  10. package/dist/Card-b41LWND_.cjs.map +1 -0
  11. package/dist/Card-vEkarkVD.cjs +2 -0
  12. package/dist/Card-vEkarkVD.cjs.map +1 -0
  13. package/dist/{LockedThumbnail-B16qP3eH.js → LockedThumbnail-BGz0NIQh.js} +2 -2
  14. package/dist/{LockedThumbnail-B16qP3eH.js.map → LockedThumbnail-BGz0NIQh.js.map} +1 -1
  15. package/dist/LockedThumbnail-JuPkpHeX.cjs +2 -0
  16. package/dist/LockedThumbnail-JuPkpHeX.cjs.map +1 -0
  17. package/dist/index-CctUDJSJ.cjs +2 -0
  18. package/dist/index-CctUDJSJ.cjs.map +1 -0
  19. package/dist/{index-Dn7BC9xK.js → index-D55UTfgC.js} +1307 -1264
  20. package/dist/index-D55UTfgC.js.map +1 -0
  21. package/dist/index.cjs +2 -0
  22. package/dist/index.cjs.map +1 -0
  23. package/dist/index.d.ts +20 -0
  24. package/dist/index.js +18 -16
  25. package/package.json +4 -3
  26. package/src/components/ChannelInfoDialog/ChannelInfoDialog.test.tsx +44 -0
  27. package/src/components/ChannelInfoDialog/index.tsx +5 -1
  28. package/src/components/ChannelList/CustomChannelPreview.test.tsx +35 -0
  29. package/src/components/ChannelList/CustomChannelPreview.tsx +2 -1
  30. package/src/components/ChannelView.test.tsx +65 -3
  31. package/src/components/ChannelView.tsx +53 -16
  32. package/src/index.ts +5 -0
  33. package/src/types.ts +9 -0
  34. package/src/utils/resolveParticipantDisplayName.test.ts +73 -0
  35. package/src/utils/resolveParticipantDisplayName.ts +40 -0
  36. package/dist/index-Dn7BC9xK.js.map +0 -1
@@ -2,8 +2,8 @@ import { jsx as e, jsxs as o } from "react/jsx-runtime";
2
2
  import { LockSimpleIcon as A, DownloadSimpleIcon as R, CheckCircleIcon as E, ImagesIcon as F } from "@phosphor-icons/react";
3
3
  import N, { useState as O, useRef as v, useCallback as z, useEffect as P } from "react";
4
4
  import "classnames";
5
- import { g as S } from "./index-Dn7BC9xK.js";
6
- import { L as T, G as _, a as $, C as q } from "./LockedThumbnail-B16qP3eH.js";
5
+ import { g as S } from "./index-D55UTfgC.js";
6
+ import { L as T, G as _, a as $, C as q } from "./LockedThumbnail-BGz0NIQh.js";
7
7
  const w = "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", H = ({
8
8
  isLocked: i,
9
9
  sourceUrl: d,
@@ -160,4 +160,4 @@ const w = "mt-3 inline-flex h-10 w-full items-center justify-center gap-2 rounde
160
160
  export {
161
161
  Y as default
162
162
  };
163
- //# sourceMappingURL=Card-EKxCn56j.js.map
163
+ //# sourceMappingURL=Card-CAliTKdt.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"Card-EKxCn56j.js","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","LockedCardShell","GalleryThumbnail","LockedThumbnail","CardBody","ImagesIcon"],"mappings":"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"}
1
+ {"version":3,"file":"Card-CAliTKdt.js","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","LockedCardShell","GalleryThumbnail","LockedThumbnail","CardBody","ImagesIcon"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("react/jsx-runtime"),w=require("@phosphor-icons/react"),s=require("react"),o=require("./LockedThumbnail-JuPkpHeX.cjs"),R=({title:u,mimeType:j="application/octet-stream",thumbnailUrl:p,detail:N,amountText:g,placeholderTitle:I="Attachment title",paymentStatus:d,gallery:i,onPreviewClick:a,onFetchSource:c})=>{const[f,L]=s.useState(),[x,l]=s.useState(!1),[y,v]=s.useState(!1),h=s.useRef(!1),n=((i==null?void 0:i.length)??0)>=2,T=s.useCallback(async()=>{if(a==null||a(),x){l(!1);return}if(n){l(!0);return}if(f){l(!0);return}if(c&&!h.current){h.current=!0,v(!0);try{const t=await c();t&&(L(t),l(!0))}finally{h.current=!1,v(!1)}}},[x,n,f,a,c]),m=n||c||a?T:void 0,b=!x,r=y,q=d==="paid"?e.jsxs(s.Fragment,{children:[e.jsx("span",{className:"text-xs font-medium text-white/55",children:"•"}),e.jsx("span",{className:"text-xs font-medium text-[#34c759]",children:"Sold"}),e.jsx(w.CheckCircleIcon,{className:"size-4 text-[#34c759]",weight:"bold"})]}):g?e.jsxs(s.Fragment,{children:[e.jsx("span",{className:"text-xs font-medium text-white/55",children:"•"}),e.jsx("span",{className:"text-xs font-medium text-white/55",children:g})]}):null,k=n?e.jsx(o.GalleryThumbnail,{variant:"dark",gallery:i,title:u,showLocked:b,paymentStatus:d}):e.jsx(o.LockedThumbnail,{variant:"dark",mimeType:j,thumbnailUrl:p,title:u,source:f,showLocked:b,paymentStatus:d});return e.jsxs(o.LockedCardShell,{variant:"dark",children:[m?e.jsx("div",{role:"button",tabIndex:r?-1:0,"aria-label":"Toggle preview","aria-busy":r,"aria-pressed":!b,"aria-disabled":r||void 0,onClick:r?void 0:m,onKeyDown:t=>{r||t.target===t.currentTarget&&(t.key==="Enter"||t.key===" ")&&(t.preventDefault(),m())},className:r?"block w-full text-left":"block w-full cursor-pointer text-left",children:k}):k,e.jsx(o.CardBody,{variant:"dark",title:u,placeholderTitle:I,mimeType:j,detail:N,statusBadge:q,icon:n?e.jsx(w.ImagesIcon,{className:"size-5 shrink-0 text-white/55"}):void 0})]})};exports.default=R;
2
+ //# sourceMappingURL=Card-CRMpOj0f.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Card-CRMpOj0f.cjs","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":"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"}
@@ -1,7 +1,7 @@
1
1
  import { jsxs as y, jsx as t } from "react/jsx-runtime";
2
2
  import { ImagesIcon as A, PencilSimpleIcon as C, XIcon as D } from "@phosphor-icons/react";
3
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-B16qP3eH.js";
4
+ import { L as P, C as S, G as X, a as q } from "./LockedThumbnail-BGz0NIQh.js";
5
5
  const Q = ({
6
6
  title: l,
7
7
  mimeType: b = "application/octet-stream",
@@ -129,4 +129,4 @@ const Q = ({
129
129
  export {
130
130
  Q as default
131
131
  };
132
- //# sourceMappingURL=Card-ChR37pLZ.js.map
132
+ //# sourceMappingURL=Card-Ca5PnHml.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"Card-ChR37pLZ.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;"}
1
+ {"version":3,"file":"Card-Ca5PnHml.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":"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"}
@@ -1,7 +1,7 @@
1
1
  import { jsxs as x, jsx as e } from "react/jsx-runtime";
2
2
  import { CheckCircleIcon as T, ImagesIcon as j } from "@phosphor-icons/react";
3
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-B16qP3eH.js";
4
+ import { L as D, C as V, G as A, a as E } from "./LockedThumbnail-BGz0NIQh.js";
5
5
  const M = ({
6
6
  title: o,
7
7
  mimeType: p = "application/octet-stream",
@@ -104,4 +104,4 @@ const M = ({
104
104
  export {
105
105
  M as default
106
106
  };
107
- //# sourceMappingURL=Card-BdTueeyk.js.map
107
+ //# sourceMappingURL=Card-Dz2v3fXf.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"Card-BdTueeyk.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":"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"}
1
+ {"version":3,"file":"Card-Dz2v3fXf.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":"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"}
@@ -0,0 +1,2 @@
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const t=require("react/jsx-runtime"),b=require("@phosphor-icons/react"),a=require("react"),c=require("./LockedThumbnail-JuPkpHeX.cjs"),A=({title:u,mimeType:g="application/octet-stream",thumbnailUrl:N,detail:I,amountText:j,placeholderTitle:L="Attachment title",placeholderAmountText:R,gallery:n,onDismiss:p,onPreviewClick:i,onFetchSource:o,onEditClick:v})=>{const[d,z]=a.useState(),[f,l]=a.useState(!1),[B,w]=a.useState(!1),h=a.useRef(!1),r=((n==null?void 0:n.length)??0)>=2,S=a.useCallback(async()=>{if(i==null||i(),f){l(!1);return}if(r){l(!0);return}if(d){l(!0);return}if(o&&!h.current){h.current=!0,w(!0);try{const e=await o();e&&(z(e),l(!0))}finally{h.current=!1,w(!1)}}},[f,r,d,i,o]),x=r||o||i?S:void 0,m=!f,s=B,T=t.jsxs(a.Fragment,{children:[t.jsx("span",{className:"text-xs font-medium text-white/55",children:"•"}),t.jsx("span",{className:j?"text-xs font-medium text-white/55":"text-xs font-medium text-white/30",children:j||R})]}),y=p?t.jsx("button",{type:"button",onClick:e=>{e.stopPropagation(),p()},className:"flex size-6 items-center justify-center rounded-full bg-[#121110] text-white","aria-label":"Dismiss attachment",children:t.jsx(b.XIcon,{className:"size-3",weight:"bold"})}):void 0,q=v?t.jsx("button",{type:"button",onClick:v,"aria-label":"Edit attachment",className:"flex size-10 items-center justify-center rounded-full bg-white/10 text-white hover:bg-white/15",children:t.jsx(b.PencilSimpleIcon,{className:"size-5",weight:"regular"})}):void 0,k=r?t.jsx(c.GalleryThumbnail,{variant:"dark",gallery:n,title:u,showLocked:m,topRight:y}):t.jsx(c.LockedThumbnail,{variant:"dark",mimeType:g,thumbnailUrl:N,title:u,source:d,showLocked:m,topRight:y});return t.jsxs(c.LockedCardShell,{variant:"dark",children:[x?t.jsx("div",{role:"button",tabIndex:s?-1:0,"aria-label":"Toggle preview","aria-busy":s,"aria-pressed":!m,"aria-disabled":s||void 0,onClick:s?void 0:x,onKeyDown:e=>{s||e.target===e.currentTarget&&(e.key==="Enter"||e.key===" ")&&(e.preventDefault(),x())},className:s?"block w-full text-left":"block w-full cursor-pointer text-left",children:k}):k,t.jsx(c.CardBody,{variant:"dark",title:u,placeholderTitle:L,mimeType:g,detail:I,statusBadge:T,icon:r?t.jsx(b.ImagesIcon,{className:"size-5 shrink-0 text-white/55"}):void 0,trailingAction:q})]})};exports.default=A;
2
+ //# sourceMappingURL=Card-b41LWND_.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Card-b41LWND_.cjs","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":"uNA0CMA,EAA4C,CAAC,CACjD,MAAAC,EACA,SAAAC,EAAW,2BACX,aAAAC,EACA,OAAAC,EACA,WAAAC,EACA,iBAAAC,EAAmB,mBACnB,sBAAAC,EACA,QAAAC,EACA,UAAAC,EACA,eAAAC,EACA,cAAAC,EACA,YAAAC,CACF,IAAM,CACJ,KAAM,CAACC,EAAQC,CAAS,EAAIC,WAAA,EACtB,CAACC,EAAkBC,CAAmB,EAAIF,EAAAA,SAAS,EAAK,EACxD,CAACG,EAAkBC,CAAmB,EAAIJ,EAAAA,SAAS,EAAK,EACxDK,EAAcC,EAAAA,OAAO,EAAK,EAE1BC,IAAad,GAAA,YAAAA,EAAS,SAAU,IAAM,EAEtCe,EAAeC,EAAAA,YAAY,SAAY,CAE3C,GADAd,GAAA,MAAAA,IACIM,EAAkB,CACpBC,EAAoB,EAAK,EACzB,MACF,CAGA,GAAIK,EAAW,CACbL,EAAoB,EAAI,EACxB,MACF,CACA,GAAIJ,EAAQ,CACVI,EAAoB,EAAI,EACxB,MACF,CACA,GAAKN,GACD,CAAAS,EAAY,QAChB,CAAAA,EAAY,QAAU,GACtBD,EAAoB,EAAI,EACxB,GAAI,CACF,MAAMM,EAAS,MAAMd,EAAA,EACjBc,IACFX,EAAUW,CAAM,EAChBR,EAAoB,EAAI,EAE5B,QAAA,CACEG,EAAY,QAAU,GACtBD,EAAoB,EAAK,CAC3B,EACF,EAAG,CAACH,EAAkBM,EAAWT,EAAQH,EAAgBC,CAAa,CAAC,EAIjEe,EACJJ,GAAaX,GAAiBD,EAAiBa,EAAe,OAC1DI,EAAa,CAACX,EACdY,EAASV,EAETW,EACJC,EAAAA,KAACC,EAAM,SAAN,CACC,SAAA,CAAAC,EAAAA,IAAC,OAAA,CAAK,UAAU,oCAAoC,SAAA,IAAM,EAC1DA,EAAAA,IAAC,OAAA,CACC,UACE3B,EACI,oCACA,oCAGL,SAAAA,GAAcE,CAAA,CAAA,CACjB,EACF,EAGI0B,EAAgBxB,EACpBuB,EAAAA,IAAC,SAAA,CACC,KAAK,SACL,QAAU,GAAM,CAGd,EAAE,gBAAA,EACFvB,EAAA,CACF,EACA,UAAU,+EACV,aAAW,qBAEX,SAAAuB,EAAAA,IAACE,QAAA,CAAM,UAAU,SAAS,OAAO,MAAA,CAAO,CAAA,CAAA,EAExC,OAEEC,EAAavB,EACjBoB,EAAAA,IAAC,SAAA,CACC,KAAK,SACL,QAASpB,EACT,aAAW,kBACX,UAAU,iGAEV,SAAAoB,EAAAA,IAACI,mBAAA,CAAiB,UAAU,SAAS,OAAO,SAAA,CAAU,CAAA,CAAA,EAEtD,OAEEC,EAAYf,EAChBU,EAAAA,IAACM,EAAAA,iBAAA,CACC,QAAQ,OACR,QAAA9B,EACA,MAAAP,EACA,WAAA0B,EACA,SAAUM,CAAA,CAAA,EAGZD,EAAAA,IAACO,EAAAA,gBAAA,CACC,QAAQ,OACR,SAAArC,EACA,aAAAC,EACA,MAAAF,EACA,OAAAY,EACA,WAAAc,EACA,SAAUM,CAAA,CAAA,EAId,OACEH,EAAAA,KAACU,EAAAA,gBAAA,CAAgB,QAAQ,OACtB,SAAA,CAAAd,EAICM,EAAAA,IAAC,MAAA,CACC,KAAK,SACL,SAAUJ,EAAS,GAAK,EACxB,aAAW,iBACX,YAAWA,EACX,eAAc,CAACD,EACf,gBAAeC,GAAU,OACzB,QAASA,EAAS,OAAYF,EAC9B,UAAY,GAAM,CACZE,GAKA,EAAE,SAAW,EAAE,gBACf,EAAE,MAAQ,SAAW,EAAE,MAAQ,OACjC,EAAE,eAAA,EACGF,EAAA,EAET,EACA,UACGE,EAEG,yBADA,wCAIL,SAAAS,CAAA,CAAA,EAGHA,EAGFL,EAAAA,IAACS,EAAAA,SAAA,CACC,QAAQ,OACR,MAAAxC,EACA,iBAAAK,EACA,SAAAJ,EACA,OAAAE,EACA,YAAAyB,EACA,KACEP,EACEU,MAACU,EAAAA,WAAA,CAAW,UAAU,gCAAgC,EACpD,OAEN,eAAgBP,CAAA,CAAA,CAClB,EACF,CAEJ"}
@@ -0,0 +1,2 @@
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-CctUDJSJ.cjs"),j=require("./LockedThumbnail-JuPkpHeX.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-vEkarkVD.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Card-vEkarkVD.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,6 +1,6 @@
1
1
  import { jsxs as o, jsx as e } from "react/jsx-runtime";
2
2
  import m from "classnames";
3
- import { r as N, A as y, g as w } from "./index-Dn7BC9xK.js";
3
+ import { r as N, A as y, g as w } from "./index-D55UTfgC.js";
4
4
  import U, { useState as _ } from "react";
5
5
  import { CaretLeftIcon as T, CaretRightIcon as C, PlayIcon as k, LockOpenIcon as z, LockSimpleIcon as I } from "@phosphor-icons/react";
6
6
  const F = ({
@@ -217,4 +217,4 @@ export {
217
217
  V as L,
218
218
  q as a
219
219
  };
220
- //# sourceMappingURL=LockedThumbnail-B16qP3eH.js.map
220
+ //# sourceMappingURL=LockedThumbnail-BGz0NIQh.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"LockedThumbnail-B16qP3eH.js","sources":["../src/components/LockedAttachment/components/_shared/CardBody.tsx","../src/components/LockedAttachment/components/_shared/GalleryThumbnail.tsx","../src/components/LockedAttachment/components/_shared/LockedCardShell.tsx","../src/components/LockedAttachment/components/_shared/LockBadge.tsx","../src/components/LockedAttachment/components/_shared/LockedThumbnail.tsx"],"sourcesContent":["import classNames from 'classnames'\nimport React from 'react'\n\nimport { renderTypeIcon } from '../../../AttachmentCard'\n\nimport type { LockedCardVariant } from './LockedCardShell'\n\nexport interface CardBodyProps {\n variant: LockedCardVariant\n title?: string\n placeholderTitle?: string\n mimeType: string\n detail?: string\n statusBadge?: React.ReactNode\n action?: React.ReactNode\n /** Overrides the auto-detected type icon (used by Gallery to swap in an `Images` icon). */\n icon?: React.ReactNode\n /** Optional control rendered on the right of the title/status block (e.g. Composer edit pencil). */\n trailingAction?: React.ReactNode\n}\n\n/**\n * Title + status row layout shared by Composer / Sent / Received cards.\n * Layout matches the Figma `Container > Labels` group (16px h-padding, 12px v-padding,\n * 4px gap between title and status row, 4px gap inside status row).\n */\nconst CardBody: React.FC<CardBodyProps> = ({\n variant,\n title,\n placeholderTitle = 'Attachment title',\n mimeType,\n detail,\n statusBadge,\n action,\n icon,\n trailingAction,\n}) => {\n const isDark = variant === 'dark'\n const displayTitle = isDark ? (title ?? placeholderTitle) : (title ?? '')\n const titleDimmed = isDark && !title\n\n const typeIcon =\n icon ??\n renderTypeIcon(mimeType, {\n className: classNames(\n 'size-5 shrink-0',\n isDark ? 'text-white/55' : 'text-black/55'\n ),\n weight: 'regular',\n })\n\n return (\n <div className=\"px-4 py-3\">\n <div className=\"flex items-end gap-3\">\n <div className=\"flex min-w-0 flex-1 flex-col gap-1\">\n {displayTitle.trim() !== '' && (\n <p\n className={classNames('truncate text-base font-medium leading-6', {\n 'text-black/90': !isDark,\n 'text-white/30': isDark && titleDimmed,\n 'text-white': isDark && !titleDimmed,\n })}\n >\n {displayTitle}\n </p>\n )}\n\n <div className=\"flex flex-wrap items-center gap-1\">\n {typeIcon}\n\n {detail != null && detail !== '' && (\n <span\n className={classNames(\n 'text-xs font-medium',\n isDark ? 'text-white/55' : 'text-black/55'\n )}\n >\n {detail}\n </span>\n )}\n\n {statusBadge}\n </div>\n </div>\n\n {trailingAction && <div className=\"shrink-0\">{trailingAction}</div>}\n </div>\n\n {action}\n </div>\n )\n}\n\nexport default CardBody\n","import {\n CaretLeftIcon,\n CaretRightIcon,\n LockOpenIcon,\n LockSimpleIcon,\n PlayIcon,\n} from '@phosphor-icons/react'\nimport classNames from 'classnames'\nimport React, { useState } from 'react'\n\nimport { AttachmentThumbnail, getSourceType } from '../../../AttachmentCard'\nimport type {\n LockedAttachmentGalleryItem,\n PaymentStatus,\n} from '../../types'\n\nimport type { LockedCardVariant } from './LockedCardShell'\n\nexport interface GalleryThumbnailProps {\n variant: LockedCardVariant\n gallery: LockedAttachmentGalleryItem[]\n title?: string\n /** When true, all items are shown blurred + the locked/play badge is overlaid. */\n showLocked: boolean\n paymentStatus?: PaymentStatus\n /** Top-right slot (e.g. dismiss X on Composer). Rendered inside the thumbnail. */\n topRight?: React.ReactNode\n}\n\nfunction isPlayableItem(item: LockedAttachmentGalleryItem): boolean {\n const sourceType = getSourceType(item.mimeType)\n return sourceType === 'video' || sourceType === 'audio'\n}\n\n/**\n * Mixed-media carousel used by every LockedAttachment card when its `gallery`\n * prop has 2+ items. Matches the \"Gallery\" frames in the Figma design system:\n * 337/386 aspect ratio, prev/next caret buttons, a counter pill, and a center\n * lock / play indicator that swaps based on the current item.\n */\nconst GalleryThumbnail: React.FC<GalleryThumbnailProps> = ({\n variant,\n gallery,\n title,\n showLocked,\n paymentStatus,\n topRight,\n}) => {\n const [index, setIndex] = useState(0)\n const total = gallery.length\n const safeIndex = Math.min(index, total - 1)\n const current = gallery[safeIndex]\n const isVideoItem = isPlayableItem(current)\n\n // Center badge: PLAY for video items, LOCK for non-video locked items.\n // Unlocked image items show no center badge — the image is the content.\n const centerBadge = (() => {\n if (isVideoItem) {\n return <PlayIcon className=\"size-6\" weight=\"fill\" />\n }\n if (showLocked) {\n return paymentStatus === 'paid' ? (\n <LockOpenIcon className=\"size-6\" weight=\"fill\" />\n ) : (\n <LockSimpleIcon className=\"size-6\" weight=\"fill\" />\n )\n }\n return null\n })()\n\n // Stop propagation so arrow taps don't bubble up to the outer preview-toggle\n // wrapper on ComposerCard / SentCard.\n const goPrev = (e: React.MouseEvent) => {\n e.stopPropagation()\n setIndex((i) => Math.max(0, i - 1))\n }\n const goNext = (e: React.MouseEvent) => {\n e.stopPropagation()\n setIndex((i) => Math.min(total - 1, i + 1))\n }\n const atStart = safeIndex === 0\n const atEnd = safeIndex === total - 1\n\n const isDark = variant === 'dark'\n\n // Locked + unlocked render entirely separate trees so the locked state\n // doesn't end up stacking a blurred `<img>` on top of an underlying\n // `AttachmentThumbnail` (whose `bg-white/10` poster shell otherwise\n // shows through as a visible horizontal seam at the 16:9 boundary\n // inside the taller 337/386 gallery aspect).\n //\n // The locked branch uses `filter: blur` on a single full-size `<img>`\n // — not `backdrop-filter` — so the rounded corners are clipped\n // normally without any clip-path workarounds (see LockedThumbnail).\n return (\n <div className=\"relative aspect-[337/386] overflow-hidden rounded-t-2xl\">\n {showLocked ? (\n <div\n className={classNames(\n 'absolute inset-0',\n isDark ? 'bg-white/10' : 'bg-black/5'\n )}\n >\n {current.thumbnailUrl && (\n <img\n src={current.thumbnailUrl}\n alt=\"\"\n aria-hidden\n draggable={false}\n // `scale-110` hides the fuzzy edge that `filter: blur` leaves\n // around the image bounds.\n className=\"absolute inset-0 h-full w-full scale-110 object-cover blur-[40px]\"\n />\n )}\n </div>\n ) : (\n <AttachmentThumbnail\n mimeType={current.mimeType}\n sourceUrl={current.sourceUrl}\n thumbnailUrl={current.thumbnailUrl}\n title={title}\n variant={variant}\n />\n )}\n\n {centerBadge && (\n <div className=\"pointer-events-none absolute inset-0 flex items-center justify-center\">\n <div className=\"flex size-12 items-center justify-center rounded-full bg-black/25 text-white\">\n {centerBadge}\n </div>\n </div>\n )}\n\n {total > 1 && (\n <React.Fragment>\n <button\n type=\"button\"\n onClick={goPrev}\n disabled={atStart}\n aria-label=\"Previous item\"\n className={classNames(\n 'absolute left-3 top-1/2 -translate-y-1/2 flex size-7 items-center justify-center rounded-full bg-black/30 text-white transition-opacity',\n atStart ? 'opacity-40' : 'hover:bg-black/40'\n )}\n >\n <CaretLeftIcon className=\"size-5\" weight=\"bold\" />\n </button>\n <button\n type=\"button\"\n onClick={goNext}\n disabled={atEnd}\n aria-label=\"Next item\"\n className={classNames(\n 'absolute right-3 top-1/2 -translate-y-1/2 flex size-7 items-center justify-center rounded-full bg-black/30 text-white transition-opacity',\n atEnd ? 'opacity-40' : 'hover:bg-black/40'\n )}\n >\n <CaretRightIcon className=\"size-5\" weight=\"bold\" />\n </button>\n\n <div className=\"pointer-events-none absolute bottom-5 left-1/2 -translate-x-1/2 rounded-lg bg-black/30 px-2 py-1\">\n <span className=\"text-xs font-medium leading-4 text-white\">\n {safeIndex + 1}/{total}\n </span>\n </div>\n </React.Fragment>\n )}\n\n {topRight && (\n <div className=\"pointer-events-auto absolute right-3 top-3 z-10\">\n {topRight}\n </div>\n )}\n </div>\n )\n}\n\nexport default GalleryThumbnail\n","import classNames from 'classnames'\nimport React from 'react'\n\nexport type LockedCardVariant = 'dark' | 'light'\n\nexport interface LockedCardShellProps {\n variant: LockedCardVariant\n children: React.ReactNode\n rootRef?: React.Ref<HTMLDivElement>\n 'data-testid'?: string\n}\n\n/**\n * Outer chrome for every LockedAttachment card.\n * Mirrors the `shadow-400` + 16px radius treatment from the Figma design system.\n */\nconst LockedCardShell: React.FC<LockedCardShellProps> = ({\n variant,\n children,\n rootRef,\n 'data-testid': dataTestId,\n}) => (\n <div\n ref={rootRef}\n data-testid={dataTestId}\n className={classNames(\n 'relative w-[280px] select-none overflow-hidden rounded-md',\n 'shadow-[0_0_0_1px_rgba(0,0,0,0.04),0_1px_2px_rgba(0,0,0,0.04),0_8px_32px_rgba(0,0,0,0.1)]',\n variant === 'dark' ? 'bg-[#121110]' : 'bg-white'\n )}\n >\n {children}\n </div>\n)\n\nexport default LockedCardShell\n","import {\n LockOpenIcon,\n LockSimpleIcon,\n PlayIcon,\n} from '@phosphor-icons/react'\nimport React from 'react'\n\nimport { getSourceType } from '../../../AttachmentCard'\nimport type { PaymentStatus } from '../../types'\n\n/**\n * Centered 48x48 lock badge that overlays the thumbnail of any locked attachment.\n * Uses `bg-black/25` from the design system's `alpha-black-25` token.\n *\n * For playable media (video/audio), the badge shows a play icon instead of a\n * lock — matching the convention used by `GalleryThumbnail`.\n */\nexport const LockBadge: React.FC<{\n paymentStatus?: PaymentStatus\n mimeType?: string\n}> = ({ paymentStatus, mimeType }) => {\n const sourceType = mimeType ? getSourceType(mimeType) : undefined\n const isPlayable = sourceType === 'video' || sourceType === 'audio'\n\n const Icon = isPlayable\n ? PlayIcon\n : paymentStatus === 'paid'\n ? LockOpenIcon\n : LockSimpleIcon\n return (\n <div className=\"pointer-events-none absolute inset-0 flex items-center justify-center\">\n <div className=\"flex size-12 items-center justify-center rounded-full bg-black/25 text-white\">\n <Icon className=\"size-6\" weight=\"fill\" />\n </div>\n </div>\n )\n}\n\nexport default LockBadge\n","import classNames from 'classnames'\nimport React from 'react'\n\nimport {\n AttachmentThumbnail,\n renderTypeIcon,\n} from '../../../AttachmentCard'\nimport type { LockedAttachmentSource, PaymentStatus } from '../../types'\n\nimport { LockBadge } from './LockBadge'\nimport type { LockedCardVariant } from './LockedCardShell'\n\nexport interface LockedThumbnailProps {\n variant: LockedCardVariant\n mimeType: string\n thumbnailUrl?: string\n title?: string\n /**\n * When provided, the underlying source is shown unblurred (preview / unlocked state).\n * When undefined, the thumbnail is treated as locked: blurred with a central lock badge.\n */\n source?: LockedAttachmentSource\n /**\n * Forces the locked appearance even when a source is provided. Used for the\n * Composer card where the locked treatment is the default and the user toggles\n * a preview in place.\n */\n showLocked: boolean\n paymentStatus?: PaymentStatus\n /** Top-right overlay (e.g. dismiss X on Composer). Rendered inside the thumbnail. */\n topRight?: React.ReactNode\n /** When true, renders the unlocked image with aspect-video + object-contain. */\n containedImage?: boolean\n}\n\n/**\n * Renders the 180px-tall media area for a LockedAttachment card.\n * Handles three visual states:\n * - locked: blurred thumbnail + centered 48px lock badge\n * - preview/paid: clear thumbnail (the underlying source)\n * - empty: type-icon placeholder\n */\nconst LockedThumbnail: React.FC<LockedThumbnailProps> = ({\n variant,\n mimeType,\n thumbnailUrl,\n title,\n source,\n showLocked,\n paymentStatus,\n topRight,\n containedImage = false,\n}) => {\n const effectiveSourceUrl = showLocked ? undefined : source?.sourceUrl\n const effectiveThumbnailUrl = showLocked\n ? thumbnailUrl\n : (source?.thumbnailUrl ?? thumbnailUrl)\n\n const isDark = variant === 'dark'\n\n // Locked + unlocked render entirely separate trees so the locked state\n // doesn't end up stacking a blurred `<img>` on top of an underlying\n // `AttachmentThumbnail` (whose `bg-white/10` poster shell otherwise\n // shows through as a visible horizontal seam at the 16:9 boundary).\n //\n // The locked branch uses `filter: blur` on a single full-size `<img>`\n // — not `backdrop-filter` — because `backdrop-filter` paints in its\n // own compositing layer that escapes ancestor `overflow: hidden` on\n // Chrome/Safari and flattens the card's top corners. `filter` is\n // clipped normally, so the rounded corners stay round without any\n // clip-path workarounds.\n return (\n <div className=\"relative overflow-hidden rounded-t-2xl\">\n {showLocked ? (\n <div\n className={classNames(\n 'relative aspect-video overflow-hidden',\n isDark ? 'bg-white/10' : 'bg-black/5'\n )}\n >\n {effectiveThumbnailUrl ? (\n <img\n src={effectiveThumbnailUrl}\n alt=\"\"\n aria-hidden\n draggable={false}\n // `scale-110` hides the fuzzy semi-transparent edge that\n // `filter: blur` leaves around the image bounds.\n className=\"absolute inset-0 h-full w-full scale-110 object-cover blur-[40px]\"\n />\n ) : (\n <div className=\"absolute inset-0 flex items-center justify-center\">\n {renderTypeIcon(mimeType, {\n className: isDark\n ? 'size-12 text-white/20'\n : 'size-12 text-black/20',\n weight: 'regular',\n })}\n </div>\n )}\n <LockBadge paymentStatus={paymentStatus} mimeType={mimeType} />\n </div>\n ) : (\n <AttachmentThumbnail\n mimeType={mimeType}\n sourceUrl={effectiveSourceUrl}\n thumbnailUrl={effectiveThumbnailUrl}\n title={title}\n variant={variant}\n containedImage={containedImage}\n mediaPlayerProps={\n effectiveSourceUrl\n ? { autoPlay: true, loop: true, controls: true, muted: false }\n : undefined\n }\n />\n )}\n\n {topRight && (\n <div className=\"pointer-events-auto absolute right-3 top-3 z-10\">\n {topRight}\n </div>\n )}\n </div>\n )\n}\n\nexport default LockedThumbnail\n"],"names":["CardBody","variant","title","placeholderTitle","mimeType","detail","statusBadge","action","icon","trailingAction","isDark","displayTitle","titleDimmed","typeIcon","renderTypeIcon","classNames","jsxs","jsx","isPlayableItem","item","sourceType","getSourceType","GalleryThumbnail","gallery","showLocked","paymentStatus","topRight","index","setIndex","useState","total","safeIndex","current","isVideoItem","centerBadge","PlayIcon","LockOpenIcon","LockSimpleIcon","goPrev","e","i","goNext","atStart","atEnd","AttachmentThumbnail","React","CaretLeftIcon","CaretRightIcon","LockedCardShell","children","rootRef","dataTestId","LockBadge","LockedThumbnail","thumbnailUrl","source","containedImage","effectiveSourceUrl","effectiveThumbnailUrl"],"mappings":"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"}
1
+ {"version":3,"file":"LockedThumbnail-BGz0NIQh.js","sources":["../src/components/LockedAttachment/components/_shared/CardBody.tsx","../src/components/LockedAttachment/components/_shared/GalleryThumbnail.tsx","../src/components/LockedAttachment/components/_shared/LockedCardShell.tsx","../src/components/LockedAttachment/components/_shared/LockBadge.tsx","../src/components/LockedAttachment/components/_shared/LockedThumbnail.tsx"],"sourcesContent":["import classNames from 'classnames'\nimport React from 'react'\n\nimport { renderTypeIcon } from '../../../AttachmentCard'\n\nimport type { LockedCardVariant } from './LockedCardShell'\n\nexport interface CardBodyProps {\n variant: LockedCardVariant\n title?: string\n placeholderTitle?: string\n mimeType: string\n detail?: string\n statusBadge?: React.ReactNode\n action?: React.ReactNode\n /** Overrides the auto-detected type icon (used by Gallery to swap in an `Images` icon). */\n icon?: React.ReactNode\n /** Optional control rendered on the right of the title/status block (e.g. Composer edit pencil). */\n trailingAction?: React.ReactNode\n}\n\n/**\n * Title + status row layout shared by Composer / Sent / Received cards.\n * Layout matches the Figma `Container > Labels` group (16px h-padding, 12px v-padding,\n * 4px gap between title and status row, 4px gap inside status row).\n */\nconst CardBody: React.FC<CardBodyProps> = ({\n variant,\n title,\n placeholderTitle = 'Attachment title',\n mimeType,\n detail,\n statusBadge,\n action,\n icon,\n trailingAction,\n}) => {\n const isDark = variant === 'dark'\n const displayTitle = isDark ? (title ?? placeholderTitle) : (title ?? '')\n const titleDimmed = isDark && !title\n\n const typeIcon =\n icon ??\n renderTypeIcon(mimeType, {\n className: classNames(\n 'size-5 shrink-0',\n isDark ? 'text-white/55' : 'text-black/55'\n ),\n weight: 'regular',\n })\n\n return (\n <div className=\"px-4 py-3\">\n <div className=\"flex items-end gap-3\">\n <div className=\"flex min-w-0 flex-1 flex-col gap-1\">\n {displayTitle.trim() !== '' && (\n <p\n className={classNames('truncate text-base font-medium leading-6', {\n 'text-black/90': !isDark,\n 'text-white/30': isDark && titleDimmed,\n 'text-white': isDark && !titleDimmed,\n })}\n >\n {displayTitle}\n </p>\n )}\n\n <div className=\"flex flex-wrap items-center gap-1\">\n {typeIcon}\n\n {detail != null && detail !== '' && (\n <span\n className={classNames(\n 'text-xs font-medium',\n isDark ? 'text-white/55' : 'text-black/55'\n )}\n >\n {detail}\n </span>\n )}\n\n {statusBadge}\n </div>\n </div>\n\n {trailingAction && <div className=\"shrink-0\">{trailingAction}</div>}\n </div>\n\n {action}\n </div>\n )\n}\n\nexport default CardBody\n","import {\n CaretLeftIcon,\n CaretRightIcon,\n LockOpenIcon,\n LockSimpleIcon,\n PlayIcon,\n} from '@phosphor-icons/react'\nimport classNames from 'classnames'\nimport React, { useState } from 'react'\n\nimport { AttachmentThumbnail, getSourceType } from '../../../AttachmentCard'\nimport type {\n LockedAttachmentGalleryItem,\n PaymentStatus,\n} from '../../types'\n\nimport type { LockedCardVariant } from './LockedCardShell'\n\nexport interface GalleryThumbnailProps {\n variant: LockedCardVariant\n gallery: LockedAttachmentGalleryItem[]\n title?: string\n /** When true, all items are shown blurred + the locked/play badge is overlaid. */\n showLocked: boolean\n paymentStatus?: PaymentStatus\n /** Top-right slot (e.g. dismiss X on Composer). Rendered inside the thumbnail. */\n topRight?: React.ReactNode\n}\n\nfunction isPlayableItem(item: LockedAttachmentGalleryItem): boolean {\n const sourceType = getSourceType(item.mimeType)\n return sourceType === 'video' || sourceType === 'audio'\n}\n\n/**\n * Mixed-media carousel used by every LockedAttachment card when its `gallery`\n * prop has 2+ items. Matches the \"Gallery\" frames in the Figma design system:\n * 337/386 aspect ratio, prev/next caret buttons, a counter pill, and a center\n * lock / play indicator that swaps based on the current item.\n */\nconst GalleryThumbnail: React.FC<GalleryThumbnailProps> = ({\n variant,\n gallery,\n title,\n showLocked,\n paymentStatus,\n topRight,\n}) => {\n const [index, setIndex] = useState(0)\n const total = gallery.length\n const safeIndex = Math.min(index, total - 1)\n const current = gallery[safeIndex]\n const isVideoItem = isPlayableItem(current)\n\n // Center badge: PLAY for video items, LOCK for non-video locked items.\n // Unlocked image items show no center badge — the image is the content.\n const centerBadge = (() => {\n if (isVideoItem) {\n return <PlayIcon className=\"size-6\" weight=\"fill\" />\n }\n if (showLocked) {\n return paymentStatus === 'paid' ? (\n <LockOpenIcon className=\"size-6\" weight=\"fill\" />\n ) : (\n <LockSimpleIcon className=\"size-6\" weight=\"fill\" />\n )\n }\n return null\n })()\n\n // Stop propagation so arrow taps don't bubble up to the outer preview-toggle\n // wrapper on ComposerCard / SentCard.\n const goPrev = (e: React.MouseEvent) => {\n e.stopPropagation()\n setIndex((i) => Math.max(0, i - 1))\n }\n const goNext = (e: React.MouseEvent) => {\n e.stopPropagation()\n setIndex((i) => Math.min(total - 1, i + 1))\n }\n const atStart = safeIndex === 0\n const atEnd = safeIndex === total - 1\n\n const isDark = variant === 'dark'\n\n // Locked + unlocked render entirely separate trees so the locked state\n // doesn't end up stacking a blurred `<img>` on top of an underlying\n // `AttachmentThumbnail` (whose `bg-white/10` poster shell otherwise\n // shows through as a visible horizontal seam at the 16:9 boundary\n // inside the taller 337/386 gallery aspect).\n //\n // The locked branch uses `filter: blur` on a single full-size `<img>`\n // — not `backdrop-filter` — so the rounded corners are clipped\n // normally without any clip-path workarounds (see LockedThumbnail).\n return (\n <div className=\"relative aspect-[337/386] overflow-hidden rounded-t-2xl\">\n {showLocked ? (\n <div\n className={classNames(\n 'absolute inset-0',\n isDark ? 'bg-white/10' : 'bg-black/5'\n )}\n >\n {current.thumbnailUrl && (\n <img\n src={current.thumbnailUrl}\n alt=\"\"\n aria-hidden\n draggable={false}\n // `scale-110` hides the fuzzy edge that `filter: blur` leaves\n // around the image bounds.\n className=\"absolute inset-0 h-full w-full scale-110 object-cover blur-[40px]\"\n />\n )}\n </div>\n ) : (\n <AttachmentThumbnail\n mimeType={current.mimeType}\n sourceUrl={current.sourceUrl}\n thumbnailUrl={current.thumbnailUrl}\n title={title}\n variant={variant}\n />\n )}\n\n {centerBadge && (\n <div className=\"pointer-events-none absolute inset-0 flex items-center justify-center\">\n <div className=\"flex size-12 items-center justify-center rounded-full bg-black/25 text-white\">\n {centerBadge}\n </div>\n </div>\n )}\n\n {total > 1 && (\n <React.Fragment>\n <button\n type=\"button\"\n onClick={goPrev}\n disabled={atStart}\n aria-label=\"Previous item\"\n className={classNames(\n 'absolute left-3 top-1/2 -translate-y-1/2 flex size-7 items-center justify-center rounded-full bg-black/30 text-white transition-opacity',\n atStart ? 'opacity-40' : 'hover:bg-black/40'\n )}\n >\n <CaretLeftIcon className=\"size-5\" weight=\"bold\" />\n </button>\n <button\n type=\"button\"\n onClick={goNext}\n disabled={atEnd}\n aria-label=\"Next item\"\n className={classNames(\n 'absolute right-3 top-1/2 -translate-y-1/2 flex size-7 items-center justify-center rounded-full bg-black/30 text-white transition-opacity',\n atEnd ? 'opacity-40' : 'hover:bg-black/40'\n )}\n >\n <CaretRightIcon className=\"size-5\" weight=\"bold\" />\n </button>\n\n <div className=\"pointer-events-none absolute bottom-5 left-1/2 -translate-x-1/2 rounded-lg bg-black/30 px-2 py-1\">\n <span className=\"text-xs font-medium leading-4 text-white\">\n {safeIndex + 1}/{total}\n </span>\n </div>\n </React.Fragment>\n )}\n\n {topRight && (\n <div className=\"pointer-events-auto absolute right-3 top-3 z-10\">\n {topRight}\n </div>\n )}\n </div>\n )\n}\n\nexport default GalleryThumbnail\n","import classNames from 'classnames'\nimport React from 'react'\n\nexport type LockedCardVariant = 'dark' | 'light'\n\nexport interface LockedCardShellProps {\n variant: LockedCardVariant\n children: React.ReactNode\n rootRef?: React.Ref<HTMLDivElement>\n 'data-testid'?: string\n}\n\n/**\n * Outer chrome for every LockedAttachment card.\n * Mirrors the `shadow-400` + 16px radius treatment from the Figma design system.\n */\nconst LockedCardShell: React.FC<LockedCardShellProps> = ({\n variant,\n children,\n rootRef,\n 'data-testid': dataTestId,\n}) => (\n <div\n ref={rootRef}\n data-testid={dataTestId}\n className={classNames(\n 'relative w-[280px] select-none overflow-hidden rounded-md',\n 'shadow-[0_0_0_1px_rgba(0,0,0,0.04),0_1px_2px_rgba(0,0,0,0.04),0_8px_32px_rgba(0,0,0,0.1)]',\n variant === 'dark' ? 'bg-[#121110]' : 'bg-white'\n )}\n >\n {children}\n </div>\n)\n\nexport default LockedCardShell\n","import {\n LockOpenIcon,\n LockSimpleIcon,\n PlayIcon,\n} from '@phosphor-icons/react'\nimport React from 'react'\n\nimport { getSourceType } from '../../../AttachmentCard'\nimport type { PaymentStatus } from '../../types'\n\n/**\n * Centered 48x48 lock badge that overlays the thumbnail of any locked attachment.\n * Uses `bg-black/25` from the design system's `alpha-black-25` token.\n *\n * For playable media (video/audio), the badge shows a play icon instead of a\n * lock — matching the convention used by `GalleryThumbnail`.\n */\nexport const LockBadge: React.FC<{\n paymentStatus?: PaymentStatus\n mimeType?: string\n}> = ({ paymentStatus, mimeType }) => {\n const sourceType = mimeType ? getSourceType(mimeType) : undefined\n const isPlayable = sourceType === 'video' || sourceType === 'audio'\n\n const Icon = isPlayable\n ? PlayIcon\n : paymentStatus === 'paid'\n ? LockOpenIcon\n : LockSimpleIcon\n return (\n <div className=\"pointer-events-none absolute inset-0 flex items-center justify-center\">\n <div className=\"flex size-12 items-center justify-center rounded-full bg-black/25 text-white\">\n <Icon className=\"size-6\" weight=\"fill\" />\n </div>\n </div>\n )\n}\n\nexport default LockBadge\n","import classNames from 'classnames'\nimport React from 'react'\n\nimport {\n AttachmentThumbnail,\n renderTypeIcon,\n} from '../../../AttachmentCard'\nimport type { LockedAttachmentSource, PaymentStatus } from '../../types'\n\nimport { LockBadge } from './LockBadge'\nimport type { LockedCardVariant } from './LockedCardShell'\n\nexport interface LockedThumbnailProps {\n variant: LockedCardVariant\n mimeType: string\n thumbnailUrl?: string\n title?: string\n /**\n * When provided, the underlying source is shown unblurred (preview / unlocked state).\n * When undefined, the thumbnail is treated as locked: blurred with a central lock badge.\n */\n source?: LockedAttachmentSource\n /**\n * Forces the locked appearance even when a source is provided. Used for the\n * Composer card where the locked treatment is the default and the user toggles\n * a preview in place.\n */\n showLocked: boolean\n paymentStatus?: PaymentStatus\n /** Top-right overlay (e.g. dismiss X on Composer). Rendered inside the thumbnail. */\n topRight?: React.ReactNode\n /** When true, renders the unlocked image with aspect-video + object-contain. */\n containedImage?: boolean\n}\n\n/**\n * Renders the 180px-tall media area for a LockedAttachment card.\n * Handles three visual states:\n * - locked: blurred thumbnail + centered 48px lock badge\n * - preview/paid: clear thumbnail (the underlying source)\n * - empty: type-icon placeholder\n */\nconst LockedThumbnail: React.FC<LockedThumbnailProps> = ({\n variant,\n mimeType,\n thumbnailUrl,\n title,\n source,\n showLocked,\n paymentStatus,\n topRight,\n containedImage = false,\n}) => {\n const effectiveSourceUrl = showLocked ? undefined : source?.sourceUrl\n const effectiveThumbnailUrl = showLocked\n ? thumbnailUrl\n : (source?.thumbnailUrl ?? thumbnailUrl)\n\n const isDark = variant === 'dark'\n\n // Locked + unlocked render entirely separate trees so the locked state\n // doesn't end up stacking a blurred `<img>` on top of an underlying\n // `AttachmentThumbnail` (whose `bg-white/10` poster shell otherwise\n // shows through as a visible horizontal seam at the 16:9 boundary).\n //\n // The locked branch uses `filter: blur` on a single full-size `<img>`\n // — not `backdrop-filter` — because `backdrop-filter` paints in its\n // own compositing layer that escapes ancestor `overflow: hidden` on\n // Chrome/Safari and flattens the card's top corners. `filter` is\n // clipped normally, so the rounded corners stay round without any\n // clip-path workarounds.\n return (\n <div className=\"relative overflow-hidden rounded-t-2xl\">\n {showLocked ? (\n <div\n className={classNames(\n 'relative aspect-video overflow-hidden',\n isDark ? 'bg-white/10' : 'bg-black/5'\n )}\n >\n {effectiveThumbnailUrl ? (\n <img\n src={effectiveThumbnailUrl}\n alt=\"\"\n aria-hidden\n draggable={false}\n // `scale-110` hides the fuzzy semi-transparent edge that\n // `filter: blur` leaves around the image bounds.\n className=\"absolute inset-0 h-full w-full scale-110 object-cover blur-[40px]\"\n />\n ) : (\n <div className=\"absolute inset-0 flex items-center justify-center\">\n {renderTypeIcon(mimeType, {\n className: isDark\n ? 'size-12 text-white/20'\n : 'size-12 text-black/20',\n weight: 'regular',\n })}\n </div>\n )}\n <LockBadge paymentStatus={paymentStatus} mimeType={mimeType} />\n </div>\n ) : (\n <AttachmentThumbnail\n mimeType={mimeType}\n sourceUrl={effectiveSourceUrl}\n thumbnailUrl={effectiveThumbnailUrl}\n title={title}\n variant={variant}\n containedImage={containedImage}\n mediaPlayerProps={\n effectiveSourceUrl\n ? { autoPlay: true, loop: true, controls: true, muted: false }\n : undefined\n }\n />\n )}\n\n {topRight && (\n <div className=\"pointer-events-auto absolute right-3 top-3 z-10\">\n {topRight}\n </div>\n )}\n </div>\n )\n}\n\nexport default LockedThumbnail\n"],"names":["CardBody","variant","title","placeholderTitle","mimeType","detail","statusBadge","action","icon","trailingAction","isDark","displayTitle","titleDimmed","typeIcon","renderTypeIcon","classNames","jsxs","jsx","isPlayableItem","item","sourceType","getSourceType","GalleryThumbnail","gallery","showLocked","paymentStatus","topRight","index","setIndex","useState","total","safeIndex","current","isVideoItem","centerBadge","PlayIcon","LockOpenIcon","LockSimpleIcon","goPrev","e","i","goNext","atStart","atEnd","AttachmentThumbnail","React","CaretLeftIcon","CaretRightIcon","LockedCardShell","children","rootRef","dataTestId","LockBadge","LockedThumbnail","thumbnailUrl","source","containedImage","effectiveSourceUrl","effectiveThumbnailUrl"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ "use strict";const e=require("react/jsx-runtime"),u=require("classnames"),h=require("./index-CctUDJSJ.cjs"),N=require("react"),d=require("@phosphor-icons/react"),I=({variant:l,title:t,placeholderTitle:n="Attachment title",mimeType:r,detail:a,statusBadge:o,action:b,icon:x,trailingAction:c})=>{const s=l==="dark",i=s?t??n:t??"",m=s&&!t,f=x??h.renderTypeIcon(r,{className:u("size-5 shrink-0",s?"text-white/55":"text-black/55"),weight:"regular"});return e.jsxs("div",{className:"px-4 py-3",children:[e.jsxs("div",{className:"flex items-end gap-3",children:[e.jsxs("div",{className:"flex min-w-0 flex-1 flex-col gap-1",children:[i.trim()!==""&&e.jsx("p",{className:u("truncate text-base font-medium leading-6",{"text-black/90":!s,"text-white/30":s&&m,"text-white":s&&!m}),children:i}),e.jsxs("div",{className:"flex flex-wrap items-center gap-1",children:[f,a!=null&&a!==""&&e.jsx("span",{className:u("text-xs font-medium",s?"text-white/55":"text-black/55"),children:a}),o]})]}),c&&e.jsx("div",{className:"shrink-0",children:c})]}),b]})};function z(l){const t=h.getSourceType(l.mimeType);return t==="video"||t==="audio"}const T=({variant:l,gallery:t,title:n,showLocked:r,paymentStatus:a,topRight:o})=>{const[b,x]=N.useState(0),c=t.length,s=Math.min(b,c-1),i=t[s],m=z(i),f=m?e.jsx(d.PlayIcon,{className:"size-6",weight:"fill"}):r?a==="paid"?e.jsx(d.LockOpenIcon,{className:"size-6",weight:"fill"}):e.jsx(d.LockSimpleIcon,{className:"size-6",weight:"fill"}):null,y=p=>{p.stopPropagation(),x(g=>Math.max(0,g-1))},k=p=>{p.stopPropagation(),x(g=>Math.min(c-1,g+1))},j=s===0,v=s===c-1,w=l==="dark";return e.jsxs("div",{className:"relative aspect-[337/386] overflow-hidden rounded-t-2xl",children:[r?e.jsx("div",{className:u("absolute inset-0",w?"bg-white/10":"bg-black/5"),children:i.thumbnailUrl&&e.jsx("img",{src:i.thumbnailUrl,alt:"","aria-hidden":!0,draggable:!1,className:"absolute inset-0 h-full w-full scale-110 object-cover blur-[40px]"})}):e.jsx(h.AttachmentThumbnail,{mimeType:i.mimeType,sourceUrl:i.sourceUrl,thumbnailUrl:i.thumbnailUrl,title:n,variant:l}),f&&e.jsx("div",{className:"pointer-events-none absolute inset-0 flex items-center justify-center",children:e.jsx("div",{className:"flex size-12 items-center justify-center rounded-full bg-black/25 text-white",children:f})}),c>1&&e.jsxs(N.Fragment,{children:[e.jsx("button",{type:"button",onClick:y,disabled:j,"aria-label":"Previous item",className:u("absolute left-3 top-1/2 -translate-y-1/2 flex size-7 items-center justify-center rounded-full bg-black/30 text-white transition-opacity",j?"opacity-40":"hover:bg-black/40"),children:e.jsx(d.CaretLeftIcon,{className:"size-5",weight:"bold"})}),e.jsx("button",{type:"button",onClick:k,disabled:v,"aria-label":"Next item",className:u("absolute right-3 top-1/2 -translate-y-1/2 flex size-7 items-center justify-center rounded-full bg-black/30 text-white transition-opacity",v?"opacity-40":"hover:bg-black/40"),children:e.jsx(d.CaretRightIcon,{className:"size-5",weight:"bold"})}),e.jsx("div",{className:"pointer-events-none absolute bottom-5 left-1/2 -translate-x-1/2 rounded-lg bg-black/30 px-2 py-1",children:e.jsxs("span",{className:"text-xs font-medium leading-4 text-white",children:[s+1,"/",c]})})]}),o&&e.jsx("div",{className:"pointer-events-auto absolute right-3 top-3 z-10",children:o})]})},P=({variant:l,children:t,rootRef:n,"data-testid":r})=>e.jsx("div",{ref:n,"data-testid":r,className:u("relative w-[280px] select-none overflow-hidden rounded-md","shadow-[0_0_0_1px_rgba(0,0,0,0.04),0_1px_2px_rgba(0,0,0,0.04),0_8px_32px_rgba(0,0,0,0.1)]",l==="dark"?"bg-[#121110]":"bg-white"),children:t}),U=({paymentStatus:l,mimeType:t})=>{const n=t?h.getSourceType(t):void 0,a=n==="video"||n==="audio"?d.PlayIcon:l==="paid"?d.LockOpenIcon:d.LockSimpleIcon;return e.jsx("div",{className:"pointer-events-none absolute inset-0 flex items-center justify-center",children:e.jsx("div",{className:"flex size-12 items-center justify-center rounded-full bg-black/25 text-white",children:e.jsx(a,{className:"size-6",weight:"fill"})})})},_=({variant:l,mimeType:t,thumbnailUrl:n,title:r,source:a,showLocked:o,paymentStatus:b,topRight:x,containedImage:c=!1})=>{const s=o||a==null?void 0:a.sourceUrl,i=o?n:(a==null?void 0:a.thumbnailUrl)??n,m=l==="dark";return e.jsxs("div",{className:"relative overflow-hidden rounded-t-2xl",children:[o?e.jsxs("div",{className:u("relative aspect-video overflow-hidden",m?"bg-white/10":"bg-black/5"),children:[i?e.jsx("img",{src:i,alt:"","aria-hidden":!0,draggable:!1,className:"absolute inset-0 h-full w-full scale-110 object-cover blur-[40px]"}):e.jsx("div",{className:"absolute inset-0 flex items-center justify-center",children:h.renderTypeIcon(t,{className:m?"size-12 text-white/20":"size-12 text-black/20",weight:"regular"})}),e.jsx(U,{paymentStatus:b,mimeType:t})]}):e.jsx(h.AttachmentThumbnail,{mimeType:t,sourceUrl:s,thumbnailUrl:i,title:r,variant:l,containedImage:c,mediaPlayerProps:s?{autoPlay:!0,loop:!0,controls:!0,muted:!1}:void 0}),x&&e.jsx("div",{className:"pointer-events-auto absolute right-3 top-3 z-10",children:x})]})};exports.CardBody=I;exports.GalleryThumbnail=T;exports.LockedCardShell=P;exports.LockedThumbnail=_;
2
+ //# sourceMappingURL=LockedThumbnail-JuPkpHeX.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LockedThumbnail-JuPkpHeX.cjs","sources":["../src/components/LockedAttachment/components/_shared/CardBody.tsx","../src/components/LockedAttachment/components/_shared/GalleryThumbnail.tsx","../src/components/LockedAttachment/components/_shared/LockedCardShell.tsx","../src/components/LockedAttachment/components/_shared/LockBadge.tsx","../src/components/LockedAttachment/components/_shared/LockedThumbnail.tsx"],"sourcesContent":["import classNames from 'classnames'\nimport React from 'react'\n\nimport { renderTypeIcon } from '../../../AttachmentCard'\n\nimport type { LockedCardVariant } from './LockedCardShell'\n\nexport interface CardBodyProps {\n variant: LockedCardVariant\n title?: string\n placeholderTitle?: string\n mimeType: string\n detail?: string\n statusBadge?: React.ReactNode\n action?: React.ReactNode\n /** Overrides the auto-detected type icon (used by Gallery to swap in an `Images` icon). */\n icon?: React.ReactNode\n /** Optional control rendered on the right of the title/status block (e.g. Composer edit pencil). */\n trailingAction?: React.ReactNode\n}\n\n/**\n * Title + status row layout shared by Composer / Sent / Received cards.\n * Layout matches the Figma `Container > Labels` group (16px h-padding, 12px v-padding,\n * 4px gap between title and status row, 4px gap inside status row).\n */\nconst CardBody: React.FC<CardBodyProps> = ({\n variant,\n title,\n placeholderTitle = 'Attachment title',\n mimeType,\n detail,\n statusBadge,\n action,\n icon,\n trailingAction,\n}) => {\n const isDark = variant === 'dark'\n const displayTitle = isDark ? (title ?? placeholderTitle) : (title ?? '')\n const titleDimmed = isDark && !title\n\n const typeIcon =\n icon ??\n renderTypeIcon(mimeType, {\n className: classNames(\n 'size-5 shrink-0',\n isDark ? 'text-white/55' : 'text-black/55'\n ),\n weight: 'regular',\n })\n\n return (\n <div className=\"px-4 py-3\">\n <div className=\"flex items-end gap-3\">\n <div className=\"flex min-w-0 flex-1 flex-col gap-1\">\n {displayTitle.trim() !== '' && (\n <p\n className={classNames('truncate text-base font-medium leading-6', {\n 'text-black/90': !isDark,\n 'text-white/30': isDark && titleDimmed,\n 'text-white': isDark && !titleDimmed,\n })}\n >\n {displayTitle}\n </p>\n )}\n\n <div className=\"flex flex-wrap items-center gap-1\">\n {typeIcon}\n\n {detail != null && detail !== '' && (\n <span\n className={classNames(\n 'text-xs font-medium',\n isDark ? 'text-white/55' : 'text-black/55'\n )}\n >\n {detail}\n </span>\n )}\n\n {statusBadge}\n </div>\n </div>\n\n {trailingAction && <div className=\"shrink-0\">{trailingAction}</div>}\n </div>\n\n {action}\n </div>\n )\n}\n\nexport default CardBody\n","import {\n CaretLeftIcon,\n CaretRightIcon,\n LockOpenIcon,\n LockSimpleIcon,\n PlayIcon,\n} from '@phosphor-icons/react'\nimport classNames from 'classnames'\nimport React, { useState } from 'react'\n\nimport { AttachmentThumbnail, getSourceType } from '../../../AttachmentCard'\nimport type {\n LockedAttachmentGalleryItem,\n PaymentStatus,\n} from '../../types'\n\nimport type { LockedCardVariant } from './LockedCardShell'\n\nexport interface GalleryThumbnailProps {\n variant: LockedCardVariant\n gallery: LockedAttachmentGalleryItem[]\n title?: string\n /** When true, all items are shown blurred + the locked/play badge is overlaid. */\n showLocked: boolean\n paymentStatus?: PaymentStatus\n /** Top-right slot (e.g. dismiss X on Composer). Rendered inside the thumbnail. */\n topRight?: React.ReactNode\n}\n\nfunction isPlayableItem(item: LockedAttachmentGalleryItem): boolean {\n const sourceType = getSourceType(item.mimeType)\n return sourceType === 'video' || sourceType === 'audio'\n}\n\n/**\n * Mixed-media carousel used by every LockedAttachment card when its `gallery`\n * prop has 2+ items. Matches the \"Gallery\" frames in the Figma design system:\n * 337/386 aspect ratio, prev/next caret buttons, a counter pill, and a center\n * lock / play indicator that swaps based on the current item.\n */\nconst GalleryThumbnail: React.FC<GalleryThumbnailProps> = ({\n variant,\n gallery,\n title,\n showLocked,\n paymentStatus,\n topRight,\n}) => {\n const [index, setIndex] = useState(0)\n const total = gallery.length\n const safeIndex = Math.min(index, total - 1)\n const current = gallery[safeIndex]\n const isVideoItem = isPlayableItem(current)\n\n // Center badge: PLAY for video items, LOCK for non-video locked items.\n // Unlocked image items show no center badge — the image is the content.\n const centerBadge = (() => {\n if (isVideoItem) {\n return <PlayIcon className=\"size-6\" weight=\"fill\" />\n }\n if (showLocked) {\n return paymentStatus === 'paid' ? (\n <LockOpenIcon className=\"size-6\" weight=\"fill\" />\n ) : (\n <LockSimpleIcon className=\"size-6\" weight=\"fill\" />\n )\n }\n return null\n })()\n\n // Stop propagation so arrow taps don't bubble up to the outer preview-toggle\n // wrapper on ComposerCard / SentCard.\n const goPrev = (e: React.MouseEvent) => {\n e.stopPropagation()\n setIndex((i) => Math.max(0, i - 1))\n }\n const goNext = (e: React.MouseEvent) => {\n e.stopPropagation()\n setIndex((i) => Math.min(total - 1, i + 1))\n }\n const atStart = safeIndex === 0\n const atEnd = safeIndex === total - 1\n\n const isDark = variant === 'dark'\n\n // Locked + unlocked render entirely separate trees so the locked state\n // doesn't end up stacking a blurred `<img>` on top of an underlying\n // `AttachmentThumbnail` (whose `bg-white/10` poster shell otherwise\n // shows through as a visible horizontal seam at the 16:9 boundary\n // inside the taller 337/386 gallery aspect).\n //\n // The locked branch uses `filter: blur` on a single full-size `<img>`\n // — not `backdrop-filter` — so the rounded corners are clipped\n // normally without any clip-path workarounds (see LockedThumbnail).\n return (\n <div className=\"relative aspect-[337/386] overflow-hidden rounded-t-2xl\">\n {showLocked ? (\n <div\n className={classNames(\n 'absolute inset-0',\n isDark ? 'bg-white/10' : 'bg-black/5'\n )}\n >\n {current.thumbnailUrl && (\n <img\n src={current.thumbnailUrl}\n alt=\"\"\n aria-hidden\n draggable={false}\n // `scale-110` hides the fuzzy edge that `filter: blur` leaves\n // around the image bounds.\n className=\"absolute inset-0 h-full w-full scale-110 object-cover blur-[40px]\"\n />\n )}\n </div>\n ) : (\n <AttachmentThumbnail\n mimeType={current.mimeType}\n sourceUrl={current.sourceUrl}\n thumbnailUrl={current.thumbnailUrl}\n title={title}\n variant={variant}\n />\n )}\n\n {centerBadge && (\n <div className=\"pointer-events-none absolute inset-0 flex items-center justify-center\">\n <div className=\"flex size-12 items-center justify-center rounded-full bg-black/25 text-white\">\n {centerBadge}\n </div>\n </div>\n )}\n\n {total > 1 && (\n <React.Fragment>\n <button\n type=\"button\"\n onClick={goPrev}\n disabled={atStart}\n aria-label=\"Previous item\"\n className={classNames(\n 'absolute left-3 top-1/2 -translate-y-1/2 flex size-7 items-center justify-center rounded-full bg-black/30 text-white transition-opacity',\n atStart ? 'opacity-40' : 'hover:bg-black/40'\n )}\n >\n <CaretLeftIcon className=\"size-5\" weight=\"bold\" />\n </button>\n <button\n type=\"button\"\n onClick={goNext}\n disabled={atEnd}\n aria-label=\"Next item\"\n className={classNames(\n 'absolute right-3 top-1/2 -translate-y-1/2 flex size-7 items-center justify-center rounded-full bg-black/30 text-white transition-opacity',\n atEnd ? 'opacity-40' : 'hover:bg-black/40'\n )}\n >\n <CaretRightIcon className=\"size-5\" weight=\"bold\" />\n </button>\n\n <div className=\"pointer-events-none absolute bottom-5 left-1/2 -translate-x-1/2 rounded-lg bg-black/30 px-2 py-1\">\n <span className=\"text-xs font-medium leading-4 text-white\">\n {safeIndex + 1}/{total}\n </span>\n </div>\n </React.Fragment>\n )}\n\n {topRight && (\n <div className=\"pointer-events-auto absolute right-3 top-3 z-10\">\n {topRight}\n </div>\n )}\n </div>\n )\n}\n\nexport default GalleryThumbnail\n","import classNames from 'classnames'\nimport React from 'react'\n\nexport type LockedCardVariant = 'dark' | 'light'\n\nexport interface LockedCardShellProps {\n variant: LockedCardVariant\n children: React.ReactNode\n rootRef?: React.Ref<HTMLDivElement>\n 'data-testid'?: string\n}\n\n/**\n * Outer chrome for every LockedAttachment card.\n * Mirrors the `shadow-400` + 16px radius treatment from the Figma design system.\n */\nconst LockedCardShell: React.FC<LockedCardShellProps> = ({\n variant,\n children,\n rootRef,\n 'data-testid': dataTestId,\n}) => (\n <div\n ref={rootRef}\n data-testid={dataTestId}\n className={classNames(\n 'relative w-[280px] select-none overflow-hidden rounded-md',\n 'shadow-[0_0_0_1px_rgba(0,0,0,0.04),0_1px_2px_rgba(0,0,0,0.04),0_8px_32px_rgba(0,0,0,0.1)]',\n variant === 'dark' ? 'bg-[#121110]' : 'bg-white'\n )}\n >\n {children}\n </div>\n)\n\nexport default LockedCardShell\n","import {\n LockOpenIcon,\n LockSimpleIcon,\n PlayIcon,\n} from '@phosphor-icons/react'\nimport React from 'react'\n\nimport { getSourceType } from '../../../AttachmentCard'\nimport type { PaymentStatus } from '../../types'\n\n/**\n * Centered 48x48 lock badge that overlays the thumbnail of any locked attachment.\n * Uses `bg-black/25` from the design system's `alpha-black-25` token.\n *\n * For playable media (video/audio), the badge shows a play icon instead of a\n * lock — matching the convention used by `GalleryThumbnail`.\n */\nexport const LockBadge: React.FC<{\n paymentStatus?: PaymentStatus\n mimeType?: string\n}> = ({ paymentStatus, mimeType }) => {\n const sourceType = mimeType ? getSourceType(mimeType) : undefined\n const isPlayable = sourceType === 'video' || sourceType === 'audio'\n\n const Icon = isPlayable\n ? PlayIcon\n : paymentStatus === 'paid'\n ? LockOpenIcon\n : LockSimpleIcon\n return (\n <div className=\"pointer-events-none absolute inset-0 flex items-center justify-center\">\n <div className=\"flex size-12 items-center justify-center rounded-full bg-black/25 text-white\">\n <Icon className=\"size-6\" weight=\"fill\" />\n </div>\n </div>\n )\n}\n\nexport default LockBadge\n","import classNames from 'classnames'\nimport React from 'react'\n\nimport {\n AttachmentThumbnail,\n renderTypeIcon,\n} from '../../../AttachmentCard'\nimport type { LockedAttachmentSource, PaymentStatus } from '../../types'\n\nimport { LockBadge } from './LockBadge'\nimport type { LockedCardVariant } from './LockedCardShell'\n\nexport interface LockedThumbnailProps {\n variant: LockedCardVariant\n mimeType: string\n thumbnailUrl?: string\n title?: string\n /**\n * When provided, the underlying source is shown unblurred (preview / unlocked state).\n * When undefined, the thumbnail is treated as locked: blurred with a central lock badge.\n */\n source?: LockedAttachmentSource\n /**\n * Forces the locked appearance even when a source is provided. Used for the\n * Composer card where the locked treatment is the default and the user toggles\n * a preview in place.\n */\n showLocked: boolean\n paymentStatus?: PaymentStatus\n /** Top-right overlay (e.g. dismiss X on Composer). Rendered inside the thumbnail. */\n topRight?: React.ReactNode\n /** When true, renders the unlocked image with aspect-video + object-contain. */\n containedImage?: boolean\n}\n\n/**\n * Renders the 180px-tall media area for a LockedAttachment card.\n * Handles three visual states:\n * - locked: blurred thumbnail + centered 48px lock badge\n * - preview/paid: clear thumbnail (the underlying source)\n * - empty: type-icon placeholder\n */\nconst LockedThumbnail: React.FC<LockedThumbnailProps> = ({\n variant,\n mimeType,\n thumbnailUrl,\n title,\n source,\n showLocked,\n paymentStatus,\n topRight,\n containedImage = false,\n}) => {\n const effectiveSourceUrl = showLocked ? undefined : source?.sourceUrl\n const effectiveThumbnailUrl = showLocked\n ? thumbnailUrl\n : (source?.thumbnailUrl ?? thumbnailUrl)\n\n const isDark = variant === 'dark'\n\n // Locked + unlocked render entirely separate trees so the locked state\n // doesn't end up stacking a blurred `<img>` on top of an underlying\n // `AttachmentThumbnail` (whose `bg-white/10` poster shell otherwise\n // shows through as a visible horizontal seam at the 16:9 boundary).\n //\n // The locked branch uses `filter: blur` on a single full-size `<img>`\n // — not `backdrop-filter` — because `backdrop-filter` paints in its\n // own compositing layer that escapes ancestor `overflow: hidden` on\n // Chrome/Safari and flattens the card's top corners. `filter` is\n // clipped normally, so the rounded corners stay round without any\n // clip-path workarounds.\n return (\n <div className=\"relative overflow-hidden rounded-t-2xl\">\n {showLocked ? (\n <div\n className={classNames(\n 'relative aspect-video overflow-hidden',\n isDark ? 'bg-white/10' : 'bg-black/5'\n )}\n >\n {effectiveThumbnailUrl ? (\n <img\n src={effectiveThumbnailUrl}\n alt=\"\"\n aria-hidden\n draggable={false}\n // `scale-110` hides the fuzzy semi-transparent edge that\n // `filter: blur` leaves around the image bounds.\n className=\"absolute inset-0 h-full w-full scale-110 object-cover blur-[40px]\"\n />\n ) : (\n <div className=\"absolute inset-0 flex items-center justify-center\">\n {renderTypeIcon(mimeType, {\n className: isDark\n ? 'size-12 text-white/20'\n : 'size-12 text-black/20',\n weight: 'regular',\n })}\n </div>\n )}\n <LockBadge paymentStatus={paymentStatus} mimeType={mimeType} />\n </div>\n ) : (\n <AttachmentThumbnail\n mimeType={mimeType}\n sourceUrl={effectiveSourceUrl}\n thumbnailUrl={effectiveThumbnailUrl}\n title={title}\n variant={variant}\n containedImage={containedImage}\n mediaPlayerProps={\n effectiveSourceUrl\n ? { autoPlay: true, loop: true, controls: true, muted: false }\n : undefined\n }\n />\n )}\n\n {topRight && (\n <div className=\"pointer-events-auto absolute right-3 top-3 z-10\">\n {topRight}\n </div>\n )}\n </div>\n )\n}\n\nexport default LockedThumbnail\n"],"names":["CardBody","variant","title","placeholderTitle","mimeType","detail","statusBadge","action","icon","trailingAction","isDark","displayTitle","titleDimmed","typeIcon","renderTypeIcon","classNames","jsxs","jsx","isPlayableItem","item","sourceType","getSourceType","GalleryThumbnail","gallery","showLocked","paymentStatus","topRight","index","setIndex","useState","total","safeIndex","current","isVideoItem","centerBadge","PlayIcon","LockOpenIcon","LockSimpleIcon","goPrev","e","i","goNext","atStart","atEnd","AttachmentThumbnail","React","CaretLeftIcon","CaretRightIcon","LockedCardShell","children","rootRef","dataTestId","LockBadge","Icon","LockedThumbnail","thumbnailUrl","source","containedImage","effectiveSourceUrl","effectiveThumbnailUrl"],"mappings":"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"}