@linktr.ee/messaging-react 2.3.4-rc-1779841404 → 2.4.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.
Files changed (30) hide show
  1. package/dist/{Card-Cmfkr70L.js → Card-3s62K02i.js} +3 -3
  2. package/dist/{Card-Cmfkr70L.js.map → Card-3s62K02i.js.map} +1 -1
  3. package/dist/{Card-9X9RSzWd.cjs → Card-CPl5CfCo.cjs} +2 -2
  4. package/dist/{Card-9X9RSzWd.cjs.map → Card-CPl5CfCo.cjs.map} +1 -1
  5. package/dist/{Card-a6NQp99K.js → Card-CkyEIXm3.js} +2 -2
  6. package/dist/{Card-a6NQp99K.js.map → Card-CkyEIXm3.js.map} +1 -1
  7. package/dist/{Card-B0y7S-V2.cjs → Card-Cp6OyEWg.cjs} +2 -2
  8. package/dist/{Card-B0y7S-V2.cjs.map → Card-Cp6OyEWg.cjs.map} +1 -1
  9. package/dist/{Card-W7jCeHUe.cjs → Card-LQWaY4ng.cjs} +2 -2
  10. package/dist/{Card-W7jCeHUe.cjs.map → Card-LQWaY4ng.cjs.map} +1 -1
  11. package/dist/{Card-C-dmAFJG.js → Card-pMjhg42K.js} +2 -2
  12. package/dist/{Card-C-dmAFJG.js.map → Card-pMjhg42K.js.map} +1 -1
  13. package/dist/{LockedThumbnail-DPVq7iJu.js → LockedThumbnail-CjznOfiC.js} +2 -2
  14. package/dist/{LockedThumbnail-DPVq7iJu.js.map → LockedThumbnail-CjznOfiC.js.map} +1 -1
  15. package/dist/{LockedThumbnail-CtZQCl64.cjs → LockedThumbnail-DbGrvo6R.cjs} +2 -2
  16. package/dist/{LockedThumbnail-CtZQCl64.cjs.map → LockedThumbnail-DbGrvo6R.cjs.map} +1 -1
  17. package/dist/index-BX305h7q.cjs +18 -0
  18. package/dist/{index-C0RXJhLE.cjs.map → index-BX305h7q.cjs.map} +1 -1
  19. package/dist/{index-BfCt0dJa.js → index-DVh2uNBI.js} +1638 -1624
  20. package/dist/{index-BfCt0dJa.js.map → index-DVh2uNBI.js.map} +1 -1
  21. package/dist/index.cjs +1 -1
  22. package/dist/index.d.ts +17 -1
  23. package/dist/index.js +1 -1
  24. package/package.json +1 -1
  25. package/src/components/ChannelView.test.tsx +32 -0
  26. package/src/components/ChannelView.tsx +17 -1
  27. package/src/components/MessagingShell/index.tsx +2 -0
  28. package/src/index.ts +1 -0
  29. package/src/types.ts +18 -0
  30. package/dist/index-C0RXJhLE.cjs +0 -18
