@linktr.ee/messaging-react 3.4.0-rc-1782387968 → 3.4.0-rc-1782392622

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 (37) hide show
  1. package/dist/{Card-BOD2Qo_b.js → Card-BKd0E-B9.js} +3 -3
  2. package/dist/{Card-BOD2Qo_b.js.map → Card-BKd0E-B9.js.map} +1 -1
  3. package/dist/{Card-utKbCGom.cjs → Card-CdaZxDEx.cjs} +2 -2
  4. package/dist/{Card-utKbCGom.cjs.map → Card-CdaZxDEx.cjs.map} +1 -1
  5. package/dist/{Card-DcEBssw0.js → Card-CrTBu2I6.js} +2 -2
  6. package/dist/{Card-DcEBssw0.js.map → Card-CrTBu2I6.js.map} +1 -1
  7. package/dist/{Card-DkNdLpVE.cjs → Card-DXWp0uwi.cjs} +2 -2
  8. package/dist/{Card-DkNdLpVE.cjs.map → Card-DXWp0uwi.cjs.map} +1 -1
  9. package/dist/{Card-UbPYWjnX.cjs → Card-DaalkoQW.cjs} +2 -2
  10. package/dist/{Card-UbPYWjnX.cjs.map → Card-DaalkoQW.cjs.map} +1 -1
  11. package/dist/{Card-beoX9cug.js → Card-XVTz8IYz.js} +2 -2
  12. package/dist/{Card-beoX9cug.js.map → Card-XVTz8IYz.js.map} +1 -1
  13. package/dist/{LockedThumbnail-Bjdf-fTw.cjs → LockedThumbnail-D3Xwdb5_.cjs} +2 -2
  14. package/dist/{LockedThumbnail-Bjdf-fTw.cjs.map → LockedThumbnail-D3Xwdb5_.cjs.map} +1 -1
  15. package/dist/{LockedThumbnail-bOjjYYyV.js → LockedThumbnail-D_44b-Uy.js} +2 -2
  16. package/dist/{LockedThumbnail-bOjjYYyV.js.map → LockedThumbnail-D_44b-Uy.js.map} +1 -1
  17. package/dist/index-Cds2w5zi.cjs +2 -0
  18. package/dist/index-Cds2w5zi.cjs.map +1 -0
  19. package/dist/{index-DG5LOHey.js → index-DPGZRLRM.js} +998 -973
  20. package/dist/index-DPGZRLRM.js.map +1 -0
  21. package/dist/index.cjs +1 -1
  22. package/dist/index.d.ts +11 -19
  23. package/dist/index.js +19 -20
  24. package/package.json +2 -2
  25. package/src/components/ChannelList/CustomChannelPreview.test.tsx +0 -38
  26. package/src/components/ChannelList/CustomChannelPreview.tsx +6 -5
  27. package/src/components/ChannelView.stories.tsx +16 -0
  28. package/src/components/ChannelView.test.tsx +20 -35
  29. package/src/components/ChannelView.tsx +55 -17
  30. package/src/components/MessagingShell/index.tsx +2 -0
  31. package/src/index.ts +0 -2
  32. package/src/stream-custom-data.ts +0 -8
  33. package/src/types.ts +12 -0
  34. package/dist/index-B3oQq9zK.cjs +0 -2
  35. package/dist/index-B3oQq9zK.cjs.map +0 -1
  36. package/dist/index-DG5LOHey.js.map +0 -1
  37. package/src/components/VerifiedBadge/index.tsx +0 -25
package/dist/index.cjs CHANGED
@@ -1,2 +1,2 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("./index-B3oQq9zK.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.VerifiedBadge=e.VerifiedBadge;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-Cds2w5zi.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.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' | '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'>;
153
153
 
154
154
  /**
155
155
  * ChannelView component props
@@ -351,6 +351,16 @@ 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
+ * Fired when the participant's name in the channel header is clicked. When
356
+ * provided, the name renders as a button with a trailing caret (an affordance
357
+ * for opening participant details / thread content); when omitted, the name
358
+ * renders as plain text.
359
+ *
360
+ * @example
361
+ * onParticipantNameClick={() => openThreadContentDialog()}
362
+ */
363
+ onParticipantNameClick?: () => void;
354
364
  }
355
365
 
