@linktr.ee/messaging-react 3.4.2-rc-1782453771 → 3.4.2-rc-1782480798

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 (44) hide show
  1. package/dist/{Card-ql9pImRa.js → Card-BKi2g6x-.js} +2 -2
  2. package/dist/{Card-ql9pImRa.js.map → Card-BKi2g6x-.js.map} +1 -1
  3. package/dist/{Card-BIa-47Ne.cjs → Card-BMmrCMqJ.cjs} +2 -2
  4. package/dist/{Card-BIa-47Ne.cjs.map → Card-BMmrCMqJ.cjs.map} +1 -1
  5. package/dist/{Card-BaXLsSK2.js → Card-BYu6OBuX.js} +2 -2
  6. package/dist/{Card-BaXLsSK2.js.map → Card-BYu6OBuX.js.map} +1 -1
  7. package/dist/{Card-BkVj6kGN.cjs → Card-DtIjtWy6.cjs} +2 -2
  8. package/dist/{Card-BkVj6kGN.cjs.map → Card-DtIjtWy6.cjs.map} +1 -1
  9. package/dist/{Card-52LnCirC.js → Card-DvE5BCPf.js} +3 -3
  10. package/dist/{Card-52LnCirC.js.map → Card-DvE5BCPf.js.map} +1 -1
  11. package/dist/{Card-CcTyYY9N.cjs → Card-VO-i6CuV.cjs} +2 -2
  12. package/dist/{Card-CcTyYY9N.cjs.map → Card-VO-i6CuV.cjs.map} +1 -1
  13. package/dist/{LockedThumbnail-D0ASm7LP.cjs → LockedThumbnail-Cl8uwCxM.cjs} +2 -2
  14. package/dist/{LockedThumbnail-D0ASm7LP.cjs.map → LockedThumbnail-Cl8uwCxM.cjs.map} +1 -1
  15. package/dist/{LockedThumbnail-C3YOnS-o.js → LockedThumbnail-DTkd_kEt.js} +2 -2
  16. package/dist/{LockedThumbnail-C3YOnS-o.js.map → LockedThumbnail-DTkd_kEt.js.map} +1 -1
  17. package/dist/index-CSSe9eu7.cjs +2 -0
  18. package/dist/index-CSSe9eu7.cjs.map +1 -0
  19. package/dist/{index-CbWEp3Zs.js → index-IzhF38yj.js} +3447 -3212
  20. package/dist/index-IzhF38yj.js.map +1 -0
  21. package/dist/index.cjs +1 -1
  22. package/dist/index.d.ts +25 -13
  23. package/dist/index.js +14 -13
  24. package/package.json +2 -2
  25. package/src/components/ChannelList/CustomChannelPreview.stories.tsx +0 -63
  26. package/src/components/ChannelList/CustomChannelPreview.test.tsx +8 -58
  27. package/src/components/ChannelList/CustomChannelPreview.tsx +10 -7
  28. package/src/components/ChannelView.stories.tsx +7 -38
  29. package/src/components/ChannelView.test.tsx +15 -29
  30. package/src/components/ChannelView.tsx +9 -19
  31. package/src/components/CustomMessage/StreamAttachmentMessage.test.tsx +109 -0
  32. package/src/components/CustomMessage/StreamAttachmentMessage.tsx +408 -0
  33. package/src/components/CustomMessage/index.tsx +42 -0
  34. package/src/components/CustomMessageInput/CustomMessageInput.test.tsx +34 -0
  35. package/src/components/CustomMessageInput/index.tsx +23 -1
  36. package/src/components/MessagingShell/index.tsx +2 -0
  37. package/src/hooks/useChannelStar.ts +6 -9
  38. package/src/index.ts +1 -0
  39. package/src/stream-custom-data.ts +1 -16
  40. package/src/types.ts +18 -0
  41. package/dist/index-CbWEp3Zs.js.map +0 -1
  42. package/dist/index-DkqM2z_j.cjs +0 -2
  43. package/dist/index-DkqM2z_j.cjs.map +0 -1
  44. package/src/components/ParticipantBadges.tsx +0 -39