package/dist/index.cjs CHANGED
@@ -1,2 +1,2 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("./index-C0RXJhLE.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-BX305h7q.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
@@ -162,7 +162,7 @@ export declare const ChannelView: default_2.NamedExoticComponent<ChannelViewProp
162
162
  * Props that MessagingShell passes through to ChannelView.
163
163
  * ChannelViewProps is the source of truth for these props.
164
164
  */
165
- declare type ChannelViewPassthroughProps = Pick<ChannelViewProps, 'renderMessageInputActions' | 'renderConversationFooter' | 'CustomChannelEmptyState' | 'onDeleteConversationClick' | 'onBlockParticipantClick' | 'onReportParticipantClick' | 'dmAgentEnabled' | 'messageMetadata' | 'onMessageSent' | 'showStarButton' | 'chatbotVotingEnabled' | 'viewerLanguage' | 'renderChannelBanner' | 'customProfileContent' | 'customChannelActions' | 'renderMessage' | 'sendButton'>;
165
+ declare type ChannelViewPassthroughProps = Pick<ChannelViewProps, 'renderMessageInputActions' | 'renderConversationFooter' | 'CustomChannelEmptyState' | 'onDeleteConversationClick' | 'onBlockParticipantClick' | 'onReportParticipantClick' | 'dmAgentEnabled' | 'messageMetadata' | 'onMessageSent' | 'showStarButton' | 'chatbotVotingEnabled' | 'viewerLanguage' | 'renderChannelBanner' | 'customProfileContent' | 'customChannelActions' | 'renderMessage' | 'onMessageLinkClick' | 'sendButton'>;
166
166
 
167
167
  /**
168
168
  * ChannelView component props
@@ -279,6 +279,15 @@ export declare interface ChannelViewProps {
279
279
  * )}
280
280
  */
281
281
  renderMessage?: (messageNode: React.ReactElement, message: LocalMessage) => React.ReactNode;
282
+ /**
283
+ * Fired when a user clicks any link inside the channel view (message
284
+ * bubbles, attachment cards, etc.). Receives the clicked URL. Fires
285
+ * alongside the native navigation, not in place of it.
286
+ *
287
+ * @example
288
+ * onMessageLinkClick={(url) => trackChannelLinkClicked(url)}
289
+ */
290
+ onMessageLinkClick?: MessageLinkClickHandler;
282
291
  /**
283
292
  * Passed to Stream `Channel` as `SendButton`. Required for hosts that replace
284
293
  * the send control: `Channel` merges this into `ComponentContext` and an
@@ -1043,6 +1052,13 @@ export declare type MessageAttachmentVideoSentProps = VideoAttachmentSharedProps
1043
1052
 
1044
1053
  declare type MessageCustomType = 'MESSAGE_TIP' | 'MESSAGE_PAID' | 'MESSAGE_CHATBOT' | 'MESSAGE_ATTACHMENT' | AgeSafetySystemType | DmAgentSystemType;
1045
1054
 
1055
+ /**
1056
+ * Callback invoked when a user clicks a link inside the channel view.
1057
+ * Receives the clicked URL. Fires alongside the native navigation,
1058
+ * not in place of it.
1059
+ */
1060
+ export declare type MessageLinkClickHandler = (url: string) => void;
1061
+
1046
1062
  export declare interface MessageMetadata {
1047
1063
  custom_type?: MessageCustomType;
1048
1064
  listing_id?: string;
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
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-BfCt0dJa.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-DVh2uNBI.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": "2.3.4-rc-1779841404",
3
+ "version": "2.4.0",
4
4
  "description": "React messaging components built on messaging-core for web applications",
5
5
  "type": "module",
6
6
  "main": "dist/index.cjs",
@@ -356,4 +356,36 @@ describe('ChannelView', () => {
356
356
 
357
357
  expect(screen.getAllByText('Custom Label').length).toBeGreaterThan(0)
358
358
  })
359
+
360
+ it('fires onMessageLinkClick with the href when a link inside the view is clicked', () => {
361
+ const handler = vi.fn()
362
+ const { container } = renderWithProviders(
363
+ <ChannelView channel={createChannel()} onMessageLinkClick={handler} />
364
+ )
365
+
366
+ const anchor = document.createElement('a')
367
+ anchor.href = 'https://example.com/clicked'
368
+ anchor.addEventListener('click', (e) => e.preventDefault())
369
+ container
370
+ .querySelector('.messaging-channel-view')
371
+ ?.appendChild(anchor)
372
+ anchor.click()
373
+
374
+ expect(handler).toHaveBeenCalledOnce()
375
+ expect(handler).toHaveBeenCalledWith('https://example.com/clicked')
376
+ })
377
+
378
+ it('does not throw when a link is clicked without onMessageLinkClick set', () => {
379
+ const { container } = renderWithProviders(
380
+ <ChannelView channel={createChannel()} />
381
+ )
382
+
383
+ const anchor = document.createElement('a')
384
+ anchor.href = 'https://example.com'
385
+ anchor.addEventListener('click', (e) => e.preventDefault())
386
+ container
387
+ .querySelector('.messaging-channel-view')
388
+ ?.appendChild(anchor)
389
+ expect(() => anchor.click()).not.toThrow()
390
+ })
359
391
  })
@@ -6,7 +6,7 @@ import {
6
6
  StarIcon,
7
7
  } from '@phosphor-icons/react'
8
8
  import classNames from 'classnames'
9
- import React, { useCallback, useRef } from 'react'
9
+ import React, { useCallback, useEffect, useRef } from 'react'
10
10
  import { Channel as ChannelType, ChannelMemberResponse } from 'stream-chat'
