@linktr.ee/messaging-react 1.21.2 → 1.22.0-rc-1771558667

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.21.2",
3
+ "version": "1.22.0-rc-1771558667",
4
4
  "description": "React messaging components built on messaging-core for web applications",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -0,0 +1,56 @@
1
+ import type { Meta, StoryFn } from '@storybook/react'
2
+ import type { EventComponentProps } from 'stream-chat-react'
3
+
4
+ import { CustomSystemMessage } from '.'
5
+
6
+ const meta: Meta<typeof CustomSystemMessage> = {
7
+ title: 'CustomSystemMessage',
8
+ component: CustomSystemMessage,
9
+ parameters: {
10
+ layout: 'centered',
11
+ },
12
+ }
13
+ export default meta
14
+
15
+ const createStoryProps = (
16
+ messageOverrides: Partial<EventComponentProps['message']>
17
+ ): EventComponentProps =>
18
+ ({
19
+ message: {
20
+ id: 'system-message-1',
21
+ type: 'system',
22
+ text: 'System message',
23
+ hide_date: true,
24
+ ...messageOverrides,
25
+ },
26
+ }) as EventComponentProps
27
+
28
+ const Template: StoryFn<EventComponentProps> = (args) => (
29
+ <div className="w-[420px] bg-white p-6">
30
+ <CustomSystemMessage {...args} />
31
+ </div>
32
+ )
33
+
34
+ export const DmAgentPaused: StoryFn<EventComponentProps> = Template.bind({})
35
+ DmAgentPaused.args = createStoryProps({
36
+ text: 'DM Agent paused for this conversation',
37
+ metadata: {
38
+ custom_type: 'SYSTEM_DM_AGENT_PAUSED',
39
+ },
40
+ })
41
+
42
+ export const DmAgentResumed: StoryFn<EventComponentProps> = Template.bind({})
43
+ DmAgentResumed.args = createStoryProps({
44
+ text: 'DM Agent resumed for this conversation',
45
+ metadata: {
46
+ custom_type: 'SYSTEM_DM_AGENT_RESUMED',
47
+ },
48
+ })
49
+
50
+ export const GenericFallback: StoryFn<EventComponentProps> = Template.bind({})
51
+ GenericFallback.args = createStoryProps({
52
+ text: 'Message activity event',
53
+ metadata: {
54
+ custom_type: 'MESSAGE_CHATBOT',
55
+ },
56
+ })
@@ -0,0 +1,96 @@
1
+ import type { EventComponentProps } from 'stream-chat-react'
2
+ import { describe, expect, it } from 'vitest'
3
+
4
+ import type { DmAgentSystemType } from '../../stream-custom-data'
5
+ import { renderWithProviders, screen } from '../../test/utils'
6
+
7
+ import { CustomSystemMessage } from '.'
8
+
9
+ const createProps = (
10
+ messageOverrides: Partial<EventComponentProps['message']> = {}
11
+ ): EventComponentProps =>
12
+ ({
13
+ message: {
14
+ id: 'system-message-1',
15
+ type: 'system',
16
+ text: 'System message',
17
+ hide_date: true,
18
+ ...messageOverrides,
19
+ },
20
+ }) as EventComponentProps
21
+
22
+ describe('CustomSystemMessage', () => {
23
+ it('detects DM Agent system subtype from metadata.custom_type', () => {
24
+ renderWithProviders(
25
+ <CustomSystemMessage
26
+ {...createProps({
27
+ text: 'DM Agent paused',
28
+ metadata: {
29
+ custom_type: 'SYSTEM_DM_AGENT_PAUSED',
30
+ },
31
+ dm_agent_system_type: 'SYSTEM_DM_AGENT_RESUMED',
32
+ })}
33
+ />
34
+ )
35
+
36
+ const dmAgentSystemMessage = screen.getByTestId('dm-agent-system-message')
37
+ expect(dmAgentSystemMessage).toHaveAttribute(
38
+ 'data-dm-agent-system-type',
39
+ 'SYSTEM_DM_AGENT_PAUSED'
40
+ )
41
+ expect(dmAgentSystemMessage).toHaveTextContent('DM Agent paused')
42
+ })
43
+
44
+ it('falls back to message.dm_agent_system_type when metadata subtype is absent', () => {
45
+ renderWithProviders(
46
+ <CustomSystemMessage
47
+ {...createProps({
48
+ text: 'DM Agent resumed',
49
+ metadata: {},
50
+ dm_agent_system_type: 'SYSTEM_DM_AGENT_RESUMED',
51
+ })}
52
+ />
53
+ )
54
+
55
+ const dmAgentSystemMessage = screen.getByTestId('dm-agent-system-message')
56
+ expect(dmAgentSystemMessage).toHaveAttribute(
57
+ 'data-dm-agent-system-type',
58
+ 'SYSTEM_DM_AGENT_RESUMED'
59
+ )
60
+ expect(dmAgentSystemMessage).toHaveTextContent('DM Agent resumed')
61
+ })
62
+
63
+ it('falls back to legacy generic rendering when subtype is absent or unknown', () => {
64
+ const { container } = renderWithProviders(
65
+ <CustomSystemMessage
66
+ {...createProps({
67
+ text: 'Legacy system message',
68
+ dm_agent_system_type:
69
+ 'SYSTEM_DM_AGENT_UNKNOWN' as unknown as DmAgentSystemType,
70
+ })}
71
+ />
72
+ )
73
+
74
+ expect(screen.queryByTestId('dm-agent-system-message')).not.toBeInTheDocument()
75
+ expect(screen.getByText('Legacy system message')).toBeInTheDocument()
76
+ expect(
77
+ container.querySelector('.str-chat__message--system__line')
78
+ ).toBeInTheDocument()
79
+ })
80
+
81
+ it('renders a trailing close glyph without interaction', () => {
82
+ renderWithProviders(
83
+ <CustomSystemMessage
84
+ {...createProps({
85
+ metadata: {
86
+ custom_type: 'SYSTEM_DM_AGENT_PAUSED',
87
+ },
88
+ })}
89
+ />
90
+ )
91
+
92
+ expect(
93
+ screen.getByTestId('dm-agent-system-message-close')
94
+ ).toBeInTheDocument()
95
+ })
96
+ })
@@ -1,7 +1,76 @@
1
+ import { Sparkle as SparkleIcon, X } from '@phosphor-icons/react'
1
2
  import { MessageTimestamp, type EventComponentProps } from 'stream-chat-react'
