This PR allows Copilot to be used without connecting the Captain assistant to an inbox. Currently, if Captain is enabled on an account, it takes over conversations and responds directly to users. This PR enables the use of Captain as a Copilot without allowing it to respond to users. Additionally, it allows using a different assistant for Copilot instead of the default Captain assistant. The selection logic for the Copilot assistant follows this order of preference: - If the user has selected a specific assistant, it takes first preference for Copilot. - If the above is not available, the assistant connected to the inbox takes preference. - If neither of the above is available, the first assistant in the account takes preference.
324 lines
10 KiB
JavaScript
324 lines
10 KiB
JavaScript
import types from '../../mutation-types';
|
|
import getters, { getSelectedChatConversation } from './getters';
|
|
import actions from './actions';
|
|
import { findPendingMessageIndex } from './helpers';
|
|
import { MESSAGE_STATUS } from 'shared/constants/messages';
|
|
import wootConstants from 'dashboard/constants/globals';
|
|
import { BUS_EVENTS } from '../../../../shared/constants/busEvents';
|
|
import { emitter } from 'shared/helpers/mitt';
|
|
|
|
const state = {
|
|
allConversations: [],
|
|
attachments: {},
|
|
listLoadingStatus: true,
|
|
chatStatusFilter: wootConstants.STATUS_TYPE.OPEN,
|
|
chatSortFilter: wootConstants.SORT_BY_TYPE.LATEST,
|
|
currentInbox: null,
|
|
selectedChatId: null,
|
|
appliedFilters: [],
|
|
contextMenuChatId: null,
|
|
conversationParticipants: [],
|
|
conversationLastSeen: null,
|
|
syncConversationsMessages: {},
|
|
conversationFilters: {},
|
|
copilotAssistant: {},
|
|
};
|
|
|
|
// mutations
|
|
export const mutations = {
|
|
[types.SET_ALL_CONVERSATION](_state, conversationList) {
|
|
const newAllConversations = [..._state.allConversations];
|
|
conversationList.forEach(conversation => {
|
|
const indexInCurrentList = newAllConversations.findIndex(
|
|
c => c.id === conversation.id
|
|
);
|
|
if (indexInCurrentList < 0) {
|
|
newAllConversations.push(conversation);
|
|
} else if (conversation.id !== _state.selectedChatId) {
|
|
// If the conversation is already in the list, replace it
|
|
// Added this to fix the issue of the conversation not being updated
|
|
// When reconnecting to the websocket. If the selectedChatId is not the same as
|
|
// the conversation.id in the store, replace the existing conversation with the new one
|
|
newAllConversations[indexInCurrentList] = conversation;
|
|
} else {
|
|
// If the conversation is already in the list and selectedChatId is the same,
|
|
// replace all data except the messages array, attachments, dataFetched, allMessagesLoaded
|
|
const existingConversation = newAllConversations[indexInCurrentList];
|
|
newAllConversations[indexInCurrentList] = {
|
|
...conversation,
|
|
allMessagesLoaded: existingConversation.allMessagesLoaded,
|
|
messages: existingConversation.messages,
|
|
dataFetched: existingConversation.dataFetched,
|
|
};
|
|
}
|
|
});
|
|
_state.allConversations = newAllConversations;
|
|
},
|
|
[types.EMPTY_ALL_CONVERSATION](_state) {
|
|
_state.allConversations = [];
|
|
_state.selectedChatId = null;
|
|
},
|
|
[types.SET_ALL_MESSAGES_LOADED](_state) {
|
|
const [chat] = getSelectedChatConversation(_state);
|
|
chat.allMessagesLoaded = true;
|
|
},
|
|
|
|
[types.CLEAR_ALL_MESSAGES_LOADED](_state) {
|
|
const [chat] = getSelectedChatConversation(_state);
|
|
chat.allMessagesLoaded = false;
|
|
},
|
|
[types.CLEAR_CURRENT_CHAT_WINDOW](_state) {
|
|
_state.selectedChatId = null;
|
|
},
|
|
|
|
[types.SET_PREVIOUS_CONVERSATIONS](_state, { id, data }) {
|
|
if (data.length) {
|
|
const [chat] = _state.allConversations.filter(c => c.id === id);
|
|
chat.messages.unshift(...data);
|
|
}
|
|
},
|
|
[types.SET_ALL_ATTACHMENTS](_state, { id, data }) {
|
|
const attachments = _state.attachments[id] || [];
|
|
|
|
attachments.push(...data);
|
|
_state.attachments[id] = [...attachments];
|
|
},
|
|
[types.SET_MISSING_MESSAGES](_state, { id, data }) {
|
|
const [chat] = _state.allConversations.filter(c => c.id === id);
|
|
if (!chat) return;
|
|
chat.messages = data;
|
|
},
|
|
|
|
[types.SET_CURRENT_CHAT_WINDOW](_state, activeChat) {
|
|
if (activeChat) {
|
|
_state.selectedChatId = activeChat.id;
|
|
}
|
|
},
|
|
|
|
[types.ASSIGN_AGENT](_state, assignee) {
|
|
const [chat] = getSelectedChatConversation(_state);
|
|
chat.meta.assignee = assignee;
|
|
},
|
|
|
|
[types.ASSIGN_TEAM](_state, { team, conversationId }) {
|
|
const [chat] = _state.allConversations.filter(c => c.id === conversationId);
|
|
chat.meta.team = team;
|
|
},
|
|
|
|
[types.UPDATE_CONVERSATION_LAST_ACTIVITY](
|
|
_state,
|
|
{ lastActivityAt, conversationId }
|
|
) {
|
|
const [chat] = _state.allConversations.filter(c => c.id === conversationId);
|
|
if (chat) {
|
|
chat.last_activity_at = lastActivityAt;
|
|
}
|
|
},
|
|
[types.ASSIGN_PRIORITY](_state, { priority, conversationId }) {
|
|
const [chat] = _state.allConversations.filter(c => c.id === conversationId);
|
|
chat.priority = priority;
|
|
},
|
|
|
|
[types.UPDATE_CONVERSATION_CUSTOM_ATTRIBUTES](_state, custom_attributes) {
|
|
const [chat] = getSelectedChatConversation(_state);
|
|
chat.custom_attributes = custom_attributes;
|
|
},
|
|
|
|
[types.CHANGE_CONVERSATION_STATUS](
|
|
_state,
|
|
{ conversationId, status, snoozedUntil }
|
|
) {
|
|
const conversation =
|
|
getters.getConversationById(_state)(conversationId) || {};
|
|
conversation.snoozed_until = snoozedUntil;
|
|
conversation.status = status;
|
|
},
|
|
|
|
[types.MUTE_CONVERSATION](_state) {
|
|
const [chat] = getSelectedChatConversation(_state);
|
|
chat.muted = true;
|
|
},
|
|
|
|
[types.UNMUTE_CONVERSATION](_state) {
|
|
const [chat] = getSelectedChatConversation(_state);
|
|
chat.muted = false;
|
|
},
|
|
|
|
[types.ADD_CONVERSATION_ATTACHMENTS](_state, message) {
|
|
// early return if the message has not been sent, or has no attachments
|
|
if (
|
|
message.status !== MESSAGE_STATUS.SENT ||
|
|
!message.attachments?.length
|
|
) {
|
|
return;
|
|
}
|
|
|
|
const id = message.conversation_id;
|
|
const existingAttachments = _state.attachments[id] || [];
|
|
|
|
const attachmentsToAdd = message.attachments.filter(attachment => {
|
|
// if the attachment is not already in the store, add it
|
|
// this is to prevent duplicates
|
|
return !existingAttachments.some(
|
|
existingAttachment => existingAttachment.id === attachment.id
|
|
);
|
|
});
|
|
|
|
// replace the attachments in the store
|
|
_state.attachments[id] = [...existingAttachments, ...attachmentsToAdd];
|
|
},
|
|
|
|
[types.DELETE_CONVERSATION_ATTACHMENTS](_state, message) {
|
|
if (message.status !== MESSAGE_STATUS.SENT) return;
|
|
|
|
const { conversation_id: id } = message;
|
|
const existingAttachments = _state.attachments[id] || [];
|
|
if (!existingAttachments.length) return;
|
|
|
|
_state.attachments[id] = existingAttachments.filter(attachment => {
|
|
return attachment.message_id !== message.id;
|
|
});
|
|
},
|
|
|
|
[types.ADD_MESSAGE]({ allConversations, selectedChatId }, message) {
|
|
const { conversation_id: conversationId } = message;
|
|
const [chat] = getSelectedChatConversation({
|
|
allConversations,
|
|
selectedChatId: conversationId,
|
|
});
|
|
if (!chat) return;
|
|
|
|
const pendingMessageIndex = findPendingMessageIndex(chat, message);
|
|
if (pendingMessageIndex !== -1) {
|
|
chat.messages[pendingMessageIndex] = message;
|
|
} else {
|
|
chat.messages.push(message);
|
|
chat.timestamp = message.created_at;
|
|
const { conversation: { unread_count: unreadCount = 0 } = {} } = message;
|
|
chat.unread_count = unreadCount;
|
|
if (selectedChatId === conversationId) {
|
|
emitter.emit(BUS_EVENTS.FETCH_LABEL_SUGGESTIONS);
|
|
emitter.emit(BUS_EVENTS.SCROLL_TO_MESSAGE);
|
|
}
|
|
}
|
|
},
|
|
|
|
[types.ADD_CONVERSATION](_state, conversation) {
|
|
_state.allConversations.push(conversation);
|
|
},
|
|
|
|
[types.UPDATE_CONVERSATION](_state, conversation) {
|
|
const { allConversations } = _state;
|
|
const currentConversationIndex = allConversations.findIndex(
|
|
c => c.id === conversation.id
|
|
);
|
|
if (currentConversationIndex > -1) {
|
|
const { messages, ...conversationAttributes } = conversation;
|
|
const currentConversation = {
|
|
...allConversations[currentConversationIndex],
|
|
...conversationAttributes,
|
|
};
|
|
allConversations[currentConversationIndex] = currentConversation;
|
|
if (_state.selectedChatId === conversation.id) {
|
|
emitter.emit(BUS_EVENTS.FETCH_LABEL_SUGGESTIONS);
|
|
emitter.emit(BUS_EVENTS.SCROLL_TO_MESSAGE);
|
|
}
|
|
} else {
|
|
_state.allConversations.push(conversation);
|
|
}
|
|
},
|
|
|
|
[types.SET_LIST_LOADING_STATUS](_state) {
|
|
_state.listLoadingStatus = true;
|
|
},
|
|
|
|
[types.CLEAR_LIST_LOADING_STATUS](_state) {
|
|
_state.listLoadingStatus = false;
|
|
},
|
|
|
|
[types.UPDATE_MESSAGE_UNREAD_COUNT](
|
|
_state,
|
|
{ id, lastSeen, unreadCount = 0 }
|
|
) {
|
|
const [chat] = _state.allConversations.filter(c => c.id === id);
|
|
if (chat) {
|
|
chat.agent_last_seen_at = lastSeen;
|
|
chat.unread_count = unreadCount;
|
|
}
|
|
},
|
|
[types.CHANGE_CHAT_STATUS_FILTER](_state, data) {
|
|
_state.chatStatusFilter = data;
|
|
},
|
|
|
|
[types.CHANGE_CHAT_SORT_FILTER](_state, data) {
|
|
_state.chatSortFilter = data;
|
|
},
|
|
|
|
// Update assignee on action cable message
|
|
[types.UPDATE_ASSIGNEE](_state, payload) {
|
|
const [chat] = _state.allConversations.filter(c => c.id === payload.id);
|
|
chat.meta.assignee = payload.assignee;
|
|
},
|
|
|
|
[types.UPDATE_CONVERSATION_CONTACT](_state, { conversationId, ...payload }) {
|
|
const [chat] = _state.allConversations.filter(c => c.id === conversationId);
|
|
if (chat) {
|
|
chat.meta.sender = payload;
|
|
}
|
|
},
|
|
|
|
[types.SET_ACTIVE_INBOX](_state, inboxId) {
|
|
_state.currentInbox = inboxId ? parseInt(inboxId, 10) : null;
|
|
},
|
|
|
|
[types.SET_CONVERSATION_CAN_REPLY](_state, { conversationId, canReply }) {
|
|
const [chat] = _state.allConversations.filter(c => c.id === conversationId);
|
|
if (chat) {
|
|
chat.can_reply = canReply;
|
|
}
|
|
},
|
|
|
|
[types.CLEAR_CONTACT_CONVERSATIONS](_state, contactId) {
|
|
const chats = _state.allConversations.filter(
|
|
c => c.meta.sender.id !== contactId
|
|
);
|
|
_state.allConversations = chats;
|
|
},
|
|
|
|
[types.SET_CONVERSATION_FILTERS](_state, data) {
|
|
_state.appliedFilters = data;
|
|
},
|
|
|
|
[types.CLEAR_CONVERSATION_FILTERS](_state) {
|
|
_state.appliedFilters = [];
|
|
},
|
|
|
|
[types.SET_LAST_MESSAGE_ID_IN_SYNC_CONVERSATION](
|
|
_state,
|
|
{ conversationId, messageId }
|
|
) {
|
|
_state.syncConversationsMessages[conversationId] = messageId;
|
|
},
|
|
|
|
[types.SET_CONTEXT_MENU_CHAT_ID](_state, chatId) {
|
|
_state.contextMenuChatId = chatId;
|
|
},
|
|
|
|
[types.SET_CHAT_LIST_FILTERS](_state, data) {
|
|
_state.conversationFilters = data;
|
|
},
|
|
[types.UPDATE_CHAT_LIST_FILTERS](_state, data) {
|
|
_state.conversationFilters = { ..._state.conversationFilters, ...data };
|
|
},
|
|
[types.SET_INBOX_CAPTAIN_ASSISTANT](_state, data) {
|
|
_state.copilotAssistant = data.assistant;
|
|
},
|
|
};
|
|
|
|
export default {
|
|
state,
|
|
getters,
|
|
actions,
|
|
mutations,
|
|
};
|