@linktr.ee/messaging-react 3.14.5 → 3.15.0

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-BdzqW4jX.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.optimizeMessagingAttachmentUrl=e.optimizeMessagingAttachmentUrl;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-NcnnPWrl.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.optimizeMessagingAttachmentUrl=e.optimizeMessagingAttachmentUrl;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
@@ -206,7 +206,7 @@ export declare const ChannelView: default_2.NamedExoticComponent<ChannelViewProp
206
206
  * Props that MessagingShell passes through to ChannelView.
207
207
  * ChannelViewProps is the source of truth for these props.
208
208
  */
209
- declare type ChannelViewPassthroughProps = Pick<ChannelViewProps, 'renderMessageInputActions' | 'renderConversationFooter' | 'CustomChannelEmptyState' | 'onBlockParticipantClick' | 'onReportParticipantClick' | 'dmAgentEnabled' | 'onMessageSent' | 'chatbotVotingEnabled' | 'viewerLanguage' | 'renderHeaderTitleBadges' | 'renderChannelBanner' | 'customChannelActions' | 'renderChannelActions' | 'onParticipantNameClick' | 'renderMessage' | 'onMessageLinkClick' | 'showChannelInfo' | 'composerInput'>;
209
+ declare type ChannelViewPassthroughProps = Pick<ChannelViewProps, 'renderMessageInputActions' | 'renderConversationFooter' | 'CustomChannelEmptyState' | 'onBlockParticipantClick' | 'onReportParticipantClick' | 'dmAgentEnabled' | 'onMessageSent' | 'chatbotVotingEnabled' | 'viewerLanguage' | 'renderChannelBanner' | 'customChannelActions' | 'renderChannelActions' | 'onParticipantNameClick' | 'renderMessage' | 'onMessageLinkClick' | 'showChannelInfo' | 'composerInput'>;
210
210
 
211
211
  /**
212
212
  * ChannelView component props
@@ -309,8 +309,8 @@ export declare interface ChannelViewProps {
309
309
  */
310
310
  showStarButton?: boolean;
311
311
  /**
312
- * Custom badges rendered inline after the participant name in the channel
313
- * header title.
312
+ * @deprecated No longer rendered. The redesigned header shows built-in paid
313
+ * and starred badges instead. Kept temporarily so existing call sites type-check.
314
314
  */
