From 482ef013f583d6d79cf3755966223489e4008f43 Mon Sep 17 00:00:00 2001 From: Tudor Stanciu Date: Sun, 7 Jun 2020 01:34:52 +0300 Subject: [PATCH 1/4] initialized bot session --- src/features/chatbot/actionCreators.js | 62 +++++++++++++++++++ src/features/chatbot/actionTypes.js | 6 ++ src/features/chatbot/api.js | 21 +++++++ src/features/chatbot/botType.js | 4 -- .../chatbot/components/BotsManager.js | 12 ++-- src/features/chatbot/components/Wizard.js | 3 +- src/features/chatbot/constants.js | 12 ++++ src/features/chatbot/reducer.js | 20 +++++- src/redux/reducers/initialState.js | 4 +- webpack.config.dev.js | 3 +- webpack.config.prod.js | 9 +-- 11 files changed, 140 insertions(+), 16 deletions(-) create mode 100644 src/features/chatbot/api.js delete mode 100644 src/features/chatbot/botType.js create mode 100644 src/features/chatbot/constants.js diff --git a/src/features/chatbot/actionCreators.js b/src/features/chatbot/actionCreators.js index 4384283..6d20314 100644 --- a/src/features/chatbot/actionCreators.js +++ b/src/features/chatbot/actionCreators.js @@ -1,4 +1,6 @@ import * as types from "./actionTypes"; +import api from "./api"; +import { sendHttpRequest } from "../../redux/actions/httpActions"; export function summonWizard() { return { type: types.SUMMON_WIZARD }; @@ -7,3 +9,63 @@ export function summonWizard() { export function dismissBot() { return { type: types.DISMISS_BOT }; } + +export function loadBotSession(botName, userKey) { + return async function(dispatch, getState) { + try { + dispatch({ type: types.INITIALIZE_BOT_SESSION_STARTED }); + const { frontendSession } = getState(); + const externalId = frontendSession.sessionId; + const clientApplication = frontendSession.applicationCode; + const data = await dispatch( + sendHttpRequest( + api.getBotSession(botName, externalId, clientApplication, userKey) + ) + ); + dispatch({ + type: types.INITIALIZE_BOT_SESSION_SUCCESS, + payload: data + }); + } catch (error) { + throw error; + } + }; +} + +export function initializeChat(sessionId) { + return async function(dispatch) { + try { + dispatch({ type: types.INITIALIZE_BOT_CHAT_STARTED }); + const data = await dispatch( + sendHttpRequest(api.initializeChat(sessionId)) + ); + dispatch({ + type: types.INITIALIZE_BOT_CHAT_SUCCESS, + payload: data + }); + } catch (error) { + throw error; + } + }; +} + +export function saveMessage( + chatId, + messageSourceId, + messageDate, + messageContent +) { + return async function(dispatch, getState) { + try { + const event = await dispatch( + sendHttpRequest( + api.saveMessage(chatId, messageSourceId, messageDate, messageContent) + ) + ); + dispatch({ type: types.SAVE_BOT_MESSAGE_SUCCESS, payload: event }); + return event; + } catch (error) { + throw error; + } + }; +} diff --git a/src/features/chatbot/actionTypes.js b/src/features/chatbot/actionTypes.js index 4ba513d..b81c657 100644 --- a/src/features/chatbot/actionTypes.js +++ b/src/features/chatbot/actionTypes.js @@ -1,2 +1,8 @@ export const DISMISS_BOT = "DISMISS_BOT"; export const SUMMON_WIZARD = "SUMMON_WIZARD"; + +export const INITIALIZE_BOT_SESSION_STARTED = "INITIALIZE_BOT_SESSION_STARTED"; +export const INITIALIZE_BOT_SESSION_SUCCESS = "INITIALIZE_BOT_SESSION_SUCCESS"; +export const INITIALIZE_BOT_CHAT_STARTED = "INITIALIZE_BOT_CHAT_STARTED"; +export const INITIALIZE_BOT_CHAT_SUCCESS = "INITIALIZE_BOT_CHAT_SUCCESS"; +export const SAVE_BOT_MESSAGE_SUCCESS = "SAVE_BOT_MESSAGE_SUCCESS"; diff --git a/src/features/chatbot/api.js b/src/features/chatbot/api.js new file mode 100644 index 0000000..c15fa27 --- /dev/null +++ b/src/features/chatbot/api.js @@ -0,0 +1,21 @@ +import { get, post } from "../../api/axiosApi"; +const baseUrl = process.env.CHATBOT_API_URL; + +const getBotSession = (botName, externalId, clientApplication, userKey) => + get( + `${baseUrl}/system/initialize-session/${botName}/${externalId}/${clientApplication}/${userKey}` + ); +const initializeChat = () => get(`${baseUrl}/chat/initialize`); +const saveMessage = (chatId, messageSourceId, messageDate, messageContent) => + post(`${baseUrl}/chat/message`, { + chatId, + messageSourceId, + messageDate, + messageContent + }); + +export default { + getBotSession, + initializeChat, + saveMessage +}; diff --git a/src/features/chatbot/botType.js b/src/features/chatbot/botType.js deleted file mode 100644 index 3512fa9..0000000 --- a/src/features/chatbot/botType.js +++ /dev/null @@ -1,4 +0,0 @@ -export const botType = { - none: Symbol("none"), - wizard: Symbol("wizard") -}; diff --git a/src/features/chatbot/components/BotsManager.js b/src/features/chatbot/components/BotsManager.js index 64e9ef9..2e284c9 100644 --- a/src/features/chatbot/components/BotsManager.js +++ b/src/features/chatbot/components/BotsManager.js @@ -2,10 +2,10 @@ import React, { useEffect, useState } from "react"; import PropTypes from "prop-types"; import { connect } from "react-redux"; import { bindActionCreators } from "redux"; -import { botType } from "../botType"; +import { botType, bots, userKey } from "../constants"; import Wizard from "./Wizard"; import { makeStyles } from "@material-ui/core/styles"; -import { dismissBot } from "../actionCreators"; +import { dismissBot, loadBotSession } from "../actionCreators"; const useStyles = makeStyles(theme => ({ bot: { @@ -24,7 +24,11 @@ const BotsManager = ({ bot, actions }) => { const classes = useStyles(); useEffect(() => { - if (bot.type) setType(bot.type); + if (!bot.type) return; + setType(bot.type); + + if (bot.type == botType.none) return; + actions.loadBotSession(bots.Zirhan, userKey.unknown); }, [bot.type]); return ( @@ -49,7 +53,7 @@ function mapStateToProps(state) { function mapDispatchToProps(dispatch) { return { - actions: bindActionCreators({ dismissBot }, dispatch) + actions: bindActionCreators({ dismissBot, loadBotSession }, dispatch) }; } diff --git a/src/features/chatbot/components/Wizard.js b/src/features/chatbot/components/Wizard.js index aa80c25..d2bd761 100644 --- a/src/features/chatbot/components/Wizard.js +++ b/src/features/chatbot/components/Wizard.js @@ -4,6 +4,7 @@ import ChatBot from "react-simple-chatbot"; import { ThemeProvider } from "styled-components"; import { useTheme } from "@material-ui/core/styles"; import { useTranslation } from "react-i18next"; +import { bots } from "../constants"; const Wizard = ({ dismissBot }) => { const theme = useTheme(); @@ -88,7 +89,7 @@ const Wizard = ({ dismissBot }) => { handleEnd={handleEnd} steps={steps} botAvatar={getAvatar()} - headerTitle="Zirhan" + headerTitle={bots.Zirhan} /> ); diff --git a/src/features/chatbot/constants.js b/src/features/chatbot/constants.js new file mode 100644 index 0000000..ceb8cce --- /dev/null +++ b/src/features/chatbot/constants.js @@ -0,0 +1,12 @@ +export const botType = { + none: Symbol("none"), + wizard: Symbol("wizard") +}; + +export const bots = { + Zirhan: "Zirhan" +}; + +export const userKey = { + unknown: "Unknown" +}; diff --git a/src/features/chatbot/reducer.js b/src/features/chatbot/reducer.js index b8220c9..a697426 100644 --- a/src/features/chatbot/reducer.js +++ b/src/features/chatbot/reducer.js @@ -1,6 +1,6 @@ import * as types from "./actionTypes"; import initialState from "../../redux/reducers/initialState"; -import { botType } from "./botType"; +import { botType } from "./constants"; export default function chatbotReducer(state = initialState.bot, action) { switch (action.type) { @@ -10,6 +10,24 @@ export default function chatbotReducer(state = initialState.bot, action) { case types.DISMISS_BOT: return { ...state, type: botType.none }; + case types.INITIALIZE_BOT_SESSION_STARTED: + return { ...state, session: { loading: true, loaded: false } }; + + case types.INITIALIZE_BOT_SESSION_SUCCESS: + return { + ...state, + session: { loading: false, loaded: true, ...action.payload } + }; + + case types.INITIALIZE_BOT_CHAT_STARTED: + return { ...state, chat: { loading: true, loaded: false } }; + + case types.INITIALIZE_BOT_CHAT_SUCCESS: + return { + ...state, + chat: { loading: false, loaded: true, ...action.payload } + }; + default: return state; } diff --git a/src/redux/reducers/initialState.js b/src/redux/reducers/initialState.js index 026f328..90d82a9 100644 --- a/src/redux/reducers/initialState.js +++ b/src/redux/reducers/initialState.js @@ -19,7 +19,9 @@ export default { type: null }, bot: { - type: null + type: null, + session: { loading: false, loaded: false }, + chat: { loading: false, loaded: false } }, ajaxCallsInProgress: 0 }; diff --git a/webpack.config.dev.js b/webpack.config.dev.js index 7b03efd..c5e0b88 100644 --- a/webpack.config.dev.js +++ b/webpack.config.dev.js @@ -26,7 +26,8 @@ module.exports = { new webpack.DefinePlugin({ "process.env.REVERSE_PROXY_API_URL": JSON.stringify( "http://localhost:5050" - ) + ), + "process.env.CHATBOT_API_URL": JSON.stringify("http://localhost:5061") }), new HtmlWebpackPlugin({ template: "src/index.html", diff --git a/webpack.config.prod.js b/webpack.config.prod.js index 1c205a6..0275ec0 100644 --- a/webpack.config.prod.js +++ b/webpack.config.prod.js @@ -3,7 +3,7 @@ const path = require("path"); const HtmlWebpackPlugin = require("html-webpack-plugin"); const MiniCssExtractPlugin = require("mini-css-extract-plugin"); const webpackBundleAnalyzer = require("webpack-bundle-analyzer"); -const CopyPlugin = require('copy-webpack-plugin'); +const CopyPlugin = require("copy-webpack-plugin"); process.env.NODE_ENV = "production"; process.env.PUBLIC_URL = "/reverse-proxy"; @@ -32,6 +32,9 @@ module.exports = { "process.env.PUBLIC_URL": JSON.stringify(process.env.PUBLIC_URL), "process.env.REVERSE_PROXY_API_URL": JSON.stringify( "https://toodle.ddns.net/reverse-proxy-api" + ), + "process.env.CHATBOT_API_URL": JSON.stringify( + "https://toodle.ddns.net/chatbot-api" ) }), new HtmlWebpackPlugin({ @@ -52,9 +55,7 @@ module.exports = { } }), new CopyPlugin({ - patterns: [ - { from: 'public', to: 'public' } - ], + patterns: [{ from: "public", to: "public" }] }) ], module: { From ba4f0cb72c509a77d4d75ba5066374adc3a51566 Mon Sep 17 00:00:00 2001 From: Tudor Stanciu Date: Sun, 7 Jun 2020 01:49:15 +0300 Subject: [PATCH 2/4] bot session initialization update --- src/features/chatbot/actionCreators.js | 16 ++++++++++------ src/features/chatbot/components/BotsManager.js | 2 +- src/features/chatbot/constants.js | 4 ++-- src/features/chatbot/reducer.js | 17 +++++++++++++++-- src/redux/reducers/initialState.js | 2 +- 5 files changed, 29 insertions(+), 12 deletions(-) diff --git a/src/features/chatbot/actionCreators.js b/src/features/chatbot/actionCreators.js index 6d20314..0c263ef 100644 --- a/src/features/chatbot/actionCreators.js +++ b/src/features/chatbot/actionCreators.js @@ -10,13 +10,16 @@ export function dismissBot() { return { type: types.DISMISS_BOT }; } -export function loadBotSession(botName, userKey) { +export function loadBotSession(botName, userKey, botType) { return async function(dispatch, getState) { try { - dispatch({ type: types.INITIALIZE_BOT_SESSION_STARTED }); - const { frontendSession } = getState(); - const externalId = frontendSession.sessionId; - const clientApplication = frontendSession.applicationCode; + const state = getState(); + const session = state.bot.session[botType]; + if (session && (session.loading || session.loaded)) return; + + dispatch({ type: types.INITIALIZE_BOT_SESSION_STARTED, botType }); + const externalId = state.frontendSession.sessionId; + const clientApplication = state.frontendSession.applicationCode; const data = await dispatch( sendHttpRequest( api.getBotSession(botName, externalId, clientApplication, userKey) @@ -24,7 +27,8 @@ export function loadBotSession(botName, userKey) { ); dispatch({ type: types.INITIALIZE_BOT_SESSION_SUCCESS, - payload: data + payload: data, + botType }); } catch (error) { throw error; diff --git a/src/features/chatbot/components/BotsManager.js b/src/features/chatbot/components/BotsManager.js index 2e284c9..f73edf3 100644 --- a/src/features/chatbot/components/BotsManager.js +++ b/src/features/chatbot/components/BotsManager.js @@ -28,7 +28,7 @@ const BotsManager = ({ bot, actions }) => { setType(bot.type); if (bot.type == botType.none) return; - actions.loadBotSession(bots.Zirhan, userKey.unknown); + actions.loadBotSession(bots.Zirhan, userKey.unknown, bot.type); }, [bot.type]); return ( diff --git a/src/features/chatbot/constants.js b/src/features/chatbot/constants.js index ceb8cce..5b9a0b9 100644 --- a/src/features/chatbot/constants.js +++ b/src/features/chatbot/constants.js @@ -1,6 +1,6 @@ export const botType = { - none: Symbol("none"), - wizard: Symbol("wizard") + none: "none", + wizard: "wizard" }; export const bots = { diff --git a/src/features/chatbot/reducer.js b/src/features/chatbot/reducer.js index a697426..01e0690 100644 --- a/src/features/chatbot/reducer.js +++ b/src/features/chatbot/reducer.js @@ -11,12 +11,25 @@ export default function chatbotReducer(state = initialState.bot, action) { return { ...state, type: botType.none }; case types.INITIALIZE_BOT_SESSION_STARTED: - return { ...state, session: { loading: true, loaded: false } }; + return { + ...state, + session: { + ...state.session, + [action.botType]: { loading: true, loaded: false } + } + }; case types.INITIALIZE_BOT_SESSION_SUCCESS: return { ...state, - session: { loading: false, loaded: true, ...action.payload } + session: { + ...state.session, + [action.botType]: { + loading: false, + loaded: true, + ...action.payload + } + } }; case types.INITIALIZE_BOT_CHAT_STARTED: diff --git a/src/redux/reducers/initialState.js b/src/redux/reducers/initialState.js index 90d82a9..4733314 100644 --- a/src/redux/reducers/initialState.js +++ b/src/redux/reducers/initialState.js @@ -20,7 +20,7 @@ export default { }, bot: { type: null, - session: { loading: false, loaded: false }, + session: {}, chat: { loading: false, loaded: false } }, ajaxCallsInProgress: 0 From 3160cf9bd080b12a3d495a4c01adeefe3588ef89 Mon Sep 17 00:00:00 2001 From: Tudor Stanciu Date: Sun, 7 Jun 2020 02:38:23 +0300 Subject: [PATCH 3/4] closeChat --- src/features/chatbot/actionCreators.js | 26 +++++++++++++---- src/features/chatbot/actionTypes.js | 1 + src/features/chatbot/api.js | 8 +++++- .../chatbot/components/BotsManager.js | 28 ++++++++++++++++--- src/features/chatbot/components/Wizard.js | 5 ++-- src/features/chatbot/constants.js | 5 ++++ src/features/chatbot/reducer.js | 3 ++ 7 files changed, 63 insertions(+), 13 deletions(-) diff --git a/src/features/chatbot/actionCreators.js b/src/features/chatbot/actionCreators.js index 0c263ef..82b9eb6 100644 --- a/src/features/chatbot/actionCreators.js +++ b/src/features/chatbot/actionCreators.js @@ -30,6 +30,7 @@ export function loadBotSession(botName, userKey, botType) { payload: data, botType }); + return data; } catch (error) { throw error; } @@ -53,14 +54,27 @@ export function initializeChat(sessionId) { }; } -export function saveMessage( - chatId, - messageSourceId, - messageDate, - messageContent -) { +export function closeChat() { return async function(dispatch, getState) { try { + const { chatId } = getState().bot.chat; + if (!chatId) return; + + const data = await dispatch(sendHttpRequest(api.closeChat(chatId))); + dispatch({ + type: types.CLOSE_BOT_CHAT_SUCCESS, + payload: data + }); + } catch (error) { + throw error; + } + }; +} + +export function saveMessage(messageSourceId, messageDate, messageContent) { + return async function(dispatch, getState) { + try { + const { chatId } = getState().bot.chat; const event = await dispatch( sendHttpRequest( api.saveMessage(chatId, messageSourceId, messageDate, messageContent) diff --git a/src/features/chatbot/actionTypes.js b/src/features/chatbot/actionTypes.js index b81c657..c2b8b1b 100644 --- a/src/features/chatbot/actionTypes.js +++ b/src/features/chatbot/actionTypes.js @@ -6,3 +6,4 @@ export const INITIALIZE_BOT_SESSION_SUCCESS = "INITIALIZE_BOT_SESSION_SUCCESS"; export const INITIALIZE_BOT_CHAT_STARTED = "INITIALIZE_BOT_CHAT_STARTED"; export const INITIALIZE_BOT_CHAT_SUCCESS = "INITIALIZE_BOT_CHAT_SUCCESS"; export const SAVE_BOT_MESSAGE_SUCCESS = "SAVE_BOT_MESSAGE_SUCCESS"; +export const CLOSE_BOT_CHAT_SUCCESS = "CLOSE_BOT_CHAT_SUCCESS"; diff --git a/src/features/chatbot/api.js b/src/features/chatbot/api.js index c15fa27..482a32f 100644 --- a/src/features/chatbot/api.js +++ b/src/features/chatbot/api.js @@ -5,7 +5,12 @@ const getBotSession = (botName, externalId, clientApplication, userKey) => get( `${baseUrl}/system/initialize-session/${botName}/${externalId}/${clientApplication}/${userKey}` ); -const initializeChat = () => get(`${baseUrl}/chat/initialize`); +const initializeChat = sessionId => + get(`${baseUrl}/chat/initialize/${sessionId}`); +const closeChat = chatId => + post(`${baseUrl}/chat/close`, { + chatId + }); const saveMessage = (chatId, messageSourceId, messageDate, messageContent) => post(`${baseUrl}/chat/message`, { chatId, @@ -17,5 +22,6 @@ const saveMessage = (chatId, messageSourceId, messageDate, messageContent) => export default { getBotSession, initializeChat, + closeChat, saveMessage }; diff --git a/src/features/chatbot/components/BotsManager.js b/src/features/chatbot/components/BotsManager.js index f73edf3..4eb1160 100644 --- a/src/features/chatbot/components/BotsManager.js +++ b/src/features/chatbot/components/BotsManager.js @@ -5,7 +5,13 @@ import { bindActionCreators } from "redux"; import { botType, bots, userKey } from "../constants"; import Wizard from "./Wizard"; import { makeStyles } from "@material-ui/core/styles"; -import { dismissBot, loadBotSession } from "../actionCreators"; +import { + dismissBot, + loadBotSession, + initializeChat, + closeChat, + saveMessage +} from "../actionCreators"; const useStyles = makeStyles(theme => ({ bot: { @@ -28,13 +34,24 @@ const BotsManager = ({ bot, actions }) => { setType(bot.type); if (bot.type == botType.none) return; - actions.loadBotSession(bots.Zirhan, userKey.unknown, bot.type); + actions + .loadBotSession(bots.Zirhan, userKey.unknown, bot.type) + .then(session => { + actions.initializeChat(session.sessionId); + }); }, [bot.type]); + const dismissBot = () => { + actions.closeChat(); + actions.dismissBot(); + }; + return (
- {type === botType.wizard && } + {type === botType.wizard && ( + + )}
); @@ -53,7 +70,10 @@ function mapStateToProps(state) { function mapDispatchToProps(dispatch) { return { - actions: bindActionCreators({ dismissBot, loadBotSession }, dispatch) + actions: bindActionCreators( + { dismissBot, loadBotSession, initializeChat, closeChat, saveMessage }, + dispatch + ) }; } diff --git a/src/features/chatbot/components/Wizard.js b/src/features/chatbot/components/Wizard.js index d2bd761..f4956f6 100644 --- a/src/features/chatbot/components/Wizard.js +++ b/src/features/chatbot/components/Wizard.js @@ -6,7 +6,7 @@ import { useTheme } from "@material-ui/core/styles"; import { useTranslation } from "react-i18next"; import { bots } from "../constants"; -const Wizard = ({ dismissBot }) => { +const Wizard = ({ dismissBot, saveMessage }) => { const theme = useTheme(); const { t } = useTranslation(); @@ -96,7 +96,8 @@ const Wizard = ({ dismissBot }) => { }; Wizard.propTypes = { - dismissBot: PropTypes.func.isRequired + dismissBot: PropTypes.func.isRequired, + saveMessage: PropTypes.func.isRequired }; export default Wizard; diff --git a/src/features/chatbot/constants.js b/src/features/chatbot/constants.js index 5b9a0b9..81703eb 100644 --- a/src/features/chatbot/constants.js +++ b/src/features/chatbot/constants.js @@ -10,3 +10,8 @@ export const bots = { export const userKey = { unknown: "Unknown" }; + +export const messageSource = { + bot: 1, + user: 2 +}; diff --git a/src/features/chatbot/reducer.js b/src/features/chatbot/reducer.js index 01e0690..82e9fdb 100644 --- a/src/features/chatbot/reducer.js +++ b/src/features/chatbot/reducer.js @@ -41,6 +41,9 @@ export default function chatbotReducer(state = initialState.bot, action) { chat: { loading: false, loaded: true, ...action.payload } }; + case types.CLOSE_BOT_CHAT_SUCCESS: + return { ...state, chat: initialState.bot.chat }; + default: return state; } From 93bb11a7579bb8f3e26ef9d7c24590ce5fb98bc9 Mon Sep 17 00:00:00 2001 From: Tudor Stanciu Date: Sun, 7 Jun 2020 04:12:20 +0300 Subject: [PATCH 4/4] save bot chat messages --- src/features/chatbot/actionCreators.js | 55 ++++++++++++++++++- src/features/chatbot/actionTypes.js | 2 + .../chatbot/components/BotsManager.js | 9 +-- src/features/chatbot/components/Wizard.js | 32 ++++++++--- src/features/chatbot/reducer.js | 9 +++ src/redux/reducers/initialState.js | 3 +- 6 files changed, 92 insertions(+), 18 deletions(-) diff --git a/src/features/chatbot/actionCreators.js b/src/features/chatbot/actionCreators.js index 82b9eb6..e16f0d7 100644 --- a/src/features/chatbot/actionCreators.js +++ b/src/features/chatbot/actionCreators.js @@ -15,7 +15,13 @@ export function loadBotSession(botName, userKey, botType) { try { const state = getState(); const session = state.bot.session[botType]; - if (session && (session.loading || session.loaded)) return; + if (session && (session.loading || session.loaded)) { + //a session exists, so check if a chat is open + if (!state.bot.chat.loaded && !state.bot.chat.loading) { + dispatch(initializeChat(session.sessionId)); + } + return; + } dispatch({ type: types.INITIALIZE_BOT_SESSION_STARTED, botType }); const externalId = state.frontendSession.sessionId; @@ -30,14 +36,15 @@ export function loadBotSession(botName, userKey, botType) { payload: data, botType }); - return data; + + dispatch(initializeChat(data.sessionId)); } catch (error) { throw error; } }; } -export function initializeChat(sessionId) { +function initializeChat(sessionId) { return async function(dispatch) { try { dispatch({ type: types.INITIALIZE_BOT_CHAT_STARTED }); @@ -75,6 +82,16 @@ export function saveMessage(messageSourceId, messageDate, messageContent) { return async function(dispatch, getState) { try { const { chatId } = getState().bot.chat; + if (!chatId) { + //the chat is not yet initialized. The message will be stored on the client and sent to the server next time. + dispatch({ + type: types.STORE_BOT_MESSAGE, + message: { messageSourceId, messageDate, messageContent } + }); + return; + } + + await dispatch(checkStorage(chatId)); const event = await dispatch( sendHttpRequest( api.saveMessage(chatId, messageSourceId, messageDate, messageContent) @@ -87,3 +104,35 @@ export function saveMessage(messageSourceId, messageDate, messageContent) { } }; } + +function checkStorage(chatId) { + return async function(dispatch, getState) { + try { + const messages = getState().bot.storage; + if (messages.length === 0) return; + + const promises = []; + messages.forEach(message => { + const promise = dispatch( + sendHttpRequest( + api.saveMessage( + chatId, + message.messageSourceId, + message.messageDate, + message.messageContent + ) + ) + ); + promises.push(promise); + }); + + //wait to save all stored messages to keep the order + await Promise.all(promises); + + //clear stored messages after save + dispatch({ type: types.CLEAR_BOT_STORAGE }); + } catch (error) { + throw error; + } + }; +} diff --git a/src/features/chatbot/actionTypes.js b/src/features/chatbot/actionTypes.js index c2b8b1b..d0d7b30 100644 --- a/src/features/chatbot/actionTypes.js +++ b/src/features/chatbot/actionTypes.js @@ -7,3 +7,5 @@ export const INITIALIZE_BOT_CHAT_STARTED = "INITIALIZE_BOT_CHAT_STARTED"; export const INITIALIZE_BOT_CHAT_SUCCESS = "INITIALIZE_BOT_CHAT_SUCCESS"; export const SAVE_BOT_MESSAGE_SUCCESS = "SAVE_BOT_MESSAGE_SUCCESS"; export const CLOSE_BOT_CHAT_SUCCESS = "CLOSE_BOT_CHAT_SUCCESS"; +export const STORE_BOT_MESSAGE = "STORE_BOT_MESSAGE"; +export const CLEAR_BOT_STORAGE = "CLEAR_BOT_STORAGE"; diff --git a/src/features/chatbot/components/BotsManager.js b/src/features/chatbot/components/BotsManager.js index 4eb1160..c9d1106 100644 --- a/src/features/chatbot/components/BotsManager.js +++ b/src/features/chatbot/components/BotsManager.js @@ -8,7 +8,6 @@ import { makeStyles } from "@material-ui/core/styles"; import { dismissBot, loadBotSession, - initializeChat, closeChat, saveMessage } from "../actionCreators"; @@ -34,11 +33,7 @@ const BotsManager = ({ bot, actions }) => { setType(bot.type); if (bot.type == botType.none) return; - actions - .loadBotSession(bots.Zirhan, userKey.unknown, bot.type) - .then(session => { - actions.initializeChat(session.sessionId); - }); + actions.loadBotSession(bots.Zirhan, userKey.unknown, bot.type); }, [bot.type]); const dismissBot = () => { @@ -71,7 +66,7 @@ function mapStateToProps(state) { function mapDispatchToProps(dispatch) { return { actions: bindActionCreators( - { dismissBot, loadBotSession, initializeChat, closeChat, saveMessage }, + { dismissBot, loadBotSession, closeChat, saveMessage }, dispatch ) }; diff --git a/src/features/chatbot/components/Wizard.js b/src/features/chatbot/components/Wizard.js index f4956f6..b528ce7 100644 --- a/src/features/chatbot/components/Wizard.js +++ b/src/features/chatbot/components/Wizard.js @@ -4,7 +4,7 @@ import ChatBot from "react-simple-chatbot"; import { ThemeProvider } from "styled-components"; import { useTheme } from "@material-ui/core/styles"; import { useTranslation } from "react-i18next"; -import { bots } from "../constants"; +import { bots, messageSource } from "../constants"; const Wizard = ({ dismissBot, saveMessage }) => { const theme = useTheme(); @@ -22,50 +22,68 @@ const Wizard = ({ dismissBot, saveMessage }) => { userFontColor: "#4a4a4a" }; + const getMessage = message => input => { + const currentDate = new Date(); + let messageToSave = message; + if (message.includes("previousValue") && input.previousValue) { + messageToSave = message.replace("{previousValue}", input.previousValue); + } + saveMessage(messageSource.bot, currentDate, messageToSave); + return message; + }; + const validate = text => { + const currentDate = new Date(); + saveMessage(messageSource.user, currentDate, text); + return true; + }; + const steps = [ { id: "1", - message: t("Chatbot.Wizard.Message1"), + message: getMessage(t("Chatbot.Wizard.Message1")), trigger: "2" }, { id: "2", - message: t("Chatbot.Wizard.Message2"), + message: getMessage(t("Chatbot.Wizard.Message2")), trigger: "3" }, { id: "3", - message: t("Chatbot.Wizard.Message3"), + message: getMessage(t("Chatbot.Wizard.Message3")), trigger: "4" }, { id: "4", user: true, + validator: validate, trigger: "5" }, { id: "5", - message: t("Chatbot.Wizard.Message5"), + message: getMessage(t("Chatbot.Wizard.Message5")), trigger: "6" }, { id: "6", user: true, + validator: validate, trigger: "7" }, { id: "7", - message: t("Chatbot.Wizard.Message7"), + message: getMessage(t("Chatbot.Wizard.Message7")), trigger: "8" }, { id: "8", user: true, + validator: validate, trigger: "9" }, { id: "9", - message: t("Chatbot.Wizard.Message9"), + message: getMessage(t("Chatbot.Wizard.Message9")), end: true } ]; diff --git a/src/features/chatbot/reducer.js b/src/features/chatbot/reducer.js index 82e9fdb..605895a 100644 --- a/src/features/chatbot/reducer.js +++ b/src/features/chatbot/reducer.js @@ -44,6 +44,15 @@ export default function chatbotReducer(state = initialState.bot, action) { case types.CLOSE_BOT_CHAT_SUCCESS: return { ...state, chat: initialState.bot.chat }; + case types.STORE_BOT_MESSAGE: { + const storage = [...state.storage]; + storage.push(action.message); + return { ...state, storage }; + } + + case types.CLEAR_BOT_STORAGE: + return { ...state, storage: initialState.bot.storage }; + default: return state; } diff --git a/src/redux/reducers/initialState.js b/src/redux/reducers/initialState.js index 4733314..0bd3aa2 100644 --- a/src/redux/reducers/initialState.js +++ b/src/redux/reducers/initialState.js @@ -21,7 +21,8 @@ export default { bot: { type: null, session: {}, - chat: { loading: false, loaded: false } + chat: { loading: false, loaded: false }, + storage: [] }, ajaxCallsInProgress: 0 };