fix(frontend): stop offline conversation request loop

This commit is contained in:
2026-07-12 22:16:16 +08:00
parent f923791d39
commit b0bb7f0702
10 changed files with 152 additions and 12 deletions
@@ -44,6 +44,7 @@ const actions = {
},
fetchAllConversations: async ({ commit, state, dispatch }, explicitFilters) => {
commit(types.CLEAR_LIST_ERROR_STATUS);
commit(types.SET_LIST_LOADING_STATUS);
try {
const params = explicitFilters || state.conversationFilters;
@@ -56,12 +57,19 @@ const actions = {
data,
params.assigneeType
);
commit(types.CLEAR_LIST_ERROR_STATUS);
} catch (error) {
// Handle error
// Stop the infinite-scroll observer from immediately retrying a failed
// page while the backend is unavailable. Reconnect/manual fetches clear
// this flag before trying again.
commit(types.SET_LIST_ERROR_STATUS);
} finally {
commit(types.CLEAR_LIST_LOADING_STATUS);
}
},
fetchFilteredConversations: async ({ commit, dispatch }, params) => {
commit(types.CLEAR_LIST_ERROR_STATUS);
commit(types.SET_LIST_LOADING_STATUS);
try {
const { data } = await ConversationApi.filter(params);
@@ -71,8 +79,11 @@ const actions = {
data,
'appliedFilters'
);
commit(types.CLEAR_LIST_ERROR_STATUS);
} catch (error) {
// Handle error
commit(types.SET_LIST_ERROR_STATUS);
} finally {
commit(types.CLEAR_LIST_LOADING_STATUS);
}
},
@@ -138,6 +138,7 @@ const getters = {
});
},
getChatListLoadingStatus: ({ listLoadingStatus }) => listLoadingStatus,
getChatListErrorStatus: ({ listErrorStatus }) => listErrorStatus,
getAllMessagesLoaded(_state) {
const [chat] = getSelectedChatConversation(_state);
return !chat || chat.allMessagesLoaded === undefined
@@ -12,6 +12,7 @@ const state = {
allConversations: [],
attachments: {},
listLoadingStatus: true,
listErrorStatus: false,
chatStatusFilter: wootConstants.STATUS_TYPE.OPEN,
chatSortFilter: wootConstants.SORT_BY_TYPE.LATEST,
currentInbox: null,
@@ -274,6 +275,14 @@ export const mutations = {
_state.listLoadingStatus = false;
},
[types.SET_LIST_ERROR_STATUS](_state) {
_state.listErrorStatus = true;
},
[types.CLEAR_LIST_ERROR_STATUS](_state) {
_state.listErrorStatus = false;
},
[types.UPDATE_MESSAGE_UNREAD_COUNT](
_state,
{ id, lastSeen, unreadCount = 0 }
@@ -431,15 +431,101 @@ describe('#actions', () => {
});
describe('#fetchFilteredConversations', () => {
it('fetches filtered conversations with a mock commit', async () => {
it('fetches filtered conversations and clears the loading state', async () => {
const localCommit = vi.fn();
const localDispatch = vi.fn();
axios.post.mockResolvedValue({
data: dataReceived,
});
await actions.fetchFilteredConversations({ commit }, dataToSend);
expect(commit).toHaveBeenCalledTimes(2);
expect(commit.mock.calls).toEqual([
['SET_LIST_LOADING_STATUS'],
['SET_ALL_CONVERSATION', dataReceived.payload],
await actions.fetchFilteredConversations(
{ commit: localCommit, dispatch: localDispatch },
dataToSend
);
expect(localCommit.mock.calls).toEqual([
[types.CLEAR_LIST_ERROR_STATUS],
[types.SET_LIST_LOADING_STATUS],
[types.SET_ALL_CONVERSATION, dataReceived.payload],
[
`contacts/${types.SET_CONTACTS}`,
dataReceived.payload.map(conversation => conversation.meta.sender),
],
[types.CLEAR_LIST_ERROR_STATUS],
[types.CLEAR_LIST_LOADING_STATUS],
]);
expect(localDispatch).toHaveBeenCalledWith(
'conversationStats/set',
dataReceived.meta
);
});
it('clears the loading state when the API request fails', async () => {
const localCommit = vi.fn();
axios.post.mockRejectedValue(new Error('Request failed'));
await actions.fetchFilteredConversations(
{ commit: localCommit, dispatch: vi.fn() },
dataToSend
);
expect(localCommit.mock.calls).toEqual([
[types.CLEAR_LIST_ERROR_STATUS],
[types.SET_LIST_LOADING_STATUS],
[types.SET_LIST_ERROR_STATUS],
[types.CLEAR_LIST_LOADING_STATUS],
]);
});
});
describe('#fetchAllConversations', () => {
it('fetches conversations and clears the loading state', async () => {
const localCommit = vi.fn();
const localDispatch = vi.fn();
const filters = {
inboxId: 4,
status: 'open',
assigneeType: 'all',
page: 1,
};
axios.get.mockResolvedValue({ data: { data: dataReceived } });
await actions.fetchAllConversations({
commit: localCommit,
dispatch: localDispatch,
state: { conversationFilters: filters },
});
expect(localCommit.mock.calls).toEqual([
[types.CLEAR_LIST_ERROR_STATUS],
[types.SET_LIST_LOADING_STATUS],
[types.SET_ALL_CONVERSATION, dataReceived.payload],
[
`contacts/${types.SET_CONTACTS}`,
dataReceived.payload.map(conversation => conversation.meta.sender),
],
[types.CLEAR_LIST_ERROR_STATUS],
[types.CLEAR_LIST_LOADING_STATUS],
]);
expect(localDispatch).toHaveBeenCalledWith(
'conversationStats/set',
dataReceived.meta
);
});
it('clears the loading state when the API request fails', async () => {
const localCommit = vi.fn();
axios.get.mockRejectedValue(new Error('Request failed'));
await actions.fetchAllConversations({
commit: localCommit,
dispatch: vi.fn(),
state: { conversationFilters: {} },
});
expect(localCommit.mock.calls).toEqual([
[types.CLEAR_LIST_ERROR_STATUS],
[types.SET_LIST_LOADING_STATUS],
[types.SET_LIST_ERROR_STATUS],
[types.CLEAR_LIST_LOADING_STATUS],
]);
});
});
@@ -1066,6 +1066,26 @@ describe('#mutations', () => {
});
});
describe('#SET_LIST_ERROR_STATUS', () => {
it('marks the conversation list request as failed', () => {
const state = { listErrorStatus: false };
mutations[types.SET_LIST_ERROR_STATUS](state);
expect(state.listErrorStatus).toBe(true);
});
});
describe('#CLEAR_LIST_ERROR_STATUS', () => {
it('clears the conversation list request error', () => {
const state = { listErrorStatus: true };
mutations[types.CLEAR_LIST_ERROR_STATUS](state);
expect(state.listErrorStatus).toBe(false);
});
});
describe('#CHANGE_CHAT_STATUS_FILTER', () => {
it('should update chat status filter', () => {
const state = {