@linktr.ee/messaging-react 2.5.1 → 2.5.3

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 (31) hide show
  1. package/dist/{Card-d6L08Luf.cjs → Card-B7TJ2lbG.cjs} +2 -2
  2. package/dist/{Card-d6L08Luf.cjs.map → Card-B7TJ2lbG.cjs.map} +1 -1
  3. package/dist/{Card-Dppew_bA.cjs → Card-BbMWytg6.cjs} +2 -2
  4. package/dist/{Card-Dppew_bA.cjs.map → Card-BbMWytg6.cjs.map} +1 -1
  5. package/dist/{Card-BpVephnj.js → Card-BwygcpzI.js} +2 -2
  6. package/dist/{Card-BpVephnj.js.map → Card-BwygcpzI.js.map} +1 -1
  7. package/dist/{Card-bUYvpOEI.js → Card-CJDGgG0O.js} +3 -3
  8. package/dist/{Card-bUYvpOEI.js.map → Card-CJDGgG0O.js.map} +1 -1
  9. package/dist/{Card-BwEzhUW6.cjs → Card-CruyYUqd.cjs} +2 -2
  10. package/dist/{Card-BwEzhUW6.cjs.map → Card-CruyYUqd.cjs.map} +1 -1
  11. package/dist/{Card-CYLat33l.js → Card-Dy66r68A.js} +2 -2
  12. package/dist/{Card-CYLat33l.js.map → Card-Dy66r68A.js.map} +1 -1
  13. package/dist/{LockedThumbnail-BvYmQvT2.js → LockedThumbnail-BJxXEcxk.js} +2 -2
  14. package/dist/{LockedThumbnail-BvYmQvT2.js.map → LockedThumbnail-BJxXEcxk.js.map} +1 -1
  15. package/dist/{LockedThumbnail-CuCWVcEL.cjs → LockedThumbnail-DrsPt5LN.cjs} +2 -2
  16. package/dist/{LockedThumbnail-CuCWVcEL.cjs.map → LockedThumbnail-DrsPt5LN.cjs.map} +1 -1
  17. package/dist/index-BIInWpum.cjs +2 -0
  18. package/dist/index-BIInWpum.cjs.map +1 -0
  19. package/dist/{index-CxKVYxq6.js → index-CPa6Qru7.js} +869 -828
  20. package/dist/index-CPa6Qru7.js.map +1 -0
  21. package/dist/index.cjs +1 -1
  22. package/dist/index.js +1 -1
  23. package/package.json +1 -1
  24. package/src/components/ChannelList/CustomChannelPreview.stories.tsx +43 -1
  25. package/src/components/ChannelList/CustomChannelPreview.tsx +8 -3
  26. package/src/components/CustomMessage/CustomMessage.stories.tsx +43 -38
  27. package/src/components/CustomMessage/CustomMessageActions.tsx +30 -16
  28. package/src/components/CustomMessage/index.tsx +37 -28
  29. package/dist/index-BQv7yPeG.cjs +0 -2
  30. package/dist/index-BQv7yPeG.cjs.map +0 -1
  31. package/dist/index-CxKVYxq6.js.map +0 -1
