Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6eaac81f15 | ||
|
|
06a74ad52a | ||
|
|
e8d69d5891 | ||
|
|
0f95371f30 | ||
|
|
c454135507 | ||
|
|
1fe357c501 |
@@ -12,6 +12,62 @@ import {
|
||||
import messageReadActions from './actions/messageReadActions';
|
||||
import messageTranslateActions from './actions/messageTranslateActions';
|
||||
import * as Sentry from '@sentry/vue';
|
||||
import { applyPageFilters } from './helpers';
|
||||
import { matchesFilters } from './helpers/filterHelpers';
|
||||
|
||||
const DEFAULT_TAB = 'all';
|
||||
|
||||
const deriveTabKeys = (conversation, state, rootGetters) => {
|
||||
const tabKeys = new Set([DEFAULT_TAB]);
|
||||
const filtersByTab = state.filtersByTab || {};
|
||||
const currentUserId = rootGetters.getCurrentUser?.id;
|
||||
|
||||
Object.entries(filtersByTab).forEach(([rawTabKey, filters = {}]) => {
|
||||
const tabKey = rawTabKey || DEFAULT_TAB;
|
||||
|
||||
if (tabKey === 'appliedFilters') {
|
||||
if (
|
||||
state.appliedFilters?.length &&
|
||||
matchesFilters(conversation, state.appliedFilters)
|
||||
) {
|
||||
tabKeys.add(tabKey);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const { inboxId, status, labels = [], teamId, conversationType } = filters;
|
||||
|
||||
const matchesFilter = applyPageFilters(conversation, {
|
||||
inboxId,
|
||||
status,
|
||||
labels,
|
||||
teamId,
|
||||
conversationType,
|
||||
});
|
||||
|
||||
if (!matchesFilter) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (tabKey === 'me') {
|
||||
if (conversation.meta?.assignee?.id === currentUserId) {
|
||||
tabKeys.add(tabKey);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (tabKey === 'unassigned') {
|
||||
if (!conversation.meta?.assignee) {
|
||||
tabKeys.add(tabKey);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
tabKeys.add(tabKey);
|
||||
});
|
||||
|
||||
return Array.from(tabKeys);
|
||||
};
|
||||
|
||||
export const hasMessageFailedWithExternalError = pendingMessage => {
|
||||
// This helper is used to check if the message has failed with an external error.
|
||||
@@ -27,10 +83,14 @@ export const hasMessageFailedWithExternalError = pendingMessage => {
|
||||
|
||||
// actions
|
||||
const actions = {
|
||||
getConversation: async ({ commit }, conversationId) => {
|
||||
getConversation: async ({ commit, state, rootGetters }, conversationId) => {
|
||||
try {
|
||||
const response = await ConversationApi.show(conversationId);
|
||||
commit(types.UPDATE_CONVERSATION, response.data);
|
||||
const tabKeys = deriveTabKeys(response.data, state, rootGetters);
|
||||
commit(types.UPDATE_CONVERSATION, {
|
||||
conversation: response.data,
|
||||
tabKeys,
|
||||
});
|
||||
commit(`contacts/${types.SET_CONTACT_ITEM}`, response.data.meta.sender);
|
||||
} catch (error) {
|
||||
// Ignore error
|
||||
@@ -125,11 +185,9 @@ const actions = {
|
||||
{ commit, state, dispatch },
|
||||
{ conversationId }
|
||||
) => {
|
||||
const { allConversations, syncConversationsMessages } = state;
|
||||
const { conversationsById, syncConversationsMessages } = state;
|
||||
const lastMessageId = syncConversationsMessages[conversationId];
|
||||
const selectedChat = allConversations.find(
|
||||
conversation => conversation.id === conversationId
|
||||
);
|
||||
const selectedChat = conversationsById[conversationId];
|
||||
if (!selectedChat) return;
|
||||
try {
|
||||
const { messages } = selectedChat;
|
||||
@@ -171,10 +229,8 @@ const actions = {
|
||||
{ commit, state },
|
||||
{ conversationId }
|
||||
) => {
|
||||
const { allConversations } = state;
|
||||
const selectedChat = allConversations.find(
|
||||
conversation => conversation.id === conversationId
|
||||
);
|
||||
const { conversationsById } = state;
|
||||
const selectedChat = conversationsById[conversationId];
|
||||
if (!selectedChat) return;
|
||||
const { messages } = selectedChat;
|
||||
const lastMessage = messages.last();
|
||||
@@ -337,7 +393,10 @@ const actions = {
|
||||
}
|
||||
},
|
||||
|
||||
addConversation({ commit, state, dispatch, rootState }, conversation) {
|
||||
addConversation(
|
||||
{ commit, state, dispatch, rootState, rootGetters },
|
||||
conversation
|
||||
) {
|
||||
const { currentInbox, appliedFilters } = state;
|
||||
const {
|
||||
inbox_id: inboxId,
|
||||
@@ -353,7 +412,8 @@ const actions = {
|
||||
!isOnUnattendedView(rootState) &&
|
||||
isMatchingInboxFilter
|
||||
) {
|
||||
commit(types.ADD_CONVERSATION, conversation);
|
||||
const tabKeys = deriveTabKeys(conversation, state, rootGetters);
|
||||
commit(types.ADD_CONVERSATION, { conversation, tabKeys });
|
||||
dispatch('contacts/setContact', sender);
|
||||
}
|
||||
},
|
||||
@@ -370,11 +430,12 @@ const actions = {
|
||||
}
|
||||
},
|
||||
|
||||
updateConversation({ commit, dispatch }, conversation) {
|
||||
updateConversation({ commit, dispatch, state, rootGetters }, conversation) {
|
||||
const {
|
||||
meta: { sender },
|
||||
} = conversation;
|
||||
commit(types.UPDATE_CONVERSATION, conversation);
|
||||
const tabKeys = deriveTabKeys(conversation, state, rootGetters);
|
||||
commit(types.UPDATE_CONVERSATION, { conversation, tabKeys });
|
||||
|
||||
dispatch('conversationLabels/setConversationLabel', {
|
||||
id: conversation.id,
|
||||
|
||||
@@ -8,22 +8,80 @@ import {
|
||||
} from '../../../helper/permissionsHelper';
|
||||
import camelcaseKeys from 'camelcase-keys';
|
||||
|
||||
const DEFAULT_TAB = 'all';
|
||||
|
||||
const normalizeTabKey = tabKey => tabKey || DEFAULT_TAB;
|
||||
|
||||
const buildOrderedIdListForTab = (state, tabKey) => {
|
||||
const tabPages = state.conversationPagesByTab[normalizeTabKey(tabKey)];
|
||||
if (!tabPages) {
|
||||
return [];
|
||||
}
|
||||
const seen = new Set();
|
||||
const orderedIds = [];
|
||||
Object.keys(tabPages)
|
||||
.map(page => Number(page))
|
||||
.sort((a, b) => a - b)
|
||||
.forEach(pageNumber => {
|
||||
const ids = tabPages[String(pageNumber)] || [];
|
||||
ids.forEach(id => {
|
||||
if (!seen.has(id)) {
|
||||
seen.add(id);
|
||||
orderedIds.push(id);
|
||||
}
|
||||
});
|
||||
});
|
||||
return orderedIds;
|
||||
};
|
||||
|
||||
const conversationsFromIds = (state, ids) =>
|
||||
ids.map(id => state.conversationsById[id]).filter(Boolean);
|
||||
|
||||
const buildTabList = (state, tabKey) => {
|
||||
const orderedIds = buildOrderedIdListForTab(state, tabKey);
|
||||
return conversationsFromIds(state, orderedIds);
|
||||
};
|
||||
|
||||
const buildAllConversationList = state => {
|
||||
const flattenIds = Object.values(state.conversationPagesByTab).reduce(
|
||||
(acc, tabPages) => {
|
||||
Object.values(tabPages).forEach(pageIds => {
|
||||
pageIds.forEach(id => {
|
||||
if (!acc.set.has(id)) {
|
||||
acc.set.add(id);
|
||||
acc.ids.push(id);
|
||||
}
|
||||
});
|
||||
});
|
||||
return acc;
|
||||
},
|
||||
{ set: new Set(), ids: [] }
|
||||
);
|
||||
|
||||
if (!flattenIds.ids.length) {
|
||||
return Object.values(state.conversationsById);
|
||||
}
|
||||
|
||||
return conversationsFromIds(state, flattenIds.ids);
|
||||
};
|
||||
|
||||
export const getSelectedChatConversation = ({
|
||||
allConversations,
|
||||
conversationsById,
|
||||
selectedChatId,
|
||||
}) =>
|
||||
allConversations.filter(conversation => conversation.id === selectedChatId);
|
||||
}) => {
|
||||
const chat = conversationsById[selectedChatId];
|
||||
return chat ? [chat] : [];
|
||||
};
|
||||
|
||||
const getters = {
|
||||
getAllConversations: ({ allConversations, chatSortFilter: sortKey }) => {
|
||||
return allConversations.sort((a, b) => sortComparator(a, b, sortKey));
|
||||
getAllConversations: state => {
|
||||
const list = buildTabList(state, DEFAULT_TAB);
|
||||
if (!list.length) {
|
||||
return buildAllConversationList(state);
|
||||
}
|
||||
return [...list].sort((a, b) => sortComparator(a, b, state.chatSortFilter));
|
||||
},
|
||||
getFilteredConversations: (
|
||||
{ allConversations, chatSortFilter, appliedFilters },
|
||||
_,
|
||||
__,
|
||||
rootGetters
|
||||
) => {
|
||||
getFilteredConversations: (state, _, __, rootGetters) => {
|
||||
const currentUser = rootGetters.getCurrentUser;
|
||||
const currentUserId = rootGetters.getCurrentUser.id;
|
||||
const currentAccountId = rootGetters.getCurrentAccountId;
|
||||
@@ -31,11 +89,15 @@ const getters = {
|
||||
const permissions = getUserPermissions(currentUser, currentAccountId);
|
||||
const userRole = getUserRole(currentUser, currentAccountId);
|
||||
|
||||
return allConversations
|
||||
const baseList = buildTabList(state, 'appliedFilters').length
|
||||
? buildTabList(state, 'appliedFilters')
|
||||
: buildAllConversationList(state);
|
||||
|
||||
return baseList
|
||||
.filter(conversation => {
|
||||
const matchesFilterResult = matchesFilters(
|
||||
conversation,
|
||||
appliedFilters
|
||||
state.appliedFilters
|
||||
);
|
||||
const allowedForRole = applyRoleFilter(
|
||||
conversation,
|
||||
@@ -46,13 +108,10 @@ const getters = {
|
||||
|
||||
return matchesFilterResult && allowedForRole;
|
||||
})
|
||||
.sort((a, b) => sortComparator(a, b, chatSortFilter));
|
||||
.sort((a, b) => sortComparator(a, b, state.chatSortFilter));
|
||||
},
|
||||
getSelectedChat: ({ selectedChatId, allConversations }) => {
|
||||
const selectedChat = allConversations.find(
|
||||
conversation => conversation.id === selectedChatId
|
||||
);
|
||||
return selectedChat || {};
|
||||
getSelectedChat: ({ selectedChatId, conversationsById }) => {
|
||||
return conversationsById[selectedChatId] || {};
|
||||
},
|
||||
getSelectedChatAttachments: ({ selectedChatId, attachments }) => {
|
||||
return attachments[selectedChatId] || [];
|
||||
@@ -72,10 +131,13 @@ const getters = {
|
||||
|
||||
return lastEmail;
|
||||
},
|
||||
getMineChats: (_state, _, __, rootGetters) => activeFilters => {
|
||||
getMineChats: (state, _, __, rootGetters) => activeFilters => {
|
||||
const currentUserID = rootGetters.getCurrentUser?.id;
|
||||
const sourceList = buildTabList(state, 'me').length
|
||||
? buildTabList(state, 'me')
|
||||
: buildAllConversationList(state);
|
||||
|
||||
return _state.allConversations.filter(conversation => {
|
||||
return sourceList.filter(conversation => {
|
||||
const { assignee } = conversation.meta;
|
||||
const isAssignedToMe = assignee && assignee.id === currentUserID;
|
||||
const shouldFilter = applyPageFilters(conversation, activeFilters);
|
||||
@@ -95,22 +157,29 @@ const getters = {
|
||||
const hasAppliedFilters = _state.appliedFilters.length !== 0;
|
||||
return hasAppliedFilters ? filterQueryGenerator(_state.appliedFilters) : [];
|
||||
},
|
||||
getUnAssignedChats: _state => activeFilters => {
|
||||
return _state.allConversations.filter(conversation => {
|
||||
getUnAssignedChats: state => activeFilters => {
|
||||
const sourceList = buildTabList(state, 'unassigned').length
|
||||
? buildTabList(state, 'unassigned')
|
||||
: buildAllConversationList(state);
|
||||
|
||||
return sourceList.filter(conversation => {
|
||||
const isUnAssigned = !conversation.meta.assignee;
|
||||
const shouldFilter = applyPageFilters(conversation, activeFilters);
|
||||
return isUnAssigned && shouldFilter;
|
||||
});
|
||||
},
|
||||
getAllStatusChats: (_state, _, __, rootGetters) => activeFilters => {
|
||||
getAllStatusChats: (state, _, __, rootGetters) => activeFilters => {
|
||||
const currentUser = rootGetters.getCurrentUser;
|
||||
const currentUserId = rootGetters.getCurrentUser.id;
|
||||
const currentAccountId = rootGetters.getCurrentAccountId;
|
||||
|
||||
const permissions = getUserPermissions(currentUser, currentAccountId);
|
||||
const userRole = getUserRole(currentUser, currentAccountId);
|
||||
const sourceList = buildTabList(state, DEFAULT_TAB).length
|
||||
? buildTabList(state, DEFAULT_TAB)
|
||||
: buildAllConversationList(state);
|
||||
|
||||
return _state.allConversations.filter(conversation => {
|
||||
return sourceList.filter(conversation => {
|
||||
const shouldFilter = applyPageFilters(conversation, activeFilters);
|
||||
const allowedForRole = applyRoleFilter(
|
||||
conversation,
|
||||
@@ -142,10 +211,8 @@ const getters = {
|
||||
getChatStatusFilter: ({ chatStatusFilter }) => chatStatusFilter,
|
||||
getChatSortFilter: ({ chatSortFilter }) => chatSortFilter,
|
||||
getSelectedInbox: ({ currentInbox }) => currentInbox,
|
||||
getConversationById: _state => conversationId => {
|
||||
return _state.allConversations.find(
|
||||
value => value.id === Number(conversationId)
|
||||
);
|
||||
getConversationById: state => conversationId => {
|
||||
return state.conversationsById[Number(conversationId)] || null;
|
||||
},
|
||||
getConversationParticipants: _state => {
|
||||
return _state.conversationParticipants;
|
||||
|
||||
@@ -45,7 +45,11 @@ export const buildConversationList = (
|
||||
filterType
|
||||
) => {
|
||||
const { payload: conversationList, meta: metaData } = responseData;
|
||||
context.commit(types.SET_ALL_CONVERSATION, conversationList);
|
||||
context.commit(types.SET_ALL_CONVERSATION, {
|
||||
tab: filterType,
|
||||
page: requestPayload.page || 1,
|
||||
conversations: conversationList,
|
||||
});
|
||||
context.dispatch('conversationStats/set', metaData);
|
||||
context.dispatch(
|
||||
'conversationLabels/setBulkConversationLabels',
|
||||
@@ -53,10 +57,14 @@ export const buildConversationList = (
|
||||
);
|
||||
context.commit(types.CLEAR_LIST_LOADING_STATUS);
|
||||
setContacts(context.commit, conversationList);
|
||||
context.commit(types.SET_CONVERSATION_TAB_FILTERS, {
|
||||
tab: filterType,
|
||||
filters: requestPayload || {},
|
||||
});
|
||||
setPageFilter({
|
||||
dispatch: context.dispatch,
|
||||
filter: filterType,
|
||||
page: requestPayload.page,
|
||||
page: requestPayload.page || 1,
|
||||
markEndReached: !conversationList.length,
|
||||
});
|
||||
};
|
||||
|
||||
@@ -1,14 +1,169 @@
|
||||
import types from '../../mutation-types';
|
||||
import getters, { getSelectedChatConversation } from './getters';
|
||||
import actions from './actions';
|
||||
import { findPendingMessageIndex } from './helpers';
|
||||
import { findPendingMessageIndex, sortComparator } 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 DEFAULT_TAB = 'all';
|
||||
const DEFAULT_PAGE = 1;
|
||||
const DEFAULT_PAGE_SIZE = 25;
|
||||
|
||||
const normalizeTabKey = tab => tab || DEFAULT_TAB;
|
||||
|
||||
const clonePagesWithUpdate = (pagesByTab, tabKey, updater) => {
|
||||
const normalizedTab = normalizeTabKey(tabKey);
|
||||
const existingTabPages = pagesByTab[normalizedTab] || {};
|
||||
const nextTabPages = updater(existingTabPages);
|
||||
if (nextTabPages === existingTabPages) {
|
||||
return pagesByTab;
|
||||
}
|
||||
return {
|
||||
...pagesByTab,
|
||||
[normalizedTab]: nextTabPages,
|
||||
};
|
||||
};
|
||||
|
||||
const removeIdFromTabPages = (tabPages, conversationId) => {
|
||||
let mutated = false;
|
||||
const nextPages = Object.entries(tabPages).reduce((acc, [pageKey, ids]) => {
|
||||
const filteredIds = ids.filter(id => id !== conversationId);
|
||||
if (filteredIds.length !== ids.length) {
|
||||
mutated = true;
|
||||
acc[pageKey] = filteredIds;
|
||||
} else {
|
||||
acc[pageKey] = ids;
|
||||
}
|
||||
return acc;
|
||||
}, {});
|
||||
return mutated ? nextPages : tabPages;
|
||||
};
|
||||
|
||||
const removeIdFromAllTabs = (pagesByTab, conversationId) => {
|
||||
let mutated = false;
|
||||
const nextPagesByTab = Object.entries(pagesByTab).reduce(
|
||||
(acc, [tabKey, tabPages]) => {
|
||||
const updatedTabPages = removeIdFromTabPages(tabPages, conversationId);
|
||||
if (updatedTabPages !== tabPages) {
|
||||
mutated = true;
|
||||
}
|
||||
acc[tabKey] = updatedTabPages;
|
||||
return acc;
|
||||
},
|
||||
{}
|
||||
);
|
||||
return mutated ? nextPagesByTab : pagesByTab;
|
||||
};
|
||||
|
||||
const rebuildTabPagesWithConversation = (_state, tabKey, conversationId) => {
|
||||
_state.conversationPagesByTab = clonePagesWithUpdate(
|
||||
_state.conversationPagesByTab,
|
||||
tabKey,
|
||||
tabPages => {
|
||||
const existingTabPages = Object.keys(tabPages).length
|
||||
? tabPages
|
||||
: { [String(DEFAULT_PAGE)]: [] };
|
||||
|
||||
const pageNumbers = Object.keys(existingTabPages)
|
||||
.map(key => Number(key))
|
||||
.sort((a, b) => a - b);
|
||||
|
||||
const collectedIds = [];
|
||||
const seen = new Set();
|
||||
|
||||
pageNumbers.forEach(pageNumber => {
|
||||
const ids = existingTabPages[String(pageNumber)] || [];
|
||||
ids.forEach(id => {
|
||||
if (id === conversationId) {
|
||||
return;
|
||||
}
|
||||
if (!seen.has(id)) {
|
||||
seen.add(id);
|
||||
collectedIds.push(id);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
collectedIds.push(conversationId);
|
||||
|
||||
const conversations = collectedIds
|
||||
.map(id => _state.conversationsById[id])
|
||||
.filter(Boolean);
|
||||
|
||||
const totalPagesNeeded = Math.max(
|
||||
Math.ceil(conversations.length / DEFAULT_PAGE_SIZE),
|
||||
1
|
||||
);
|
||||
|
||||
const basePageNumbers = pageNumbers.length
|
||||
? [...pageNumbers]
|
||||
: [DEFAULT_PAGE];
|
||||
|
||||
while (basePageNumbers.length < totalPagesNeeded) {
|
||||
const lastPage = basePageNumbers[basePageNumbers.length - 1];
|
||||
basePageNumbers.push(lastPage + 1);
|
||||
}
|
||||
|
||||
if (basePageNumbers.length > totalPagesNeeded) {
|
||||
basePageNumbers.length = totalPagesNeeded;
|
||||
}
|
||||
|
||||
if (!conversations.length) {
|
||||
return basePageNumbers.reduce((acc, pageNumber) => {
|
||||
acc[String(pageNumber)] = [];
|
||||
return acc;
|
||||
}, {});
|
||||
}
|
||||
|
||||
const sorted = conversations
|
||||
.slice()
|
||||
.sort((a, b) => sortComparator(a, b, _state.chatSortFilter));
|
||||
|
||||
const sortedIds = sorted.map(item => item.id);
|
||||
|
||||
const nextPages = {};
|
||||
basePageNumbers.forEach((pageNumber, index) => {
|
||||
const startIndex = index * DEFAULT_PAGE_SIZE;
|
||||
nextPages[String(pageNumber)] = sortedIds.slice(
|
||||
startIndex,
|
||||
startIndex + DEFAULT_PAGE_SIZE
|
||||
);
|
||||
});
|
||||
|
||||
return nextPages;
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
const upsertConversationEntity = (_state, conversation) => {
|
||||
const existing = _state.conversationsById[conversation.id];
|
||||
let updatedConversation = conversation;
|
||||
|
||||
if (existing) {
|
||||
if (conversation.id === _state.selectedChatId) {
|
||||
updatedConversation = {
|
||||
...conversation,
|
||||
allMessagesLoaded: existing.allMessagesLoaded,
|
||||
messages: existing.messages,
|
||||
dataFetched: existing.dataFetched,
|
||||
};
|
||||
} else {
|
||||
updatedConversation = { ...existing, ...conversation };
|
||||
}
|
||||
}
|
||||
|
||||
_state.conversationsById = {
|
||||
..._state.conversationsById,
|
||||
[conversation.id]: updatedConversation,
|
||||
};
|
||||
};
|
||||
|
||||
const state = {
|
||||
allConversations: [],
|
||||
conversationsById: {},
|
||||
conversationPagesByTab: {},
|
||||
filtersByTab: {},
|
||||
attachments: {},
|
||||
listLoadingStatus: true,
|
||||
chatStatusFilter: wootConstants.STATUS_TYPE.OPEN,
|
||||
@@ -26,36 +181,27 @@ const state = {
|
||||
|
||||
// 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,
|
||||
};
|
||||
}
|
||||
[types.SET_ALL_CONVERSATION](_state, { tab, page, conversations }) {
|
||||
const pageNumber = page || DEFAULT_PAGE;
|
||||
const tabKey = normalizeTabKey(tab);
|
||||
const conversationIds = conversations.map(conversation => {
|
||||
upsertConversationEntity(_state, conversation);
|
||||
return conversation.id;
|
||||
});
|
||||
_state.allConversations = newAllConversations;
|
||||
|
||||
_state.conversationPagesByTab = clonePagesWithUpdate(
|
||||
_state.conversationPagesByTab,
|
||||
tabKey,
|
||||
tabPages => ({
|
||||
...tabPages,
|
||||
[String(pageNumber)]: conversationIds,
|
||||
})
|
||||
);
|
||||
},
|
||||
[types.EMPTY_ALL_CONVERSATION](_state) {
|
||||
_state.allConversations = [];
|
||||
_state.conversationsById = {};
|
||||
_state.conversationPagesByTab = {};
|
||||
_state.filtersByTab = {};
|
||||
_state.selectedChatId = null;
|
||||
},
|
||||
[types.SET_ALL_MESSAGES_LOADED](_state) {
|
||||
@@ -72,17 +218,25 @@ export const mutations = {
|
||||
},
|
||||
|
||||
[types.SET_PREVIOUS_CONVERSATIONS](_state, { id, data }) {
|
||||
if (data.length) {
|
||||
const [chat] = _state.allConversations.filter(c => c.id === id);
|
||||
chat.messages.unshift(...data);
|
||||
if (!data.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
const chat = _state.conversationsById[id];
|
||||
if (!chat) {
|
||||
return;
|
||||
}
|
||||
|
||||
chat.messages = [...data, ...(chat.messages || [])];
|
||||
},
|
||||
[types.SET_ALL_ATTACHMENTS](_state, { id, data }) {
|
||||
_state.attachments[id] = [...data];
|
||||
},
|
||||
[types.SET_MISSING_MESSAGES](_state, { id, data }) {
|
||||
const [chat] = _state.allConversations.filter(c => c.id === id);
|
||||
if (!chat) return;
|
||||
const chat = _state.conversationsById[id];
|
||||
if (!chat) {
|
||||
return;
|
||||
}
|
||||
chat.messages = data;
|
||||
},
|
||||
|
||||
@@ -98,22 +252,27 @@ export const mutations = {
|
||||
},
|
||||
|
||||
[types.ASSIGN_TEAM](_state, { team, conversationId }) {
|
||||
const [chat] = _state.allConversations.filter(c => c.id === conversationId);
|
||||
chat.meta.team = team;
|
||||
const chat = _state.conversationsById[conversationId];
|
||||
if (chat) {
|
||||
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;
|
||||
const chat = _state.conversationsById[conversationId];
|
||||
if (!chat) {
|
||||
return;
|
||||
}
|
||||
chat.last_activity_at = lastActivityAt;
|
||||
},
|
||||
[types.ASSIGN_PRIORITY](_state, { priority, conversationId }) {
|
||||
const [chat] = _state.allConversations.filter(c => c.id === conversationId);
|
||||
chat.priority = priority;
|
||||
const chat = _state.conversationsById[conversationId];
|
||||
if (chat) {
|
||||
chat.priority = priority;
|
||||
}
|
||||
},
|
||||
|
||||
[types.UPDATE_CONVERSATION_CUSTOM_ATTRIBUTES](_state, custom_attributes) {
|
||||
@@ -177,59 +336,89 @@ export const mutations = {
|
||||
});
|
||||
},
|
||||
|
||||
[types.ADD_MESSAGE]({ allConversations, selectedChatId }, message) {
|
||||
[types.ADD_MESSAGE](_state, message) {
|
||||
const { conversation_id: conversationId } = message;
|
||||
const [chat] = getSelectedChatConversation({
|
||||
allConversations,
|
||||
selectedChatId: conversationId,
|
||||
});
|
||||
if (!chat) return;
|
||||
const chat = _state.conversationsById[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);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
chat.messages.push(message);
|
||||
chat.timestamp = message.created_at;
|
||||
const { conversation: { unread_count: unreadCount = 0 } = {} } = message;
|
||||
chat.unread_count = unreadCount;
|
||||
if (_state.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.ADD_CONVERSATION](_state, { conversation, tabKeys = [DEFAULT_TAB] }) {
|
||||
upsertConversationEntity(_state, conversation);
|
||||
|
||||
tabKeys.forEach(tabKey => {
|
||||
rebuildTabPagesWithConversation(_state, tabKey, conversation.id);
|
||||
});
|
||||
},
|
||||
|
||||
[types.DELETE_CONVERSATION](_state, conversationId) {
|
||||
_state.allConversations = _state.allConversations.filter(
|
||||
c => c.id !== conversationId
|
||||
const { [conversationId]: removed, ...rest } = _state.conversationsById;
|
||||
if (!removed) {
|
||||
return;
|
||||
}
|
||||
_state.conversationsById = rest;
|
||||
_state.conversationPagesByTab = removeIdFromAllTabs(
|
||||
_state.conversationPagesByTab,
|
||||
conversationId
|
||||
);
|
||||
},
|
||||
|
||||
[types.UPDATE_CONVERSATION](_state, conversation) {
|
||||
const { allConversations } = _state;
|
||||
const index = allConversations.findIndex(c => c.id === conversation.id);
|
||||
[types.UPDATE_CONVERSATION](
|
||||
_state,
|
||||
{ conversation, tabKeys = [DEFAULT_TAB] }
|
||||
) {
|
||||
const existingConversation = _state.conversationsById[conversation.id];
|
||||
|
||||
if (index > -1) {
|
||||
const selectedConversation = allConversations[index];
|
||||
|
||||
// ignore out of order events
|
||||
if (conversation.updated_at < selectedConversation.updated_at) {
|
||||
if (existingConversation) {
|
||||
if (
|
||||
conversation.updated_at &&
|
||||
existingConversation.updated_at &&
|
||||
conversation.updated_at < existingConversation.updated_at
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { messages, ...updates } = conversation;
|
||||
allConversations[index] = { ...selectedConversation, ...updates };
|
||||
if (_state.selectedChatId === conversation.id) {
|
||||
emitter.emit(BUS_EVENTS.FETCH_LABEL_SUGGESTIONS);
|
||||
emitter.emit(BUS_EVENTS.SCROLL_TO_MESSAGE);
|
||||
}
|
||||
const mergedConversation = {
|
||||
...existingConversation,
|
||||
...updates,
|
||||
};
|
||||
_state.conversationsById = {
|
||||
..._state.conversationsById,
|
||||
[conversation.id]: mergedConversation,
|
||||
};
|
||||
} else {
|
||||
_state.allConversations.push(conversation);
|
||||
upsertConversationEntity(_state, conversation);
|
||||
}
|
||||
|
||||
_state.conversationPagesByTab = removeIdFromAllTabs(
|
||||
_state.conversationPagesByTab,
|
||||
conversation.id
|
||||
);
|
||||
|
||||
tabKeys.forEach(tabKey => {
|
||||
rebuildTabPagesWithConversation(_state, tabKey, conversation.id);
|
||||
});
|
||||
|
||||
if (_state.selectedChatId === conversation.id) {
|
||||
emitter.emit(BUS_EVENTS.FETCH_LABEL_SUGGESTIONS);
|
||||
emitter.emit(BUS_EVENTS.SCROLL_TO_MESSAGE);
|
||||
}
|
||||
},
|
||||
|
||||
@@ -245,7 +434,7 @@ export const mutations = {
|
||||
_state,
|
||||
{ id, lastSeen, unreadCount = 0 }
|
||||
) {
|
||||
const [chat] = _state.allConversations.filter(c => c.id === id);
|
||||
const chat = _state.conversationsById[id];
|
||||
if (chat) {
|
||||
chat.agent_last_seen_at = lastSeen;
|
||||
chat.unread_count = unreadCount;
|
||||
@@ -261,12 +450,14 @@ export const mutations = {
|
||||
|
||||
// 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;
|
||||
const chat = _state.conversationsById[payload.id];
|
||||
if (chat) {
|
||||
chat.meta.assignee = payload.assignee;
|
||||
}
|
||||
},
|
||||
|
||||
[types.UPDATE_CONVERSATION_CONTACT](_state, { conversationId, ...payload }) {
|
||||
const [chat] = _state.allConversations.filter(c => c.id === conversationId);
|
||||
const chat = _state.conversationsById[conversationId];
|
||||
if (chat) {
|
||||
chat.meta.sender = payload;
|
||||
}
|
||||
@@ -277,23 +468,49 @@ export const mutations = {
|
||||
},
|
||||
|
||||
[types.SET_CONVERSATION_CAN_REPLY](_state, { conversationId, canReply }) {
|
||||
const [chat] = _state.allConversations.filter(c => c.id === conversationId);
|
||||
const chat = _state.conversationsById[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;
|
||||
const remaining = {};
|
||||
const removedIds = [];
|
||||
Object.values(_state.conversationsById).forEach(conversation => {
|
||||
if (conversation.meta?.sender?.id === contactId) {
|
||||
removedIds.push(conversation.id);
|
||||
} else {
|
||||
remaining[conversation.id] = conversation;
|
||||
}
|
||||
});
|
||||
|
||||
if (!removedIds.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
_state.conversationsById = remaining;
|
||||
removedIds.forEach(conversationId => {
|
||||
_state.conversationPagesByTab = removeIdFromAllTabs(
|
||||
_state.conversationPagesByTab,
|
||||
conversationId
|
||||
);
|
||||
});
|
||||
},
|
||||
|
||||
[types.SET_CONVERSATION_FILTERS](_state, data) {
|
||||
_state.appliedFilters = data;
|
||||
},
|
||||
|
||||
[types.SET_CONVERSATION_TAB_FILTERS](_state, { tab, filters }) {
|
||||
const normalizedTab = normalizeTabKey(tab);
|
||||
const { page, ...rest } = filters || {};
|
||||
_state.filtersByTab = {
|
||||
..._state.filtersByTab,
|
||||
[normalizedTab]: rest,
|
||||
};
|
||||
},
|
||||
|
||||
[types.CLEAR_CONVERSATION_FILTERS](_state) {
|
||||
_state.appliedFilters = [];
|
||||
},
|
||||
|
||||
@@ -22,6 +22,7 @@ export default {
|
||||
UPDATE_CONVERSATION_CONTACT: 'UPDATE_CONVERSATION_CONTACT',
|
||||
CLEAR_CONTACT_CONVERSATIONS: 'CLEAR_CONTACT_CONVERSATIONS',
|
||||
SET_CONVERSATION_FILTERS: 'SET_CONVERSATION_FILTERS',
|
||||
SET_CONVERSATION_TAB_FILTERS: 'SET_CONVERSATION_TAB_FILTERS',
|
||||
CLEAR_CONVERSATION_FILTERS: 'CLEAR_CONVERSATION_FILTERS',
|
||||
SET_CONVERSATION_LAST_SEEN: 'SET_CONVERSATION_LAST_SEEN',
|
||||
SET_LAST_MESSAGE_ID_IN_SYNC_CONVERSATION:
|
||||
|
||||
Reference in New Issue
Block a user