From 904c8d9b76c4b525a74e29c26a22a6779e73e1ef Mon Sep 17 00:00:00 2001 From: William Wong Date: Tue, 17 Dec 2019 20:38:26 -0800 Subject: [PATCH 1/3] Move to functional components --- .../package.json | 2 +- .../src/App.js | 20 +- .../src/MinimizableWebChat.css | 4 + .../src/MinimizableWebChat.js | 182 +++++++++--------- .../src/WebChat.js | 63 +++--- 5 files changed, 127 insertions(+), 144 deletions(-) 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..d3dd09410d 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,111 +7,106 @@ 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(() => { - dispatch({ - type: 'WEB_CHAT/SEND_EVENT', - payload: { - name: 'webchat/join', - value: { - language: window.navigator.language +const MinimizableWebChat = () => { + const store = useMemo( + () => + createStore({}, ({ dispatch }) => next => action => { + if (action.type === 'DIRECT_LINE/CONNECT_FULFILLED') { + setTimeout(() => { + dispatch({ + type: 'WEB_CHAT/SEND_EVENT', + payload: { + name: 'webchat/join', + value: { + language: window.navigator.language + } } - } - }); - }, 1000); - } else if (action.type === 'DIRECT_LINE/INCOMING_ACTIVITY') { - if (action.payload.activity.from.role === 'bot') { - this.setState(() => ({ newMessage: true })); + }); + }, 1000); + } 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; From 975c74fb80649b1e445c3246e190a93fd97ab904 Mon Sep 17 00:00:00 2001 From: William Wong Date: Tue, 17 Dec 2019 20:47:49 -0800 Subject: [PATCH 2/3] Add entry --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5889eae737..cb8a8f7c01 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) ### Changed From 10bab2a4f2af281083c8ac45097a985b6d1b2e0a Mon Sep 17 00:00:00 2001 From: William Wong Date: Wed, 18 Dec 2019 13:18:39 -0800 Subject: [PATCH 3/3] Remove timeout --- .../src/MinimizableWebChat.js | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/samples/12.customization-minimizable-web-chat/src/MinimizableWebChat.js b/samples/12.customization-minimizable-web-chat/src/MinimizableWebChat.js index d3dd09410d..31c5a6c225 100644 --- a/samples/12.customization-minimizable-web-chat/src/MinimizableWebChat.js +++ b/samples/12.customization-minimizable-web-chat/src/MinimizableWebChat.js @@ -12,17 +12,15 @@ const MinimizableWebChat = () => { () => createStore({}, ({ dispatch }) => next => action => { if (action.type === 'DIRECT_LINE/CONNECT_FULFILLED') { - setTimeout(() => { - dispatch({ - type: 'WEB_CHAT/SEND_EVENT', - payload: { - name: 'webchat/join', - value: { - language: window.navigator.language - } + dispatch({ + type: 'WEB_CHAT/SEND_EVENT', + payload: { + name: 'webchat/join', + value: { + language: window.navigator.language } - }); - }, 1000); + } + }); } else if (action.type === 'DIRECT_LINE/INCOMING_ACTIVITY') { if (action.payload.activity.from.role === 'bot') { setNewMessage(true);