package/dist/index.cjs CHANGED
@@ -1,2 +1,2 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("./index-BQv7yPeG.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.resolveLinkAttachment=e.resolveLinkAttachment;exports.resolveMediaFromMessage=e.resolveMediaFromMessage;exports.resolveParticipantDisplayName=e.resolveParticipantDisplayName;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-BIInWpum.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.resolveLinkAttachment=e.resolveLinkAttachment;exports.resolveMediaFromMessage=e.resolveMediaFromMessage;exports.resolveParticipantDisplayName=e.resolveParticipantDisplayName;exports.useCustomMessage=e.useCustomMessage;exports.useMessageVote=e.useMessageVote;exports.useMessaging=e.useMessaging;
2
2
  //# sourceMappingURL=index.cjs.map
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { a as e, b as t, C as i, c as n, d as o, e as m, F as g, f as l, L as r, h as M, M as u, i as L, j as c, k as h, l as d, m as p, n as v, o as A, p as C, q as F, s as k, t as b, u as f, v as x, w as y, x as P, y as S, z as q, B as z, D as B } from "./index-CxKVYxq6.js";
1
+ import { a as e, b as t, C as i, c as n, d as o, e as m, F as g, f as l, L as r, h as M, M as u, i as L, j as c, k as h, l as d, m as p, n as v, o as A, p as C, q as F, s as k, t as b, u as f, v as x, w as y, x as P, y as S, z as q, B as z, D as B } from "./index-CPa6Qru7.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": "2.5.1",
3
+ "version": "2.5.3",
4
4
  "description": "React messaging components built on messaging-core for web applications",
5
5
  "type": "module",
6
6
  "main": "dist/index.cjs",
@@ -84,7 +84,7 @@ const createMockChannel = (options: {
84
84
  },
85
85
  },
86
86
  messages:
87
- lastMessageText || lastMessageAttachments
87
+ lastMessageText || lastMessageAttachments || lastMessageMetadata
88
88
  ? ([
89
89
  {
90
90
  id: `msg-${id}-1`,
@@ -241,6 +241,48 @@ ChatbotMessage.args = {
241
241
  }),
242
242
  }
243
243
 
244
+ export const TipMessage: StoryFn<ComponentProps> = Template.bind({})
245
+ TipMessage.args = {
246
+ channel: createMockChannel({
247
+ id: 'channel-tip',
248
+ participantName: 'Alice Johnson',
249
+ participantId: 'participant-tip',
250
+ participantImage: 'https://i.pravatar.cc/150?img=2',
251
+ lastMessageText: 'Take my money!',
252
+ lastMessageTime: new Date(Date.now() - 1000 * 60 * 3),
253
+ lastMessageMetadata: { custom_type: 'MESSAGE_TIP', amount_text: '$5.00' },
254
+ }),
255
+ }
256
+
257
+ export const PaidMessage: StoryFn<ComponentProps> = Template.bind({})
258
+ PaidMessage.args = {
259
+ channel: createMockChannel({
260
+ id: 'channel-paid',
261
+ participantName: 'Bob Smith',
262
+ participantId: 'participant-paid',
263
+ participantImage: 'https://i.pravatar.cc/150?img=3',
264
+ lastMessageText: 'I paid for this message!',
265
+ lastMessageTime: new Date(Date.now() - 1000 * 60 * 10),
266
+ lastMessageMetadata: { custom_type: 'MESSAGE_PAID', amount_text: '$10.00' },
267
+ }),
268
+ }
269
+
270
+ export const PaidAttachment: StoryFn<ComponentProps> = Template.bind({})
271
+ PaidAttachment.args = {
272
+ channel: createMockChannel({
273
+ id: 'channel-paid-attachment',
274
+ participantName: 'Carol Williams',
275
+ participantId: 'participant-paid-attachment',
276
+ participantImage: 'https://i.pravatar.cc/150?img=4',
277
+ lastMessageText: 'Check out this exclusive photo!',
278
+ lastMessageTime: new Date(Date.now() - 1000 * 60 * 20),
279
+ lastMessageMetadata: {
280
+ custom_type: 'MESSAGE_ATTACHMENT',
281
+ attachment_title: 'exclusive-photo.jpg',
282
+ },
283
+ }),
284
+ }
285
+
244
286
  const renderMessagePreviewWithMarkers = (
245
287
  message: LocalMessage | undefined,
246
288
  defaultPreview?: string
@@ -64,11 +64,16 @@ const CustomChannelPreview = React.memo<ChannelPreviewUIComponentProps>(
64
64
  message: lastMessage,
65
65
  viewerLanguage,
66
66
  })
67
- if (displayText) return displayText
68
67
 
69
68
  const isTip = lastMessage?.metadata?.custom_type === 'MESSAGE_TIP'
70
- if (isTip) return '💵 Sent a tip'
71
-
69
+ if (isTip) return displayText ? `💵 ${displayText}` : '💵 Sent a tip'
70
+ const isPaid = lastMessage?.metadata?.custom_type === 'MESSAGE_PAID'
71
+ if (isPaid) return displayText ? `💰 ${displayText}` : '💰 Sent a message'
72
+ const isPaidAttachment = lastMessage?.metadata?.custom_type === 'MESSAGE_ATTACHMENT'
73
+ if (isPaidAttachment) return displayText ? `📎 ${displayText}` : '📎 Sent an attachment'
74
+
75
+ if (displayText) return displayText
76
+
72
77
  const attachment = lastMessage?.attachments?.[0]
73
78
  if (attachment) {
74
79
  // Link previews - show the actual URL
@@ -518,70 +518,75 @@ const WithActionsTemplate: StoryFn<TemplateProps> = ({
518
518
  />
519
519
  )
520
520
 
521
- export const DeleteOwnMessage: StoryFn<TemplateProps> =
521
+ export const ConversationWithPaidAttachments: StoryFn<TemplateProps> =
522
522
  WithActionsTemplate.bind({})
523
- DeleteOwnMessage.args = {
523
+ ConversationWithPaidAttachments.args = {
524
524
  currentUser: storyUsers.creator,
525
525
  messages: [
526
526
  {
527
527
  id: 'msg-1',
528
- text: 'Hey, how are you?',
528
+ text: 'Can I have your workout plan?',
529
529
  user: storyUsers.visitor,
530
530
  },
531
531
  {
532
532
  id: 'msg-2',
533
- text: 'Doing great — hover me to delete!',
533
+ text: 'Yes, of course!',
534
534
  user: storyUsers.creator,
535
535
  },
536
- ],
537
- }
538
- DeleteOwnMessage.play = async ({ canvasElement }) => {
539
- const canvas = within(canvasElement)
540
- const ownBubble = await canvas.findByText('Doing great — hover me to delete!')
541
- await userEvent.hover(ownBubble)
542
- const toggle = await canvas.findByTestId('message-actions-toggle-button')
543
- await userEvent.click(toggle)
544
- await expect(canvas.getByText('Delete')).toBeInTheDocument()
545
- }
546
- DeleteOwnMessage.parameters = {
547
- docs: {
548
- description: {
549
- story:
550
- 'Current user hovers their own message. Only "Delete" is shown — "Report" is hidden.',
536
+ {
537
+ id: 'msg-3',
538
+ text: 'Let me know if you have any questions!',
539
+ user: storyUsers.creator,
540
+ metadata: {
541
+ custom_type: 'MESSAGE_ATTACHMENT',
542
+ payment_status: 'paid',
543
+ amount_text: 'AU$9.99',
544
+ attachment_title: "Alicia's Workout Plan",
545
+ attachment_mime_type: 'video/mp4',
546
+ attachment_thumbnail: '/video-thumbnail-blurred.jpg',
547
+ attachment_detail: '1:20',
548
+ },
551
549
  },
552
- },
553
- }
554
-
555
- export const ReportOthersMessage: StoryFn<TemplateProps> =
556
- WithActionsTemplate.bind({})
557
- ReportOthersMessage.args = {
558
- currentUser: storyUsers.creator,
559
- messages: [
560
550
  {
561
- id: 'msg-1',
562
- text: 'Hover me to report!',
551
+ id: 'msg-4',
552
+ text: 'Looks amazing, unlocking now!',
563
553
  user: storyUsers.visitor,
564
554
  },
565
555
  {
566
- id: 'msg-2',
567
- text: "That's a fine message.",
568
- user: storyUsers.creator,
556
+ id: 'msg-5',
557
+ text: 'Here is another resource for you.',
558
+ user: storyUsers.visitor,
559
+ metadata: {
560
+ custom_type: 'MESSAGE_ATTACHMENT',
561
+ amount_text: 'AU$4.99',
562
+ attachment_title: 'Nutrition Guide',
563
+ attachment_mime_type: 'application/pdf',
564
+ attachment_thumbnail: '/video-thumbnail-blurred.jpg',
565
+ attachment_detail: '12 pages',
566
+ },
569
567
  },
570
568
  ],
571
569
  }
572
- ReportOthersMessage.play = async ({ canvasElement }) => {
570
+ ConversationWithPaidAttachments.play = async ({ canvasElement }) => {
573
571
  const canvas = within(canvasElement)
574
- const otherBubble = await canvas.findByText('Hover me to report!')
575
- await userEvent.hover(otherBubble)
572
+ const attachmentMsg = canvasElement.querySelector('[data-message-id="msg-3"]')
573
+ await userEvent.hover(attachmentMsg!)
576
574
  const toggle = await canvas.findByTestId('message-actions-toggle-button')
577
575
  await userEvent.click(toggle)
578
- await expect(canvas.getByText('Report')).toBeInTheDocument()
576
+ const deleteButton = await canvas.findByRole('button', { name: 'Delete' })
577
+ await userEvent.click(deleteButton)
578
+ await expect(
579
+ canvas.getByRole('heading', { name: 'Delete attachment?' })
580
+ ).toBeInTheDocument()
581
+ await expect(
582
+ canvas.getByText(/Deleting it will remove access for the buyer/)
583
+ ).toBeInTheDocument()
579
584
  }
580
- ReportOthersMessage.parameters = {
585
+ ConversationWithPaidAttachments.parameters = {
581
586
  docs: {
582
587
  description: {
583
588
  story:
584
- "Current user hovers the other participant's message. Only \"Report\" is shown \"Delete\" is hidden.",
589
+ 'Report and Delete actions only appear on paid attachment messages — regular messages show no actions. Hovering msg-3 (a purchased attachment) shows the delete confirmation with buyer warning.',
585
590
  },
586
591
  },
587
592
  }
@@ -1,17 +1,22 @@
1
+ import { FlagIcon, TrashSimpleIcon } from '@phosphor-icons/react'
1
2
  import React from 'react'
2
- import { useMessageContext, useTranslationContext } from 'stream-chat-react'
3
+ import { useMessageContext } from 'stream-chat-react'
3
4
  import {
4
5
  DefaultDropdownActionButton,
5
6
  MessageActions,
6
- type MessageActionSetItem,
7
7
  } from 'stream-chat-react/experimental'
8
8
 
9
9
  const DeleteAction = () => {
10
- const { handleDelete } = useMessageContext('CustomMessageActions')
11
- const { t } = useTranslationContext('CustomMessageActions')
10
+ const { handleDelete, message } = useMessageContext('CustomMessageActions')
11
+ if (message.metadata?.payment_status !== 'paid') return null
12
12
  return (
13
- <DefaultDropdownActionButton onClick={handleDelete}>
14
- {t('Delete')}
13
+ <DefaultDropdownActionButton
14
+ onClick={handleDelete}
15
+ aria-label="Delete"
16
+ title="Delete"
17
+ className="bg-marble rounded-full p-2 hover:bg-sand transition-all"
18
+ >
19
+ <TrashSimpleIcon size={16} weight="light" aria-hidden />
15
20
  </DefaultDropdownActionButton>
16
21
  )
17
22
  }
@@ -19,17 +24,26 @@ const DeleteAction = () => {
19
24
  const ReportAction = () => {
20
25
  const { handleFlag } = useMessageContext('CustomMessageActions')
21
26
  return (
22
- <DefaultDropdownActionButton onClick={handleFlag}>
23
- Report
27
+ <DefaultDropdownActionButton
28
+ onClick={handleFlag}
29
+ aria-label="Report"
30
+ title="Report"
31
+ className="bg-marble rounded-full p-2 hover:bg-sand transition-all"
32
+ >
33
+ <FlagIcon size={16} weight="light" aria-hidden />
24
34
  </DefaultDropdownActionButton>
25
35
  )
26
36
  }
27
37
 
28
- const MESSAGE_ACTION_SET: MessageActionSetItem[] = [
29
- { Component: DeleteAction, placement: 'dropdown', type: 'delete' },
30
- { Component: ReportAction, placement: 'dropdown', type: 'flag' },
31
- ]
32
-
33
- export const CustomMessageActions = () => (
34
- <MessageActions messageActionSet={MESSAGE_ACTION_SET} />
35
- )
38
+ export const CustomMessageActions = () => {
39
+ const { message } = useMessageContext('CustomMessageActions')
40
+ if (message.metadata?.custom_type !== 'MESSAGE_ATTACHMENT') return null
41
+ return (
42
+ <MessageActions
43
+ messageActionSet={[
44
+ { Component: DeleteAction, placement: 'quick', type: 'delete' },
45
+ { Component: ReportAction, placement: 'quick', type: 'flag' },
46
+ ]}
47
+ />
48
+ )
49
+ }
@@ -194,7 +194,11 @@ const CustomMessageWithContext = (props: CustomMessageWithContextProps) => {
194
194
  open={isBounceDialogOpen}
195
195
  />
196
196
  )}
197
- <div className={rootClassName} key={message.id} data-message-id={message.id}>
197
+ <div
198
+ className={rootClassName}
199
+ key={message.id}
200
+ data-message-id={message.id}
201
+ >
198
202
  {PinIndicator && <PinIndicator />}
199
203
  {!!reminder && <ReminderNotification reminder={reminder} />}
200
204
  {message.user && (
@@ -228,33 +232,39 @@ const CustomMessageWithContext = (props: CustomMessageWithContextProps) => {
228
232
  {isAttachment ? (
229
233
  <div className="str-chat__message-bubble-wrapper">
230
234
  {isMine ? (
231
- <LockedAttachment.Sent
232
- title={message.metadata?.attachment_title}
233
- mimeType={message.metadata?.attachment_mime_type}
234
- thumbnailUrl={message.metadata?.attachment_thumbnail}
235
- amountText={message.metadata?.amount_text}
236
- detail={message.metadata?.attachment_detail}
237
- paymentStatus={message.metadata?.payment_status}
238
- onPreviewClick={() => onUnlockClick?.(message, channel)}
239
- onFetchSource={async () =>
240
- await onFetchSource?.(message, channel)
241
- }
242
- />
235
+ <div className="flex items-center gap-2">
236
+ {MessageActions && <MessageActions />}
237
+ <LockedAttachment.Sent
238
+ title={message.metadata?.attachment_title}
239
+ mimeType={message.metadata?.attachment_mime_type}
240
+ thumbnailUrl={message.metadata?.attachment_thumbnail}
241
+ amountText={message.metadata?.amount_text}
242
+ detail={message.metadata?.attachment_detail}
243
+ paymentStatus={message.metadata?.payment_status}
244
+ onPreviewClick={() => onUnlockClick?.(message, channel)}
245
+ onFetchSource={async () =>
246
+ await onFetchSource?.(message, channel)
247
+ }
248
+ />
249
+ </div>
243
250
  ) : (
244
- <LockedAttachment.Received
245
- title={message.metadata?.attachment_title}
246
- mimeType={message.metadata?.attachment_mime_type}
247
- thumbnailUrl={message.metadata?.attachment_thumbnail}
248
- amountText={message.metadata?.amount_text}
249
- detail={message.metadata?.attachment_detail}
250
- paymentStatus={message.metadata?.payment_status}
251
- isUnlocking={isUnlocking(message.id)}
252
- onUnlockClick={() => onUnlockClick?.(message, channel)}
253
- onFetchSource={async () =>
254
- await onFetchSource?.(message, channel)
255
- }
256
- onDownloadClick={() => onDownloadClick?.(message, channel)}
257
- />
251
+ <div className="flex items-center gap-2">
252
+ <LockedAttachment.Received
253
+ title={message.metadata?.attachment_title}
254
+ mimeType={message.metadata?.attachment_mime_type}
255
+ thumbnailUrl={message.metadata?.attachment_thumbnail}
256
+ amountText={message.metadata?.amount_text}
257
+ detail={message.metadata?.attachment_detail}
258
+ paymentStatus={message.metadata?.payment_status}
259
+ isUnlocking={isUnlocking(message.id)}
260
+ onUnlockClick={() => onUnlockClick?.(message, channel)}
261
+ onFetchSource={async () =>
262
+ await onFetchSource?.(message, channel)
263
+ }
264
+ onDownloadClick={() => onDownloadClick?.(message, channel)}
265
+ />
266
+ {MessageActions && <MessageActions />}
267
+ </div>
258
268
  )}
259
269
  {message.text && (
260
270
  <div className="str-chat__message-bubble">
@@ -300,7 +310,6 @@ const CustomMessageWithContext = (props: CustomMessageWithContextProps) => {
300
310
  </div>
301
311
  </div>
302
312
  )}
303
- {MessageActions && <MessageActions />}
304
313
  </div>
305
314
  {!isAttachment && !isTipOnly && (
306
315
  <div className="str-chat__message-footer">