From 93bb11a7579bb8f3e26ef9d7c24590ce5fb98bc9 Mon Sep 17 00:00:00 2001 From: Tudor Stanciu Date: Sun, 7 Jun 2020 04:12:20 +0300 Subject: [PATCH] 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 };