diff --git a/app/javascript/dashboard/store/modules/conversations/helpers.js b/app/javascript/dashboard/store/modules/conversations/helpers.js index 0063c8cfc..7d1a5d23b 100644 --- a/app/javascript/dashboard/store/modules/conversations/helpers.js +++ b/app/javascript/dashboard/store/modules/conversations/helpers.js @@ -112,3 +112,57 @@ export const sortComparator = (a, b, sortKey) => { SORT_OPTIONS[sortKey] || SORT_OPTIONS.last_activity_at_desc; return sortConfig[sortMethod](a, b, sortDirection); }; + +export const deepObjectDiff = (original, updated, maxDepth = 3) => { + const changes = { + added: {}, + removed: {}, + modified: {}, + }; + + function isObject(item) { + return item && typeof item === 'object' && !Array.isArray(item); + } + + function compareObjects(orig, upd, path = '', depth = 1) { + if (depth > maxDepth) { + if (JSON.stringify(orig) !== JSON.stringify(upd)) { + changes.modified[path] = { from: orig, to: upd }; + } + return; + } + + // Get all keys from both objects + const allKeys = [...new Set([...Object.keys(orig), ...Object.keys(upd)])]; + + allKeys.forEach(key => { + const currentPath = path ? `${path}.${key}` : key; + + // Handle removed properties + if (!Object.keys(upd).includes(key)) { + changes.removed[currentPath] = orig[key]; + return; + } + + // Handle added properties + if (!Object.keys(orig).includes(key)) { + changes.added[currentPath] = upd[key]; + return; + } + + // Handle nested objects + if (isObject(orig[key]) && isObject(upd[key])) { + compareObjects(orig[key], upd[key], currentPath, depth + 1); + return; + } + + // Handle modified values + if (JSON.stringify(orig[key]) !== JSON.stringify(upd[key])) { + changes.modified[currentPath] = { from: orig[key], to: upd[key] }; + } + }); + } + + compareObjects(original, updated); + return changes; +}; diff --git a/app/javascript/dashboard/store/modules/conversations/index.js b/app/javascript/dashboard/store/modules/conversations/index.js index 8715d24ec..0da0de99b 100644 --- a/app/javascript/dashboard/store/modules/conversations/index.js +++ b/app/javascript/dashboard/store/modules/conversations/index.js @@ -1,7 +1,7 @@ import types from '../../mutation-types'; import getters, { getSelectedChatConversation } from './getters'; import actions from './actions'; -import { findPendingMessageIndex } from './helpers'; +import { findPendingMessageIndex, deepObjectDiff } from './helpers'; import { MESSAGE_STATUS } from 'shared/constants/messages'; import wootConstants from 'dashboard/constants/globals'; import { BUS_EVENTS } from '../../../../shared/constants/busEvents'; @@ -222,6 +222,12 @@ export const mutations = { scope.setContext('stored', selectedConversation); scope.setContext('incoming_meta', conversation.meta); scope.setContext('stored_meta', selectedConversation.meta); + + const diff = deepObjectDiff(selectedConversation, conversation); + scope.setContext('added', diff.added); + scope.setContext('removed', diff.removed); + scope.setContext('modified', diff.modified); + Sentry.captureMessage('Conversation update mismatch'); }); @@ -229,6 +235,20 @@ export const mutations = { } if (conversation.updated_at === selectedConversation.updated_at) { + Sentry.withScope(scope => { + scope.setContext('incoming', conversation); + scope.setContext('stored', selectedConversation); + scope.setContext('incoming_meta', conversation.meta); + scope.setContext('stored_meta', selectedConversation.meta); + + const diff = deepObjectDiff(selectedConversation, conversation); + scope.setContext('added', diff.added); + scope.setContext('removed', diff.removed); + scope.setContext('modified', diff.modified); + + Sentry.captureMessage('Conversation update overlap'); + }); + return; }