package/dist/index.cjs CHANGED
@@ -1,2 +1,2 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("./index-DkqM2z_j.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-CSSe9eu7.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.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
@@ -149,7 +149,7 @@ export declare const ChannelView: default_2.NamedExoticComponent<ChannelViewProp
149
149
  * Props that MessagingShell passes through to ChannelView.
150
150
  * ChannelViewProps is the source of truth for these props.
151
151
  */
152
- declare type ChannelViewPassthroughProps = Pick<ChannelViewProps, 'renderMessageInputActions' | 'renderConversationFooter' | 'CustomChannelEmptyState' | 'onBlockParticipantClick' | 'onReportParticipantClick' | 'dmAgentEnabled' | 'onMessageSent' | 'chatbotVotingEnabled' | 'viewerLanguage' | 'renderHeaderTitleBadges' | 'renderChannelBanner' | 'customChannelActions' | 'renderChannelActions' | 'onParticipantNameClick' | 'renderMessage' | 'onMessageLinkClick' | 'showChannelInfo'>;
152
+ declare type ChannelViewPassthroughProps = Pick<ChannelViewProps, 'renderMessageInputActions' | 'renderConversationFooter' | 'CustomChannelEmptyState' | 'onBlockParticipantClick' | 'onReportParticipantClick' | 'dmAgentEnabled' | 'onMessageSent' | 'chatbotVotingEnabled' | 'viewerLanguage' | 'renderHeaderTitleBadges' | 'renderChannelBanner' | 'customChannelActions' | 'renderChannelActions' | 'onParticipantNameClick' | 'renderMessage' | 'onMessageLinkClick' | 'showChannelInfo' | 'composerInput'>;
153
153
 
154
154
  /**
155
155
  * ChannelView component props
@@ -351,6 +351,22 @@ export declare interface ChannelViewProps {
351
351
  * Use this to render staged paid or media attachments inside the input.
352
352
  */
353
353
  attachmentPreviewList?: ComponentType;
354
+ /**
355
+ * Replace the entire inner composer — the textarea + send arrangement — with
356
+ * a host-owned component. Passed to Stream's `<MessageInput>` as the `Input`
357
+ * component, so it runs inside the message-input context and reuses the
358
+ * library's send pipeline (`doSendMessageRequest`, message metadata, DM-agent
359
+ * suppression), attachments, and previews via the Stream hooks
360
+ * (`useMessageInputContext`, `useMessageComposerHasSendableData`,
361
+ * `TextareaComposer`, …). Use it when a surface needs a different composer
362
+ * layout (e.g. a full-width textarea above a toolbar row) than the default.
363
+ *
364
+ * The outer wrapper — frozen handling and the `renderMessageInputActions` /
365
+ * `renderMessageInputFooter` slots — is unchanged. Read the exported
366
+ * `useComposerLocked()` hook to honour the frozen state. Defaults to the
367
+ * built-in composer when omitted.
368
+ */
369
+ composerInput?: ComponentType;
354
370
  /**
355
371
  * Fired when the participant's name in the channel header is clicked. When
356
372
  * provided, the name renders as a button with a trailing caret (an affordance
@@ -1376,6 +1392,14 @@ export declare interface SentCardProps extends LockedAttachmentBaseProps {
1376
1392
  onFetchSource?: () => Promise<LockedAttachmentSource | void>;
1377
1393
  }
1378
1394
 
1395
+ /**
1396
+ * Read the composer's locked (frozen-channel) state from inside a custom
1397
+ * composer supplied via `composerInput`. Returns true when the channel is
1398
+ * frozen; mirror the default composer by rendering the textarea read-only and
1399
+ * disabling the send control.
1400
+ */
1401
+ export declare const useComposerLocked: () => boolean;
1402
+
1379
1403
  export declare function useCustomMessage<K extends keyof CustomMessageRegistry>(key: K): CustomMessageRegistry[K];
1380
1404
 
1381
1405
  /**
@@ -1471,13 +1495,6 @@ declare module 'stream-chat' {
1471
1495
  */
1472
1496
  chatbot_paused?: boolean;
1473
1497
  }
