diff --git a/src/features/chatbot/actionCreators.js b/src/features/chatbot/actionCreators.js index 4384283..e16f0d7 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,130 @@ export function summonWizard() { export function dismissBot() { return { type: types.DISMISS_BOT }; } + +export function loadBotSession(botName, userKey, botType) { + return async function(dispatch, getState) { + try { + const state = getState(); + const session = state.bot.session[botType]; + 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; + const clientApplication = state.frontendSession.applicationCode; + const data = await dispatch( + sendHttpRequest( + api.getBotSession(botName, externalId, clientApplication, userKey) + ) + ); + dispatch({ + type: types.INITIALIZE_BOT_SESSION_SUCCESS, + payload: data, + botType + }); + + dispatch(initializeChat(data.sessionId)); + } catch (error) { + throw error; + } + }; +} + +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 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; + 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) + ) + ); + dispatch({ type: types.SAVE_BOT_MESSAGE_SUCCESS, payload: event }); + return event; + } catch (error) { + throw error; + } + }; +} + +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 4ba513d..d0d7b30 100644 --- a/src/features/chatbot/actionTypes.js +++ b/src/features/chatbot/actionTypes.js @@ -1,2 +1,11 @@ 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"; +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/api.js b/src/features/chatbot/api.js new file mode 100644 index 0000000..482a32f --- /dev/null +++ b/src/features/chatbot/api.js @@ -0,0 +1,27 @@ +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 = 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, + messageSourceId, + messageDate, + messageContent + }); + +export default { + getBotSession, + initializeChat, + closeChat, + 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..c9d1106 100644 --- a/src/features/chatbot/components/BotsManager.js +++ b/src/features/chatbot/components/BotsManager.js @@ -2,10 +2,15 @@ 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, + closeChat, + saveMessage +} from "../actionCreators"; const useStyles = makeStyles(theme => ({ bot: { @@ -24,13 +29,24 @@ 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); }, [bot.type]); + const dismissBot = () => { + actions.closeChat(); + actions.dismissBot(); + }; + return (