11
11
  import {
12
12
  Channel,
@@ -476,6 +476,7 @@ export const ChannelView = React.memo<ChannelViewProps>(
476
476
  customProfileContent,
477
477
  customChannelActions,
478
478
  renderMessage,
479
+ onMessageLinkClick,
479
480
  sendButton,
480
481
  viewerLanguage,
481
482
  getParticipantDisplayName: getParticipantDisplayNameProp,
@@ -531,8 +532,23 @@ export const ChannelView = React.memo<ChannelViewProps>(
531
532
  [channel, dmAgentEnabled, messageMetadata, onMessageSent]
532
533
  )
533
534
 
535
+ const containerRef = useRef<HTMLDivElement>(null)
536
+ useEffect(() => {
537
+ if (!onMessageLinkClick) return
538
+ const container = containerRef.current
539
+ if (!container) return
540
+ const handler = (event: MouseEvent) => {
541
+ const anchor = (event.target as HTMLElement | null)?.closest('a[href]')
542
+ const href = anchor?.getAttribute('href')
543
+ if (href) onMessageLinkClick(href)
544
+ }
545
+ container.addEventListener('click', handler)
546
+ return () => container.removeEventListener('click', handler)
547
+ }, [onMessageLinkClick])
548
+
534
549
  return (
535
550
  <div
551
+ ref={containerRef}
536
552
  className={classNames(
537
553
  'messaging-channel-view h-full flex flex-col',
538
554
  className
@@ -41,6 +41,7 @@ export const MessagingShell: React.FC<MessagingShellProps> = ({
41
41
  customProfileContent,
42
42
  customChannelActions,
43
43
  renderMessage,
44
+ onMessageLinkClick,
44
45
  sendButton,
45
46
  }) => {
46
47
  const {
@@ -421,6 +422,7 @@ export const MessagingShell: React.FC<MessagingShellProps> = ({
421
422
  customProfileContent={customProfileContent}
422
423
  customChannelActions={customChannelActions}
423
424
  renderMessage={renderMessage}
425
+ onMessageLinkClick={onMessageLinkClick}
424
426
  sendButton={sendButton}
425
427
  />
426
428
  </div>
package/src/index.ts CHANGED
@@ -53,6 +53,7 @@ export type {
53
53
  ChannelViewProps,
54
54
  MessagingProviderProps,
55
55
  MessagingCapabilities,
56
+ MessageLinkClickHandler,
56
57
  Participant,
57
58
  LockedAttachmentSource,
58
59
  } from './types'
package/src/types.ts CHANGED
@@ -20,6 +20,13 @@ import type { MessageMetadata } from './stream-custom-data'
20
20
 
21
21
  export type { LockedAttachmentSource } from './components/LockedAttachment'
22
22
 
23
+ /**
24
+ * Callback invoked when a user clicks a link inside the channel view.
25
+ * Receives the clicked URL. Fires alongside the native navigation,
26
+ * not in place of it.
27
+ */
28
+ export type MessageLinkClickHandler = (url: string) => void
29
+
23
30
  /**
24
31
  * Generic participant interface for different host environments
25
32
  */
@@ -232,6 +239,16 @@ export interface ChannelViewProps {
232
239
  message: LocalMessage
233
240
  ) => React.ReactNode
234
241
 
242
+ /**
243
+ * Fired when a user clicks any link inside the channel view (message
244
+ * bubbles, attachment cards, etc.). Receives the clicked URL. Fires
245
+ * alongside the native navigation, not in place of it.
246
+ *
247
+ * @example
248
+ * onMessageLinkClick={(url) => trackChannelLinkClicked(url)}
249
+ */
250
+ onMessageLinkClick?: MessageLinkClickHandler
251
+
235
252
  /**
236
253
  * Passed to Stream `Channel` as `SendButton`. Required for hosts that replace
237
254
  * the send control: `Channel` merges this into `ComponentContext` and an
@@ -267,6 +284,7 @@ export type ChannelViewPassthroughProps = Pick<
267
284
  | 'customProfileContent'
268
285
  | 'customChannelActions'
269
286
  | 'renderMessage'
287
+ | 'onMessageLinkClick'
270
288
  | 'sendButton'
271
289
  >
272
290