diff --git a/app/javascript/dashboard/components/ChatList.vue b/app/javascript/dashboard/components/ChatList.vue index 39fe35f3d..3cec279f7 100644 --- a/app/javascript/dashboard/components/ChatList.vue +++ b/app/javascript/dashboard/components/ChatList.vue @@ -401,7 +401,10 @@ function fetchFilteredConversations(payload) { queryData: filterQueryGenerator(payload), page, }) - .then(emitConversationLoaded); + .catch(() => useAlert(t('CHAT_LIST.FETCH_ERROR'))) + // emit even on failure so a deep-linked conversation still loads via + // fetchConversationIfUnavailable + .finally(emitConversationLoaded); showAdvancedFilters.value = false; } @@ -414,7 +417,8 @@ function fetchSavedFilteredConversations(payload) { queryData: payload, page, }) - .then(emitConversationLoaded); + .catch(() => useAlert(t('CHAT_LIST.FETCH_ERROR'))) + .finally(emitConversationLoaded); } function onApplyFilter(payload) { diff --git a/app/javascript/dashboard/helper/ReconnectService.js b/app/javascript/dashboard/helper/ReconnectService.js index 12ada24bd..3a5598d24 100644 --- a/app/javascript/dashboard/helper/ReconnectService.js +++ b/app/javascript/dashboard/helper/ReconnectService.js @@ -63,10 +63,14 @@ class ReconnectService { }; fetchFilteredOrSavedConversations = async queryData => { - await this.store.dispatch('fetchFilteredConversations', { - queryData, - page: 1, - }); + try { + await this.store.dispatch('fetchFilteredConversations', { + queryData, + page: 1, + }); + } catch (error) { + // Ignore error, reconnect flow should continue + } }; fetchConversationsOnReconnect = async () => { diff --git a/app/javascript/dashboard/i18n/locale/en/chatlist.json b/app/javascript/dashboard/i18n/locale/en/chatlist.json index 45755892d..af25c9148 100644 --- a/app/javascript/dashboard/i18n/locale/en/chatlist.json +++ b/app/javascript/dashboard/i18n/locale/en/chatlist.json @@ -7,6 +7,7 @@ "404": "There are no active conversations in this group." }, "FAILED_TO_SEND": "Failed to send", + "FETCH_ERROR": "Couldn't load conversations. Please try again.", "TAB_HEADING": "Conversations", "MENTION_HEADING": "Mentions", "UNATTENDED_HEADING": "Unattended", diff --git a/app/javascript/dashboard/store/modules/conversations/actions.js b/app/javascript/dashboard/store/modules/conversations/actions.js index 47481b697..9e149e8df 100644 --- a/app/javascript/dashboard/store/modules/conversations/actions.js +++ b/app/javascript/dashboard/store/modules/conversations/actions.js @@ -72,7 +72,8 @@ const actions = { 'appliedFilters' ); } catch (error) { - // Handle error + commit(types.CLEAR_LIST_LOADING_STATUS); + throw error; } }, diff --git a/app/javascript/dashboard/store/modules/specs/conversations/actions.spec.js b/app/javascript/dashboard/store/modules/specs/conversations/actions.spec.js index 5014b63ca..5294fbb46 100644 --- a/app/javascript/dashboard/store/modules/specs/conversations/actions.spec.js +++ b/app/javascript/dashboard/store/modules/specs/conversations/actions.spec.js @@ -437,11 +437,30 @@ describe('#actions', () => { axios.post.mockResolvedValue({ data: dataReceived, }); - await actions.fetchFilteredConversations({ commit }, dataToSend); - expect(commit).toHaveBeenCalledTimes(2); + await actions.fetchFilteredConversations( + { commit, dispatch }, + dataToSend + ); + expect(commit).toHaveBeenCalledTimes(4); expect(commit.mock.calls).toEqual([ ['SET_LIST_LOADING_STATUS'], ['SET_ALL_CONVERSATION', dataReceived.payload], + ['CLEAR_LIST_LOADING_STATUS'], + [ + `contacts/${types.SET_CONTACTS}`, + dataReceived.payload.map(chat => chat.meta.sender), + ], + ]); + }); + + it('clears the loading state and rethrows if the request fails', async () => { + axios.post.mockRejectedValue(new Error('Request failed')); + await expect( + actions.fetchFilteredConversations({ commit }, dataToSend) + ).rejects.toThrow('Request failed'); + expect(commit.mock.calls).toEqual([ + ['SET_LIST_LOADING_STATUS'], + ['CLEAR_LIST_LOADING_STATUS'], ]); }); });