@linktr.ee/messaging-react 1.15.2 → 1.16.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@linktr.ee/messaging-react",
3
- "version": "1.15.2",
3
+ "version": "1.16.0",
4
4
  "description": "React messaging components built on messaging-core for web applications",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -9,7 +9,11 @@ import {
9
9
  } from '@phosphor-icons/react'
10
10
  import classNames from 'classnames'
11
11
  import React, { useState, useCallback, useRef, useEffect } from 'react'
12
- import { Channel as ChannelType, ChannelMemberResponse, Event } from 'stream-chat'
12
+ import {
13
+ Channel as ChannelType,
14
+ ChannelMemberResponse,
15
+ Event,
16
+ } from 'stream-chat'
13
17
  import {
14
18
  Channel,
15
19
  Window,
@@ -66,11 +70,17 @@ const CustomChannelHeader: React.FC<{
66
70
  participant?.user?.name || participant?.user?.id || 'Unknown member'
67
71
  const participantImage = participant?.user?.image
68
72
 
69
- const [isStarred, setIsStarred] = useState(!!channel.state.membership?.pinned_at)
73
+ const [isStarred, setIsStarred] = useState(
74
+ !!channel.state.membership?.pinned_at
75
+ )
70
76
 
71
77
  useEffect(() => {
72
78
  const handleMemberUpdate = (event: Event) => {
73
- setIsStarred(event?.member ? !!event.member.pinned_at : !!channel.state.membership?.pinned_at)
79
+ setIsStarred(
80
+ event?.member
81
+ ? !!event.member.pinned_at
82
+ : !!channel.state.membership?.pinned_at
83
+ )
74
84
  }
75
85
 
76
86
  channel.on('member.updated', handleMemberUpdate)
@@ -88,7 +98,10 @@ const CustomChannelHeader: React.FC<{
88
98
  await channel.pin()
89
99
  }
90
100
  } catch (error) {
91
- console.error('[CustomChannelHeader] Failed to update pinned status:', error)
101
+ console.error(
102
+ '[CustomChannelHeader] Failed to update pinned status:',
103
+ error
104
+ )
92
105
  }
93
106
  }
94
107
 
@@ -614,7 +627,31 @@ export const ChannelView = React.memo<ChannelViewProps>(
614
627
  onDeleteConversationClick,
615
628
  onBlockParticipantClick,
616
629
  onReportParticipantClick,
630
+ dmAgentEnabled,
617
631
  }) => {
632
+ // Custom send message handler that adds skip_push and silent when DM agent is active
633
+ // Read chatbot_paused inside callback to get current value at send time (not stale closure)
634
+ const doSendMessageRequest = useCallback(
635
+ async (
636
+ _channel: ChannelType,
637
+ message: Parameters<ChannelType['sendMessage']>[0],
638
+ options?: Parameters<ChannelType['sendMessage']>[1]
639
+ ) => {
640
+ const agentPaused =
641
+ (channel.data as { chatbot_paused?: boolean })?.chatbot_paused === true
642
+ const shouldSuppressNotifications = dmAgentEnabled && !agentPaused
643
+
644
+ if (shouldSuppressNotifications) {
645
+ return channel.sendMessage(
646
+ { ...message, silent: true },
647
+ { ...options, skip_push: true }
648
+ )
649
+ }
650
+ return channel.sendMessage(message, options)
651
+ },
652
+ [channel, dmAgentEnabled]
653
+ )
654
+
618
655
  return (
619
656
  <div
620
657
  className={classNames(
@@ -628,6 +665,7 @@ export const ChannelView = React.memo<ChannelViewProps>(
628
665
  EmptyStateIndicator={CustomChannelEmptyState}
629
666
  LoadingIndicator={LoadingState}
630
667
  DateSeparator={CustomDateSeparator}
668
+ doSendMessageRequest={doSendMessageRequest}
631
669
  >
632
670
  <ChannelViewInner
633
671
  onBack={onBack}
@@ -30,6 +30,7 @@ export const MessagingShell: React.FC<MessagingShellProps> = ({
30
30
  onDeleteConversationClick,
31
31
  onBlockParticipantClick,
32
32
  onReportParticipantClick,
33
+ dmAgentEnabled,
33
34
  }) => {
34
35
  const {
35
36
  service,
@@ -475,6 +476,7 @@ export const MessagingShell: React.FC<MessagingShellProps> = ({
475
476
  onDeleteConversationClick={onDeleteConversationClick}
476
477
  onBlockParticipantClick={onBlockParticipantClick}
477
478
  onReportParticipantClick={onReportParticipantClick}
479
+ dmAgentEnabled={dmAgentEnabled}
478
480
  />
479
481
  </div>
480
482
  ) : initialParticipantFilter ? (
package/src/types.ts CHANGED
@@ -51,13 +51,79 @@ export interface MessagingCapabilities {
51
51
  showDeleteConversation?: boolean
52
52
  }
53
53
 
54
+ /**
55
+ * ChannelList component props
56
+ */
57
+ export interface ChannelListProps {
58
+ onChannelSelect: (channel: Channel) => void
59
+ selectedChannel?: Channel
60
+ showStartConversation?: boolean
61
+ onStartConversation?: () => void
62
+ participantLabel?: string
63
+ className?: string
64
+ filters: ChannelFilters
65
+ customEmptyStateIndicator?: React.ComponentType<EmptyStateIndicatorProps>
66
+ }
67
+
68
+ /**
69
+ * ChannelView component props
70
+ */
71
+ export interface ChannelViewProps {
72
+ channel: Channel
73
+ onBack?: () => void
74
+ showBackButton?: boolean
75
+ renderMessageInputActions?: (channel: Channel) => React.ReactNode
76
+ onLeaveConversation?: (channel: Channel) => void
77
+ onBlockParticipant?: (participantId?: string) => void
78
+ className?: string
79
+ CustomChannelEmptyState?: React.ComponentType
80
+ /**
81
+ * Show the "Delete Conversation" button in channel info dialog.
82
+ * Defaults to true for backward compatibility.
83
+ */
84
+ showDeleteConversation?: boolean
85
+ /**
86
+ * Analytics callback fired when "Delete Conversation" is clicked.
87
+ */
88
+ onDeleteConversationClick?: () => void
89
+ /**
90
+ * Analytics callback fired when "Block" or "Unblock" is clicked.
91
+ */
92
+ onBlockParticipantClick?: () => void
93
+ /**
94
+ * Analytics callback fired when "Report" is clicked.
95
+ */
96
+ onReportParticipantClick?: () => void
97
+
98
+ /**
99
+ * When true and DM agent is active on the channel (not paused),
100
+ * messages will be sent with skip_push and silent flags to suppress
101
+ * notifications to the creator until the agent responds.
102
+ * The library reads chatbot_paused from channel.data internally.
103
+ */
104
+ dmAgentEnabled?: boolean
105
+ }
106
+
107
+ /**
108
+ * Props that MessagingShell passes through to ChannelView.
109
+ * ChannelViewProps is the source of truth for these props.
110
+ */
111
+ export type ChannelViewPassthroughProps = Pick<
112
+ ChannelViewProps,
113
+ | 'renderMessageInputActions'
114
+ | 'CustomChannelEmptyState'
115
+ | 'onDeleteConversationClick'
116
+ | 'onBlockParticipantClick'
117
+ | 'onReportParticipantClick'
118
+ | 'dmAgentEnabled'
119
+ >
120
+
54
121
  /**
55
122
  * Main MessagingShell component props
56
123
  */
57
- export interface MessagingShellProps {
124
+ export interface MessagingShellProps extends ChannelViewPassthroughProps {
58
125
  capabilities?: MessagingCapabilities
59
126
  className?: string
60
- renderMessageInputActions?: (channel: Channel) => React.ReactNode
61
127
  onChannelSelect?: (channel: Channel) => void
62
128
  onParticipantSelect?: (participant: Participant) => void
63
129
 
@@ -80,12 +146,6 @@ export interface MessagingShellProps {
80
146
  */
81
147
  initialParticipantData?: Participant
82
148
 
83
- /**
84
- * Custom empty state component to render when a channel has no messages.
85
- * Useful for showing FAQs or other contextual information in empty channels.
86
- */
87
- CustomChannelEmptyState?: React.ComponentType
88
-
89
149
  /**
90
150
  * Controls whether the channel list is shown. When false, the channel list
91
151
  * is immediately hidden. Useful for direct conversation mode where you want
@@ -103,69 +163,6 @@ export interface MessagingShellProps {
103
163
  * Useful for showing a custom empty state indicator when the channel list is empty.
104
164
  */
105
165
  channelListCustomEmptyStateIndicator?: React.ComponentType<EmptyStateIndicatorProps>
106
-
107
- /**
108
- * Analytics callback fired when "Delete Conversation" is clicked.
109
- * Called before the action is performed.
110
- */
111
- onDeleteConversationClick?: () => void
112
-
113
- /**
114
- * Analytics callback fired when "Block" or "Unblock" is clicked.
115
- * Called before the action is performed.
116
- */
117
- onBlockParticipantClick?: () => void
118
-
119
- /**
120
- * Analytics callback fired when "Report" is clicked.
121
- * Called before the action is performed.
122
- */
123
- onReportParticipantClick?: () => void
124
- }
125
-
126
- /**
127
- * ChannelList component props
128
- */
129
- export interface ChannelListProps {
130
- onChannelSelect: (channel: Channel) => void
131
- selectedChannel?: Channel
132
- showStartConversation?: boolean
133
- onStartConversation?: () => void
134
- participantLabel?: string
135
- className?: string
136
- filters: ChannelFilters
137
- customEmptyStateIndicator?: React.ComponentType<EmptyStateIndicatorProps>
138
- }
139
-
140
- /**
141
- * ChannelView component props
142
- */
143
- export interface ChannelViewProps {
144
- channel: Channel
145
- onBack?: () => void
146
- showBackButton?: boolean
147
- renderMessageInputActions?: (channel: Channel) => React.ReactNode
148
- onLeaveConversation?: (channel: Channel) => void
149
- onBlockParticipant?: (participantId?: string) => void
150
- className?: string
151
- CustomChannelEmptyState?: React.ComponentType
152
- /**
153
- * Show the "Delete Conversation" button in channel info dialog.
154
- * Defaults to true for backward compatibility.
155
- */
156
- showDeleteConversation?: boolean
157
- /**
158
- * Analytics callback fired when "Delete Conversation" is clicked.
159
- */
160
- onDeleteConversationClick?: () => void
161
- /**
162
- * Analytics callback fired when "Block" or "Unblock" is clicked.
163
- */
164
- onBlockParticipantClick?: () => void
165
- /**
166
- * Analytics callback fired when "Report" is clicked.
167
- */
168
- onReportParticipantClick?: () => void
169
166
  }
170
167
 
171
168
  /**