@linktr.ee/messaging-react 3.12.3-rc-1784034329 → 3.12.3-rc-1784036626

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.
package/dist/index.cjs CHANGED
@@ -1,2 +1,2 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("./index-C1eZGd_f.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.MessageBubble=e.MessageBubble;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-BB78YkWG.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.MessageBubble=e.MessageBubble;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
@@ -1713,6 +1713,16 @@ declare module 'stream-chat' {
1713
1713
  * Whether DM agent auto-replies are paused for this channel.
1714
1714
  */
1715
1715
  chatbot_paused?: boolean;
1716
+ /**
1717
+ * Customer classification tag for this conversation (e.g. CUSTOMER_PAID).
1718
+ */
1719
+ customer_tag?: string;
1720
+ }
1721
+ interface CustomMemberData {
1722
+ /**
1723
+ * Whether this member follows the channel's linker account.
1724
+ */
1725
+ is_follower?: boolean;
1716
1726
  }
1717
1727
  interface CustomMessageData {
1718
1728
  /**
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 u, i as c, j as L, k as h, l as d, m as p, n as v, p as C, q as A, s as k, t as F, u as b, v as P, w as f, x, y, z as B, B as S, D as q, E as z, G as D, H as E, I as V } from "./index-BvuCjzqJ.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 u, i as c, j as L, k as h, l as d, m as p, n as v, p as C, q as A, s as k, t as F, u as b, v as P, w as f, x, y, z as B, B as S, D as q, E as z, G as D, H as E, I as V } from "./index-5kR8BcYv.js";
2
2
  export {
3
3
  e as ActionButton,
4
4
  t as Avatar,
package/dist/testing.d.ts CHANGED
@@ -133,6 +133,16 @@ declare module 'stream-chat' {
133
133
  * Whether DM agent auto-replies are paused for this channel.
134
134
  */
135
135
  chatbot_paused?: boolean;
136
+ /**
137
+ * Customer classification tag for this conversation (e.g. CUSTOMER_PAID).
138
+ */
139
+ customer_tag?: string;
140
+ }
141
+ interface CustomMemberData {
142
+ /**
143
+ * Whether this member follows the channel's linker account.
144
+ */
145
+ is_follower?: boolean;
136
146
  }
137
147
  interface CustomMessageData {
138
148
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@linktr.ee/messaging-react",
3
- "version": "3.12.3-rc-1784034329",
3
+ "version": "3.12.3-rc-1784036626",
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-1784034329",
53
+ "@linktr.ee/messaging-core": "2.3.0-rc-1784036626",
54
54
  "@phosphor-icons/react": "^2.1.10"
55
55
  },
56
56
  "devDependencies": {
@@ -7,7 +7,7 @@ import {
7
7
  } from '@phosphor-icons/react'
8
8
  import classNames from 'classnames'
9
9
  import React from 'react'
10
- import type { ChannelMemberResponse, Channel as ChannelType } from 'stream-chat'
10
+ import type { Channel as ChannelType } from 'stream-chat'
11
11
  import { useChannelStateContext, useChatContext } from 'stream-chat-react'
12
12
 
13
13
  import { useChannelStar } from '../hooks/useChannelStar'
@@ -30,17 +30,6 @@ const isCustomerPaidTag = (value: unknown) =>
30
30
  .replace(/[\s-]+/g, '_')
31
31
  .toUpperCase() === CUSTOMER_PAID_TAG
32
32
 
33
- const hasCustomerPaidTag = (participant?: ChannelMemberResponse) => {
34
- const member = participant as
35
- | {
36
- customerTag?: unknown
37
- customer_tag?: unknown
38
- }
39
- | undefined
40
-
41
- return [member?.customerTag, member?.customer_tag].some(isCustomerPaidTag)
42
- }
43
-
44
33
  interface ChannelHeaderRedesignProps {
45
34
  className?: string
46
35
  onBack?: () => void
@@ -96,7 +85,9 @@ export const ChannelHeaderRedesign: React.FC<ChannelHeaderRedesignProps> = ({
96
85
  const participantName = resolveParticipantDisplayName(participant?.user)
97
86
  const participantImage = participant?.user?.image
98
87
  const isStarred = useChannelStar(channel)
99
- const isPaidCustomer = hasCustomerPaidTag(participant)
88
+ const isPaidCustomer =
89
+ isCustomerPaidTag(channel.data?.customer_tag) &&
90
+ participant?.is_follower === true
100
91
 
101
92
  const handleStarClick = async () => {
102
93
  try {
@@ -44,7 +44,7 @@ const createMockChannel = async (
44
44
  hasMessages = true,
45
45
  followerStatus?: string | boolean,
46
46
  isFrozen = false,
47
- participantCustomerTag?: string,
47
+ channelCustomerTag?: string,
48
48
  hasScrollableMessages = false
49
49
  ) => {
50
50
  const participant = mockParticipants[0]
@@ -140,6 +140,7 @@ const createMockChannel = async (
140
140
  const channelData: Record<string, unknown> = {
141
141
  members: [mockUser.id, participant.id],
142
142
  frozen: isFrozen,
143
+ ...(channelCustomerTag ? { customer_tag: channelCustomerTag } : {}),
143
144
  }
144
145
 
145
146
  // Add follower status if provided
@@ -178,7 +179,7 @@ const createMockChannel = async (
178
179
  user_id: participant.id,
179
180
  role: 'member',
180
181
  is_account: true,
181
- customerTag: participantCustomerTag,
182
+ ...(channelCustomerTag ? { is_follower: true } : {}),
182
183
  },
183
184
  }
184
185
  return {
@@ -214,7 +215,7 @@ type TemplateProps = ComponentProps & {
214
215
  name?: string
215
216
  image?: string
216
217
  }
217
- participantCustomerTag?: string
218
+ channelCustomerTag?: string
218
219
  hasScrollableMessages?: boolean
219
220
  }
220
221
 
@@ -223,7 +224,7 @@ const Template: StoryFn<TemplateProps> = (args) => {
223
224
  hasMessages = true,
224
225
  followerStatus,
225
226
  isFrozen = false,
226
- participantCustomerTag,
227
+ channelCustomerTag,
227
228
  hasScrollableMessages,
228
229
  typingUser,
229
230
  ...channelViewProps
@@ -238,7 +239,7 @@ const Template: StoryFn<TemplateProps> = (args) => {
238
239
  hasMessages,
239
240
  followerStatus,
240
241
  isFrozen,
241
- participantCustomerTag,
242
+ channelCustomerTag,
242
243
  hasScrollableMessages
243
244
  ).then((mockChannel) => {
244
245
  setChannel(mockChannel)
@@ -248,7 +249,7 @@ const Template: StoryFn<TemplateProps> = (args) => {
248
249
  hasMessages,
249
250
  followerStatus,
250
251
  isFrozen,
251
- participantCustomerTag,
252
+ channelCustomerTag,
252
253
  hasScrollableMessages,
253
254
  ])
254
255
 
@@ -305,7 +306,7 @@ export const ParticipantIdentityTrigger: StoryFn<TemplateProps> = Template.bind(
305
306
  ParticipantIdentityTrigger.args = {
306
307
  showBackButton: false,
307
308
  showStarButton: true,
308
- participantCustomerTag: 'CUSTOMER_PAID',
309
+ channelCustomerTag: 'CUSTOMER_PAID',
309
310
  hasScrollableMessages: true,
310
311
  onParticipantNameClick: () =>
311
312
  // eslint-disable-next-line no-alert
@@ -103,12 +103,14 @@ const createChannel = ({
103
103
  username: undefined as string | undefined,
104
104
  is_account: true,
105
105
  },
106
- otherMemberCustomerTag,
106
+ channelCustomerTag,
107
+ otherMemberIsFollower,
107
108
  }: {
108
109
  currentUserId?: 'visitor-1' | 'linker-1'
109
110
  currentUserRole?: 'owner' | 'member'
110
111
  messages?: Channel['state']['messages']
111
- otherMemberCustomerTag?: string
112
+ channelCustomerTag?: string
113
+ otherMemberIsFollower?: boolean
112
114
  otherUser?: {
113
115
  id: string
114
116
  name?: string
@@ -119,7 +121,9 @@ const createChannel = ({
119
121
  ({
120
122
  id: 'channel-1',
121
123
  cid: 'messaging:channel-1',
122
- data: {},
124
+ data: {
125
+ customer_tag: channelCustomerTag,
126
+ },
123
127
  _client: { userID: currentUserId },
124
128
  state: {
125
129
  members: {
@@ -139,7 +143,7 @@ const createChannel = ({
139
143
  (currentUserId === 'visitor-1' ? 'linker-1' : 'visitor-1'),
140
144
  is_account: otherUser.is_account ?? currentUserId === 'visitor-1',
141
145
  },
142
- customerTag: otherMemberCustomerTag,
146
+ is_follower: otherMemberIsFollower,
143
147
  role: currentUserRole === 'owner' ? 'member' : 'owner',
144
148
  },
145
149
  },
@@ -242,7 +246,8 @@ describe('ChannelView', () => {
242
246
  renderWithProviders(
243
247
  <ChannelView
244
248
  channel={createChannel({
245
- otherMemberCustomerTag: 'CUSTOMER_PAID',
249
+ channelCustomerTag: 'CUSTOMER_PAID',
250
+ otherMemberIsFollower: true,
246
251
  })}
247
252
  onParticipantNameClick={vi.fn()}
248
253
  showStarButton={true}
@@ -253,6 +258,20 @@ describe('ChannelView', () => {
253
258
  expect(screen.getByLabelText('Starred conversation')).toBeInTheDocument()
254
259
  })
255
260
 
261
+ it('hides the paid customer badge when the participant is not a follower', () => {
262
+ renderWithProviders(
263
+ <ChannelView
264
+ channel={createChannel({
265
+ channelCustomerTag: 'CUSTOMER_PAID',
266
+ otherMemberIsFollower: false,
267
+ })}
268
+ onParticipantNameClick={vi.fn()}
269
+ />
270
+ )
271
+
272
+ expect(screen.queryByLabelText('Paid customer')).not.toBeInTheDocument()
273
+ })
274
+
256
275
  it('renders the redesigned header outside MessageList head when all messages are deleted', () => {
257
276
  renderWithProviders(
258
277
  <ChannelView
@@ -97,6 +97,17 @@ declare module 'stream-chat' {
97
97
  * Whether DM agent auto-replies are paused for this channel.
98
98
  */
99
99
  chatbot_paused?: boolean
100
+ /**
101
+ * Customer classification tag for this conversation (e.g. CUSTOMER_PAID).
102
+ */
103
+ customer_tag?: string
104
+ }
105
+
106
+ interface CustomMemberData {
107
+ /**
108
+ * Whether this member follows the channel's linker account.
109
+ */
110
+ is_follower?: boolean
100
111
  }
101
112
 
102
113
  interface CustomMessageData {
@@ -34,4 +34,24 @@ describe('optimizeMessagingAttachmentUrl', () => {
34
34
  'https://ugc.production.linktr.ee/photo.jpg?io=true&size=messaging-attachment-v1_0'
35
35
  )
36
36
  })
37
+
38
+ it('replaces a different optimized size preset on Linktree hosts', () => {
39
+ expect(
40
+ optimizeMessagingAttachmentUrl(
41
+ 'https://ugc.production.linktr.ee/photo.jpg?io=true&size=avatar-v1_0'
42
+ )
43
+ ).toBe(
44
+ 'https://ugc.production.linktr.ee/photo.jpg?io=true&size=messaging-attachment-v1_0'
45
+ )
46
+ })
47
+
48
+ it('preserves unrelated query params when replacing an existing size preset', () => {
49
+ expect(
50
+ optimizeMessagingAttachmentUrl(
51
+ 'https://ugc.production.linktr.ee/photo.jpg?token=abc&size=preview-v2_0&io=true'
52
+ )
53
+ ).toBe(
54
+ 'https://ugc.production.linktr.ee/photo.jpg?token=abc&size=messaging-attachment-v1_0&io=true'
55
+ )
56
+ })
37
57
  })
@@ -25,10 +25,17 @@ export function optimizeMessagingAttachmentUrl(
25
25
  const hostname = hostnameFromUrl(trimmed)
26
26
  if (!hostname || !isLinktreeCdnHost(hostname)) return trimmed
27
27
 
28
- if (/[?&]io=true(?:&|$)/.test(trimmed) && /[?&]size=/.test(trimmed)) {
28
+ const parsedUrl = new URL(trimmed)
29
+ const isAlreadyMessagingOptimized =
30
+ parsedUrl.searchParams.get('io') === 'true' &&
31
+ parsedUrl.searchParams.get('size') === ATTACHMENT_IMAGE_SIZE_PRESET
32
+
33
+ if (isAlreadyMessagingOptimized) {
29
34
  return trimmed
30
35
  }
31
36
 
32
- const separator = trimmed.includes('?') ? '&' : '?'
33
- return `${trimmed}${separator}io=true&size=${ATTACHMENT_IMAGE_SIZE_PRESET}`
37
+ parsedUrl.searchParams.set('io', 'true')
38
+ parsedUrl.searchParams.set('size', ATTACHMENT_IMAGE_SIZE_PRESET)
39
+
40
+ return parsedUrl.toString()
34
41
  }