315
315
  renderHeaderTitleBadges?: (context: {
316
316
  channel: Channel;
@@ -425,10 +425,9 @@ export declare interface ChannelViewProps {
425
425
  */
426
426
  composerInput?: ComponentType;
427
427
  /**
428
- * Fired when the participant's name in the channel header is clicked. When
429
- * provided, the name renders as a button with a trailing caret (an affordance
430
- * for opening participant details / thread content); when omitted, the name
431
- * renders as plain text.
428
+ * Fired when the participant identity in the channel header is clicked. When
429
+ * provided, the avatar + name pill render as a button with a trailing caret;
430
+ * when omitted, they render as non-interactive chrome.
432
431
  *
433
432
  * @example
434
433
  * onParticipantNameClick={() => openThreadContentDialog()}
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 c, i as u, j as L, k as h, l as p, m as d, 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, o as y, y as z, z as B, B as S, D as q, E as D, G as E, H as V, I as w } from "./index-DMYxJz1w.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 c, i as u, j as L, k as h, l as p, m as d, 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, o as y, y as z, z as B, B as S, D as q, E as D, G as E, H as V, I as w } from "./index-DdVzOymJ.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": "3.14.5",
3
+ "version": "3.15.0",
4
4
  "description": "React messaging components built on messaging-core for web applications",
5
5
  "type": "module",
6
6
  "main": "dist/index.cjs",
@@ -2,7 +2,6 @@ import {
2
2
  ArrowLeftIcon,
3
3
  CaretRightIcon,
4
4
  CurrencyDollarIcon,
5
- SparkleIcon,
6
5
  StarIcon,
7
6
  } from '@phosphor-icons/react'
8
7
  import classNames from 'classnames'
@@ -21,7 +20,6 @@ const ICON_BTN_CLASS =
21
20
  'flex size-10 flex-shrink-0 items-center justify-center rounded-full bg-white/65 shadow-[0px_4px_8px_0px_rgba(0,0,0,0.06),0px_0px_0px_1px_rgba(0,0,0,0.04)] backdrop-blur-[40px] transition-filter duration-150 hover:brightness-95 aria-expanded:brightness-95 focus-ring'
22
21
 
23
22
  const CUSTOMER_PAID_TAG = 'CUSTOMER_PAID'
24
- const DM_AGENT_HEADER_HELPER_TEXT = 'Replies instantly with AI assistant'
25
23
 
26
24
  const isCustomerPaidTag = (value: unknown) =>
27
25
  typeof value === 'string' &&
@@ -35,7 +33,6 @@ interface ChannelHeaderRedesignProps {
35
33
  onBack?: () => void
36
34
  showBackButton: boolean
37
35
  showStarButton?: boolean
38
- dmAgentEnabled?: boolean
39
36
  onLeaveConversation?: (channel: ChannelType) => void
40
37
  onBlockParticipant?: (participantId?: string) => void
41
38
  showDeleteConversation?: boolean
@@ -47,7 +44,7 @@ interface ChannelHeaderRedesignProps {
47
44
  customChannelActions?: React.ReactNode
48
45
  renderChannelActions?: React.ReactNode
49
46
  showChannelInfo?: boolean
50
- onParticipantNameClick: () => void
47
+ onParticipantNameClick?: () => void
51
48
  }
52
49
 
53
50
  export const ChannelHeaderRedesign: React.FC<ChannelHeaderRedesignProps> = ({
@@ -55,7 +52,6 @@ export const ChannelHeaderRedesign: React.FC<ChannelHeaderRedesignProps> = ({
55
52
  onBack,
56
53
  showBackButton,
57
54
  showStarButton = false,
58
- dmAgentEnabled = false,
59
55
  onLeaveConversation,
60
56
  onBlockParticipant,
61
57
  showDeleteConversation = true,
@@ -104,6 +100,51 @@ export const ChannelHeaderRedesign: React.FC<ChannelHeaderRedesignProps> = ({
104
100
  }
105
101
  }
106
102
 
103
+ const identityContent = (
104
+ <>
105
+ <Avatar
106
+ id={participant?.user?.id || channel.id || 'unknown'}
107
+ name={participantName}
108
+ image={participantImage}
109
+ shape="circle"
110
+ size={48}
111
+ className="-mb-2"
112
+ />
113
+ <span className="relative z-10 flex max-w-full flex-col items-center justify-center rounded-full border border-white/10 bg-white/65 py-1.5 pl-3 pr-2 shadow-[0px_0px_0px_0px_rgba(0,0,0,0.04),0px_1px_2px_0px_rgba(0,0,0,0.04),0px_8px_32px_0px_rgba(0,0,0,0.1)] backdrop-blur-[2px] transition-filter duration-150 group-hover:brightness-95">
114
+ <span className="flex max-w-full items-center justify-center">
115
+ <span className="min-w-0 truncate text-sm font-bold leading-[1.43] tracking-[0.28px] text-black/90">
116
+ {participantName}
117
+ </span>
118
+ <span className="ml-2 flex gap-1">
119
+ {isPaidCustomer && (
120
+ <span
121
+ aria-label="Paid customer"
122
+ className="flex size-4 shrink-0 items-center justify-center rounded-full bg-[#34C759] text-white"
123
+ >
124
+ <CurrencyDollarIcon className="size-2.5" weight="bold" />
125
+ </span>
126
+ )}
127
+ {showStarButton && isStarred && (
128
+ <span
129
+ aria-label="Starred conversation"
130
+ className="flex size-4 shrink-0 items-center justify-center rounded-full bg-[#FFD60A] text-white"
131
+ >
132
+ <StarIcon className="size-2.5" weight="fill" />
133
+ </span>
134
+ )}
135
+ {onParticipantNameClick && (
136
+ <CaretRightIcon
137
+ aria-hidden="true"
138
+ className="size-4 shrink-0 text-black/65"
139
+ weight="bold"
140
+ />
141
+ )}
142
+ </span>
143
+ </span>
144
+ </span>
145
+ </>
146
+ )
147
+
107
148
  return (
108
149
  <div
109
150
  className={classNames(
@@ -129,58 +170,20 @@ export const ChannelHeaderRedesign: React.FC<ChannelHeaderRedesignProps> = ({
129
170
  </div>
130
171
 
131
172
  <div className="flex min-w-0 justify-center" data-dd-privacy="mask">
132
- <button
133
- type="button"
134
- onClick={onParticipantNameClick}
135
- aria-label={`View details for ${participantName}`}
136
- className="group flex min-w-0 max-w-full appearance-none flex-col items-center border-0 bg-transparent px-2 focus-ring focus-visible:outline-none"
137
- >
138
- <Avatar
139
- id={participant?.user?.id || channel.id || 'unknown'}
140
- name={participantName}
141
- image={participantImage}
142
- dmAgentEnabled={dmAgentEnabled}
143
- shape="circle"
144
- size={48}
145
- className="-mb-2"
146
- />
147
- <span className="relative z-10 flex max-w-full flex-col items-center justify-center rounded-full border border-white/10 bg-white/65 py-1.5 pl-3 pr-2 shadow-[0px_0px_0px_0px_rgba(0,0,0,0.04),0px_1px_2px_0px_rgba(0,0,0,0.04),0px_8px_32px_0px_rgba(0,0,0,0.1)] backdrop-blur-[2px] transition-filter duration-150 group-hover:brightness-95">
148
- <span className="flex max-w-full items-center justify-center">
149
- <span className="min-w-0 truncate text-sm font-bold leading-[1.43] tracking-[0.28px] text-black/90">
150
- {participantName}
151
- </span>
152
- <span className="ml-2 flex gap-1">
153
- {isPaidCustomer && (
154
- <span
155
- aria-label="Paid customer"
156
- className="flex size-4 shrink-0 items-center justify-center rounded-full bg-[#34C759] text-white"
157
- >
158
- <CurrencyDollarIcon className="size-2.5" weight="bold" />
159
- </span>
160
- )}
161
- {showStarButton && isStarred && (
162
- <span
163
- aria-label="Starred conversation"
164
- className="flex size-4 shrink-0 items-center justify-center rounded-full bg-[#FFD60A] text-white"
165
- >
166
- <StarIcon className="size-2.5" weight="fill" />
167
- </span>
168
- )}
169
- <CaretRightIcon
170
- aria-hidden="true"
171
- className="size-4 shrink-0 text-black/65"
172
- weight="bold"
173
- />
174
- </span>
175
- </span>
176
- {dmAgentEnabled && (
177
- <span className="mt-1 flex items-center gap-1 px-1 pb-0.5 text-[10px] leading-3 text-black/55">
178
- <SparkleIcon className="size-3 shrink-0 text-black/55" />
179
- <span>{DM_AGENT_HEADER_HELPER_TEXT}</span>
180
- </span>
181
- )}
182
- </span>
183
- </button>
173
+ {onParticipantNameClick ? (
174
+ <button
175
+ type="button"
176
+ onClick={onParticipantNameClick}
177
+ aria-label={`View details for ${participantName}`}
178
+ className="group flex min-w-0 max-w-full appearance-none flex-col items-center border-0 bg-transparent px-2 focus-ring focus-visible:outline-none"
179
+ >
180
+ {identityContent}
181
+ </button>
182
+ ) : (
183
+ <div className="flex min-w-0 max-w-full flex-col items-center px-2">
184
+ {identityContent}
185
+ </div>
186
+ )}
184
187
  </div>
185
188
 
186
189
  <div className="flex items-start justify-end gap-2">
@@ -370,25 +370,6 @@ ParticipantIdentityTrigger.parameters = {
370
370
  },
371
371
  }
372
372
 
373
- export const DmAgentHeader: StoryFn<TemplateProps> = Template.bind({})
374
- DmAgentHeader.args = {
375
- showBackButton: false,
376
- dmAgentEnabled: true,
377
- onBack: () => console.log('Back clicked'),
378
- onLeaveConversation: (channel) =>
379
- console.log('Leave conversation:', channel.id),
380
- onBlockParticipant: (participantId) =>
381
- console.log('Block participant:', participantId),
382
- }
383
- DmAgentHeader.parameters = {
384
- docs: {
385
- description: {
386
- story:
387
- 'Channel view with DM agent header treatment enabled (AI avatar border and "Replies instantly with AI assistant" helper text).',
388
- },
389
- },
390
- }
391
-
392
373
  export const WithHeaderTitleBadges: StoryFn<TemplateProps> = Template.bind({})
393
374
  WithHeaderTitleBadges.args = {
394
375
  showBackButton: false,
@@ -207,24 +207,18 @@ describe('ChannelView', () => {
207
207
  expect(lastActionsMenuProps().showReportParticipant).toBe(false)
208
208
  })
209
209
 
210
- it('renders the participant name without an info trigger', () => {
210
+ it('renders the redesigned header without a participant click trigger by default', () => {
211
211
  renderWithProviders(<ChannelView channel={createChannel()} />)
212
212
 
213
- // The info sidebar was removed, so the participant name is plain text on
214
- // both headers — there is no clickable affordance to open a profile
215
- // dialog anymore.
216
213
  expect(
217
- screen.queryByRole('button', { name: /view info for/i })
214
+ screen.queryByRole('button', { name: /view details for linker/i })
218
215
  ).not.toBeInTheDocument()
219
216
  expect(screen.getAllByText('Linker').length).toBeGreaterThanOrEqual(1)
220
217
 
221
218
  const avatars = screen.getAllByTestId('avatar')
222
219
  expect(avatars.length).toBeGreaterThanOrEqual(1)
223
- expect(avatars[0].parentElement).toHaveClass(
224
- 'rounded-[12px]',
225
- 'px-1',
226
- 'py-0.5'
227
- )
220
+ expect(avatars[0].closest('button')).toBeNull()
221
+ expect(screen.getByTestId('channel-actions-menu')).toBeInTheDocument()
228
222
  })
229
223
 
230
224
  it('renders the redesigned header button when onParticipantNameClick is provided', () => {
@@ -264,7 +258,6 @@ describe('ChannelView', () => {
264
258
  channelCustomerTag: 'CUSTOMER_PAID',
265
259
  otherMemberIsFollower: true,
266
260
  })}
267
- onParticipantNameClick={vi.fn()}
268
261
  showStarButton={true}
269
262
  />
270
263
  )
@@ -298,14 +291,12 @@ describe('ChannelView', () => {
298
291
  } as Channel['state']['messages'][number],
299
292
  ],
300
293
  })}
301
- onParticipantNameClick={vi.fn()}
302
294
  />
303
295
  )
304
296
 
305
- const button = screen.getByRole('button', {
306
- name: /view details for linker/i,
307
- })
308
- expect(button.closest('[data-testid="message-list"]')).toBeNull()
297
+ expect(
298
+ screen.getByText('Linker').closest('[data-testid="message-list"]')
299
+ ).toBeNull()
309
300
  })
310
301
 
311
302
  it('renders the actions menu by default', () => {
@@ -416,79 +407,36 @@ describe('ChannelView', () => {
416
407
  expect(screen.queryByTestId('conversation-footer')).not.toBeInTheDocument()
417
408
  })
418
409
 
419
- it('does not pass starred to avatar when showStarButton is false', () => {
410
+ it('does not show a starred badge when showStarButton is false', () => {
420
411
  mockIsStarred = true
421
- const channel = createChannel()
422
412
 
423
413
  renderWithProviders(
424
- <ChannelView channel={channel} showStarButton={false} />
414
+ <ChannelView channel={createChannel()} showStarButton={false} />
425
415
  )
426
416
 
427
- expect(avatarRenderCalls.length).toBeGreaterThan(0)
428
- expect(avatarRenderCalls.every((call) => call.starred === false)).toBe(true)
417
+ expect(
418
+ screen.queryByLabelText('Starred conversation')
419
+ ).not.toBeInTheDocument()
429
420
  })
430
421
 
431
- it('passes starred to avatar when showStarButton is true and channel is starred', () => {
422
+ it('shows a starred badge when showStarButton is true and channel is starred', () => {
432
423
  mockIsStarred = true
433
- const channel = createChannel()
434
424
 
435
- renderWithProviders(<ChannelView channel={channel} showStarButton={true} />)
425
+ renderWithProviders(
426
+ <ChannelView channel={createChannel()} showStarButton={true} />
427
+ )
436
428
 
437
- expect(avatarRenderCalls.length).toBeGreaterThan(0)
438
- expect(avatarRenderCalls.every((call) => call.starred === true)).toBe(true)
429
+ expect(screen.getByLabelText('Starred conversation')).toBeInTheDocument()
439
430
  })
440
431
 
441
- it('does not pass starred to avatar by default (showStarButton defaults to false)', () => {
432
+ it('does not show a starred badge by default (showStarButton defaults to false)', () => {
442
433
  mockIsStarred = true
443
- const channel = createChannel()
444
-
445
- renderWithProviders(<ChannelView channel={channel} />)
446
-
447
- expect(avatarRenderCalls.length).toBeGreaterThan(0)
448
- expect(avatarRenderCalls.every((call) => call.starred === false)).toBe(true)
449
- })
450
-
451
- it('passes dmAgentEnabled to avatar when dmAgentEnabled is true for visitor view', () => {
452
- const channel = createChannel({
453
- currentUserId: 'visitor-1',
454
- currentUserRole: 'owner',
455
- })
456
-
457
- renderWithProviders(<ChannelView channel={channel} dmAgentEnabled={true} />)
458
-
459
- expect(avatarRenderCalls.length).toBeGreaterThan(0)
460
- expect(
461
- avatarRenderCalls.every((call) => call.dmAgentEnabled === true)
462
- ).toBe(true)
463
- })
464
-
465
- it('renders dm agent helper text when dmAgentEnabled is true for visitor view', () => {
466
- const channel = createChannel({
467
- currentUserId: 'visitor-1',
468
- currentUserRole: 'owner',
469
- })
470
434
 
471
- renderWithProviders(<ChannelView channel={channel} dmAgentEnabled={true} />)
472
-
473
- expect(
474
- screen.getAllByText('Replies instantly with AI assistant').length
475
- ).toBeGreaterThan(0)
476
- })
477
-
478
- it('does not render dm agent helper text for linker view even when dmAgentEnabled is true', () => {
479
- const channel = createChannel({
480
- currentUserId: 'linker-1',
481
- currentUserRole: 'member',
482
- })
483
-
484
- renderWithProviders(<ChannelView channel={channel} dmAgentEnabled={true} />)
435
+ renderWithProviders(<ChannelView channel={createChannel()} />)
485
436
 
486
437
  expect(
487
- screen.queryByText('Replies instantly with AI assistant')
438
+ screen.queryByLabelText('Starred conversation')
488
439
  ).not.toBeInTheDocument()
489
- expect(
490
- avatarRenderCalls.every((call) => call.dmAgentEnabled === false)
491
- ).toBe(true)
492
440
  })
493
441
 
494
442
  it('keeps channel banner and input action renderers working', () => {