@linktr.ee/messaging-react 3.9.0-rc-1783472582 → 3.9.0-rc-1783575293

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 (85) hide show
  1. package/dist/Card-B7ccxsKf.cjs +2 -0
  2. package/dist/Card-B7ccxsKf.cjs.map +1 -0
  3. package/dist/Card-CXRSSrCh.cjs +2 -0
  4. package/dist/Card-CXRSSrCh.cjs.map +1 -0
  5. package/dist/Card-DaUKmwtb.js +105 -0
  6. package/dist/Card-DaUKmwtb.js.map +1 -0
  7. package/dist/Card-Q1Ee_UCB.js +693 -0
  8. package/dist/Card-Q1Ee_UCB.js.map +1 -0
  9. package/dist/{LoadingDots-nW6r0-Kg.js → LoadingDots-Ct3WmVlT.js} +18 -21
  10. package/dist/LoadingDots-Ct3WmVlT.js.map +1 -0
  11. package/dist/LoadingDots-D9GGphgM.cjs +2 -0
  12. package/dist/LoadingDots-D9GGphgM.cjs.map +1 -0
  13. package/dist/assets/index.css +1 -1
  14. package/dist/{index-B8gqCioX.js → index-BiBvPADG.js} +1301 -1315
  15. package/dist/index-BiBvPADG.js.map +1 -0
  16. package/dist/index-DuPa2mUh.cjs +2 -0
  17. package/dist/index-DuPa2mUh.cjs.map +1 -0
  18. package/dist/index.cjs +1 -1
  19. package/dist/index.d.ts +149 -129
  20. package/dist/index.js +1 -1
  21. package/package.json +2 -2
  22. package/src/components/ChannelList/CustomChannelPreview.test.tsx +95 -0
  23. package/src/components/ChannelList/CustomChannelPreview.tsx +21 -9
  24. package/src/components/{LockedAttachment → CustomMessage/LockedAttachment}/LockedAttachment.stories.tsx +87 -78
  25. package/src/components/{LockedAttachment → CustomMessage/LockedAttachment}/LockedAttachment.test.tsx +24 -11
  26. package/src/components/CustomMessage/LockedAttachment/components/Media/Card.tsx +358 -0
  27. package/src/components/{LockedAttachment → CustomMessage/LockedAttachment}/components/Media/CardActions.tsx +5 -0
  28. package/src/components/CustomMessage/LockedAttachment/components/Media/types.ts +79 -0
  29. package/src/components/CustomMessage/LockedAttachment/components/Text/Card.tsx +155 -0
  30. package/src/components/CustomMessage/LockedAttachment/components/Text/types.ts +58 -0
  31. package/src/components/{LockedAttachment → CustomMessage/LockedAttachment}/components/_shared/CardBody.tsx +5 -4
  32. package/src/components/{LockedAttachment → CustomMessage/LockedAttachment}/components/_shared/GalleryThumbnail.tsx +2 -2
  33. package/src/components/{LockedAttachment → CustomMessage/LockedAttachment}/components/_shared/LockBadge.tsx +1 -1
  34. package/src/components/{LockedAttachment → CustomMessage/LockedAttachment}/components/_shared/PurchaseStatusBadge.tsx +0 -12
  35. package/src/components/{LockedAttachment → CustomMessage/LockedAttachment}/components/_shared/SingleThumbnail.tsx +1 -1
  36. package/src/components/{LockedAttachment → CustomMessage/LockedAttachment}/components/_shared/SkeletonTextLines.tsx +3 -13
  37. package/src/components/CustomMessage/LockedAttachment/components/_shared/TextShell.tsx +30 -0
  38. package/src/components/{LockedAttachment → CustomMessage/LockedAttachment}/components/_shared/usePreviewToggle.test.ts +1 -1
  39. package/src/components/CustomMessage/LockedAttachment/index.tsx +105 -0
  40. package/src/components/CustomMessage/LockedAttachment/types.ts +44 -0
  41. package/src/components/CustomMessage/context.tsx +1 -1
  42. package/src/components/CustomMessage/index.tsx +45 -50
  43. package/src/components/CustomMessageInput/CustomMessageInput.stories.tsx +5 -3
  44. package/src/index.ts +3 -5
  45. package/src/styles.css +8 -1
  46. package/src/types.ts +1 -1
  47. package/dist/Card-BVZM0-06.js +0 -108
  48. package/dist/Card-BVZM0-06.js.map +0 -1
  49. package/dist/Card-D0hLrOxX.js +0 -221
  50. package/dist/Card-D0hLrOxX.js.map +0 -1
  51. package/dist/Card-DIr1UnfC.cjs +0 -2
  52. package/dist/Card-DIr1UnfC.cjs.map +0 -1
  53. package/dist/Card-DeSgaRTL.cjs +0 -2
  54. package/dist/Card-DeSgaRTL.cjs.map +0 -1
  55. package/dist/Card-DnaXMVrT.cjs +0 -2
  56. package/dist/Card-DnaXMVrT.cjs.map +0 -1
  57. package/dist/Card-xv52Ug43.js +0 -61
  58. package/dist/Card-xv52Ug43.js.map +0 -1
  59. package/dist/LoadingDots-Cn2y7g7L.cjs +0 -2
  60. package/dist/LoadingDots-Cn2y7g7L.cjs.map +0 -1
  61. package/dist/LoadingDots-nW6r0-Kg.js.map +0 -1
  62. package/dist/TextShell-CKQOFxoH.cjs +0 -2
  63. package/dist/TextShell-CKQOFxoH.cjs.map +0 -1
  64. package/dist/TextShell-DIyj9zQ7.js +0 -40
  65. package/dist/TextShell-DIyj9zQ7.js.map +0 -1
  66. package/dist/index-B8gqCioX.js.map +0 -1
  67. package/dist/index-IkikzyWM.cjs +0 -2
  68. package/dist/index-IkikzyWM.cjs.map +0 -1
  69. package/dist/usePreviewToggle-B0B8cfxy.cjs +0 -2
  70. package/dist/usePreviewToggle-B0B8cfxy.cjs.map +0 -1
  71. package/dist/usePreviewToggle-DRhRTUou.js +0 -357
  72. package/dist/usePreviewToggle-DRhRTUou.js.map +0 -1
  73. package/src/components/LockedAttachment/components/Composer/Card.tsx +0 -201
  74. package/src/components/LockedAttachment/components/Media/Card.tsx +0 -235
  75. package/src/components/LockedAttachment/components/Text/Card.tsx +0 -177
  76. package/src/components/LockedAttachment/components/_shared/TextShell.tsx +0 -19
  77. package/src/components/LockedAttachment/index.tsx +0 -52
  78. package/src/components/LockedAttachment/types.ts +0 -75
  79. /package/src/components/{LockedAttachment → CustomMessage/LockedAttachment}/components/_shared/CardThumbnail.tsx +0 -0
  80. /package/src/components/{LockedAttachment → CustomMessage/LockedAttachment}/components/_shared/LoadingDots.tsx +0 -0
  81. /package/src/components/{LockedAttachment → CustomMessage/LockedAttachment}/components/_shared/MediaShell.tsx +0 -0
  82. /package/src/components/{LockedAttachment → CustomMessage/LockedAttachment}/components/_shared/PreviewToggle.tsx +0 -0
  83. /package/src/components/{LockedAttachment → CustomMessage/LockedAttachment}/components/_shared/PriceLockRow.tsx +0 -0
  84. /package/src/components/{LockedAttachment → CustomMessage/LockedAttachment}/components/_shared/usePreviewToggle.ts +0 -0
  85. /package/src/components/{LockedAttachment → CustomMessage/LockedAttachment}/components/_shared/useReceivedSource.ts +0 -0
