diff --git a/CHANGELOG.md b/CHANGELOG.md index 54534608e3..916c969ca3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,6 +27,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ### Fixed - Fixes [#2611](https://github.com/microsoft/BotFramework-WebChat/issues/2611). Fix sample 21: hooks errors, by [@corinagum](https://github.com/corinagum) in PR [#2740](https://github.com/microsoft/BotFramework-WebChat/pull/2740) +- Fixes [#2609](https://github.com/microsoft/BotFramework-WebChat/issues/2609). Fix sample 12: minimizable button is causing another reconnect on restore, by [@compulim](https://github.com/compulim) in PR [#2758](https://github.com/microsoft/BotFramework-WebChat/pull/2758) - Fixes [#2773](https://github.com/microsoft/BotFramework-WebChat/issues/2773). Import ES5 version of the following bundles, by [@compulim](https://github.com/compulim) in PR [#2774](https://github.com/microsoft/BotFramework-WebChat/pull/2773) - [`abort-controller`](https://npmjs.com/package/abort-controller) - [`event-target-shim`](https://npmjs.com/package/event-target-shim) diff --git a/samples/12.customization-minimizable-web-chat/package.json b/samples/12.customization-minimizable-web-chat/package.json index 085f7b34d9..9ee72b3b3c 100644 --- a/samples/12.customization-minimizable-web-chat/package.json +++ b/samples/12.customization-minimizable-web-chat/package.json @@ -5,7 +5,7 @@ "homepage": "https://microsoft.github.io/BotFramework-WebChat/12.customization-minimizable-web-chat/", "dependencies": { "botframework-webchat": "^4.7.1", - "memoize-one": "^5.0.2", + "classnames": "^2.2.6", "react": "16.12.0", "react-dom": "16.12.0", "react-scripts": "^3.3.0" diff --git a/samples/12.customization-minimizable-web-chat/src/App.js b/samples/12.customization-minimizable-web-chat/src/App.js index 188418913f..7e9318f4a1 100644 --- a/samples/12.customization-minimizable-web-chat/src/App.js +++ b/samples/12.customization-minimizable-web-chat/src/App.js @@ -1,18 +1,14 @@ -import React, { Component } from 'react'; -import MinimizableWebChat from './MinimizableWebChat'; +import React from 'react'; +import MinimizableWebChat from './MinimizableWebChat'; import WebPageBackground from './WebPage.jpg'; import './App.css'; -class App extends Component { - render() { - return ( -
- product background - -
- ); - } -} +const App = () => ( +
+ product background + +
+); export default App; diff --git a/samples/12.customization-minimizable-web-chat/src/MinimizableWebChat.css b/samples/12.customization-minimizable-web-chat/src/MinimizableWebChat.css index 4f8d74abc6..e5778b2bef 100644 --- a/samples/12.customization-minimizable-web-chat/src/MinimizableWebChat.css +++ b/samples/12.customization-minimizable-web-chat/src/MinimizableWebChat.css @@ -49,6 +49,10 @@ width: 30%; } +.minimizable-web-chat > .chat-box.hide { + display: none; +} + .minimizable-web-chat > .chat-box.left { left: 20px; } diff --git a/samples/12.customization-minimizable-web-chat/src/MinimizableWebChat.js b/samples/12.customization-minimizable-web-chat/src/MinimizableWebChat.js index 332daf3e89..31c5a6c225 100644 --- a/samples/12.customization-minimizable-web-chat/src/MinimizableWebChat.js +++ b/samples/12.customization-minimizable-web-chat/src/MinimizableWebChat.js @@ -1,4 +1,5 @@ -import React from 'react'; +import classNames from 'classnames'; +import React, { useCallback, useMemo, useState } from 'react'; import { createStore, createStyleSet } from 'botframework-webchat'; import WebChat from './WebChat'; @@ -6,18 +7,11 @@ import WebChat from './WebChat'; import './fabric-icons-inline.css'; import './MinimizableWebChat.css'; -export default class extends React.Component { - constructor(props) { - super(props); - - this.handleFetchToken = this.handleFetchToken.bind(this); - this.handleMaximizeButtonClick = this.handleMaximizeButtonClick.bind(this); - this.handleMinimizeButtonClick = this.handleMinimizeButtonClick.bind(this); - this.handleSwitchButtonClick = this.handleSwitchButtonClick.bind(this); - - const store = createStore({}, ({ dispatch }) => next => action => { - if (action.type === 'DIRECT_LINE/CONNECT_FULFILLED') { - setTimeout(() => { +const MinimizableWebChat = () => { + const store = useMemo( + () => + createStore({}, ({ dispatch }) => next => action => { + if (action.type === 'DIRECT_LINE/CONNECT_FULFILLED') { dispatch({ type: 'WEB_CHAT/SEND_EVENT', payload: { @@ -27,90 +21,90 @@ export default class extends React.Component { } } }); - }, 1000); - } else if (action.type === 'DIRECT_LINE/INCOMING_ACTIVITY') { - if (action.payload.activity.from.role === 'bot') { - this.setState(() => ({ newMessage: true })); + } else if (action.type === 'DIRECT_LINE/INCOMING_ACTIVITY') { + if (action.payload.activity.from.role === 'bot') { + setNewMessage(true); + } } - } - return next(action); - }); + return next(action); + }), + [] + ); - this.state = { - minimized: true, - newMessage: false, - side: 'right', - store, - styleSet: createStyleSet({ + const styleSet = useMemo( + () => + createStyleSet({ backgroundColor: 'Transparent' }), - token: null - }; - } + [] + ); - async handleFetchToken() { - if (!this.state.token) { + const [loaded, setLoaded] = useState(false); + const [minimized, setMinimized] = useState(true); + const [newMessage, setNewMessage] = useState(false); + const [side, setSide] = useState('right'); + const [token, setToken] = useState(); + + const handleFetchToken = useCallback(async () => { + if (!token) { const res = await fetch('https://webchat-mockbot.azurewebsites.net/directline/token', { method: 'POST' }); const { token } = await res.json(); - this.setState(() => ({ token })); + setToken(token); } - } + }, [setToken, token]); + + const handleMaximizeButtonClick = useCallback(async () => { + setLoaded(true); + setMinimized(false); + setNewMessage(false); + }, [setMinimized, setNewMessage]); - handleMaximizeButtonClick() { - this.setState(() => ({ - minimized: false, - newMessage: false - })); - } + const handleMinimizeButtonClick = useCallback(() => { + setMinimized(true); + setNewMessage(false); + }, [setMinimized, setNewMessage]); - handleMinimizeButtonClick() { - this.setState(() => ({ - minimized: true, - newMessage: false - })); - } + const handleSwitchButtonClick = useCallback(() => { + setSide(side === 'left' ? 'right' : 'left'); + }, [setSide, side]); - handleSwitchButtonClick() { - this.setState(({ side }) => ({ - side: side === 'left' ? 'right' : 'left' - })); - } + // TODO: [P2] Currently, we cannot unmount Web Chat from DOM when it is minimized. + // Today, if we unmount it, Web Chat will call disconnect on DirectLineJS object. + // When minimized, we still want to maintain that connection while the UI is gone. + // This is related to https://github.com/microsoft/BotFramework-WebChat/issues/2750. - render() { - const { - state: { minimized, newMessage, side, store, styleSet, token } - } = this; + return ( +
+ {minimized && ( + + )} + {loaded && ( +
+
+
+ + +
+ +
+ )} +
+ ); +}; - return ( -
- {minimized ? ( - - ) : ( -
-
-
- - -
- -
- )} -
- ); - } -} +export default MinimizableWebChat; diff --git a/samples/12.customization-minimizable-web-chat/src/WebChat.js b/samples/12.customization-minimizable-web-chat/src/WebChat.js index 90d39229d3..85c8834930 100644 --- a/samples/12.customization-minimizable-web-chat/src/WebChat.js +++ b/samples/12.customization-minimizable-web-chat/src/WebChat.js @@ -1,48 +1,35 @@ -import memoize from 'memoize-one'; -import React from 'react'; +import React, { useEffect, useMemo } from 'react'; import ReactWebChat, { createDirectLine, createStyleSet } from 'botframework-webchat'; import './WebChat.css'; -export default class extends React.Component { - constructor(props) { - super(props); +const WebChat = ({ className, onFetchToken, store, token }) => { + const directLine = useMemo(() => createDirectLine({ token }), [token]); - this.createDirectLine = memoize(token => createDirectLine({ token })); - - this.state = { - styleSet: createStyleSet({ + const styleSet = useMemo( + () => + createStyleSet({ backgroundColor: 'Transparent' - }) - }; - } - - componentDidMount() { - !this.props.token && this.props.onFetchToken(); - } + }), + [] + ); - render() { - const { - props: { className, store, token }, - state: { styleSet } - } = this; + useEffect(() => { + onFetchToken(); + }, [onFetchToken]); - return token ? ( - - ) : ( -
-
-
- -
-

Please wait while we are connecting.

+ return token ? ( + + ) : ( +
+
+
+
+

Please wait while we are connecting.

- ); - } -} +
+ ); +}; + +export default WebChat;