@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/dist/assets/index.css +1 -1
- package/dist/index.d.ts +10 -1
- package/dist/index.js +817 -767
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/components/CustomSystemMessage/CustomSystemMessage.stories.tsx +56 -0
- package/src/components/CustomSystemMessage/CustomSystemMessage.test.tsx +96 -0
- package/src/components/CustomSystemMessage/index.tsx +69 -1
- package/src/stream-custom-data.ts +16 -1
- package/src/styles.css +41 -0
package/package.json
CHANGED
|
@@ -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?:
|
|
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
|
+
}
|