package/dist/index.cjs CHANGED
@@ -1,2 +1,2 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("./index-IkikzyWM.cjs");exports.ActionButton=e.ActionButton;exports.Avatar=e.Avatar;exports.ChannelEmptyState=e.ChannelEmptyState;exports.ChannelList=e.ChannelList;exports.ChannelView=e.ChannelView;exports.CustomMessageProvider=e.CustomMessageProvider;exports.FaqList=e.FaqList;exports.FaqListItem=e.FaqListItem;exports.LinkAttachment=e.LinkAttachment;exports.LockedAttachment=e.LockedAttachment;exports.MediaMessage=e.MediaMessage;exports.MessageAttachment=e.MessageAttachment;exports.MessageVoteButtons=e.MessageVoteButtons;exports.MessagingProvider=e.MessagingProvider;exports.MessagingShell=e.MessagingShell;exports.buildCompactMetaLabel=e.buildCompactMetaLabel;exports.formatFileSize=e.formatFileSize;exports.formatRelativeTime=e.formatRelativeTime;exports.getFileExtensionLabel=e.getFileExtensionLabel;exports.getMessageDisplayText=e.getMessageDisplayText;exports.isLinkAttachment=e.isLinkAttachment;exports.isUuidLike=e.isUuidLike;exports.messageAttachmentGroupPositionFromStream=e.bubbleGroupPositionFromStream;exports.normalizeLanguageCode=e.normalizeLanguageCode;exports.resolveConversationParticipant=e.resolveConversationParticipant;exports.resolveLinkAttachment=e.resolveLinkAttachment;exports.resolveMediaFromMessage=e.resolveMediaFromMessage;exports.resolveParticipantDisplayName=e.resolveParticipantDisplayName;exports.useComposerLocked=e.useComposerLocked;exports.useCustomMessage=e.useCustomMessage;exports.useMessageVote=e.useMessageVote;exports.useMessaging=e.useMessaging;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("./index-DuPa2mUh.cjs");exports.ActionButton=e.ActionButton;exports.Avatar=e.Avatar;exports.ChannelEmptyState=e.ChannelEmptyState;exports.ChannelList=e.ChannelList;exports.ChannelView=e.ChannelView;exports.CustomMessageProvider=e.CustomMessageProvider;exports.FaqList=e.FaqList;exports.FaqListItem=e.FaqListItem;exports.LinkAttachment=e.LinkAttachment;exports.LockedAttachment=e.LockedAttachment;exports.MediaMessage=e.MediaMessage;exports.MessageAttachment=e.MessageAttachment;exports.MessageVoteButtons=e.MessageVoteButtons;exports.MessagingProvider=e.MessagingProvider;exports.MessagingShell=e.MessagingShell;exports.buildCompactMetaLabel=e.buildCompactMetaLabel;exports.formatFileSize=e.formatFileSize;exports.formatRelativeTime=e.formatRelativeTime;exports.getFileExtensionLabel=e.getFileExtensionLabel;exports.getMessageDisplayText=e.getMessageDisplayText;exports.isLinkAttachment=e.isLinkAttachment;exports.isUuidLike=e.isUuidLike;exports.messageAttachmentGroupPositionFromStream=e.bubbleGroupPositionFromStream;exports.normalizeLanguageCode=e.normalizeLanguageCode;exports.resolveConversationParticipant=e.resolveConversationParticipant;exports.resolveLinkAttachment=e.resolveLinkAttachment;exports.resolveMediaFromMessage=e.resolveMediaFromMessage;exports.resolveParticipantDisplayName=e.resolveParticipantDisplayName;exports.useComposerLocked=e.useComposerLocked;exports.useCustomMessage=e.useCustomMessage;exports.useMessageVote=e.useMessageVote;exports.useMessaging=e.useMessaging;
2
2
  //# sourceMappingURL=index.cjs.map