1474
- interface CustomMemberData {
1475
- /**
1476
- * Whether the member should be identified as a customer in participant UI.
1477
- * Stored on the channel member so it is scoped to this conversation.
1478
- */
1479
- customer?: boolean;
1480
- }
1481
1498
  interface CustomMessageData {
1482
1499
  /**
1483
1500
  * When true, hides the date timestamp in system messages.
@@ -1495,10 +1512,5 @@ declare module 'stream-chat' {
1495
1512
  */
1496
1513
  dm_agent_system_type?: DmAgentSystemType;
1497
1514
  }
1498
- interface CustomUserData {
1499
- is_account?: boolean;
1500
- is_follower?: boolean;
1501
- username?: string;
1502
- }
1503
1515
  }
1504
1516
 
package/dist/index.js CHANGED
@@ -1,14 +1,14 @@
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-CbWEp3Zs.js";
1
+ import { a as e, b as t, C as i, c as o, d as n, e as m, F as g, f as r, L as l, h as M, M as u, i as L, j as c, k as h, l as d, m as p, n as C, o as v, p as A, q as k, s as F, 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, E as D } from "./index-IzhF38yj.js";
2
2
  export {
3
3
  e as ActionButton,
4
4
  t as Avatar,
5
5
  i as ChannelEmptyState,
6
- n as ChannelList,
7
- o as ChannelView,
6
+ o as ChannelList,
7
+ n as ChannelView,
8
8
  m as CustomMessageProvider,
9
9
  g as FaqList,
10
- l as FaqListItem,
11
- r as LinkAttachment,
10
+ r as FaqListItem,
11
+ l as LinkAttachment,
12
12
  M as LockedAttachment,
13
13
  u as MediaMessage,
14
14
  L as MessageAttachment,
@@ -16,19 +16,20 @@ export {
16
16
  h as MessagingProvider,
17
17
  d as MessagingShell,
18
18
  p as buildCompactMetaLabel,
19
- v as formatFileSize,
20
- A as formatRelativeTime,
21
- C as getFileExtensionLabel,
22
- F as getMessageDisplayText,
23
- k as isLinkAttachment,
19
+ C as formatFileSize,
20
+ v as formatRelativeTime,
21
+ A as getFileExtensionLabel,
22
+ k as getMessageDisplayText,
23
+ F as isLinkAttachment,
24
24
  b as isUuidLike,
25
25
  f as messageAttachmentGroupPositionFromStream,
26
26
  x as normalizeLanguageCode,
27
27
  y as resolveLinkAttachment,
28
28
  P as resolveMediaFromMessage,
29
29
  S as resolveParticipantDisplayName,
30
- q as useCustomMessage,
31
- z as useMessageVote,
32
- B as useMessaging
30
+ q as useComposerLocked,
31
+ z as useCustomMessage,
32
+ B as useMessageVote,
33
+ D as useMessaging
33
34
  };
34
35
  //# sourceMappingURL=index.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@linktr.ee/messaging-react",
3
- "version": "3.4.2-rc-1782453771",
3
+ "version": "3.4.2-rc-1782480798",
4
4
  "description": "React messaging components built on messaging-core for web applications",
5
5
  "type": "module",
6
6
  "main": "dist/index.cjs",
@@ -38,7 +38,7 @@
38
38
  },
39
39
  "dependencies": {
40
40
  "@linktr.ee/component-library": "11.8.6",
41
- "@linktr.ee/messaging-core": "2.3.0-rc-1782453771",
41
+ "@linktr.ee/messaging-core": "2.3.0-rc-1782480798",
42
42
  "@phosphor-icons/react": "^2.1.10"
43
43
  },