2
3
 
4
+ import type { DmAgentSystemType } from '../../stream-custom-data'
5
+
6
+ const DM_AGENT_SYSTEM_TYPES: readonly DmAgentSystemType[] = [
7
+ 'SYSTEM_DM_AGENT_PAUSED',
8
+ 'SYSTEM_DM_AGENT_RESUMED',
9
+ ]
10
+
11
+ const DM_AGENT_SYSTEM_MESSAGE_FALLBACK_TEXT: Record<DmAgentSystemType, string> =
12
+ {
13
+ SYSTEM_DM_AGENT_PAUSED: 'DM Agent has left the conversation',
14
+ SYSTEM_DM_AGENT_RESUMED: 'DM Agent has rejoined the conversation',
15
+ }
16
+
17
+ const isDmAgentSystemType = (
18
+ value: string | undefined
19
+ ): value is DmAgentSystemType => {
20
+ return DM_AGENT_SYSTEM_TYPES.includes(value as DmAgentSystemType)
21
+ }
22
+
23
+ const getDmAgentSystemType = (
24
+ message: EventComponentProps['message']
25
+ ): DmAgentSystemType | undefined => {
26
+ const metadataCustomType = message.metadata?.custom_type
27
+ if (isDmAgentSystemType(metadataCustomType)) {
28
+ return metadataCustomType
29
+ }
30
+
31
+ const fallbackType = message.dm_agent_system_type
32
+ if (isDmAgentSystemType(fallbackType)) {
33
+ return fallbackType
34
+ }
35
+
36
+ return undefined
37
+ }
38
+
3
39
  export const CustomSystemMessage: React.FC<EventComponentProps> = (props) => {
4
40
  const isDateHidden = props.message.hide_date === true
41
+ const dmAgentSystemType = getDmAgentSystemType(props.message)
42
+
43
+ if (dmAgentSystemType) {
44
+ const messageText =
45
+ props.message.text?.trim() ||
46
+ DM_AGENT_SYSTEM_MESSAGE_FALLBACK_TEXT[dmAgentSystemType]
47
+
48
+ return (
49
+ <div className="str-chat__message--system" data-testid="message-system">
50
+ <div
51
+ className="mes-dm-agent-system-message"
52
+ data-testid="dm-agent-system-message"
53
+ data-dm-agent-system-type={dmAgentSystemType}
54
+ >
55
+ <SparkleIcon
56
+ size={16}
57
+ weight="regular"
58
+ aria-hidden
59
+ className="mes-dm-agent-system-message__sparkle"
60
+ />
61
+ <p className="mes-dm-agent-system-message__text">{messageText}</p>
62
+ <span
63
+ className="mes-dm-agent-system-message__close"
64
+ aria-hidden
65
+ data-testid="dm-agent-system-message-close"
66
+ >
67
+ <X size={14} weight="regular" aria-hidden />
68
+ </span>
69
+ </div>
70
+ {!isDateHidden && <MessageTimestamp message={props.message} />}
71
+ </div>
72
+ )
73
+ }
5
74
 
6
75
  return (
7
76
  <div className="str-chat__message--system" data-testid="message-system">
@@ -14,4 +83,3 @@ export const CustomSystemMessage: React.FC<EventComponentProps> = (props) => {
14
83
  </div>
15
84
  )
16
85
  }
17
-
@@ -14,12 +14,22 @@
14
14
 
15
15
  import 'stream-chat'
16
16
 
17
+ export type DmAgentSystemType =
18
+ | 'SYSTEM_DM_AGENT_PAUSED'
19
+ | 'SYSTEM_DM_AGENT_RESUMED'
20
+
21
+ export type MessageCustomType =
22
+ | 'MESSAGE_TIP'
23
+ | 'MESSAGE_PAID'
24
+ | 'MESSAGE_CHATBOT'
25
+ | DmAgentSystemType
26
+
17
27
  /**
18
28
  * Message metadata for paid messaging and chatbot flows.
19
29
  * Used to identify message types and payment status.
20
30
  */
21
31
  export interface MessageMetadata {
22
- custom_type?: 'MESSAGE_TIP' | 'MESSAGE_PAID' | 'MESSAGE_CHATBOT'
32
+ custom_type?: MessageCustomType
23
33
  amount_text?: string
24
34
  payment_status?: string
25
35
  payment_intent_id?: string
@@ -38,5 +48,10 @@ declare module 'stream-chat' {
38
48
  * Contains type and payment information.
39
49
  */
40
50
  metadata?: MessageMetadata
51
+ /**
52
+ * DM Agent-specific system message type.
53
+ * Used as a fallback when metadata.custom_type is not available.
54
+ */
55
+ dm_agent_system_type?: DmAgentSystemType
41
56
  }
42
57
  }
package/src/styles.css CHANGED
@@ -187,3 +187,44 @@
187
187
  color: #016e1a;
188
188
  background-color: #dbf0e0;
189
189
  }
