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
This commit is contained in:
Tony Anziano 2021-03-02 16:31:36 -08:00 коммит произвёл GitHub
Родитель e16706f489
Коммит 5e2ea09c7f
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
15 изменённых файлов: 1399 добавлений и 1662 удалений

2
.vscode/launch.json поставляемый
Просмотреть файл

@ -33,7 +33,7 @@
"outputCapture": "std",
"internalConsoleOptions": "openOnSessionStart",
"cwd": "${workspaceFolder}/packages/app/main",
"outFiles": [ "${workspaceRoot}/packages/app/main/app/**/*.js"]
"outFiles": [ "${workspaceRoot}/packages/app/main/app/**/*.js", "${workspaceRoot}/packages/app/shared/**/*.js"]
},
{
"type": "node",

Просмотреть файл

@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- [main] Bumped `electron` from `4.1.1` to `11.0.1` in PR [2226](https://github.com/microsoft/BotFramework-Emulator/pull/2226)
- [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)
- [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)
- [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)
## v4.11.0 - 2020 - 11 - 05
- [client] Moved from master to main as the default branch. [2194](https://github.com/microsoft/BotFramework-Emulator/pull/2194)

2927
package-lock.json сгенерированный

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -110,8 +110,8 @@
"base64url": "3.0.0",
"botframework-config": "4.4.0",
"botframework-schema": "^4.3.4",
"botframework-webchat": "4.11.0",
"botframework-webchat-core": "4.11.0",
"botframework-webchat": "4.12.0",
"botframework-webchat-core": "4.12.0",
"core-js": "^3.6.5",
"eslint-plugin-react": "^7.12.3",
"markdown-it": "^8.4.2",

Просмотреть файл

@ -567,8 +567,7 @@ describe('The ChatSagas,', () => {
// call createCognitiveServicesSpeechServicesPonyfillFactory
expect(gen.next({}).value).toEqual(
call(createCognitiveServicesSpeechServicesPonyfillFactory, {
authorizationToken: jasmine.anything(), // any(Promise) doesn't match correctly
region: 'westus',
credentials: jasmine.any(Function),
})
);
@ -769,8 +768,7 @@ describe('The ChatSagas,', () => {
const existingFactory = {};
expect(gen.next(existingFactory).value).toEqual(
call(createCognitiveServicesSpeechServicesPonyfillFactory, {
authorizationToken: jasmine.anything(), // .any(Promise) doesn't match correctly
region: 'westus',
credentials: jasmine.any(Function),
})
);
@ -904,8 +902,7 @@ describe('The ChatSagas,', () => {
const existingFactory = {};
expect(gen.next(existingFactory).value).toEqual(
call(createCognitiveServicesSpeechServicesPonyfillFactory, {
authorizationToken: jasmine.anything(), // .any(Promise) doesn't match correctly
region: 'westus',
credentials: jasmine.any(Function),
})
);

Просмотреть файл

@ -467,11 +467,13 @@ export class ChatSagas {
endpointId,
!!existingFactory
);
const factory = yield call(createCognitiveServicesSpeechServicesPonyfillFactory, {
authorizationToken: speechAuthenticationToken,
const fetchSpeechCredentials = async () => ({
authorizationToken: await speechAuthenticationToken,
region: 'westus', // Currently, the prod speech service is only deployed to westus
});
const factory = yield call(createCognitiveServicesSpeechServicesPonyfillFactory, {
credentials: fetchSpeechCredentials,
});
yield put(webSpeechFactoryUpdated(documentId, factory)); // Provide the new factory to the store
} catch (e) {
@ -651,11 +653,13 @@ export class ChatSagas {
botEndpoint.id,
!!existingFactory
);
const factory = yield call(createCognitiveServicesSpeechServicesPonyfillFactory, {
authorizationToken: speechAuthenticationToken,
const fetchSpeechCredentials = async () => ({
authorizationToken: await speechAuthenticationToken,
region: 'westus', // Currently, the prod speech service is only deployed to westus
});
const factory = yield call(createCognitiveServicesSpeechServicesPonyfillFactory, {
credentials: fetchSpeechCredentials,
});
yield put(webSpeechFactoryUpdated(documentId, factory)); // Provide the new factory to the store
} catch (e) {

Просмотреть файл

@ -38,7 +38,7 @@
background-color: white;
display: flex;
position: relative;
height: 100%;
height: calc(100% - 36px); // 36px is the height of the <header> in chatPanel.tsx
width: 100%;
user-select: text;

Просмотреть файл

@ -34,7 +34,7 @@
import * as React from 'react';
import { mount, ReactWrapper, shallow, ShallowWrapper } from 'enzyme';
import { Provider } from 'react-redux';
import ReactWebChat, { createDirectLine, createStyleSet } from 'botframework-webchat';
import { createDirectLine, createStyleSet, Components } from 'botframework-webchat';
import { ActivityTypes } from 'botframework-schema';
import {
bot,
@ -56,6 +56,8 @@ import webChatStyleOptions from './webChatTheme';
import { ChatContainer } from './chatContainer';
import { ChatProps, Chat } from './chat';
const { Composer } = Components;
jest.mock('./chat.scss', () => ({
get bubbleContentColor() {
return '#fff';
@ -160,7 +162,7 @@ describe('<ChatContainer />', () => {
wrapper.setProps({
children: <ChatContainer {...updatedProps} />,
});
expect(wrapper.find(ReactWebChat).props().disabled).toBeTruthy();
expect(wrapper.find(Composer).props().disabled).toBeTruthy();
updatedProps = {
...props,
@ -170,7 +172,7 @@ describe('<ChatContainer />', () => {
wrapper.setProps({
children: <ChatContainer {...updatedProps} />,
});
expect(wrapper.find(ReactWebChat).props().disabled).toBeFalsy();
expect(wrapper.find(Composer).props().disabled).toBeFalsy();
updatedProps = {
...props,
@ -180,7 +182,7 @@ describe('<ChatContainer />', () => {
wrapper.setProps({
children: <ChatContainer {...updatedProps} />,
});
expect(wrapper.find(ReactWebChat).props().disabled).toBeFalsy();
expect(wrapper.find(Composer).props().disabled).toBeFalsy();
updatedProps = {
...props,
@ -190,7 +192,7 @@ describe('<ChatContainer />', () => {
wrapper.setProps({
children: <ChatContainer {...updatedProps} />,
});
expect(wrapper.find(ReactWebChat).props().disabled).toBeFalsy();
expect(wrapper.find(Composer).props().disabled).toBeFalsy();
updatedProps = {
...props,
@ -200,7 +202,7 @@ describe('<ChatContainer />', () => {
wrapper.setProps({
children: <ChatContainer {...updatedProps} />,
});
expect(wrapper.find(ReactWebChat).props().disabled).toBeFalsy();
expect(wrapper.find(Composer).props().disabled).toBeFalsy();
updatedProps = {
...props,
@ -210,7 +212,7 @@ describe('<ChatContainer />', () => {
wrapper.setProps({
children: <ChatContainer {...updatedProps} />,
});
expect(wrapper.find(ReactWebChat).props().disabled).toBeTruthy();
expect(wrapper.find(Composer).props().disabled).toBeTruthy();
});
describe('when there is no direct line client', () => {
@ -223,7 +225,7 @@ describe('<ChatContainer />', () => {
describe('when there is a direct line client', () => {
it('renders the WebChat component', () => {
const webChat = wrapper.find(ReactWebChat);
const webChat = wrapper.find(Composer);
const styleSet = createStyleSet({ ...webChatStyleOptions });
styleSet.fileContent = {

Просмотреть файл

@ -33,7 +33,7 @@
import { ValueTypes, RestartConversationStatus } from '@bfemulator/app-shared';
import { Activity, ActivityTypes } from 'botframework-schema';
import ReactWebChat, { createStyleSet } from 'botframework-webchat';
import { createStyleSet, Components } from 'botframework-webchat';
import * as React from 'react';
import { PureComponent, KeyboardEvent, MouseEvent, ReactNode } from 'react';
import { EmulatorMode } from '@bfemulator/sdk-shared';
@ -44,6 +44,9 @@ import * as styles from './chat.scss';
import webChatStyleOptions from './webChatTheme';
import { TraceActivityContainer } from './traceActivityContainer';
import { ConnectionMessageContainer } from './connectionMessageContainer';
import { TranscriptFocusListener } from './transcriptFocusListener';
const { BasicWebChat, Composer } = Components;
export interface ChatProps {
botId?: string;
@ -65,9 +68,11 @@ interface ChatState {
highlightedActivities?: Activity[];
}
type ActivityMap = Record<string, Activity>;
export class Chat extends PureComponent<ChatProps, ChatState> {
public state = { waitForSpeechToken: false } as ChatState;
private activityMap: { [activityId: string]: Activity } = {};
private activityMap: ActivityMap = {};
public render() {
const {
@ -113,9 +118,11 @@ export class Chat extends PureComponent<ChatProps, ChatState> {
name: 'Bot',
};
const boundUpdateSelectedActivity = this.updateSelectedActivity.bind(this);
return (
<div className={styles.chat}>
<ReactWebChat
<Composer
store={webchatStore}
activityMiddleware={this.createActivityMiddleware}
cardActionMiddleware={this.cardActionMiddleware}
@ -128,7 +135,10 @@ export class Chat extends PureComponent<ChatProps, ChatState> {
userID={currentUser.id}
username={currentUser.name || 'User'}
webSpeechPonyfillFactory={webSpeechPonyfillFactory}
/>
>
<BasicWebChat />
<TranscriptFocusListener updateSelectedActivity={boundUpdateSelectedActivity} />
</Composer>
<ConnectionMessageContainer documentId={this.props.documentId} />
</div>
);

Просмотреть файл

@ -31,7 +31,6 @@
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
import { connect } from 'react-redux';
import { User } from '@bfemulator/sdk-shared';
import { Activity } from 'botframework-schema';
import {
executeCommand,

Просмотреть файл

@ -0,0 +1,52 @@
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license.
//
// Microsoft Bot Framework: http://botframework.com
//
// Bot Framework Emulator Github:
// https://github.com/Microsoft/BotFramwork-Emulator
//
// Copyright (c) Microsoft Corporation
// All rights reserved.
//
// MIT License:
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED ""AS IS"", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
import { FC, useCallback } from 'react';
import { hooks } from 'botframework-webchat';
import { Activity } from 'botframework-schema';
const { useObserveTranscriptFocus } = hooks;
type TranscriptFocusListenerProps = { updateSelectedActivity?: (id: string) => void };
export const TranscriptFocusListener: FC<TranscriptFocusListenerProps> = (props: TranscriptFocusListenerProps) => {
const onActivityFocused = useCallback(({ activity }: { activity: Activity }) => {
if (activity) {
props.updateSelectedActivity(activity.id);
}
}, []);
useObserveTranscriptFocus && useObserveTranscriptFocus(onActivityFocused, [onActivityFocused]);
// strictly listen for and act on a new activity being selected -- do not render anything
return null;
};

Просмотреть файл

@ -64,4 +64,6 @@ export default {
transcriptOverlayButtonColor: 'var(--webchat-transcript-overlay-text)',
transcriptOverlayButtonColorOnFocus: 'var(--webchat-transcript-overlay-text-focus)',
transcriptOverlayButtonColorOnHover: 'var(--webchat-transcript-overlay-text-focus)',
transcriptVisualKeyboardIndicatorColor: 'var(--webchat-transcript-visual-kb-indicator-color)',
};

Просмотреть файл

@ -55,6 +55,9 @@ html {
--webchat-transcript-overlay-text: var(--neutral-1);
--webchat-transcript-overlay-text-focus: var(--neutral-1);
/* The outer box that indicates the transcript is focused */
--webchat-transcript-visual-kb-indicator-color: var(--global-focus-outline-color);
/* suggested actions */
--webchat-sa-bg: var(--webchat-bubble-bg);
--webchat-sa-border-color: var(--neutral-4);

Просмотреть файл

@ -54,6 +54,9 @@ html {
--webchat-transcript-overlay-text: var(--neutral-15);
--webchat-transcript-overlay-text-focus: var(--neutral-15);
/* The outer box that indicates the transcript is focused */
--webchat-transcript-visual-kb-indicator-color: var(--global-focus-outline-color);
/* suggested actions */
--webchat-sa-bg: var(--webchat-bubble-bg);
--webchat-sa-border-color: var(--neutral-4);

Просмотреть файл

@ -53,6 +53,9 @@ html {
--webchat-transcript-overlay-text: var(--neutral-1);
--webchat-transcript-overlay-text-focus: var(--neutral-1);
/* The outer box that indicates the transcript is focused */
--webchat-transcript-visual-kb-indicator-color: var(--global-focus-outline-color);
/* suggested actions */
--webchat-sa-bg: var(--webchat-bubble-bg);
--webchat-sa-border-color: transparent;