44
44
  "devDependencies": {
@@ -55,8 +55,6 @@ const createMockChannel = (options: {
55
55
  thumb_url?: string
56
56
  }>
57
57
  lastMessageMetadata?: CustomMessageData['metadata']
58
- isCustomer?: boolean
59
- isStarred?: boolean
60
58
  }): Channel => {
61
59
  const {
62
60
  id,
@@ -68,8 +66,6 @@ const createMockChannel = (options: {
68
66
  unreadCount = 0,
69
67
  lastMessageAttachments,
70
68
  lastMessageMetadata,
71
- isCustomer = false,
72
- isStarred = false,
73
69
  } = options
74
70
 
75
71
  const eventListeners = new Map<string, Set<(...args: unknown[]) => void>>()
@@ -93,12 +89,8 @@ const createMockChannel = (options: {
93
89
  image: participantImage,
94
90
  },
95
91
  user_id: participantId,
96
- ...(isCustomer ? { customer: true } : {}),
97
92
  },
98
93
  },
99
- membership: {
100
- pinned_at: isStarred ? now().toISOString() : null,
101
- },
102
94
  messages:
103
95
  lastMessageText || lastMessageAttachments || lastMessageMetadata
104
96
  ? ([
@@ -257,61 +249,6 @@ ChatbotMessage.args = {
257
249
  }),
258
250
  }
259
251
 
260
- export const ParticipantBadges: StoryFn = () => {
261
- const channels = [
262
- createMockChannel({
263
- id: 'channel-customer',
264
- participantName: 'Alice Customer',
265
- participantId: 'participant-customer',
266
- participantImage: 'https://i.pravatar.cc/150?img=40',
267
- lastMessageText: 'I bought this from your Linktree',
268
- lastMessageTime: minutesAgo(2),
269
- isCustomer: true,
270
- }),
271
- createMockChannel({
272
- id: 'channel-starred',
273
- participantName: 'Bob Starred',
274
- participantId: 'participant-starred',
275
- participantImage: 'https://i.pravatar.cc/150?img=41',
276
- lastMessageText: 'Following up on this thread',
277
- lastMessageTime: minutesAgo(8),
278
- isStarred: true,
279
- }),
280
- createMockChannel({
281
- id: 'channel-customer-starred',
282
- participantName: 'Carol Customer',
283
- participantId: 'participant-customer-starred',
284
- participantImage: 'https://i.pravatar.cc/150?img=42',
285
- lastMessageText: 'Thanks for the quick reply',
286
- lastMessageTime: minutesAgo(15),
287
- isCustomer: true,
288
- isStarred: true,
289
- }),
290
- ]
291
-
292
- return (
293
- <div className="w-[360px] bg-chalk border border-sand rounded-lg overflow-hidden">
294
- {channels.map((channel) => (
295
- <CustomChannelPreview
296
- key={channel.id}
297
- channel={channel}
298
- onChannelSelect={(channel) => {
299
- console.log('Channel selected:', channel.id)
300
- }}
301
- />
302
- ))}
303
- </div>
304
- )
305
- }
306
- ParticipantBadges.parameters = {
307
- docs: {
308
- description: {
309
- story:
310
- 'Shows the built-in customer badge from participant.member customer data and the starred badge from the current user membership pinned_at value.',
311
- },
312
- },
313
- }
314
-
315
252
  export const TipMessage: StoryFn<ComponentProps> = Template.bind({})
316
253
  TipMessage.args = {
317
254
  channel: createMockChannel({
@@ -12,15 +12,12 @@ const mockUser = {
12
12
  }
13
13
 
14
14
  type MockChannel = Channel & {
15
- emitMemberUpdated: (member?: {
16
- pinned_at?: string | null
17
- user_id?: string
18
- }) => void
15
+ emitMemberUpdated: (member?: { pinned_at?: string | null }) => void
19
16
  }
20
17
 
21
18
  const createMockChannel = (messages: Partial<LocalMessage>[]): MockChannel => {
22
19
  const memberUpdatedListeners = new Set<
23
- (event: { member?: { pinned_at?: string | null; user_id?: string } }) => void
20
+ (event: { member?: { pinned_at?: string | null } }) => void
24
21
  >()
25
22
 
26
23
  return {
@@ -41,34 +38,24 @@ const createMockChannel = (messages: Partial<LocalMessage>[]): MockChannel => {
41
38
  on: vi.fn(
42
39
  (
43
40
  eventName: string,
44
- listener: (event: {
45
- member?: { pinned_at?: string | null; user_id?: string }
46
- }) => void
41
+ listener: (event: { member?: { pinned_at?: string | null } }) => void
47
42
  ) => {
48
43
  if (eventName === 'member.updated') {
49
44
  memberUpdatedListeners.add(listener)
50
45
  }
51
- return {
52
- unsubscribe: () => memberUpdatedListeners.delete(listener),
53
- }
54
46
  }
55
47
  ),
56
48
  off: vi.fn(
57
49
  (
58
50
  eventName: string,
59
- listener: (event: {
60
- member?: { pinned_at?: string | null; user_id?: string }
61
- }) => void
51
+ listener: (event: { member?: { pinned_at?: string | null } }) => void
62
52
  ) => {
63
53
  if (eventName === 'member.updated') {
64
54
  memberUpdatedListeners.delete(listener)
65
55
  }
66
56
  }
67
57
  ),
68
- emitMemberUpdated: (member?: {
69
- pinned_at?: string | null
70
- user_id?: string
71
- }) => {
58
+ emitMemberUpdated: (member?: { pinned_at?: string | null }) => {
72
59
  memberUpdatedListeners.forEach((listener) => listener({ member }))
73
60
  },
74
61
  } as unknown as MockChannel
@@ -266,21 +253,6 @@ describe('CustomChannelPreview', () => {
266
253
  expect(screen.getByText('No messages yet')).toBeInTheDocument()
267
254
  })
268
255
 
269
- it('shows a customer badge from participant member data', () => {
270
- const channel = createMockChannel([])
271
- channel.state.members['participant-1'] = {
272
- user: { id: 'participant-1', name: 'Alice' },
273
- user_id: 'participant-1',
274
- customer: true,
275
- }
276
-
277
- renderWithProviders(
278
- <CustomChannelPreview {...defaultProps} channel={channel} />
279
- )
280
-
281
- expect(screen.getByRole('img', { name: 'Customer' })).toBeInTheDocument()
282
- })
283
-
284
256
  it('updates starred state when the channel membership changes', () => {
285
257
  const channel = createMockChannel([])
286
258
 
@@ -289,37 +261,15 @@ describe('CustomChannelPreview', () => {
289
261
  )
290
262
 
291
263
  expect(
292
- screen.queryByRole('img', { name: 'Starred conversation' })
264
+ screen.queryByRole('heading', { name: /starred conversation/i })
293
265
  ).not.toBeInTheDocument()
294
266
 
295
267
  act(() => {
296
- channel.emitMemberUpdated({
297
- pinned_at: new Date().toISOString(),
298
- user_id: mockUser.id,
299
- })
268
+ channel.emitMemberUpdated({ pinned_at: new Date().toISOString() })
300
269
  })
301
270
 
302
271
  expect(
303
- screen.getByRole('img', { name: 'Starred conversation' })
272
+ screen.getByRole('heading', { name: /starred conversation/i })
304
273
  ).toBeInTheDocument()
305
274
  })
306
-
307
- it('ignores starred updates for other members', () => {
308
- const channel = createMockChannel([])
309
-
310
- renderWithProviders(
311
- <CustomChannelPreview {...defaultProps} channel={channel} />
312
- )
313
-
314
- act(() => {
315
- channel.emitMemberUpdated({
316
- pinned_at: new Date().toISOString(),
317
- user_id: 'participant-1',
318
- })
319
- })
320
-
321
- expect(
322
- screen.queryByRole('img', { name: 'Starred conversation' })
323
- ).not.toBeInTheDocument()
324
- })
325
275
  })
@@ -8,7 +8,6 @@ import { getMessageDisplayText } from '../../utils/getMessageDisplayText'
8
8
  import { resolveParticipantDisplayName } from '../../utils/resolveParticipantDisplayName'
9
9
  import { Avatar } from '../Avatar'
10
10
  import { isChatbotMessage } from '../CustomMessage/MessageTag'
11
- import { ParticipantBadges } from '../ParticipantBadges'
12
11
 
13
12
  import { useChannelListContext } from './ChannelListContext'
14
13
 
@@ -140,18 +139,22 @@ const CustomChannelPreview = React.memo<ChannelPreviewUIComponentProps>(
140
139
  name={participantName}
141
140
  image={participantImage}
142
141
  size={48}
142
+ starred={isChannelStarred}
143
143
  />
144
144
 
145
145
  {/* Content column */}
146
146
  <div className="flex-1 min-w-0 flex flex-col gap-1">
147
147
  {/* Name and timestamp row */}
148
148
  <div className="flex items-center justify-between gap-2">
149
- <h3 className="flex min-w-0 items-center gap-1 text-sm font-medium text-[#191918]">
150
- <span className="min-w-0 truncate">{participantName}</span>
151
- <ParticipantBadges
152
- participant={participant}
153
- isStarred={isChannelStarred}
154
- />
149
+ <h3
150
+ className={classNames(
151
+ 'text-sm font-medium truncate text-[#191918]'
152
+ )}
153
+ >
154
+ {isChannelStarred && (
155
+ <span className="sr-only">Starred conversation. </span>
156
+ )}
157
+ {participantName}
155
158
  </h3>
156
159
  {lastMessageTime && (
157
160
  <span className="text-xs text-[#717070] flex-shrink-0">
@@ -41,14 +41,7 @@ const createMockChannel = async (
41
41
  client: StreamChat,
42
42
  hasMessages = true,
43
43
  followerStatus?: string | boolean,
44
- isFrozen = false,
45
- {
46
- isCustomer = false,
47
- isStarred = false,
48
- }: {
49
- isCustomer?: boolean
50
- isStarred?: boolean
51
- } = {}
44
+ isFrozen = false
52
45
  ) => {
53
46
  const participant = mockParticipants[0]
54
47
 
@@ -149,12 +142,8 @@ const createMockChannel = async (
149
142
  user_id: participant.id,
150
143
  role: 'member',
151
144
  is_account: true,
152
- ...(isCustomer ? { customer: true } : {}),
153
145
  },
154
146
  }
155
- channel.state.membership = {
156
- pinned_at: isStarred ? now().toISOString() : null,
157
- } as ChannelMemberResponse
158
147
  return {
159
148
  channel: channelData,
160
149
  members: [],
@@ -182,8 +171,6 @@ const createMockChannel = async (
182
171
  type TemplateProps = ComponentProps & {
183
172
  followerStatus?: string | boolean
184
173
  isFrozen?: boolean
185
- isCustomer?: boolean
186
- isStarred?: boolean
187
174
  typingUser?: {
188
175
  id: string
189
176
  name?: string
@@ -195,8 +182,6 @@ const Template: StoryFn<TemplateProps> = (args) => {
195
182
  const {
196
183
  followerStatus,
197
184
  isFrozen = false,
198
- isCustomer = false,
199
- isStarred = false,
200
185
  typingUser,
201
186
  ...channelViewProps
202
187
  } = args
@@ -212,13 +197,12 @@ const Template: StoryFn<TemplateProps> = (args) => {
212
197
  const [channel, setChannel] = React.useState<ChannelType | null>(null)
213
198
 
214
199
  useEffect(() => {
215
- createMockChannel(client, true, followerStatus, isFrozen, {
216
- isCustomer,
217
- isStarred,
218
- }).then((mockChannel) => {
219
- setChannel(mockChannel)
220
- })
221
- }, [client, followerStatus, isCustomer, isFrozen, isStarred])
200
+ createMockChannel(client, true, followerStatus, isFrozen).then(
201
+ (mockChannel) => {
202
+ setChannel(mockChannel)
203
+ }
204
+ )
205
+ }, [client, followerStatus, isFrozen])
222
206
 
223
207
  useEffect(() => {
224
208
  if (!channel || !typingUser) {
@@ -323,21 +307,6 @@ WithHeaderTitleBadges.parameters = {
323
307
  },
324
308
  }
325
309
 
326
- export const ParticipantBadges: StoryFn<TemplateProps> = Template.bind({})
327
- ParticipantBadges.args = {
328
- showBackButton: false,
329
- isCustomer: true,
330
- isStarred: true,
331
- }
332
- ParticipantBadges.parameters = {
333
- docs: {
334
- description: {
335
- story:
336
- 'Shows the built-in customer badge from participant.customer and the starred badge from the current user membership pinned_at value in the channel header.',
337
- },
338
- },
339
- }
340
-
341
310
  export const RestrictedOfficialChannel: StoryFn<TemplateProps> = Template.bind(
342
311
  {}
343
312
  )
@@ -98,11 +98,9 @@ const createChannel = ({
98
98
  username: undefined as string | undefined,
99
99
  is_account: true,
100
100
  },
101
- otherMemberCustomer = false,
102
101
  }: {
103
102
  currentUserId?: 'visitor-1' | 'linker-1'
104
103
  currentUserRole?: 'owner' | 'member'
105
- otherMemberCustomer?: boolean
106
104
  otherUser?: {
107
105
  id: string
108
106
  name?: string
@@ -135,7 +133,6 @@ const createChannel = ({
135
133
  otherUser.is_account ?? currentUserId === 'visitor-1',
136
134
  },
137
135
  role: currentUserRole === 'owner' ? 'member' : 'owner',
138
- customer: otherMemberCustomer,
139
136
  },
140
137
  },
141
138
  membership: {},
@@ -290,49 +287,38 @@ describe('ChannelView', () => {
290
287
  expect(screen.queryByTestId('conversation-footer')).not.toBeInTheDocument()
291
288
  })
292
289
 
293
- it('does not pass starred to avatar when channel is starred', () => {
290
+ it('does not pass starred to avatar when showStarButton is false', () => {
294
291
  mockIsStarred = true
295
292
  const channel = createChannel()
296
293
 
297
294
  renderWithProviders(
298
- <ChannelView channel={channel} showStarButton={true} />
295
+ <ChannelView channel={channel} showStarButton={false} />
299
296
  )
300
297
 
301
298
  expect(avatarRenderCalls.length).toBeGreaterThan(0)
302
- expect(avatarRenderCalls.every((call) => !('starred' in call))).toBe(true)
299
+ expect(avatarRenderCalls.every((call) => call.starred === false)).toBe(true)
303
300
  })
304
301
 
305
- it('renders built-in customer badges from participant member data', () => {
302
+ it('passes starred to avatar when showStarButton is true and channel is starred', () => {
303
+ mockIsStarred = true
304
+ const channel = createChannel()
305
+
306
306
  renderWithProviders(
307
- <ChannelView
308
- channel={createChannel({ otherMemberCustomer: true })}
309
- renderHeaderTitleBadges={() => (
310
- <span data-testid="custom-header-badge">VIP</span>
311
- )}
312
- />
307
+ <ChannelView channel={channel} showStarButton={true} />
313
308
  )
314
309
 
315
- const customerBadges = screen.getAllByRole('img', { name: 'Customer' })
316
- const customBadges = screen.getAllByTestId('custom-header-badge')
317
-
318
- expect(customerBadges.length).toBeGreaterThan(0)
319
- expect(customBadges.length).toBe(customerBadges.length)
320
- customerBadges.forEach((badge, index) => {
321
- expect(
322
- badge.compareDocumentPosition(customBadges[index]) &
323
- Node.DOCUMENT_POSITION_FOLLOWING
324
- ).toBeTruthy()
325
- })
310
+ expect(avatarRenderCalls.length).toBeGreaterThan(0)
311
+ expect(avatarRenderCalls.every((call) => call.starred === true)).toBe(true)
326
312
  })
327
313
 
328
- it('renders built-in starred badges from channel star state', () => {
314
+ it('does not pass starred to avatar by default (showStarButton defaults to false)', () => {
329
315
  mockIsStarred = true
316
+ const channel = createChannel()
330
317
 
331
- renderWithProviders(<ChannelView channel={createChannel()} />)
318
+ renderWithProviders(<ChannelView channel={channel} />)
332
319
 
333
- expect(
334
- screen.getAllByRole('img', { name: 'Starred conversation' }).length
335
- ).toBeGreaterThan(0)
320
+ expect(avatarRenderCalls.length).toBeGreaterThan(0)
321
+ expect(avatarRenderCalls.every((call) => call.starred === false)).toBe(true)
336
322
  })
337
323
 
338
324
  it('passes dmAgentEnabled to avatar when dmAgentEnabled is true for visitor view', () => {