Skip to content

Commit 5c17908

Browse files
author
kim
committed
feat: show loading indicator on bot replying
1 parent 0c030dd commit 5c17908

File tree

16 files changed

+410
-374
lines changed

16 files changed

+410
-374
lines changed

cypress/e2e/player/main.cy.ts

Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ const defaultAppData = [
2222
},
2323
];
2424

25+
const SEND_BUTTON = '[name="Send message"]';
26+
2527
describe('Player View', () => {
2628
it('Show messages and write a new one', () => {
2729
cy.setUpApi(
@@ -46,7 +48,7 @@ describe('Player View', () => {
4648
// type and send message
4749
const message = 'My message';
4850
cy.get('[role="textbox"]').type(message);
49-
cy.get('[name="send"]').click();
51+
cy.get(SEND_BUTTON).click();
5052

5153
// expect user message
5254
cy.get(buildDataCy(buildCommentContainerDataCy('1'))).should(
@@ -83,7 +85,7 @@ describe('Player View', () => {
8385
// type and send message
8486
const message = 'My message';
8587
cy.get('[role="textbox"]').type(message);
86-
cy.get('[name="send"]').click();
88+
cy.get(SEND_BUTTON).click();
8789

8890
// expect user message
8991
cy.get(buildDataCy(buildCommentContainerDataCy('2'))).should(
@@ -171,4 +173,46 @@ describe('Player View', () => {
171173

172174
cy.get('button').should('not.contain', suggestion);
173175
});
176+
177+
it('Type and send messages with enter key', () => {
178+
cy.setUpApi(
179+
{
180+
appData: [],
181+
appSettings: [MOCK_APP_SETTING],
182+
},
183+
{
184+
context: Context.Player,
185+
permission: PermissionLevel.Write,
186+
},
187+
);
188+
cy.visit('/');
189+
190+
// type and send message with enter key
191+
const message = 'My message';
192+
cy.get('[role="textbox"]').type(message).type('{enter}');
193+
194+
// expect user message
195+
cy.get(buildDataCy(buildCommentContainerDataCy('2'))).should(
196+
'contain',
197+
message,
198+
);
199+
200+
// type and send message with enter key
201+
const message1 = 'My second message';
202+
cy.get('[role="textbox"]').type(message1, { delay: 20 }).type('{enter}');
203+
204+
// expect message to not be sent
205+
cy.get('[role="log"]').should('not.contain', message1);
206+
207+
// explicitely wait for chatbot to answer before sending another message
208+
cy.wait(1000);
209+
210+
cy.get('[role="textbox"]').type('{enter}');
211+
212+
// expect user message
213+
cy.get(buildDataCy(buildCommentContainerDataCy('4'))).should(
214+
'contain',
215+
message1,
216+
);
217+
});
174218
});

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
"@codemirror/lang-javascript": "6.2.4",
1515
"@emotion/react": "11.14.0",
1616
"@emotion/styled": "11.14.1",
17-
"@graasp/apps-query-client": "4.0.0",
17+
"@graasp/apps-query-client": "github:graasp/graasp-apps-query-client#delay-chatbot-answer",
1818
"@graasp/sdk": "5.16.5",
1919
"@graasp/ui": "5.5.4",
2020
"@mui/icons-material": "6.5.0",

src/config/constants.ts

Lines changed: 0 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,50 +1,9 @@
1-
export const GRAASP_LOGO_HEADER_HEIGHT = 40;
2-
export const BUTTON_LOADER_SIZE = 24;
3-
4-
// Admin view constants
5-
export const NB_COL_TABLE_VIEW_TABLE = 4;
6-
7-
// reset timeout in ms for settings dialog
8-
export const CLOSE_SETTINGS_TIMEOUT = 500;
9-
101
// commit message truncation length
112
export const DEFAULT_TRUNCATION_COMMIT_MESSAGE_LENGTH = 15;
123

13-
export const MAX_INITIALS_AVATAR = 2;
14-
15-
export const DEFAULT_REPL_INPUT_VALUE = '';
16-
174
export const ANONYMOUS_USER = 'You';
18-
export const INSTRUCTOR_CODE_NAME = 'Default';
19-
20-
export const INSTRUCTOR_CODE_ID = 'instructor';
21-
22-
export const JAVASCRIPT = 'javascript';
23-
export const JAVA = 'java';
24-
export const PYTHON = 'python';
25-
export const MATLAB = 'matlab';
26-
export const JSON_LANG = 'json';
27-
28-
export const REVIEW_MODE_INDIVIDUAL = 'individual';
29-
export const REVIEW_MODE_COLLABORATIVE = 'collaborative';
30-
31-
export const VISIBILITY_MEMBER = 'member';
32-
export const VISIBILITY_ITEM = 'item';
33-
34-
export const REVIEW_MODES = [
35-
{
36-
label: 'Individual - Each student works in isolation',
37-
value: REVIEW_MODE_INDIVIDUAL,
38-
},
39-
{
40-
label: 'Collaborative - Students see other students work',
41-
value: REVIEW_MODE_COLLABORATIVE,
42-
},
43-
] as const;
445

456
export const DEFAULT_BOT_USERNAME = 'Graasp Bot';
467

478
// maximum number of messages allowed in a chatbot thread
489
export const MAX_CHATBOT_THREAD_LENGTH = 50;
49-
50-
export const DEFAULT_CHATBOT_PROMPT_APP_DATA = { chatbotPromptSettingId: '' };

src/config/selectors.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
1-
export const GRAASP_LOGO_CYPRESS = 'graasp_logo';
21
export const TABLE_VIEW_TABLE_CYPRESS = 'table_view_table';
32
export const TABLE_VIEW_PANE_CYPRESS = 'table_view_pane';
43
export const SETTINGS_VIEW_PANE_CYPRESS = 'settings_view_pane';
5-
export const ABOUT_VIEW_PANE_CYPRESS = 'about_view_pane';
64
export const BUILDER_VIEW_CY = 'builder-view';
75
export const ANALYTICS_VIEW_CY = 'analytics_view';
86
export const TAB_PRESET_VIEW_CYPRESS = 'tab_preset_view';

src/langs/en.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@
7474
"STARTER_SUGGESTION_NAME": "Starter suggestion number {{nb}}",
7575
"DELETE_STARTER_SUGGESTION_BUTTON_TITLE": "Delete suggestion number {{nb}}",
7676
"STARTER_SUGGESTION_PLACEHOLDER": "Write a suggestion here",
77-
"STARTER_SUGGESTION_PLAYER_BUTTON_ARIA_LABEL": "Ask {{suggestion}}"
77+
"STARTER_SUGGESTION_PLAYER_BUTTON_ARIA_LABEL": "Ask {{suggestion}}",
78+
"SEND_MESSAGE_BUTTON": "Send message"
7879
}
7980
}
Lines changed: 61 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,69 @@
1-
import { styled } from '@mui/material';
1+
import { Divider, Stack, SxProps, Theme, styled } from '@mui/material';
2+
3+
import { ChatbotPromptSettings } from '@/config/appSetting';
24

35
import { BIG_BORDER_RADIUS } from '../../constants';
6+
import { ChatbotTyping } from '../common/ChatbotTyping';
7+
import CommentEditor from '../common/CommentEditor';
8+
import CommentThread from '../common/CommentThread';
9+
import { Suggestions } from '../common/Suggestions';
10+
import { Comment } from '../common/useConversation';
11+
import { useSendMessageAndAskChatbot } from '../common/useSendMessageAndAskChatbot';
12+
import ChatbotHeader from './ChatbotHeader';
413

5-
const CommentContainer = styled('div')(({ theme }) => ({
14+
const Container = styled('div')(({ theme }) => ({
615
backgroundColor: 'white',
716
border: 'solid silver 1px',
817
padding: theme.spacing(3, 0),
918
borderRadius: BIG_BORDER_RADIUS,
1019
}));
11-
export default CommentContainer;
20+
21+
export type CommentContainerProps = Readonly<{
22+
chatbotAvatar?: Blob;
23+
chatbotName: string;
24+
chatbotPrompt: ChatbotPromptSettings;
25+
comments: Comment[];
26+
mode?: 'read' | 'write';
27+
suggestions?: string[];
28+
threadSx?: SxProps<Theme>;
29+
}>;
30+
31+
export function CommentContainer({
32+
threadSx,
33+
comments,
34+
chatbotPrompt,
35+
chatbotName,
36+
chatbotAvatar,
37+
mode = 'read',
38+
suggestions,
39+
}: CommentContainerProps) {
40+
const { send, isLoading: isSendingMessageAndAsking } =
41+
useSendMessageAndAskChatbot({
42+
chatbotPrompt,
43+
});
44+
45+
return (
46+
<Container>
47+
<Stack gap={3} px={2}>
48+
<Stack role="log" gap={3}>
49+
<ChatbotHeader avatar={chatbotAvatar} name={chatbotName} />
50+
<Divider />
51+
<CommentThread
52+
chatbotAvatar={chatbotAvatar}
53+
threadSx={threadSx}
54+
comments={comments}
55+
/>
56+
{suggestions && <Suggestions suggestions={suggestions} send={send} />}
57+
{isSendingMessageAndAsking && (
58+
<ChatbotTyping avatar={chatbotAvatar} />
59+
)}
60+
</Stack>
61+
<Stack>
62+
{'write' === mode && (
63+
<CommentEditor send={send} isLoading={isSendingMessageAndAsking} />
64+
)}
65+
</Stack>
66+
</Stack>
67+
</Container>
68+
);
69+
}

src/modules/comment/Conversation.tsx

Lines changed: 15 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,11 @@
11
import { useTranslation } from 'react-i18next';
22

3-
import {
4-
Alert,
5-
Box,
6-
CircularProgress,
7-
Divider,
8-
Stack,
9-
SxProps,
10-
Theme,
11-
} from '@mui/material';
3+
import { Alert, Box, CircularProgress } from '@mui/material';
124

135
import { ChatbotPromptSettings } from '@/config/appSetting';
146

15-
import CommentEditor from '../common/CommentEditor';
16-
import CommentThread from '../common/CommentThread';
17-
import { Suggestions } from '../common/Suggestions';
187
import { Comment } from '../common/useConversation';
19-
import ChatbotHeader from './ChatbotHeader';
20-
import CommentContainer from './CommentContainer';
8+
import { CommentContainer, CommentContainerProps } from './CommentContainer';
219

2210
function Conversation({
2311
threadSx,
@@ -28,13 +16,13 @@ function Conversation({
2816
mode = 'read',
2917
suggestions,
3018
}: Readonly<{
31-
chatbotPrompt?: ChatbotPromptSettings;
3219
chatbotAvatar?: Blob;
33-
threadSx?: SxProps<Theme>;
34-
isLoading?: boolean;
20+
chatbotPrompt?: ChatbotPromptSettings;
3521
comments: Comment[];
36-
mode?: 'read' | 'write';
22+
isLoading?: boolean;
23+
mode?: CommentContainerProps['mode'];
3724
suggestions?: string[];
25+
threadSx?: CommentContainerProps['threadSx'];
3826
}>) {
3927
const { t } = useTranslation();
4028

@@ -50,26 +38,15 @@ function Conversation({
5038
height: '100%',
5139
}}
5240
>
53-
<CommentContainer>
54-
<Stack gap={3} px={2}>
55-
<ChatbotHeader avatar={chatbotAvatar} name={chatbotName} />
56-
<Divider />
57-
<CommentThread
58-
chatbotAvatar={chatbotAvatar}
59-
threadSx={threadSx}
60-
comments={comments}
61-
/>
62-
{suggestions && (
63-
<Suggestions
64-
suggestions={suggestions}
65-
chatbotPrompt={chatbotPrompt}
66-
/>
67-
)}
68-
{'write' === mode && (
69-
<CommentEditor chatbotPrompt={chatbotPrompt} />
70-
)}
71-
</Stack>
72-
</CommentContainer>
41+
<CommentContainer
42+
chatbotAvatar={chatbotAvatar}
43+
chatbotName={chatbotName}
44+
threadSx={threadSx}
45+
comments={comments}
46+
chatbotPrompt={chatbotPrompt}
47+
mode={mode}
48+
suggestions={suggestions}
49+
/>
7350
</Box>
7451
);
7552
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { Skeleton, Stack } from '@mui/material';
2+
3+
import ChatbotAvatar from './ChatbotAvatar';
4+
5+
export function ChatbotTyping({ avatar }: Readonly<{ avatar?: Blob }>) {
6+
return (
7+
<Stack direction="row" gap={1}>
8+
<ChatbotAvatar size="small" avatar={avatar} />
9+
<Skeleton
10+
sx={{ borderRadius: 2 }}
11+
variant="rectangular"
12+
width={300}
13+
height={40}
14+
/>
15+
</Stack>
16+
);
17+
}

src/modules/common/CodeEditor.tsx

Lines changed: 0 additions & 50 deletions
This file was deleted.

0 commit comments

Comments
 (0)