Skip to content

Commit 5e2ea09

Browse files
authored
Upgraded Web Chat to 4.12.0 and fixed various Web Chat bugs (#2236)
* Added ability to debug shared package (redux state). * Bumped Web Chat to 4.12.0 * Integrated inspector with new WC activity focus hook * Updated fallback speech service ponyfill API * Fixed WC send box overflow visual bug * Added changelog entry
1 parent e16706f commit 5e2ea09

15 files changed

+1402
-1665
lines changed

.vscode/launch.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333
"outputCapture": "std",
3434
"internalConsoleOptions": "openOnSessionStart",
3535
"cwd": "${workspaceFolder}/packages/app/main",
36-
"outFiles": [ "${workspaceRoot}/packages/app/main/app/**/*.js"]
36+
"outFiles": [ "${workspaceRoot}/packages/app/main/app/**/*.js", "${workspaceRoot}/packages/app/shared/**/*.js"]
3737
},
3838
{
3939
"type": "node",

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1010
- [main] Bumped `electron` from `4.1.1` to `11.0.1` in PR [2226](https://github.com/microsoft/BotFramework-Emulator/pull/2226)
1111
- [main] Bumped `electron-builder` to `22.9.1` and `electron-updater` to `4.3.5` to fix the Mac build in PR [2230](https://github.com/microsoft/BotFramework-Emulator/pull/2230)
1212
- [client] Re-enabled the `<webview>` tag in the client so that the inspectors show again in PR [2233](https://github.com/microsoft/BotFramework-Emulator/pull/2233)
13+
- [client] Bumped `botframework-webchat` to v4.12.0 and fixed various Web Chat-related bugs in PR [2236](https://github.com/microsoft/BotFramework-Emulator/pull/2236)
1314

1415
## v4.11.0 - 2020 - 11 - 05
1516
- [client] Moved from master to main as the default branch. [2194](https://github.com/microsoft/BotFramework-Emulator/pull/2194)

package-lock.json

+1,297-1,636
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/app/client/package.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -110,8 +110,8 @@
110110
"base64url": "3.0.0",
111111
"botframework-config": "4.4.0",
112112
"botframework-schema": "^4.3.4",
113-
"botframework-webchat": "4.11.0",
114-
"botframework-webchat-core": "4.11.0",
113+
"botframework-webchat": "4.12.0",
114+
"botframework-webchat-core": "4.12.0",
115115
"core-js": "^3.6.5",
116116
"eslint-plugin-react": "^7.12.3",
117117
"markdown-it": "^8.4.2",

packages/app/client/src/state/sagas/chatSagas.spec.ts

+3-6
Original file line numberDiff line numberDiff line change
@@ -567,8 +567,7 @@ describe('The ChatSagas,', () => {
567567
// call createCognitiveServicesSpeechServicesPonyfillFactory
568568
expect(gen.next({}).value).toEqual(
569569
call(createCognitiveServicesSpeechServicesPonyfillFactory, {
570-
authorizationToken: jasmine.anything(), // any(Promise) doesn't match correctly
571-
region: 'westus',
570+
credentials: jasmine.any(Function),
572571
})
573572
);
574573

@@ -769,8 +768,7 @@ describe('The ChatSagas,', () => {
769768
const existingFactory = {};
770769
expect(gen.next(existingFactory).value).toEqual(
771770
call(createCognitiveServicesSpeechServicesPonyfillFactory, {
772-
authorizationToken: jasmine.anything(), // .any(Promise) doesn't match correctly
773-
region: 'westus',
771+
credentials: jasmine.any(Function),
774772
})
775773
);
776774

@@ -904,8 +902,7 @@ describe('The ChatSagas,', () => {
904902
const existingFactory = {};
905903
expect(gen.next(existingFactory).value).toEqual(
906904
call(createCognitiveServicesSpeechServicesPonyfillFactory, {
907-
authorizationToken: jasmine.anything(), // .any(Promise) doesn't match correctly
908-
region: 'westus',
905+
credentials: jasmine.any(Function),
909906
})
910907
);
911908

packages/app/client/src/state/sagas/chatSagas.ts

+10-6
Original file line numberDiff line numberDiff line change
@@ -467,11 +467,13 @@ export class ChatSagas {
467467
endpointId,
468468
!!existingFactory
469469
);
470-
471-
const factory = yield call(createCognitiveServicesSpeechServicesPonyfillFactory, {
472-
authorizationToken: speechAuthenticationToken,
470+
const fetchSpeechCredentials = async () => ({
471+
authorizationToken: await speechAuthenticationToken,
473472
region: 'westus', // Currently, the prod speech service is only deployed to westus
474473
});
474+
const factory = yield call(createCognitiveServicesSpeechServicesPonyfillFactory, {
475+
credentials: fetchSpeechCredentials,
476+
});
475477

476478
yield put(webSpeechFactoryUpdated(documentId, factory)); // Provide the new factory to the store
477479
} catch (e) {
@@ -651,11 +653,13 @@ export class ChatSagas {
651653
botEndpoint.id,
652654
!!existingFactory
653655
);
654-
655-
const factory = yield call(createCognitiveServicesSpeechServicesPonyfillFactory, {
656-
authorizationToken: speechAuthenticationToken,
656+
const fetchSpeechCredentials = async () => ({
657+
authorizationToken: await speechAuthenticationToken,
657658
region: 'westus', // Currently, the prod speech service is only deployed to westus
658659
});
660+
const factory = yield call(createCognitiveServicesSpeechServicesPonyfillFactory, {
661+
credentials: fetchSpeechCredentials,
662+
});
659663

660664
yield put(webSpeechFactoryUpdated(documentId, factory)); // Provide the new factory to the store
661665
} catch (e) {

packages/app/client/src/ui/editor/emulator/parts/chat/chat.scss

+1-1
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@
3838
background-color: white;
3939
display: flex;
4040
position: relative;
41-
height: 100%;
41+
height: calc(100% - 36px); // 36px is the height of the <header> in chatPanel.tsx
4242
width: 100%;
4343
user-select: text;
4444

packages/app/client/src/ui/editor/emulator/parts/chat/chat.spec.tsx

+10-8
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
import * as React from 'react';
3535
import { mount, ReactWrapper, shallow, ShallowWrapper } from 'enzyme';
3636
import { Provider } from 'react-redux';
37-
import ReactWebChat, { createDirectLine, createStyleSet } from 'botframework-webchat';
37+
import { createDirectLine, createStyleSet, Components } from 'botframework-webchat';
3838
import { ActivityTypes } from 'botframework-schema';
3939
import {
4040
bot,
@@ -56,6 +56,8 @@ import webChatStyleOptions from './webChatTheme';
5656
import { ChatContainer } from './chatContainer';
5757
import { ChatProps, Chat } from './chat';
5858

59+
const { Composer } = Components;
60+
5961
jest.mock('./chat.scss', () => ({
6062
get bubbleContentColor() {
6163
return '#fff';
@@ -160,7 +162,7 @@ describe('<ChatContainer />', () => {
160162
wrapper.setProps({
161163
children: <ChatContainer {...updatedProps} />,
162164
});
163-
expect(wrapper.find(ReactWebChat).props().disabled).toBeTruthy();
165+
expect(wrapper.find(Composer).props().disabled).toBeTruthy();
164166

165167
updatedProps = {
166168
...props,
@@ -170,7 +172,7 @@ describe('<ChatContainer />', () => {
170172
wrapper.setProps({
171173
children: <ChatContainer {...updatedProps} />,
172174
});
173-
expect(wrapper.find(ReactWebChat).props().disabled).toBeFalsy();
175+
expect(wrapper.find(Composer).props().disabled).toBeFalsy();
174176

175177
updatedProps = {
176178
...props,
@@ -180,7 +182,7 @@ describe('<ChatContainer />', () => {
180182
wrapper.setProps({
181183
children: <ChatContainer {...updatedProps} />,
182184
});
183-
expect(wrapper.find(ReactWebChat).props().disabled).toBeFalsy();
185+
expect(wrapper.find(Composer).props().disabled).toBeFalsy();
184186

185187
updatedProps = {
186188
...props,
@@ -190,7 +192,7 @@ describe('<ChatContainer />', () => {
190192
wrapper.setProps({
191193
children: <ChatContainer {...updatedProps} />,
192194
});
193-
expect(wrapper.find(ReactWebChat).props().disabled).toBeFalsy();
195+
expect(wrapper.find(Composer).props().disabled).toBeFalsy();
194196

195197
updatedProps = {
196198
...props,
@@ -200,7 +202,7 @@ describe('<ChatContainer />', () => {
200202
wrapper.setProps({
201203
children: <ChatContainer {...updatedProps} />,
202204
});
203-
expect(wrapper.find(ReactWebChat).props().disabled).toBeFalsy();
205+
expect(wrapper.find(Composer).props().disabled).toBeFalsy();
204206

205207
updatedProps = {
206208
...props,
@@ -210,7 +212,7 @@ describe('<ChatContainer />', () => {
210212
wrapper.setProps({
211213
children: <ChatContainer {...updatedProps} />,
212214
});
213-
expect(wrapper.find(ReactWebChat).props().disabled).toBeTruthy();
215+
expect(wrapper.find(Composer).props().disabled).toBeTruthy();
214216
});
215217

216218
describe('when there is no direct line client', () => {
@@ -223,7 +225,7 @@ describe('<ChatContainer />', () => {
223225

224226
describe('when there is a direct line client', () => {
225227
it('renders the WebChat component', () => {
226-
const webChat = wrapper.find(ReactWebChat);
228+
const webChat = wrapper.find(Composer);
227229
const styleSet = createStyleSet({ ...webChatStyleOptions });
228230

229231
styleSet.fileContent = {

packages/app/client/src/ui/editor/emulator/parts/chat/chat.tsx

+14-4
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333

3434
import { ValueTypes, RestartConversationStatus } from '@bfemulator/app-shared';
3535
import { Activity, ActivityTypes } from 'botframework-schema';
36-
import ReactWebChat, { createStyleSet } from 'botframework-webchat';
36+
import { createStyleSet, Components } from 'botframework-webchat';
3737
import * as React from 'react';
3838
import { PureComponent, KeyboardEvent, MouseEvent, ReactNode } from 'react';
3939
import { EmulatorMode } from '@bfemulator/sdk-shared';
@@ -44,6 +44,9 @@ import * as styles from './chat.scss';
4444
import webChatStyleOptions from './webChatTheme';
4545
import { TraceActivityContainer } from './traceActivityContainer';
4646
import { ConnectionMessageContainer } from './connectionMessageContainer';
47+
import { TranscriptFocusListener } from './transcriptFocusListener';
48+
49+
const { BasicWebChat, Composer } = Components;
4750

4851
export interface ChatProps {
4952
botId?: string;
@@ -65,9 +68,11 @@ interface ChatState {
6568
highlightedActivities?: Activity[];
6669
}
6770

71+
type ActivityMap = Record<string, Activity>;
72+
6873
export class Chat extends PureComponent<ChatProps, ChatState> {
6974
public state = { waitForSpeechToken: false } as ChatState;
70-
private activityMap: { [activityId: string]: Activity } = {};
75+
private activityMap: ActivityMap = {};
7176

7277
public render() {
7378
const {
@@ -113,9 +118,11 @@ export class Chat extends PureComponent<ChatProps, ChatState> {
113118
name: 'Bot',
114119
};
115120

121+
const boundUpdateSelectedActivity = this.updateSelectedActivity.bind(this);
122+
116123
return (
117124
<div className={styles.chat}>
118-
<ReactWebChat
125+
<Composer
119126
store={webchatStore}
120127
activityMiddleware={this.createActivityMiddleware}
121128
cardActionMiddleware={this.cardActionMiddleware}
@@ -128,7 +135,10 @@ export class Chat extends PureComponent<ChatProps, ChatState> {
128135
userID={currentUser.id}
129136
username={currentUser.name || 'User'}
130137
webSpeechPonyfillFactory={webSpeechPonyfillFactory}
131-
/>
138+
>
139+
<BasicWebChat />
140+
<TranscriptFocusListener updateSelectedActivity={boundUpdateSelectedActivity} />
141+
</Composer>
132142
<ConnectionMessageContainer documentId={this.props.documentId} />
133143
</div>
134144
);

packages/app/client/src/ui/editor/emulator/parts/chat/chatContainer.ts

-1
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@
3131
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
3232
//
3333
import { connect } from 'react-redux';
34-
import { User } from '@bfemulator/sdk-shared';
3534
import { Activity } from 'botframework-schema';
3635
import {
3736
executeCommand,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
//
2+
// Copyright (c) Microsoft. All rights reserved.
3+
// Licensed under the MIT license.
4+
//
5+
// Microsoft Bot Framework: http://botframework.com
6+
//
7+
// Bot Framework Emulator Github:
8+
// https://github.com/Microsoft/BotFramwork-Emulator
9+
//
10+
// Copyright (c) Microsoft Corporation
11+
// All rights reserved.
12+
//
13+
// MIT License:
14+
// Permission is hereby granted, free of charge, to any person obtaining
15+
// a copy of this software and associated documentation files (the
16+
// "Software"), to deal in the Software without restriction, including
17+
// without limitation the rights to use, copy, modify, merge, publish,
18+
// distribute, sublicense, and/or sell copies of the Software, and to
19+
// permit persons to whom the Software is furnished to do so, subject to
20+
// the following conditions:
21+
//
22+
// The above copyright notice and this permission notice shall be
23+
// included in all copies or substantial portions of the Software.
24+
//
25+
// THE SOFTWARE IS PROVIDED ""AS IS"", WITHOUT WARRANTY OF ANY KIND,
26+
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
27+
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
28+
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
29+
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
30+
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
31+
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
32+
//
33+
34+
import { FC, useCallback } from 'react';
35+
import { hooks } from 'botframework-webchat';
36+
import { Activity } from 'botframework-schema';
37+
38+
const { useObserveTranscriptFocus } = hooks;
39+
40+
type TranscriptFocusListenerProps = { updateSelectedActivity?: (id: string) => void };
41+
export const TranscriptFocusListener: FC<TranscriptFocusListenerProps> = (props: TranscriptFocusListenerProps) => {
42+
const onActivityFocused = useCallback(({ activity }: { activity: Activity }) => {
43+
if (activity) {
44+
props.updateSelectedActivity(activity.id);
45+
}
46+
}, []);
47+
48+
useObserveTranscriptFocus && useObserveTranscriptFocus(onActivityFocused, [onActivityFocused]);
49+
50+
// strictly listen for and act on a new activity being selected -- do not render anything
51+
return null;
52+
};

packages/app/client/src/ui/editor/emulator/parts/chat/webChatTheme.ts

+2
Original file line numberDiff line numberDiff line change
@@ -64,4 +64,6 @@ export default {
6464
transcriptOverlayButtonColor: 'var(--webchat-transcript-overlay-text)',
6565
transcriptOverlayButtonColorOnFocus: 'var(--webchat-transcript-overlay-text-focus)',
6666
transcriptOverlayButtonColorOnHover: 'var(--webchat-transcript-overlay-text-focus)',
67+
68+
transcriptVisualKeyboardIndicatorColor: 'var(--webchat-transcript-visual-kb-indicator-color)',
6769
};

packages/app/client/src/ui/styles/themes/dark.css

+3
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,9 @@ html {
5555
--webchat-transcript-overlay-text: var(--neutral-1);
5656
--webchat-transcript-overlay-text-focus: var(--neutral-1);
5757

58+
/* The outer box that indicates the transcript is focused */
59+
--webchat-transcript-visual-kb-indicator-color: var(--global-focus-outline-color);
60+
5861
/* suggested actions */
5962
--webchat-sa-bg: var(--webchat-bubble-bg);
6063
--webchat-sa-border-color: var(--neutral-4);

packages/app/client/src/ui/styles/themes/high-contrast.css

+3
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,9 @@ html {
5454
--webchat-transcript-overlay-text: var(--neutral-15);
5555
--webchat-transcript-overlay-text-focus: var(--neutral-15);
5656

57+
/* The outer box that indicates the transcript is focused */
58+
--webchat-transcript-visual-kb-indicator-color: var(--global-focus-outline-color);
59+
5760
/* suggested actions */
5861
--webchat-sa-bg: var(--webchat-bubble-bg);
5962
--webchat-sa-border-color: var(--neutral-4);

packages/app/client/src/ui/styles/themes/light.css

+3
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,9 @@ html {
5353
--webchat-transcript-overlay-text: var(--neutral-1);
5454
--webchat-transcript-overlay-text-focus: var(--neutral-1);
5555

56+
/* The outer box that indicates the transcript is focused */
57+
--webchat-transcript-visual-kb-indicator-color: var(--global-focus-outline-color);
58+
5659
/* suggested actions */
5760
--webchat-sa-bg: var(--webchat-bubble-bg);
5861
--webchat-sa-border-color: transparent;

0 commit comments

Comments
 (0)