190
+
191
+ /* DM Agent pause/resume system message variants */
192
+ .mes-dm-agent-system-message {
193
+ display: flex;
194
+ align-items: center;
195
+ gap: 10px;
196
+ margin-inline: auto;
197
+ margin-bottom: 4px;
198
+ max-width: 100%;
199
+ padding: 12px;
200
+ border: 1px solid rgba(0, 0, 0, 0.08);
201
+ border-radius: 12px;
202
+ color: rgba(0, 0, 0, 0.55);
203
+ }
204
+
205
+ .mes-dm-agent-system-message__sparkle {
206
+ flex-shrink: 0;
207
+ }
208
+
209
+ .mes-dm-agent-system-message__text {
210
+ flex: 1 1 auto;
211
+ min-width: 0;
212
+ margin: 0;
213
+ font-size: 14px;
214
+ font-weight: 400;
215
+ line-height: 20px;
216
+ letter-spacing: 0.21px;
217
+ color: rgba(0, 0, 0, 0.55);
218
+ text-align: left;
219
+ }
220
+
221
+ .mes-dm-agent-system-message__close {
222
+ display: inline-flex;
223
+ align-items: center;
224
+ justify-content: center;
225
+ width: 32px;
226
+ height: 32px;
227
+ flex-shrink: 0;
228
+ color: rgba(0, 0, 0, 0.5);
229
+ pointer-events: none;
230
+ }