Skip to content

Commit b8527bb

Browse files
committed
fix: update message handling
1 parent 003e33e commit b8527bb

File tree

10 files changed

+269
-182
lines changed

10 files changed

+269
-182
lines changed

src/components/elementRenderer/elementRenderer.cy.tsx

Lines changed: 57 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,14 @@ import {
44
supportedViewports,
55
testUser,
66
} from '../../../cypress/support/variables'
7-
import { YoutubeVideo } from '..'
7+
import { StreamingText, YoutubeVideo } from '..'
88
import Text from '../text/text'
99
import ElementRenderer from './elementRenderer'
1010

1111
const supportedElements = {
1212
text: Text,
1313
video: YoutubeVideo,
14+
streamingText: StreamingText,
1415
}
1516

1617
const sampleMessage = {
@@ -38,23 +39,27 @@ describe('ElementRenderer', () => {
3839
cy.viewport(viewport)
3940
cy.mount(
4041
<ElementRenderer
41-
message={{
42-
...sampleMessage,
43-
data: { text: 'Test Text' },
44-
format: 'text',
45-
}}
42+
messages={[
43+
{
44+
...sampleMessage,
45+
data: { text: 'Test Text' },
46+
format: 'text',
47+
},
48+
]}
4649
{...commonProps}
4750
ws={mockWsClient}
4851
/>
4952
)
5053
cy.get('p').should('contain.text', 'Test Text')
5154
cy.mount(
5255
<ElementRenderer
53-
message={{
54-
...sampleMessage,
55-
data: { youtubeVideoId: 'MtN1YnoL46Q' },
56-
format: 'video',
57-
}}
56+
messages={[
57+
{
58+
...sampleMessage,
59+
data: { youtubeVideoId: 'MtN1YnoL46Q' },
60+
format: 'video',
61+
},
62+
]}
5863
{...commonProps}
5964
ws={mockWsClient}
6065
/>
@@ -71,6 +76,40 @@ describe('ElementRenderer', () => {
7176
})
7277
})
7378

79+
it(`renders the original message and its update messages correctly on ${viewport} screen`, () => {
80+
const mockWsClient = {
81+
send: cy.stub(),
82+
close: cy.stub(),
83+
reconnect: cy.stub(),
84+
}
85+
86+
cy.viewport(viewport)
87+
cy.mount(
88+
<ElementRenderer
89+
messages={[
90+
{
91+
...sampleMessage,
92+
data: { text: 'Test' },
93+
format: 'streamingText',
94+
},
95+
{
96+
id: '2',
97+
timestamp: '2020-01-02T00:00:00.000Z',
98+
sender: testUser,
99+
conversationId: 'lkd9vc',
100+
topic: 'default',
101+
threadId: '1',
102+
data: { text: ' Text' },
103+
format: 'streamingText',
104+
},
105+
]}
106+
{...commonProps}
107+
ws={mockWsClient}
108+
/>
109+
)
110+
cy.get('p').should('contain.text', 'Test Text')
111+
})
112+
74113
it(`renders a message for an unsupported format on ${viewport} screen`, () => {
75114
const mockWsClient = {
76115
send: cy.stub(),
@@ -81,11 +120,13 @@ describe('ElementRenderer', () => {
81120
cy.viewport(viewport)
82121
cy.mount(
83122
<ElementRenderer
84-
message={{
85-
...sampleMessage,
86-
data: { text: 'Test Text' },
87-
format: 'unsupported',
88-
}}
123+
messages={[
124+
{
125+
...sampleMessage,
126+
data: { text: 'Test Text' },
127+
format: 'unsupported',
128+
},
129+
]}
89130
{...commonProps}
90131
ws={mockWsClient}
91132
/>

src/components/elementRenderer/elementRenderer.tsx

Lines changed: 10 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,34 @@
11
import Typography from '@mui/material/Typography'
22
import React from 'react'
33

4-
import type {
5-
ComponentMap,
6-
Sender,
7-
ThreadableMessage,
8-
WebSocketClient,
9-
} from '../types'
4+
import type { ComponentMap, Message, Sender, WebSocketClient } from '../types'
105

116
interface ElementRendererProps {
127
sender: Sender
138
ws: WebSocketClient
14-
message: ThreadableMessage
9+
messages: Message[]
1510
supportedElements: ComponentMap
1611
}
1712

1813
const ElementRenderer = (props: ElementRendererProps) => {
19-
const MaybeElement = props.supportedElements[props.message.format]
14+
const rootMessage = props.messages[0]
15+
const updateMessages = props.messages.slice(1)
2016

17+
const MaybeElement = props.supportedElements[rootMessage.format]
2118
return (
2219
<>
2320
{MaybeElement ? (
2421
React.createElement(MaybeElement, {
2522
sender: props.sender,
2623
ws: props.ws,
27-
messageId: props.message.id,
28-
conversationId: props.message.conversationId,
29-
...props.message.data,
30-
...(props.message.threadMessagesData && {
31-
updatedData: props.message.threadMessagesData,
32-
}),
24+
messageId: rootMessage.id,
25+
conversationId: rootMessage.conversationId,
26+
...rootMessage.data,
27+
updatedData: updateMessages.map((message) => message.data),
3328
})
3429
) : (
3530
<Typography variant="body2">
36-
Unsupported element format: {props.message.format}
31+
Unsupported element format: {rootMessage.format}
3732
</Typography>
3833
)}
3934
</>

src/components/messageCanvas/messageCanvas.cy.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import {
66
testUser,
77
} from '../../../cypress/support/variables'
88
import Icon from '../icon/icon'
9-
import type { ThreadableMessage } from '../types'
9+
import type { Message } from '../types'
1010
import CopyText from './actions/copy/copyText'
1111
import MessageCanvas from './messageCanvas'
1212

@@ -43,7 +43,7 @@ describe('MessageCanvas', () => {
4343
cy.mount(
4444
<MessageCanvas
4545
message={testMessage}
46-
getActionsComponent={(message: ThreadableMessage) => {
46+
getActionsComponent={(message: Message) => {
4747
const copyButton = message.format === 'text' && (
4848
<CopyText message={message} />
4949
)

src/components/messageCanvas/messageCanvas.stories.tsx

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
import Typography from '@mui/material/Typography'
22
import React from 'react'
33

4-
import { ElementRenderer, MarkedMarkdown, type ThreadableMessage } from '..'
4+
import ElementRenderer from '../elementRenderer'
55
import Icon from '../icon/icon'
6+
import MarkedMarkdown from '../markdown'
67
import Text from '../text/text'
8+
import type { Message } from '../types'
79
import CopyText from './actions/copy/copyText'
810
import TextToSpeech from './actions/textToSpeech/textToSpeech'
911
import MessageCanvas from './messageCanvas'
@@ -111,7 +113,7 @@ const elementRendererString = `<ElementRenderer
111113
supportedElements={{ text: Text }}
112114
/>`
113115

114-
const profileString = `(message: ThreadableMessage) => {
116+
const profileString = `(message: Message) => {
115117
<>
116118
{getProfileIcon(message)}
117119
<Typography variant="body1" color="text.secondary">
@@ -120,23 +122,23 @@ const profileString = `(message: ThreadableMessage) => {
120122
</>
121123
}`
122124

123-
function getProfileIcon(message: ThreadableMessage) {
125+
function getProfileIcon(message: Message) {
124126
if (message.sender.name?.includes('agent')) {
125127
return <Icon name="smart_toy" />
126128
} else {
127129
return <Icon name="account_circle" />
128130
}
129131
}
130132

131-
function getProfileName(message: ThreadableMessage) {
133+
function getProfileName(message: Message) {
132134
return (
133135
<Typography variant="body1" color="text.secondary">
134136
{message.sender.name}
135137
</Typography>
136138
)
137139
}
138140

139-
function getProfileIconAndName(message: ThreadableMessage) {
141+
function getProfileIconAndName(message: Message) {
140142
return (
141143
<>
142144
{getProfileIcon(message)}
@@ -149,7 +151,7 @@ export const WithProfileIcon = {
149151
args: {
150152
children: (
151153
<ElementRenderer
152-
message={messageFromHuman}
154+
messages={[messageFromHuman]}
153155
supportedElements={{ text: Text }}
154156
{...commonElementRendererProps}
155157
/>
@@ -175,7 +177,7 @@ export const NoIcon = {
175177
args: {
176178
children: (
177179
<ElementRenderer
178-
message={messageFromAgent}
180+
messages={[messageFromAgent]}
179181
supportedElements={{ text: Text }}
180182
{...commonElementRendererProps}
181183
/>
@@ -201,14 +203,14 @@ export const WithCopyIcon = {
201203
args: {
202204
children: (
203205
<ElementRenderer
204-
message={messageFromHuman}
206+
messages={[messageFromHuman]}
205207
supportedElements={{ text: Text }}
206208
{...commonElementRendererProps}
207209
/>
208210
),
209211
message: messageFromHuman,
210212
getProfileComponent: getProfileIconAndName,
211-
getActionsComponent: (message: ThreadableMessage) => {
213+
getActionsComponent: (message: Message) => {
212214
const copyButton = message.format === 'text' && (
213215
<CopyText message={message} />
214216
)
@@ -222,7 +224,7 @@ export const WithCopyIcon = {
222224
source: {
223225
code: `<MessageCanvas
224226
getProfileComponent={${profileString}}
225-
getActionsComponent={(message: ThreadableMessage) => {
227+
getActionsComponent={(message: Message) => {
226228
const copyButton = message.format === 'text' && <CopyText message={message} />
227229
if (copyButton) {
228230
return <>{copyButton}</>
@@ -241,14 +243,14 @@ export const WithTextToSpeech = {
241243
args: {
242244
children: (
243245
<ElementRenderer
244-
message={markdownMessage}
246+
messages={[markdownMessage]}
245247
supportedElements={{ markdown: MarkedMarkdown }}
246248
{...commonElementRendererProps}
247249
/>
248250
),
249251
message: markdownMessage,
250252
getProfileComponent: getProfileIconAndName,
251-
getActionsComponent: (message: ThreadableMessage) => {
253+
getActionsComponent: (message: Message) => {
252254
return (
253255
<>
254256
<TextToSpeech message={message} />
@@ -265,7 +267,7 @@ export const WithTextToSpeech = {
265267
source: {
266268
code: `<MessageCanvas
267269
getProfileComponent={${profileString}}
268-
getActionsComponent={(message: ThreadableMessage) => {
270+
getActionsComponent={(message: Message) => {
269271
const copyButton = message.format === 'text' && <CopyText message={message} />
270272
if (copyButton) {
271273
return <>{copyButton}</>

src/components/messageSpace/messageSpace.stories.tsx

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,7 @@ const tableData = [
197197
]
198198

199199
const chartColors = ['#648FFF', '#785EF0', '#DC267F', '#FE6100', '#FFB000']
200-
200+
const streamingMarkdownRootMessageId = getUUID()
201201
export const Default = {
202202
args: {
203203
ws: { send: () => {} },
@@ -214,11 +214,21 @@ export const Default = {
214214
},
215215
{
216216
...agentMessageData,
217-
id: getUUID(),
217+
id: streamingMarkdownRootMessageId,
218218
timestamp: '2024-01-02T00:01:00.000Z',
219-
format: 'markdown',
219+
format: 'streamingMarkdown',
220+
data: {
221+
text: '# Title\n\n---\n\n ## Subtitle',
222+
},
223+
},
224+
{
225+
...agentMessageData,
226+
id: getUUID(),
227+
timestamp: '2024-01-02T00:02:01.000Z',
228+
format: 'updateStreamingMarkdown',
229+
threadId: streamingMarkdownRootMessageId,
220230
data: {
221-
text: '# Title\n\n---\n\n ## Subtitle\n\nThis is a paragraph. Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry\'s standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.\n\n- This is an **inline notation**\n- This is a *inline notation*.\n- This is a _inline notation_.\n- This is a __inline notation__.\n- This is a ~~inline notation~~.\n\n```\nconst string = "Hello World"\nconst number = 123\n```\n\n> This is a blockquote.\n\n1. Item 1\n2. Item 2\n3. Item 3\n\n| Column 1 | Column 2 | Column 3 |\n| -------- | -------- | -------- |\n| Item 1 | Item 2 | Item 3 |',
231+
text: '\n\nThis is a paragraph. Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry\'s standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.\n\n- This is an **inline notation**\n- This is a *inline notation*.\n- This is a _inline notation_.\n- This is a __inline notation__.\n- This is a ~~inline notation~~.\n\n```\nconst string = "Hello World"\nconst number = 123\n```\n\n> This is a blockquote.\n\n1. Item 1\n2. Item 2\n3. Item 3\n\n| Column 1 | Column 2 | Column 3 |\n| -------- | -------- | -------- |\n| Item 1 | Item 2 | Item 3 |',
222232
},
223233
},
224234
{

0 commit comments

Comments
 (0)