fix: Prevent duplicate conversations in conversation list (#13713)
Agents using API channel inboxes (e.g., WhatsApp Automate) reported seeing the same conversation appear twice in their conversation list — one showing the last message preview and the other showing "No Messages". Backend investigation confirmed no duplicate conversations exist in the database, making this purely a frontend issue. The root cause is a race condition in WebSocket event delivery. When a conversation is created via the API with auto-assignment, the backend enqueues multiple ActionCable broadcast jobs (`conversation.created`, `assignee.changed`, `team.changed`) within milliseconds of each other. In production with multi-threaded Sidekiq workers, these events can arrive at the frontend out of order. If `assignee.changed` arrives before `conversation.created`, the `UPDATE_CONVERSATION` mutation pushes the conversation into the store (since it doesn't exist yet), and then `ADD_CONVERSATION` blindly pushes it again — resulting in a duplicate entry. The fix adds a uniqueness check in the `ADD_CONVERSATION` mutation to skip the push if a conversation with the same ID already exists in the store, matching the dedup pattern already used by `SET_ALL_CONVERSATION`.
This commit is contained in:
@@ -228,7 +228,10 @@ export const mutations = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
[types.ADD_CONVERSATION](_state, conversation) {
|
[types.ADD_CONVERSATION](_state, conversation) {
|
||||||
|
const exists = _state.allConversations.some(c => c.id === conversation.id);
|
||||||
|
if (!exists) {
|
||||||
_state.allConversations.push(conversation);
|
_state.allConversations.push(conversation);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
[types.DELETE_CONVERSATION](_state, conversationId) {
|
[types.DELETE_CONVERSATION](_state, conversationId) {
|
||||||
|
|||||||
@@ -975,6 +975,16 @@ describe('#mutations', () => {
|
|||||||
mutations[types.ADD_CONVERSATION](state, conversation);
|
mutations[types.ADD_CONVERSATION](state, conversation);
|
||||||
expect(state.allConversations).toEqual([conversation]);
|
expect(state.allConversations).toEqual([conversation]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should not add a duplicate conversation', () => {
|
||||||
|
const conversation = { id: 1, messages: [] };
|
||||||
|
const state = {
|
||||||
|
allConversations: [conversation],
|
||||||
|
};
|
||||||
|
|
||||||
|
mutations[types.ADD_CONVERSATION](state, { id: 1, messages: [] });
|
||||||
|
expect(state.allConversations).toHaveLength(1);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('#DELETE_CONVERSATION', () => {
|
describe('#DELETE_CONVERSATION', () => {
|
||||||
|
|||||||
Reference in New Issue
Block a user