356
366
  export declare interface ComposerCardProps extends LockedAttachmentBaseProps {
@@ -1389,17 +1399,6 @@ declare interface UseMessageVoteResult {
1389
1399
  */
1390
1400
  export declare const useMessaging: () => MessagingContextValue;
1391
1401
 
1392
- /**
1393
- * Linktree verification badge shown next to the name of a verified profile.
1394
- * Rendered when a participant's Stream user has `is_verified` set to true.
1395
- */
1396
- export declare const VerifiedBadge: default_2.FC<VerifiedBadgeProps>;
1397
-
1398
- export declare interface VerifiedBadgeProps {
1399
- className?: string;
1400
- size?: number;
1401
- }
1402
-
1403
1402
  declare interface VideoAttachmentSharedProps extends MessageAttachmentBaseProps {
1404
1403
  /** Single video — convenience for the most common case. */
1405
1404
  src?: string;
@@ -1472,13 +1471,6 @@ declare module 'stream-chat' {
1472
1471
  */
1473
1472
  chatbot_paused?: boolean;
1474
1473
  }
1475
- interface CustomUserData {
1476
- /**
1477
- * Whether the user is a verified Linktree profile. When true, a
1478
- * verification badge is shown next to their name in messaging.
1479
- */
1480
- is_verified?: boolean;
1481
- }
1482
1474
  interface CustomMessageData {
1483
1475
  /**
1484
1476
  * When true, hides the date timestamp in system messages.
package/dist/index.js CHANGED
@@ -1,35 +1,34 @@
1
- import { a as s, b as t, C as i, c as n, d as o, 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 d, l as h, V as p, m as v, n as A, o as C, p as F, q as k, s as f, t as V, u as b, v as x, w as y, x as B, y as P, z as S, B as q, D as z } from "./index-DG5LOHey.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-DPGZRLRM.js";
2
2
  export {
3
- s as ActionButton,
3
+ e as ActionButton,
4
4
  t as Avatar,
5
5
  i as ChannelEmptyState,
6
6
  n as ChannelList,
7
7
  o as ChannelView,
8
8
  m as CustomMessageProvider,
9
9
  g as FaqList,
10
- r as FaqListItem,
11
- l as LinkAttachment,
10
+ l as FaqListItem,
11
+ r as LinkAttachment,
12
12
  M as LockedAttachment,
13
13
  u as MediaMessage,
14
14
  L as MessageAttachment,
15
15
  c as MessageVoteButtons,
16
- d as MessagingProvider,
17
- h as MessagingShell,
18
- p as VerifiedBadge,
19
- v as buildCompactMetaLabel,
20
- A as formatFileSize,
21
- C as formatRelativeTime,
22
- F as getFileExtensionLabel,
23
- k as getMessageDisplayText,
24
- f as isLinkAttachment,
25
- V as isUuidLike,
26
- b as messageAttachmentGroupPositionFromStream,
16
+ h as MessagingProvider,
17
+ d as MessagingShell,
18
+ p as buildCompactMetaLabel,
19
+ v as formatFileSize,
20
+ A as formatRelativeTime,
21
+ C as getFileExtensionLabel,
22
+ F as getMessageDisplayText,
23
+ k as isLinkAttachment,
24
+ b as isUuidLike,
25
+ f as messageAttachmentGroupPositionFromStream,
27
26
  x as normalizeLanguageCode,
28
27
  y as resolveLinkAttachment,
29
- B as resolveMediaFromMessage,
30
- P as resolveParticipantDisplayName,
31
- S as useCustomMessage,
32
- q as useMessageVote,
33
- z as useMessaging
28
+ P as resolveMediaFromMessage,
29
+ S as resolveParticipantDisplayName,
30
+ q as useCustomMessage,
31
+ z as useMessageVote,
32
+ B as useMessaging
34
33
  };
35
34
  //# 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.0-rc-1782387968",
3
+ "version": "3.4.0-rc-1782392622",
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-1782387968",
41
+ "@linktr.ee/messaging-core": "2.3.0-rc-1782392622",
42
42
  "@phosphor-icons/react": "^2.1.10"
43
43
  },
44
44
  "devDependencies": {
@@ -101,44 +101,6 @@ describe('CustomChannelPreview', () => {
101
101
  expect(screen.getByText('Unknown member')).toBeInTheDocument()
102
102
  })
103
103
 
104
- it('shows the verification badge when the participant is verified', () => {
105
- const channel = createMockChannel([])
106
- channel.state.members['participant-1'] = {
107
- user: { id: 'participant-1', name: 'Alice', is_verified: true },
108
- user_id: 'participant-1',
109
- }
110
-
111
- renderWithProviders(
112
- <CustomChannelPreview {...defaultProps} channel={channel} />
113
- )
114
-
115
- expect(screen.getByLabelText('Verified')).toBeInTheDocument()
116
- })
117
-
118
- it('does not show the verification badge when the participant is not verified', () => {
119
- const channel = createMockChannel([])
120
- channel.state.members['participant-1'] = {
121
- user: { id: 'participant-1', name: 'Alice', is_verified: false },
122
- user_id: 'participant-1',
123
- }
124
-
125
- renderWithProviders(
126
- <CustomChannelPreview {...defaultProps} channel={channel} />
127
- )
128
-
129
- expect(screen.queryByLabelText('Verified')).not.toBeInTheDocument()
130
- })
131
-
132
- it('does not show the verification badge when is_verified is absent', () => {
133
- const channel = createMockChannel([])
134
-
135
- renderWithProviders(
136
- <CustomChannelPreview {...defaultProps} channel={channel} />
137
- )
138
-
139
- expect(screen.queryByLabelText('Verified')).not.toBeInTheDocument()
140
- })
141
-
142
104
  it('shows the latest non-system message when the last message is a system message', () => {
143
105
  const channel = createMockChannel([
144
106
  {
@@ -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 { VerifiedBadge } from '../VerifiedBadge'
12
11
 
13
12
  import { useChannelListContext } from './ChannelListContext'
14
13
 
@@ -49,7 +48,6 @@ const CustomChannelPreview = React.memo<ChannelPreviewUIComponentProps>(
49
48
  )
50
49
  const participantName = resolveParticipantDisplayName(participant?.user)
51
50
  const participantImage = participant?.user?.image
52
- const isParticipantVerified = participant?.user?.is_verified === true
53
51
 
54
52
  // Get last non-system message for preview
55
53
  const lastMessage = (() => {
@@ -148,12 +146,15 @@ const CustomChannelPreview = React.memo<ChannelPreviewUIComponentProps>(
148
146
  <div className="flex-1 min-w-0 flex flex-col gap-1">
149
147
  {/* Name and timestamp row */}
150
148
  <div className="flex items-center justify-between gap-2">
151
- <h3 className="flex min-w-0 items-center gap-1 text-sm font-medium text-[#191918]">
149
+ <h3
150
+ className={classNames(
151
+ 'text-sm font-medium truncate text-[#191918]'
152
+ )}
153
+ >
152
154
  {isChannelStarred && (
153
155
  <span className="sr-only">Starred conversation. </span>
154
156
  )}
155
- <span className="min-w-0 truncate">{participantName}</span>
156
- {isParticipantVerified && <VerifiedBadge />}
157
+ {participantName}
157
158
  </h3>
158
159
  {lastMessageTime && (
159
160
  <span className="text-xs text-[#717070] flex-shrink-0">
@@ -251,6 +251,22 @@ Default.parameters = {
251
251
  },
252
252
  }
253
253
 
254
+ export const ParticipantNameTrigger: StoryFn<TemplateProps> = Template.bind({})
255
+ ParticipantNameTrigger.args = {
256
+ showBackButton: false,
257
+ onParticipantNameClick: () =>
258
+ // eslint-disable-next-line no-alert
259
+ window.alert('Participant name clicked — open Thread content'),
260
+ }
261
+ ParticipantNameTrigger.parameters = {
262
+ docs: {
263
+ description: {
264
+ story:
265
+ 'Header with `onParticipantNameClick` set: the participant name renders as a button with a trailing caret (both the mobile and desktop header layouts). Click the name to fire the callback (shows an alert here). With the prop omitted (see Default) the name is plain, non-interactive text.',
266
+ },
267
+ },
268
+ }
269
+
254
270
  export const DmAgentHeader: StoryFn<TemplateProps> = Template.bind({})
255
271
  DmAgentHeader.args = {
256
272
  showBackButton: false,
@@ -106,7 +106,6 @@ const createChannel = ({
106
106
  name?: string
107
107
  username?: string
108
108
  is_account?: boolean
109
- is_verified?: boolean
110
109
  }
111
110
  } = {}) =>
112
111
  ({
@@ -194,6 +193,26 @@ describe('ChannelView', () => {
194
193
  expect(screen.getAllByText('Linker').length).toBeGreaterThanOrEqual(1)
195
194
  })
196
195
 
196
+ it('renders the participant name as a button that fires onParticipantNameClick', () => {
197
+ const onParticipantNameClick = vi.fn()
198
+ renderWithProviders(
199
+ <ChannelView
200
+ channel={createChannel()}
201
+ onParticipantNameClick={onParticipantNameClick}
202
+ />
203
+ )
204
+
205
+ // Both the mobile and desktop header layouts render the button (one is
206
+ // hidden via CSS), so there is at least one match.
207
+ const buttons = screen.getAllByRole('button', {
208
+ name: /view details for linker/i,
209
+ })
210
+ expect(buttons.length).toBeGreaterThanOrEqual(1)
211
+
212
+ buttons[0].click()
213
+ expect(onParticipantNameClick).toHaveBeenCalledTimes(1)
214
+ })
215
+
197
216
  it('renders the actions menu by default', () => {
198
217
  renderWithProviders(<ChannelView channel={createChannel()} />)
199
218
 
@@ -456,40 +475,6 @@ describe('ChannelView', () => {
456
475
  expect(screen.getAllByText('Unknown member').length).toBeGreaterThan(0)
457
476
  })
458
477
 
459
- it('shows the verification badge in the header when the participant is verified', () => {
460
- renderWithProviders(
461
- <ChannelView
462
- channel={createChannel({
463
- otherUser: {
464
- id: 'linker-1',
465
- name: 'Linker',
466
- is_account: true,
467
- is_verified: true,
468
- },
469
- })}
470
- />
471
- )
472
-
473
- expect(screen.getAllByLabelText('Verified').length).toBeGreaterThan(0)
474
- })
475
-
476
- it('does not show the verification badge in the header when the participant is not verified', () => {
477
- renderWithProviders(
478
- <ChannelView
479
- channel={createChannel({
480
- otherUser: {
481
- id: 'linker-1',
482
- name: 'Linker',
483
- is_account: true,
484
- is_verified: false,
485
- },
486
- })}
487
- />
488
- )
489
-
490
- expect(screen.queryByLabelText('Verified')).not.toBeInTheDocument()
491
- })
492
-
493
478
  it('fires onMessageLinkClick with the href and undefined message when the link is not inside a message wrapper', () => {
494
479
  const handler = vi.fn()
495
480
  const { container } = renderWithProviders(
@@ -1,4 +1,9 @@
1
- import { ArrowLeftIcon, SparkleIcon, StarIcon } from '@phosphor-icons/react'
1
+ import {
2
+ ArrowLeftIcon,
3
+ CaretRightIcon,
4
+ SparkleIcon,
5
+ StarIcon,
6
+ } from '@phosphor-icons/react'
2
7
  import classNames from 'classnames'
3
8
  import React, { useCallback, useEffect, useRef } from 'react'
4
9
  import { Channel as ChannelType } from 'stream-chat'
@@ -27,7 +32,6 @@ import CustomTypingIndicator from './CustomTypingIndicator'
27
32
  import { DmAgentEnabledContext } from './CustomTypingIndicator/DmAgentContext'
28
33
  import { ChannelEmptyState } from './MessagingShell/ChannelEmptyState'
29
34
  import { LoadingState } from './MessagingShell/LoadingState'
30
- import { VerifiedBadge } from './VerifiedBadge'
31
35
 
32
36
  const ICON_BTN_CLASS =
33
37
  'size-10 rounded-full hover:bg-[#E5E4E1] flex items-center justify-center transition-colors duration-150 focus-ring'
@@ -53,6 +57,7 @@ const CustomChannelHeader: React.FC<{
53
57
  customChannelActions?: React.ReactNode
54
58
  renderChannelActions?: React.ReactNode
55
59
  showChannelInfo?: boolean
60
+ onParticipantNameClick?: () => void
56
61
  renderHeaderTitleBadges?: ChannelViewProps['renderHeaderTitleBadges']
57
62
  }> = ({
58
63
  onBack,
@@ -70,6 +75,7 @@ const CustomChannelHeader: React.FC<{
70
75
  customChannelActions,
71
76
  renderChannelActions,
72
77
  showChannelInfo = true,
78
+ onParticipantNameClick,
73
79
  renderHeaderTitleBadges,
74
80
  }) => {
75
81
  const { channel } = useChannelStateContext()
@@ -86,7 +92,6 @@ const CustomChannelHeader: React.FC<{
86
92
 
87
93
  const participantName = resolveParticipantDisplayName(participant?.user)
88
94
  const participantImage = participant?.user?.image
89
- const isParticipantVerified = participant?.user?.is_verified === true
90
95
  const isStarred = useChannelStar(channel)
91
96
  const headerTitleBadges = renderHeaderTitleBadges?.({
92
97
  channel,
@@ -135,13 +140,27 @@ const CustomChannelHeader: React.FC<{
135
140
  dmAgentEnabled={dmAgentEnabled}
136
141
  size={48}
137
142
  />
138
- <p className="flex max-w-full items-center justify-center gap-1 text-center text-xs font-medium text-black/90">
139
- <span className="min-w-0 truncate">{participantName}</span>
140
- {isParticipantVerified && <VerifiedBadge />}
141
- {headerTitleBadges && (
142
- <span className="shrink-0">{headerTitleBadges}</span>
143
- )}
144
- </p>
143
+ {onParticipantNameClick ? (
144
+ <button
145
+ type="button"
146
+ onClick={onParticipantNameClick}
147
+ aria-label={`View details for ${participantName}`}
148
+ className="flex max-w-full items-center gap-0.5 rounded text-center text-xs font-medium text-black/90 transition-opacity hover:opacity-70"
149
+ >
150
+ <span className="min-w-0 truncate">{participantName}</span>
151
+ {headerTitleBadges && (
152
+ <span className="shrink-0">{headerTitleBadges}</span>
153
+ )}
154
+ <CaretRightIcon className="size-3 shrink-0" weight="bold" />
155
+ </button>
156
+ ) : (
157
+ <p className="flex max-w-full items-center justify-center gap-1 text-center text-xs font-medium text-black/90">
158
+ <span className="min-w-0 truncate">{participantName}</span>
159
+ {headerTitleBadges && (
160
+ <span className="shrink-0">{headerTitleBadges}</span>
161
+ )}
162
+ </p>
163
+ )}
145
164
  {dmAgentEnabled && (
146
165
  <div className="flex items-center gap-1 text-[10px] leading-3 text-black/55">
147
166
  <SparkleIcon className="size-3 shrink-0 text-black/55" />
@@ -214,13 +233,27 @@ const CustomChannelHeader: React.FC<{
214
233
  size={48}
215
234
  />
216
235
  <div className="min-w-0">
217
- <h1 className="flex min-w-0 items-center gap-1 font-medium text-black/90">
218
- <span className="min-w-0 truncate">{participantName}</span>
219
- {isParticipantVerified && <VerifiedBadge />}
220
- {headerTitleBadges && (
221
- <span className="shrink-0">{headerTitleBadges}</span>
222
- )}
223
- </h1>
236
+ {onParticipantNameClick ? (
237
+ <button
238
+ type="button"
239
+ onClick={onParticipantNameClick}
240
+ aria-label={`View details for ${participantName}`}
241
+ className="flex min-w-0 max-w-full items-center gap-1 rounded font-medium text-black/90 transition-opacity hover:opacity-70"
242
+ >
243
+ <span className="min-w-0 truncate">{participantName}</span>
244
+ {headerTitleBadges && (
245
+ <span className="shrink-0">{headerTitleBadges}</span>
246
+ )}
247
+ <CaretRightIcon className="size-4 shrink-0" weight="bold" />
248
+ </button>
249
+ ) : (
250
+ <h1 className="flex min-w-0 items-center gap-1 font-medium text-black/90">
251
+ <span className="min-w-0 truncate">{participantName}</span>
252
+ {headerTitleBadges && (
253
+ <span className="shrink-0">{headerTitleBadges}</span>
254
+ )}
255
+ </h1>
256
+ )}
224
257
  {dmAgentEnabled && (
225
258
  <div className="mt-0.5 flex items-center gap-1 text-[10px] leading-3 text-black/55">
226
259
  <SparkleIcon className="size-3 shrink-0 text-black/55" />
@@ -306,6 +339,7 @@ const ChannelViewInner: React.FC<{
306
339
  dmAgentEnabled?: boolean
307
340
  viewerLanguage?: string
308
341
  showChannelInfo?: boolean
342
+ onParticipantNameClick?: () => void
309
343
  }> = ({
310
344
  onBack,
311
345
  showBackButton,
@@ -332,6 +366,7 @@ const ChannelViewInner: React.FC<{
332
366
  dmAgentEnabled = false,
333
367
  viewerLanguage,
334
368
  showChannelInfo = true,
369
+ onParticipantNameClick,
335
370
  }) => {
336
371
  const { channel } = useChannelStateContext()
337
372
 
@@ -413,6 +448,7 @@ const ChannelViewInner: React.FC<{
413
448
  onReportParticipantClick={onReportParticipantClick}
414
449
  customChannelActions={customChannelActions}
415
450
  renderChannelActions={renderChannelActions}
451
+ onParticipantNameClick={onParticipantNameClick}
416
452
  renderHeaderTitleBadges={renderHeaderTitleBadges}
417
453
  />
418
454
  </div>
@@ -492,6 +528,7 @@ export const ChannelView = React.memo<ChannelViewProps>(
492
528
  attachmentPreviewList,
493
529
  viewerLanguage,
494
530
  showChannelInfo = true,
531
+ onParticipantNameClick,
495
532
  }) => {
496
533
  // Custom send message handler that:
497
534
  // 1. Applies messageMetadata if provided
@@ -605,6 +642,7 @@ export const ChannelView = React.memo<ChannelViewProps>(
605
642
  renderMessage={renderMessage}
606
643
  viewerLanguage={viewerLanguage}
607
644
  showChannelInfo={showChannelInfo}
645
+ onParticipantNameClick={onParticipantNameClick}
608
646
  />
609
647
  </Channel>
610
648
  </DmAgentEnabledContext.Provider>
@@ -35,6 +35,7 @@ export const MessagingShell: React.FC<MessagingShellProps> = ({
35
35
  renderChannelBanner,
36
36
  customChannelActions,
37
37
  renderChannelActions,
38
+ onParticipantNameClick,
38
39
  renderMessage,
39
40
  onMessageLinkClick,
40
41
  showChannelInfo,
@@ -259,6 +260,7 @@ export const MessagingShell: React.FC<MessagingShellProps> = ({
259
260
  renderHeaderTitleBadges={renderHeaderTitleBadges}
260
261
  customChannelActions={customChannelActions}
261
262
  renderChannelActions={renderChannelActions}
263
+ onParticipantNameClick={onParticipantNameClick}
262
264
  renderMessage={renderMessage}
263
265
  onMessageLinkClick={onMessageLinkClick}
264
266
  showChannelInfo={showChannelInfo}
package/src/index.ts CHANGED
@@ -10,8 +10,6 @@ export { default as LockedAttachment } from './components/LockedAttachment'
10
10
  export { default as LinkAttachment } from './components/LinkAttachment'
11
11
  export { default as MessageAttachment } from './components/MessageAttachment'
12
12
  export { Avatar } from './components/Avatar'
13
- export { VerifiedBadge } from './components/VerifiedBadge'
14
- export type { VerifiedBadgeProps } from './components/VerifiedBadge'
15
13
  export { FaqList } from './components/FaqList'
16
14
  export { FaqListItem } from './components/FaqList/FaqListItem'
17
15
  export { ChannelEmptyState } from './components/MessagingShell/ChannelEmptyState'
@@ -98,14 +98,6 @@ declare module 'stream-chat' {
98
98
  chatbot_paused?: boolean
99
99
  }
100
100
 
101
- interface CustomUserData {
102
- /**
103
- * Whether the user is a verified Linktree profile. When true, a
104
- * verification badge is shown next to their name in messaging.
105
- */
106
- is_verified?: boolean
107
- }
108
-
109
101
  interface CustomMessageData {
110
102
  /**
111
103
  * When true, hides the date timestamp in system messages.
package/src/types.ts CHANGED
@@ -323,6 +323,17 @@ export interface ChannelViewProps {
323
323
  * Use this to render staged paid or media attachments inside the input.
324
324
  */
325
325
  attachmentPreviewList?: ComponentType
326
+
327
+ /**
328
+ * Fired when the participant's name in the channel header is clicked. When
329
+ * provided, the name renders as a button with a trailing caret (an affordance
330
+ * for opening participant details / thread content); when omitted, the name
331
+ * renders as plain text.
332
+ *
333
+ * @example
334
+ * onParticipantNameClick={() => openThreadContentDialog()}
335
+ */
336
+ onParticipantNameClick?: () => void
326
337
  }
327
338
 
328
339
  /**
@@ -344,6 +355,7 @@ export type ChannelViewPassthroughProps = Pick<
344
355
  | 'renderChannelBanner'
345
356
  | 'customChannelActions'
346
357
  | 'renderChannelActions'
358
+ | 'onParticipantNameClick'
347
359
  | 'renderMessage'
348
360
  | 'onMessageLinkClick'
349
361
  | 'showChannelInfo'