package/dist/index.d.ts CHANGED
@@ -404,38 +404,6 @@ export declare interface ChannelViewProps {
404
404
  onParticipantNameClick?: () => void;
405
405
  }
406
406
 
407
- export declare interface ComposerCardProps extends LockedAttachmentBaseProps {
408
- /**
409
- * The message's own text, shown as-is (not redacted yet — the message
410
- * hasn't been sent). Only used when `contentType` is `"text"`.
411
- */
412
- text?: string;
413
- /** Placeholder shown in the amount slot before one is configured. */
414
- placeholderAmountText?: string;
415
- /**
416
- * Renders a persistent dismiss X on the thumbnail (media) or next to the
417
- * text (text mode); called to remove the draft attachment. Unrelated to
418
- * `TextCardProps.onDismiss`, which only appears for `isMine` after a
419
- * failed payment.
420
- */
421
- onDismiss?: () => void;
422
- /** Fired the first time the composer taps the thumbnail to preview. */
423
- onPreviewClick?: () => void;
424
- /**
425
- * Lazily loads the underlying source so the composer can preview the
426
- * attachment they're about to send. Called the first time the thumbnail is
427
- * tapped; the returned source is cached and reused on subsequent toggles.
428
- * Ignored when `contentType` is `"text"`.
429
- */
430
- onFetchSource?: FetchSourceHandler;
431
- /**
432
- * When provided, renders a pencil button in the body bottom-right that the
433
- * composer can use to edit the attachment metadata (e.g. open the price /
434
- * gallery editor). Matches the Composer "Button" instance in Figma.
435
- */
436
- onEditClick?: () => void;
437
- }
438
-
439
407
  /** Shared props for the `Composer` state of every attachment type. */
