@linktr.ee/messaging-react 3.4.2 → 3.5.0-rc-1782480113

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 (34) hide show
  1. package/dist/{Card-BKalDyyu.js → Card-D34G7gvm.js} +2 -2
  2. package/dist/{Card-BKalDyyu.js.map → Card-D34G7gvm.js.map} +1 -1
  3. package/dist/{Card-DxroYNrS.cjs → Card-DIFgYVKx.cjs} +2 -2
  4. package/dist/{Card-DxroYNrS.cjs.map → Card-DIFgYVKx.cjs.map} +1 -1
  5. package/dist/{Card-mpVcQ8-W.js → Card-DVDoFfkP.js} +3 -3
  6. package/dist/{Card-mpVcQ8-W.js.map → Card-DVDoFfkP.js.map} +1 -1
  7. package/dist/{Card-V-bNUxcw.cjs → Card-DhAhEetQ.cjs} +2 -2
  8. package/dist/{Card-V-bNUxcw.cjs.map → Card-DhAhEetQ.cjs.map} +1 -1
  9. package/dist/{Card-CphU_jG-.js → Card-Dy1Qj3MR.js} +2 -2
  10. package/dist/{Card-CphU_jG-.js.map → Card-Dy1Qj3MR.js.map} +1 -1
  11. package/dist/{Card-DFy_ZvFH.cjs → Card-yHlBBfOt.cjs} +2 -2
  12. package/dist/{Card-DFy_ZvFH.cjs.map → Card-yHlBBfOt.cjs.map} +1 -1
  13. package/dist/{LockedThumbnail-BD7qtcso.js → LockedThumbnail-1S_Rjelj.js} +2 -2
  14. package/dist/{LockedThumbnail-BD7qtcso.js.map → LockedThumbnail-1S_Rjelj.js.map} +1 -1
  15. package/dist/{LockedThumbnail-B3W7upHs.cjs → LockedThumbnail-P2BVsLli.cjs} +2 -2
  16. package/dist/{LockedThumbnail-B3W7upHs.cjs.map → LockedThumbnail-P2BVsLli.cjs.map} +1 -1
  17. package/dist/index-BZjaMDgI.cjs +2 -0
  18. package/dist/index-BZjaMDgI.cjs.map +1 -0
  19. package/dist/{index-CjwQ2Hj3.js → index-CgczmQkU.js} +1160 -1150
  20. package/dist/index-CgczmQkU.js.map +1 -0
  21. package/dist/index.cjs +1 -1
  22. package/dist/index.d.ts +18 -0
  23. package/dist/index.js +18 -17
  24. package/package.json +2 -2
  25. package/src/components/ChannelList/CustomChannelPreview.test.tsx +38 -0
  26. package/src/components/ChannelList/CustomChannelPreview.tsx +5 -6
  27. package/src/components/ChannelView.test.tsx +35 -0
  28. package/src/components/ChannelView.tsx +6 -0
  29. package/src/components/VerifiedBadge/index.tsx +25 -0
  30. package/src/index.ts +2 -0
  31. package/src/stream-custom-data.ts +8 -0
  32. package/dist/index-CjwQ2Hj3.js.map +0 -1
  33. package/dist/index-tI-4cxV1.cjs +0 -2
  34. package/dist/index-tI-4cxV1.cjs.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-tI-4cxV1.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;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("./index-BZjaMDgI.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.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
@@ -1423,6 +1423,17 @@ declare interface UseMessageVoteResult {
1423
1423
  */
1424
1424
  export declare const useMessaging: () => MessagingContextValue;
1425
1425
 
1426
+ /**
1427
+ * Linktree verification badge shown next to the name of a verified profile.
1428
+ * Rendered when a participant's Stream user has `is_verified` set to true.
1429
+ */
1430
+ export declare const VerifiedBadge: default_2.FC<VerifiedBadgeProps>;
1431
+
1432
+ export declare interface VerifiedBadgeProps {
1433
+ className?: string;
1434
+ size?: number;
1435
+ }
1436
+
1426
1437
  declare interface VideoAttachmentSharedProps extends MessageAttachmentBaseProps {
1427
1438
  /** Single video — convenience for the most common case. */
1428
1439
  src?: string;
@@ -1495,6 +1506,13 @@ declare module 'stream-chat' {
1495
1506
  */
1496
1507
  chatbot_paused?: boolean;
1497
1508
  }
1509
+ interface CustomUserData {
1510
+ /**
1511
+ * Whether the user is a verified Linktree profile. When true, a
1512
+ * verification badge is shown next to their name in messaging.
1513
+ */
1514
+ is_verified?: boolean;
1515
+ }
1498
1516
  interface CustomMessageData {
1499
1517
  /**
1500
1518
  * When true, hides the date timestamp in system messages.
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
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-CjwQ2Hj3.js";
1
+ import { a as s, 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 d, l as h, V as p, m as C, n as v, o as A, p as k, q as F, 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, E as D } from "./index-CgczmQkU.js";
2
2
  export {
3
- e as ActionButton,
3
+ s as ActionButton,
4
4
  t as Avatar,
5
5
  i as ChannelEmptyState,
6
6
  o as ChannelList,
@@ -13,23 +13,24 @@ export {
13
13
  u as MediaMessage,
14
14
  L as MessageAttachment,
15
15
  c as MessageVoteButtons,
16
- h as MessagingProvider,
17
- d as MessagingShell,
18
- p as buildCompactMetaLabel,
19
- C as formatFileSize,
20
- v as formatRelativeTime,
21
- A as getFileExtensionLabel,
22
- k as getMessageDisplayText,
23
- F as isLinkAttachment,
24
- b as isUuidLike,
25
- f as messageAttachmentGroupPositionFromStream,
16
+ d as MessagingProvider,
17
+ h as MessagingShell,
18
+ p as VerifiedBadge,
19
+ C as buildCompactMetaLabel,
20
+ v as formatFileSize,
21
+ A as formatRelativeTime,
22
+ k as getFileExtensionLabel,
23
+ F as getMessageDisplayText,
24
+ f as isLinkAttachment,
25
+ V as isUuidLike,
26
+ b as messageAttachmentGroupPositionFromStream,
26
27
  x as normalizeLanguageCode,
27
28
  y as resolveLinkAttachment,
28
- P as resolveMediaFromMessage,
29
- S as resolveParticipantDisplayName,
30
- q as useComposerLocked,
31
- z as useCustomMessage,
32
- B as useMessageVote,
29
+ B as resolveMediaFromMessage,
30
+ P as resolveParticipantDisplayName,
31
+ S as useComposerLocked,
32
+ q as useCustomMessage,
33
+ z as useMessageVote,
33
34
  D as useMessaging
34
35
  };
35
36
  //# 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",
3
+ "version": "3.5.0-rc-1782480113",
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.2.0",
41
+ "@linktr.ee/messaging-core": "2.3.0-rc-1782480113",
42
42
  "@phosphor-icons/react": "^2.1.10"
43
43
  },
44
44
  "devDependencies": {
@@ -101,6 +101,44 @@ 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
+
104
142
  it('shows the latest non-system message when the last message is a system message', () => {
105
143
  const channel = createMockChannel([
106
144
  {
@@ -8,6 +8,7 @@ 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'
11
12
 
12
13
  import { useChannelListContext } from './ChannelListContext'
13
14
 
@@ -48,6 +49,7 @@ const CustomChannelPreview = React.memo<ChannelPreviewUIComponentProps>(
48
49
  )
49
50
  const participantName = resolveParticipantDisplayName(participant?.user)
50
51
  const participantImage = participant?.user?.image
52
+ const isParticipantVerified = participant?.user?.is_verified === true
51
53
 
52
54
  // Get last non-system message for preview
53
55
  const lastMessage = (() => {
@@ -146,15 +148,12 @@ const CustomChannelPreview = React.memo<ChannelPreviewUIComponentProps>(
146
148
  <div className="flex-1 min-w-0 flex flex-col gap-1">
147
149
  {/* Name and timestamp row */}
148
150
  <div className="flex items-center justify-between gap-2">
149
- <h3
150
- className={classNames(
151
- 'text-sm font-medium truncate text-[#191918]'
152
- )}
153
- >
151
+ <h3 className="flex min-w-0 items-center gap-1 text-sm font-medium text-[#191918]">
154
152
  {isChannelStarred && (
155
153
  <span className="sr-only">Starred conversation. </span>
156
154
  )}
157
- {participantName}
155
+ <span className="min-w-0 truncate">{participantName}</span>
156
+ {isParticipantVerified && <VerifiedBadge />}
158
157
  </h3>
159
158
  {lastMessageTime && (
160
159
  <span className="text-xs text-[#717070] flex-shrink-0">
@@ -106,6 +106,7 @@ const createChannel = ({
106
106
  name?: string
107
107
  username?: string
108
108
  is_account?: boolean
109
+ is_verified?: boolean
109
110
  }
110
111
  } = {}) =>
111
112
  ({
@@ -475,6 +476,40 @@ describe('ChannelView', () => {
475
476
  expect(screen.getAllByText('Unknown member').length).toBeGreaterThan(0)
476
477
  })
477
478
 
479
+ it('shows the verification badge in the header when the participant is verified', () => {
480
+ renderWithProviders(
481
+ <ChannelView
482
+ channel={createChannel({
483
+ otherUser: {
484
+ id: 'linker-1',
485
+ name: 'Linker',
486
+ is_account: true,
487
+ is_verified: true,
488
+ },
489
+ })}
490
+ />
491
+ )
492
+
493
+ expect(screen.getAllByLabelText('Verified').length).toBeGreaterThan(0)
494
+ })
495
+
496
+ it('does not show the verification badge in the header when the participant is not verified', () => {
497
+ renderWithProviders(
498
+ <ChannelView
499
+ channel={createChannel({
500
+ otherUser: {
501
+ id: 'linker-1',
502
+ name: 'Linker',
503
+ is_account: true,
504
+ is_verified: false,
505
+ },
506
+ })}
507
+ />
508
+ )
509
+
510
+ expect(screen.queryByLabelText('Verified')).not.toBeInTheDocument()
511
+ })
512
+
478
513
  it('fires onMessageLinkClick with the href and undefined message when the link is not inside a message wrapper', () => {
479
514
  const handler = vi.fn()
480
515
  const { container } = renderWithProviders(
@@ -32,6 +32,7 @@ import CustomTypingIndicator from './CustomTypingIndicator'
32
32
  import { DmAgentEnabledContext } from './CustomTypingIndicator/DmAgentContext'
33
33
  import { ChannelEmptyState } from './MessagingShell/ChannelEmptyState'
34
34
  import { LoadingState } from './MessagingShell/LoadingState'
35
+ import { VerifiedBadge } from './VerifiedBadge'
35
36
 
36
37
  const ICON_BTN_CLASS =
37
38
  'size-10 rounded-full hover:bg-[#E5E4E1] flex items-center justify-center transition-colors duration-150 focus-ring'
@@ -92,6 +93,7 @@ const CustomChannelHeader: React.FC<{
92
93
 
93
94
  const participantName = resolveParticipantDisplayName(participant?.user)
94
95
  const participantImage = participant?.user?.image
96
+ const isParticipantVerified = participant?.user?.is_verified === true
95
97
  const isStarred = useChannelStar(channel)
96
98
  const headerTitleBadges = renderHeaderTitleBadges?.({
97
99
  channel,
@@ -148,6 +150,7 @@ const CustomChannelHeader: React.FC<{
148
150
  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
151
  >
150
152
  <span className="min-w-0 truncate">{participantName}</span>
153
+ {isParticipantVerified && <VerifiedBadge />}
151
154
  {headerTitleBadges && (
152
155
  <span className="shrink-0">{headerTitleBadges}</span>
153
156
  )}
@@ -156,6 +159,7 @@ const CustomChannelHeader: React.FC<{
156
159
  ) : (
157
160
  <p className="flex max-w-full items-center justify-center gap-1 text-center text-xs font-medium text-black/90">
158
161
  <span className="min-w-0 truncate">{participantName}</span>
162
+ {isParticipantVerified && <VerifiedBadge />}
159
163
  {headerTitleBadges && (
160
164
  <span className="shrink-0">{headerTitleBadges}</span>
161
165
  )}
@@ -241,6 +245,7 @@ const CustomChannelHeader: React.FC<{
241
245
  className="flex min-w-0 max-w-full items-center gap-1 rounded font-medium text-black/90 transition-opacity hover:opacity-70"
242
246
  >
243
247
  <span className="min-w-0 truncate">{participantName}</span>
248
+ {isParticipantVerified && <VerifiedBadge />}
244
249
  {headerTitleBadges && (
245
250
  <span className="shrink-0">{headerTitleBadges}</span>
246
251
  )}
@@ -249,6 +254,7 @@ const CustomChannelHeader: React.FC<{
249
254
  ) : (
250
255
  <h1 className="flex min-w-0 items-center gap-1 font-medium text-black/90">
251
256
  <span className="min-w-0 truncate">{participantName}</span>
257
+ {isParticipantVerified && <VerifiedBadge />}
252
258
  {headerTitleBadges && (
253
259
  <span className="shrink-0">{headerTitleBadges}</span>
254
260
  )}
@@ -0,0 +1,25 @@
1
+ import { SealCheckIcon } from '@phosphor-icons/react'
2
+ import classNames from 'classnames'
3
+ import React from 'react'
4
+
5
+ export interface VerifiedBadgeProps {
6
+ className?: string
7
+ size?: number
8
+ }
9
+
10
+ /**
11
+ * Linktree verification badge shown next to the name of a verified profile.
12
+ * Rendered when a participant's Stream user has `is_verified` set to true.
13
+ */
14
+ export const VerifiedBadge: React.FC<VerifiedBadgeProps> = ({
15
+ className,
16
+ size = 16,
17
+ }) => (
18
+ <SealCheckIcon
19
+ weight="fill"
20
+ size={size}
21
+ className={classNames('shrink-0 text-[#1D9BF0]', className)}
22
+ aria-label="Verified"
23
+ role="img"
24
+ />
25
+ )
package/src/index.ts CHANGED
@@ -10,6 +10,8 @@ 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'
13
15
  export { FaqList } from './components/FaqList'
14
16
  export { FaqListItem } from './components/FaqList/FaqListItem'
15
17
  export { ChannelEmptyState } from './components/MessagingShell/ChannelEmptyState'
@@ -98,6 +98,14 @@ 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
+
101
109
  interface CustomMessageData {
102
110
  /**
103
111
  * When true, hides the date timestamp in system messages.