diff --git a/app/javascript/dashboard/helper/ConversationMetaThrottleManager.js b/app/javascript/dashboard/helper/ConversationMetaThrottleManager.js deleted file mode 100644 index 4f7c7a610..000000000 --- a/app/javascript/dashboard/helper/ConversationMetaThrottleManager.js +++ /dev/null @@ -1,29 +0,0 @@ -class ConversationMetaThrottleManager { - constructor() { - this.lastUpdatedTime = null; - } - - shouldThrottle(threshold = 10000) { - if (!this.lastUpdatedTime) { - return false; - } - - const currentTime = new Date().getTime(); - const lastUpdatedTime = new Date(this.lastUpdatedTime).getTime(); - - if (currentTime - lastUpdatedTime < threshold) { - return true; - } - return false; - } - - markUpdate() { - this.lastUpdatedTime = new Date(); - } - - reset() { - this.lastUpdatedTime = null; - } -} - -export default new ConversationMetaThrottleManager(); diff --git a/app/javascript/dashboard/helper/specs/ConversationMetaThrottleManager.spec.js b/app/javascript/dashboard/helper/specs/ConversationMetaThrottleManager.spec.js deleted file mode 100644 index b0c67ffaa..000000000 --- a/app/javascript/dashboard/helper/specs/ConversationMetaThrottleManager.spec.js +++ /dev/null @@ -1,34 +0,0 @@ -import ConversationMetaThrottleManager from '../ConversationMetaThrottleManager'; - -describe('ConversationMetaThrottleManager', () => { - beforeEach(() => { - // Reset the lastUpdatedTime before each test - ConversationMetaThrottleManager.lastUpdatedTime = null; - }); - - describe('shouldThrottle', () => { - it('returns false when lastUpdatedTime is not set', () => { - expect(ConversationMetaThrottleManager.shouldThrottle()).toBe(false); - }); - - it('returns true when time difference is less than threshold', () => { - ConversationMetaThrottleManager.markUpdate(); - expect(ConversationMetaThrottleManager.shouldThrottle()).toBe(true); - }); - - it('returns false when time difference is more than threshold', () => { - ConversationMetaThrottleManager.lastUpdatedTime = new Date( - Date.now() - 11000 - ); - expect(ConversationMetaThrottleManager.shouldThrottle()).toBe(false); - }); - - it('respects custom threshold value', () => { - ConversationMetaThrottleManager.lastUpdatedTime = new Date( - Date.now() - 5000 - ); - expect(ConversationMetaThrottleManager.shouldThrottle(3000)).toBe(false); - expect(ConversationMetaThrottleManager.shouldThrottle(6000)).toBe(true); - }); - }); -}); diff --git a/app/javascript/dashboard/store/modules/conversationStats.js b/app/javascript/dashboard/store/modules/conversationStats.js index 8de20370b..ef70500ef 100644 --- a/app/javascript/dashboard/store/modules/conversationStats.js +++ b/app/javascript/dashboard/store/modules/conversationStats.js @@ -1,7 +1,6 @@ import types from '../mutation-types'; import ConversationApi from '../../api/inbox/conversation'; - -import ConversationMetaThrottleManager from 'dashboard/helper/ConversationMetaThrottleManager'; +import { debounce } from '@chatwoot/utils'; const state = { mineCount: 0, @@ -13,38 +12,24 @@ export const getters = { getStats: $state => $state, }; -export const shouldThrottle = conversationCount => { - // The threshold for throttling is different for normal users and large accounts - // Normal users: 2 seconds - // Large accounts: 10 seconds - // We would only update the conversation stats based on the threshold above. - // This is done to reduce the number of /meta request made to the server. - const NORMAL_USER_THRESHOLD = 2000; - const LARGE_ACCOUNT_THRESHOLD = 10000; - - const threshold = - conversationCount > 100 ? LARGE_ACCOUNT_THRESHOLD : NORMAL_USER_THRESHOLD; - return ConversationMetaThrottleManager.shouldThrottle(threshold); +// Create a debounced version of the actual API call function +const fetchMetaData = async (commit, params) => { + try { + const response = await ConversationApi.meta(params); + const { + data: { meta }, + } = response; + commit(types.SET_CONV_TAB_META, meta); + } catch (error) { + // ignore + } }; -export const actions = { - get: async ({ commit, state: $state }, params) => { - if (shouldThrottle($state.allCount)) { - // eslint-disable-next-line no-console - console.warn('Throttle /meta fetch, will resume after threshold'); - return; - } - ConversationMetaThrottleManager.markUpdate(); +const debouncedFetchMetaData = debounce(fetchMetaData, 500, false, 2500); - try { - const response = await ConversationApi.meta(params); - const { - data: { meta }, - } = response; - commit(types.SET_CONV_TAB_META, meta); - } catch (error) { - // Ignore error - } +export const actions = { + get: async ({ commit }, params) => { + debouncedFetchMetaData(commit, params); }, set({ commit }, meta) { commit(types.SET_CONV_TAB_META, meta); diff --git a/app/javascript/dashboard/store/modules/specs/conversationStats/actions.spec.js b/app/javascript/dashboard/store/modules/specs/conversationStats/actions.spec.js index 4572387ac..43f0efb3a 100644 --- a/app/javascript/dashboard/store/modules/specs/conversationStats/actions.spec.js +++ b/app/javascript/dashboard/store/modules/specs/conversationStats/actions.spec.js @@ -6,22 +6,41 @@ const commit = vi.fn(); global.axios = axios; vi.mock('axios'); +vi.mock('@chatwoot/utils', () => ({ + debounce: vi.fn(fn => { + return fn; + }), +})); + describe('#actions', () => { + beforeEach(() => { + vi.useFakeTimers(); // Set up fake timers + commit.mockClear(); + }); + + afterEach(() => { + vi.useRealTimers(); // Reset to real timers after each test + }); + describe('#get', () => { it('sends correct mutations if API is success', async () => { axios.get.mockResolvedValue({ data: { meta: { mine_count: 1 } } }); - await actions.get( - { commit, state: { updatedOn: null } }, + actions.get( + { commit, state: { allCount: 0 } }, { inboxId: 1, assigneeTpe: 'me', status: 'open' } ); + + await vi.runAllTimersAsync(); + await vi.waitFor(() => expect(commit).toHaveBeenCalled()); + expect(commit.mock.calls).toEqual([ [types.default.SET_CONV_TAB_META, { mine_count: 1 }], ]); }); it('sends correct actions if API is error', async () => { axios.get.mockRejectedValue({ message: 'Incorrect header' }); - await actions.get( - { commit, state: { updatedOn: null } }, + actions.get( + { commit, state: { allCount: 0 } }, { inboxId: 1, assigneeTpe: 'me', status: 'open' } ); expect(commit.mock.calls).toEqual([]); diff --git a/app/javascript/dashboard/store/modules/specs/conversationStats/helper.spec.js b/app/javascript/dashboard/store/modules/specs/conversationStats/helper.spec.js deleted file mode 100644 index eb07fd1c8..000000000 --- a/app/javascript/dashboard/store/modules/specs/conversationStats/helper.spec.js +++ /dev/null @@ -1,37 +0,0 @@ -import { describe, it, expect, vi, beforeEach } from 'vitest'; -import ConversationMetaThrottleManager from 'dashboard/helper/ConversationMetaThrottleManager'; -import { shouldThrottle } from '../../conversationStats'; - -vi.mock('dashboard/helper/ConversationMetaThrottleManager', () => ({ - default: { - shouldThrottle: vi.fn(), - }, -})); - -describe('shouldThrottle', () => { - beforeEach(() => { - vi.clearAllMocks(); - }); - - it('uses normal threshold for accounts with 100 or fewer conversations', () => { - shouldThrottle(100); - expect(ConversationMetaThrottleManager.shouldThrottle).toHaveBeenCalledWith( - 2000 - ); - }); - - it('uses large account threshold for accounts with more than 100 conversations', () => { - shouldThrottle(101); - expect(ConversationMetaThrottleManager.shouldThrottle).toHaveBeenCalledWith( - 10000 - ); - }); - - it('returns the throttle value from ConversationMetaThrottleManager', () => { - ConversationMetaThrottleManager.shouldThrottle.mockReturnValue(true); - expect(shouldThrottle(50)).toBe(true); - - ConversationMetaThrottleManager.shouldThrottle.mockReturnValue(false); - expect(shouldThrottle(150)).toBe(false); - }); -}); diff --git a/package.json b/package.json index 9e5bab631..6d7300667 100644 --- a/package.json +++ b/package.json @@ -34,7 +34,7 @@ "@breezystack/lamejs": "^1.2.7", "@chatwoot/ninja-keys": "1.2.3", "@chatwoot/prosemirror-schema": "1.1.1-next", - "@chatwoot/utils": "^0.0.41", + "@chatwoot/utils": "^0.0.42", "@formkit/core": "^1.6.7", "@formkit/vue": "^1.6.7", "@hcaptcha/vue3-hcaptcha": "^1.3.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6749103a8..b32dc7521 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -23,8 +23,8 @@ importers: specifier: 1.1.1-next version: 1.1.1-next '@chatwoot/utils': - specifier: ^0.0.41 - version: 0.0.41 + specifier: ^0.0.42 + version: 0.0.42 '@formkit/core': specifier: ^1.6.7 version: 1.6.7 @@ -406,8 +406,8 @@ packages: '@chatwoot/prosemirror-schema@1.1.1-next': resolution: {integrity: sha512-/M2qZ+ZF7GlQNt1riwVP499fvp3hxSqd5iy8hxyF9pkj9qQ+OKYn5JK+v3qwwqQY3IxhmNOn1Lp6tm7vstrd9Q==} - '@chatwoot/utils@0.0.41': - resolution: {integrity: sha512-f0D+XArVYbc9m9M7KZpCaVJ+EUVzobX+D9P5Vt/h2jUipg706GoBhGwsP8kjfWhUdNdcS+H+OB4ZCKGF1NIkTQ==} + '@chatwoot/utils@0.0.42': + resolution: {integrity: sha512-TrEywcG1zjgBScVrQla7GMJwXsbLyc5u/verm/LbLrGxizU2NcNoJecRvJOUgL65kYVEtcO9//+gIDswwqnt6g==} engines: {node: '>=10'} '@codemirror/commands@6.7.0': @@ -5250,7 +5250,7 @@ snapshots: prosemirror-utils: 1.2.2(prosemirror-model@1.22.3)(prosemirror-state@1.4.3) prosemirror-view: 1.34.1 - '@chatwoot/utils@0.0.41': + '@chatwoot/utils@0.0.42': dependencies: date-fns: 2.30.0