440
408
  declare interface ComposerExtras {
441
409
  /** Renders a dismiss `×` button overlaid on the attachment. */
@@ -450,6 +418,19 @@ export declare interface CustomMessageRegistry {
450
418
 
451
419
  declare type DmAgentSystemType = 'SYSTEM_DM_AGENT_PAUSED' | 'SYSTEM_DM_AGENT_RESUMED';
452
420
 
421
+ declare type DraftProps = MediaCardDraftProps & {
422
+ /** Drafting is always the sender — doesn't apply here. */
423
+ isMine?: never;
424
+ /**
425
+ * "text" renders as a chat bubble showing the real draft `text` instead of
426
+ * a media thumbnail card. Media-only fields below are ignored in text mode.
427
+ * Defaults to `"media"`.
428
+ */
429
+ contentType?: 'text' | 'media';
430
+ /** The message's own text, shown as-is (not redacted yet). Only used when `contentType` is `"text"`. */
431
+ renderedText?: default_2.ReactNode;
432
+ };
433
+
453
434
  export declare interface Faq {
454
435
  id: string;
455
436
  question: string;
@@ -479,11 +460,7 @@ export declare interface FaqListProps {
479
460
  avatarName?: string;
480
461
  }
481
462
 
482
- /**
483
- * Lazily fetches the underlying source (URL/thumbnail) for a locked
484
- * attachment. Returns `void` to signal "no source available" — the card
485
- * stays locked. Shared by `ComposerCardProps`/`MediaCardProps.onFetchSource`.
486
- */
463
+ /** Fetches the underlying source; `void` return means none available. */
487
464
  declare type FetchSourceHandler = () => Promise<LockedAttachmentSource | void>;
488
465
 
489
466
  declare interface FileAttachmentSharedProps extends MessageAttachmentBaseProps {
@@ -735,117 +712,110 @@ export declare interface LinkAttachmentSentCardProps extends LinkAttachmentBaseP
735
712
  onClick?: () => void;
736
713
  }
737
714
 
738
- export declare const LockedAttachment: {
739
- Composer: (props: ComposerCardProps) => JSX_2.Element;
740
- Text: (props: LockedAttachmentTextCardProps) => JSX_2.Element;
741
- Media: (props: LockedAttachmentMediaCardProps) => JSX_2.Element;
742
- };
743
-
744
- declare interface LockedAttachmentBaseProps {
745
- title?: string;
746
- /** Dimmed fallback shown in the title slot when `title` is unset (no attachment metadata yet — still drafting). */
747
- placeholderTitle?: string;
748
- mimeType?: string;
749
- thumbnailUrl?: string;
750
- detail?: string;
751
- amountText?: string;
752
- /**
753
- * When provided with 2+ items, the card renders as a mixed-media carousel
754
- * (e.g. a couple of photos + a video) instead of a single thumbnail. Each
755
- * item brings its own thumbnail and optional source so that
756
- * `LockedAttachment.Composer` / `.Media` can share the same carousel
757
- * chrome.
758
- */
759
- gallery?: LockedAttachmentGalleryItem[];
760
- /**
761
- * "text" renders the card as a locked/plain chat bubble showing the
762
- * message's own text (skeleton-blurred until purchased) instead of a media
763
- * thumbnail card — for a paid text-only message. Media-only props
764
- * (`mimeType`, `thumbnailUrl`, `title`, `detail`, `gallery`) are ignored in
765
- * this mode. Defaults to `"media"`.
766
- */
767
- contentType?: 'text' | 'media';
768
- }
715
+ export declare const LockedAttachment: default_2.FC<LockedAttachmentProps>;
769
716
 
770
717
  export declare interface LockedAttachmentContextValue {
718
+ /** Whether the given message is mid-checkout. */
771
719
  isUnlocking: (id: string) => boolean;
720
+ /** Opens the checkout flow for a message. */
772
721
  onUnlockClick?: (message: LocalMessage, channel: Channel) => void;
722
+ /** Handles a Download click for a message. */
773
723
  onDownloadClick?: (message: LocalMessage, channel: Channel) => void;
774
- /**
775
- * Reveal the locked media's underlying source (URL/thumbnail). Not used
776
- * for a paid text message — the backend writes the real text directly onto
777
- * the message on purchase and `message.text` alone drives that reveal.
778
- */
724
+ /** Reveals the locked media's source. Unused for text — payment writes `message.text` directly. */
779
725
  onFetchSource?: (message: LocalMessage, channel: Channel) => Promise<LockedAttachmentSource | void>;
780
726
  }
781
727
 
782
728
  export declare interface LockedAttachmentGalleryItem {
783
- /** MIME type of this carousel item — drives the per-item play / lock affordance. */
729
+ /** Drives the per-item play/lock affordance. */
784
730
  mimeType: string;
785
- /** Poster image used for both the locked (blurred) and unlocked preview state. */
731
+ /** Poster image, locked or unlocked. */
786
732
  thumbnailUrl?: string;
787
- /** Underlying source (image or video URL) — shown only when unlocked. */
733
+ /** Media source, shown only when unlocked. */
788
734
  sourceUrl?: string;
789
735
  }
790
736
 
791
- export declare type LockedAttachmentMediaCardProps = (MediaCardSharedProps & {
792
- isMine: true;
793
- /** Dimmed fallback shown in the title slot when `title` is unset. */
794
- placeholderTitle?: string;
795
- /** Fired the first time the sender taps the thumbnail to preview their own attachment. */
796
- onPreviewClick?: () => void;
797
- }) | (MediaCardSharedProps & {
798
- isMine: false;
799
- /**
800
- * When true, shows a loading spinner on the Unlock button. Driven by
801
- * the LockedAttachmentContext (e.g. checkout in progress).
802
- */
803
- isUnlocking?: boolean;
804
- /**
805
- * Called when the recipient clicks Unlock on an unpaid attachment.
806
- * Use this to open a checkout flow. Omit to hide the Unlock button.
807
- */
808
- onUnlockClick?: () => void;
809
- /**
810
- * Called when the recipient clicks Download on an unlocked card.
811
- * Omit to hide the Download button.
812
- */
813
- onDownloadClick?: () => void;
814
- });
737
+ /**
738
+ * The card for a paid attachment — a still-drafting `isDraft` card, a static
739
+ * receiver-facing `isPreview` mockup, or a sent text/media card discriminated
740
+ * by `isMine` + `contentType`. Internally lazy-loads only the one underlying
741
+ * implementation the resolved branch needs.
742
+ */
743
+ export declare type LockedAttachmentProps = DraftProps | PreviewProps | SentTextOwnProps | SentTextOtherProps | SentMediaOwnProps | SentMediaOtherProps;
815
744
 
816
745
  export declare interface LockedAttachmentSource {
817
- /** Proxied URL used by the media player for in-app playback. */
746
+ /** Proxied URL for in-app playback. */
818
747
  sourceUrl: string;
819
- /** URL opened when the visitor clicks Download — may be a file or a web destination. */
748
+ /** Opened when the visitor clicks Download. */
820
749
  redeemUrl?: string;
821
- /** Thumbnail URL from the fetched asset — overrides the metadata thumbnail when present. */
750
+ /** Overrides the metadata thumbnail when present. */
822
751
  thumbnailUrl?: string;
823
752
  }
824
753
 
825
- export declare type LockedAttachmentTextCardProps = (TextCardSharedProps & {
826
- isMine: true;
827
- }) | (TextCardSharedProps & {
828
- isMine: false;
829
- /**
830
- * Called when the recipient clicks Unlock on an unpaid attachment.
831
- * Use this to open a checkout flow. Omit to hide the Unlock button.
832
- */
833
- onUnlockClick?: () => void;
834
- /**
835
- * When true, shows a loading spinner on the Unlock button. Driven by
836
- * the LockedAttachmentContext (e.g. checkout in progress).
837
- */
838
- isUnlocking?: boolean;
839
- });
840
-
841
- declare interface MediaCardSharedProps {
754
+ declare interface MediaCardBaseProps {
755
+ /** Attachment title. */
842
756
  title?: string;
757
+ /** Fallback shown when `title` is unset. */
758
+ placeholderTitle?: string;
759
+ /** Drives icon/thumbnail rendering. */
843
760
  mimeType?: string;
761
+ /** Poster image, locked or unlocked. */
844
762
  thumbnailUrl?: string;
763
+ /** Secondary description text. */
845
764
  detail?: string;
765
+ /** Amount label. */
846
766
  amountText?: string;
847
- paymentStatus?: PaymentStatus;
767
+ /** 2+ items renders as a carousel instead of a single thumbnail. */
848
768
  gallery?: LockedAttachmentGalleryItem[];
769
+ }
770
+
771
+ declare interface MediaCardDraftProps extends MediaCardBaseProps {
772
+ /** True while composing — shows the real (unredacted) thumbnail. */
773
+ isDraft: true;
774
+ isPreview?: false;
775
+ /** Fallback amount before a price is set. */
776
+ placeholderAmountText?: string;
777
+ /** Lazily fetches the source on first tap; cached and reused after. */
778
+ onFetchSource?: FetchSourceHandler;
779
+ /** Fired on first tap to preview. */
780
+ onPreviewClick?: () => void;
781
+ /** Removes the draft attachment. */
782
+ onDismiss?: () => void;
783
+ /** Renders a pencil button to edit the attachment metadata. */
784
+ onEditClick?: () => void;
785
+ }
786
+
787
+ declare type MediaCardOtherProps = MediaCardSentBaseProps & {
788
+ /** Recipient's view. */
789
+ isMine: false;
790
+ /** Opens checkout on click. Omit to hide the button. */
791
+ onUnlockClick?: () => void;
792
+ /** Omit to hide the Download button. */
793
+ onDownloadClick?: () => void;
794
+ /** Spinner on Unlock while checkout runs. */
795
+ isUnlocking?: boolean;
796
+ };
797
+
798
+ declare type MediaCardOwnProps = MediaCardSentBaseProps & {
799
+ /** Sender's own view. */
800
+ isMine: true;
801
+ /** Fired on first tap to preview own attachment. */
802
+ onPreviewClick?: () => void;
803
+ };
804
+
805
+ declare interface MediaCardPreviewProps extends MediaCardBaseProps {
806
+ /** True to render exactly as the recipient will see it before purchase — always locked/blurred. */
807
+ isDraft?: false;
808
+ isPreview: true;
809
+ /** Fallback amount before a price is set. */
810
+ placeholderAmountText?: string;
811
+ }
812
+
813
+ declare interface MediaCardSentBaseProps extends MediaCardBaseProps {
814
+ /** False once sent. */
815
+ isDraft?: false;
816
+ isPreview?: false;
817
+ /** Drives the lock/skeleton/badge state. */
818
+ paymentStatus?: PaymentStatus;
849
819
  onFetchSource?: FetchSourceHandler;
850
820
  }
851
821
 
@@ -1468,6 +1438,16 @@ declare interface PdfAttachmentSharedProps extends MessageAttachmentBaseProps {
1468
1438
  onClick?: (index: number) => boolean | void;
1469
1439
  }
1470
1440
 
1441
+ declare type PreviewProps = MediaCardPreviewProps & {
1442
+ /** Drafting is always the sender — doesn't apply here. */
1443
+ isMine?: never;
1444
+ /**
1445
+ * "text" renders as a chat bubble instead of a media thumbnail card.
1446
+ * Media-only fields below are ignored in text mode. Defaults to `"media"`.
1447
+ */
1448
+ contentType?: 'text' | 'media';
1449
+ };
1450
+
1471
1451
  /**
1472
1452
  * Resolves the member to display as the conversation's "other party".
1473
1453
  *
@@ -1490,16 +1470,56 @@ export declare function resolveMediaFromMessage(message: LocalMessage): MediaMes
1490
1470
  */
1491
1471
  export declare function resolveParticipantDisplayName(user?: ParticipantDisplayUser | null): string;
1492
1472
 
1493
- declare interface TextCardSharedProps {
1473
+ declare type SentMediaOtherProps = MediaCardOtherProps & {
1474
+ contentType?: 'media';
1475
+ };
1476
+
1477
+ declare type SentMediaOwnProps = MediaCardOwnProps & {
1478
+ contentType?: 'media';
1479
+ /** Sender's view has no unlock/download flow — doesn't apply here. */
1480
+ onUnlockClick?: never;
1481
+ isUnlocking?: never;
1482
+ onDownloadClick?: never;
1483
+ };
1484
+
1485
+ declare type SentTextOtherProps = TextCardOtherProps & {
1486
+ contentType: 'text';
1487
+ };
1488
+
1489
+ declare type SentTextOwnProps = TextCardOwnProps & {
1490
+ contentType: 'text';
1491
+ /** Sender's view has no unlock flow — doesn't apply here. */
1492
+ onUnlockClick?: never;
1493
+ isUnlocking?: never;
1494
+ };
1495
+
1496
+ declare interface TextCardBaseProps {
1497
+ /** Amount label. */
1494
1498
  amountText?: string;
1499
+ }
1500
+
1501
+ declare type TextCardOtherProps = TextCardSentBaseProps & {
1502
+ /** Recipient's view. */
1503
+ isMine: false;
1504
+ /** Spinner on Unlock while checkout runs. */
1505
+ isUnlocking?: boolean;
1506
+ /** Opens checkout on click. Omit to hide the button. */
1507
+ onUnlockClick?: () => void;
1508
+ };
1509
+
1510
+ declare type TextCardOwnProps = TextCardSentBaseProps & {
1511
+ /** Sender's own view. */
1512
+ isMine: true;
1513
+ };
1514
+
1515
+ declare interface TextCardSentBaseProps extends TextCardBaseProps {
1516
+ /** False once sent. */
1517
+ isDraft?: false;
1518
+ isPreview?: false;
1519
+ /** Revealed content once paid. */
1520
+ renderedText?: default_2.ReactNode;
1521
+ /** Drives the lock/skeleton/badge state. */
1495
1522
  paymentStatus?: PaymentStatus;
1496
- /**
1497
- * The message's real, revealed content — shown instead of the skeleton
1498
- * loader once `paymentStatus` is `'paid'`. Ignored otherwise. Lets the
1499
- * caller supply its own text renderer (e.g. Stream's `MessageText`)
1500
- * without `LockedAttachment` depending on it.
1501
- */
1502
- children?: default_2.ReactNode;
1503
1523
  }
1504
1524
 
1505
1525
  /**
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { a as e, b as t, C as i, c as o, d as n, e as m, F as r, f as g, L as l, h as M, M as c, i as u, j as L, k as h, l as d, m as p, n as v, o as C, p as A, q as k, s as F, t as P, u as b, v as f, w as x, x as y, y as S, z as q, B as z, D as B, E as D, G as E } from "./index-B8gqCioX.js";
1
+ import { a as e, b as t, C as i, c as o, d as n, e as m, F as r, f as g, L as l, h as M, M as c, i as u, j as L, k as h, l as d, m as p, n as v, o as C, p as A, q as k, s as F, t as P, u as b, v as f, w as x, x as y, y as S, z as q, B as z, D as B, E as D, G as E } from "./index-BiBvPADG.js";
2
2
  export {
3
3
  e as ActionButton,
4
4
  t as Avatar,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@linktr.ee/messaging-react",
3
- "version": "3.9.0-rc-1783472582",
3
+ "version": "3.9.0-rc-1783575293",
4
4
  "description": "React messaging components built on messaging-core for web applications",
5
5
  "type": "module",
6
6
  "main": "dist/index.cjs",
@@ -50,7 +50,7 @@
50
50
  },
51
51
  "dependencies": {
52
52
  "@linktr.ee/component-library": "11.8.6",
53
- "@linktr.ee/messaging-core": "2.3.0-rc-1783472582",
53
+ "@linktr.ee/messaging-core": "2.3.0-rc-1783575293",
54
54
  "@phosphor-icons/react": "^2.1.10"
55
55
  },
56
56
  "devDependencies": {
@@ -260,6 +260,101 @@ describe('CustomChannelPreview', () => {
260
260
  expect(screen.getByText('No messages yet')).toBeInTheDocument()
261
261
  })
262
262
 
263
+ it('shows a locked indicator for a text-only attachment message, even when paid', () => {
264
+ // The channel-list preview always shows the locked wording for an
265
+ // attachment-type message and never echoes `message.text` — the
266
+ // revealed content is only ever shown inside the full conversation.
267
+ const channel = createMockChannel([
268
+ {
269
+ id: 'msg-1',
270
+ text: 'REDACTED',
271
+ type: 'regular',
272
+ created_at: new Date('2026-01-01T00:00:00.000Z'),
273
+ user: { id: 'participant-1', name: 'Alice' },
274
+ metadata: {
275
+ custom_type: 'MESSAGE_ATTACHMENT',
276
+ attachment_content_type: 'text',
277
+ payment_status: 'paid',
278
+ },
279
+ },
280
+ ])
281
+
282
+ renderWithProviders(
283
+ <CustomChannelPreview {...defaultProps} channel={channel} />
284
+ )
285
+
286
+ expect(screen.getByText('🔒 Sent a locked message')).toBeInTheDocument()
287
+ expect(screen.queryByText(/REDACTED/)).not.toBeInTheDocument()
288
+ })
289
+
290
+ it('shows a locked indicator for an unpaid text-only attachment message', () => {
291
+ const channel = createMockChannel([
292
+ {
293
+ id: 'msg-1',
294
+ text: 'REDACTED',
295
+ type: 'regular',
296
+ created_at: new Date('2026-01-01T00:00:00.000Z'),
297
+ user: { id: 'participant-1', name: 'Alice' },
298
+ metadata: {
299
+ custom_type: 'MESSAGE_ATTACHMENT',
300
+ attachment_content_type: 'text',
301
+ payment_status: 'pending',
302
+ },
303
+ },
304
+ ])
305
+
306
+ renderWithProviders(
307
+ <CustomChannelPreview {...defaultProps} channel={channel} />
308
+ )
309
+
310
+ expect(screen.getByText('🔒 Sent a locked message')).toBeInTheDocument()
311
+ expect(screen.queryByText(/REDACTED/)).not.toBeInTheDocument()
312
+ })
313
+
314
+ it('shows a locked indicator for a media attachment message, even when paid', () => {
315
+ const channel = createMockChannel([
316
+ {
317
+ id: 'msg-1',
318
+ type: 'regular',
319
+ created_at: new Date('2026-01-01T00:00:00.000Z'),
320
+ user: { id: 'participant-1', name: 'Alice' },
321
+ metadata: {
322
+ custom_type: 'MESSAGE_ATTACHMENT',
323
+ attachment_content_type: 'media',
324
+ payment_status: 'paid',
325
+ },
326
+ },
327
+ ])
328
+
329
+ renderWithProviders(
330
+ <CustomChannelPreview {...defaultProps} channel={channel} />
331
+ )
332
+
333
+ expect(screen.getByText('🔒 Sent a locked attachment')).toBeInTheDocument()
334
+ })
335
+
336
+ it('shows a locked indicator for an unpaid media attachment message', () => {
337
+ const channel = createMockChannel([
338
+ {
339
+ id: 'msg-1',
340
+ type: 'regular',
341
+ created_at: new Date('2026-01-01T00:00:00.000Z'),
342
+ user: { id: 'participant-1', name: 'Alice' },
343
+ metadata: {
344
+ custom_type: 'MESSAGE_ATTACHMENT',
345
+ attachment_content_type: 'media',
346
+ payment_status: 'pending',
347
+ },
348
+ },
349
+ ])
350
+
351
+ renderWithProviders(
352
+ <CustomChannelPreview {...defaultProps} channel={channel} />
353
+ )
354
+
355
+ expect(screen.getByText('🔒 Sent a locked attachment')).toBeInTheDocument()
356
+ })
357
+
263
358
  it('updates starred state when the channel membership changes', () => {
264
359
  const channel = createMockChannel([])
265
360
 
@@ -1,6 +1,9 @@
1
1
  import classNames from 'classnames'
2
2
  import React from 'react'
3
- import { ChannelPreviewUIComponentProps, useChatContext } from 'stream-chat-react'
3
+ import {
4
+ ChannelPreviewUIComponentProps,
5
+ useChatContext,
6
+ } from 'stream-chat-react'
4
7
 
5
8
  import { useChannelStar } from '../../hooks/useChannelStar'
6
9
  import { formatRelativeTime } from '../../utils/formatRelativeTime'
@@ -85,14 +88,23 @@ const CustomChannelPreview = React.memo<ChannelPreviewUIComponentProps>(
85
88
  viewerLanguage,
86
89
  })
87
90
 
88
- const isTip = lastMessage?.metadata?.custom_type === 'MESSAGE_TIP'
89
- if (isTip) return displayText ? `💵 ${displayText}` : '💵 Sent a tip'
90
- const isPaid = lastMessage?.metadata?.custom_type === 'MESSAGE_PAID'
91
- if (isPaid) return displayText ? `💰 ${displayText}` : '💰 Sent a message'
92
- const isPaidAttachment =
93
- lastMessage?.metadata?.custom_type === 'MESSAGE_ATTACHMENT'
94
- if (isPaidAttachment)
95
- return displayText ? `📎 ${displayText}` : '📎 Sent an attachment'
91
+ const { custom_type, attachment_content_type } =
92
+ lastMessage?.metadata || {}
93
+
94
+ if (custom_type === 'MESSAGE_TIP') {
95
+ return displayText ? displayText : 'Sent a tip'
96
+ }
97
+ if (custom_type === 'MESSAGE_PAID') {
98
+ return displayText ? displayText : 'Sent a message'
99
+ }
100
+ if (custom_type === 'MESSAGE_ATTACHMENT') {
101
+ if (attachment_content_type === 'text') {
102
+ return '🔒 Sent a locked message'
103
+ }
104
+ if (attachment_content_type === 'media') {
105
+ return '🔒 Sent a locked attachment'
106
+ }
107
+ }
96
108
 
97
109
  if (displayText) return displayText
98
110