diff --git a/app/javascript/dashboard/components-next/sidebar/Sidebar.vue b/app/javascript/dashboard/components-next/sidebar/Sidebar.vue index d9f523dfc..f71652ca0 100644 --- a/app/javascript/dashboard/components-next/sidebar/Sidebar.vue +++ b/app/javascript/dashboard/components-next/sidebar/Sidebar.vue @@ -175,6 +175,9 @@ useEventListener(document, 'touchend', onResizeEnd); const inboxes = useMapGetter('inboxes/getInboxes'); const labels = useMapGetter('labels/getLabelsOnSidebar'); +const allUnreadCount = useMapGetter( + 'conversationUnreadCounts/getAllUnreadCount' +); const getInboxUnreadCount = useMapGetter( 'conversationUnreadCounts/getInboxUnreadCount' ); @@ -297,6 +300,7 @@ const menuItems = computed(() => { { name: 'All', label: t('SIDEBAR.ALL_CONVERSATIONS'), + badgeCount: allUnreadCount.value, activeOn: ['inbox_conversation'], to: accountScopedRoute('home'), }, diff --git a/app/javascript/dashboard/store/modules/conversationUnreadCounts.js b/app/javascript/dashboard/store/modules/conversationUnreadCounts.js index 0503c0806..c03249311 100644 --- a/app/javascript/dashboard/store/modules/conversationUnreadCounts.js +++ b/app/javascript/dashboard/store/modules/conversationUnreadCounts.js @@ -2,15 +2,21 @@ import ConversationAPI from '../../api/conversations'; import types from '../mutation-types'; export const state = { + allCount: 0, inboxes: {}, labels: {}, teams: {}, }; +const normalizeCount = count => { + const parsedCount = Number(count); + return Number.isFinite(parsedCount) && parsedCount > 0 ? parsedCount : 0; +}; + const normalizeCounts = counts => { return Object.entries(counts || {}).reduce((result, [id, count]) => { - const parsedCount = Number(count); - if (Number.isFinite(parsedCount) && parsedCount > 0) { + const parsedCount = normalizeCount(count); + if (parsedCount > 0) { result[String(id)] = parsedCount; } @@ -19,6 +25,9 @@ const normalizeCounts = counts => { }; export const getters = { + getAllUnreadCount($state) { + return $state.allCount; + }, getInboxUnreadCount: $state => inboxId => { return $state.inboxes[String(inboxId)] || 0; }, @@ -55,6 +64,7 @@ export const actions = { export const mutations = { [types.SET_CONVERSATION_UNREAD_COUNTS]($state, payload = {}) { + $state.allCount = normalizeCount(payload.all_count); $state.inboxes = normalizeCounts(payload.inboxes); $state.labels = normalizeCounts(payload.labels); $state.teams = normalizeCounts(payload.teams); diff --git a/app/javascript/dashboard/store/modules/specs/conversationUnreadCounts/actions.spec.js b/app/javascript/dashboard/store/modules/specs/conversationUnreadCounts/actions.spec.js index 3100cdd10..29fadc897 100644 --- a/app/javascript/dashboard/store/modules/specs/conversationUnreadCounts/actions.spec.js +++ b/app/javascript/dashboard/store/modules/specs/conversationUnreadCounts/actions.spec.js @@ -15,6 +15,7 @@ describe('#actions', () => { describe('#get', () => { it('commits unread counts when API is successful', async () => { const payload = { + all_count: 2, inboxes: { 1: '2' }, labels: { 3: 4 }, teams: { 5: 6 }, diff --git a/app/javascript/dashboard/store/modules/specs/conversationUnreadCounts/getters.spec.js b/app/javascript/dashboard/store/modules/specs/conversationUnreadCounts/getters.spec.js index a3e74fc37..9fe19e22d 100644 --- a/app/javascript/dashboard/store/modules/specs/conversationUnreadCounts/getters.spec.js +++ b/app/javascript/dashboard/store/modules/specs/conversationUnreadCounts/getters.spec.js @@ -3,6 +3,7 @@ import { getters } from '../../conversationUnreadCounts'; describe('#getters', () => { it('returns inbox unread count by id', () => { const state = { + allCount: 0, inboxes: { 1: 2 }, labels: {}, teams: {}, @@ -15,6 +16,7 @@ describe('#getters', () => { it('returns label unread count by id', () => { const state = { + allCount: 0, inboxes: {}, labels: { 3: 4 }, teams: {}, @@ -27,6 +29,7 @@ describe('#getters', () => { it('returns team unread count by id', () => { const state = { + allCount: 0, inboxes: {}, labels: {}, teams: { 5: 6 }, @@ -37,8 +40,20 @@ describe('#getters', () => { expect(getters.getTeamUnreadCount(state)(6)).toBe(0); }); + it('returns all unread count', () => { + const state = { + allCount: 7, + inboxes: {}, + labels: {}, + teams: {}, + }; + + expect(getters.getAllUnreadCount(state)).toBe(7); + }); + it('returns unread count maps', () => { const state = { + allCount: 0, inboxes: { 1: 2 }, labels: { 3: 4 }, teams: { 5: 6 }, diff --git a/app/javascript/dashboard/store/modules/specs/conversationUnreadCounts/mutations.spec.js b/app/javascript/dashboard/store/modules/specs/conversationUnreadCounts/mutations.spec.js index 3f7e2b1ec..8941d7430 100644 --- a/app/javascript/dashboard/store/modules/specs/conversationUnreadCounts/mutations.spec.js +++ b/app/javascript/dashboard/store/modules/specs/conversationUnreadCounts/mutations.spec.js @@ -4,9 +4,10 @@ import { mutations } from '../../conversationUnreadCounts'; describe('#mutations', () => { describe('#SET_CONVERSATION_UNREAD_COUNTS', () => { it('normalizes unread count payload', () => { - const state = { inboxes: {}, labels: {}, teams: {} }; + const state = { allCount: 0, inboxes: {}, labels: {}, teams: {} }; mutations[types.SET_CONVERSATION_UNREAD_COUNTS](state, { + all_count: '3', inboxes: { 1: '2', 2: 0, @@ -23,6 +24,7 @@ describe('#mutations', () => { }); expect(state).toEqual({ + allCount: 3, inboxes: { 1: 2 }, labels: { 4: 5 }, teams: { 6: 7 }, @@ -31,6 +33,7 @@ describe('#mutations', () => { it('clears counts when payload is empty', () => { const state = { + allCount: 2, inboxes: { 1: 2 }, labels: { 4: 5 }, teams: { 6: 7 }, @@ -39,10 +42,21 @@ describe('#mutations', () => { mutations[types.SET_CONVERSATION_UNREAD_COUNTS](state, {}); expect(state).toEqual({ + allCount: 0, inboxes: {}, labels: {}, teams: {}, }); }); + + it('normalizes invalid aggregate counts to zero', () => { + const state = { allCount: 2, inboxes: {}, labels: {}, teams: {} }; + + mutations[types.SET_CONVERSATION_UNREAD_COUNTS](state, { + all_count: 'invalid', + }); + + expect(state.allCount).toBe(0); + }); }); }); diff --git a/app/services/conversations/unread_counts/counter.rb b/app/services/conversations/unread_counts/counter.rb index f4126b61f..b1ba5ddb0 100644 --- a/app/services/conversations/unread_counts/counter.rb +++ b/app/services/conversations/unread_counts/counter.rb @@ -19,8 +19,11 @@ class Conversations::UnreadCounts::Counter ensure_base_cache! ensure_assignment_cache! if assignment_mode? + inbox_counts = unread_inbox_counts + { - inboxes: unread_inbox_counts, + all_count: inbox_counts.values.sum, + inboxes: inbox_counts, labels: unread_label_counts, teams: unread_team_counts } @@ -191,7 +194,7 @@ class Conversations::UnreadCounts::Counter end def empty_counts - { inboxes: {}, labels: {}, teams: {} } + { all_count: 0, inboxes: {}, labels: {}, teams: {} } end def store diff --git a/spec/controllers/api/v1/accounts/conversations_controller_spec.rb b/spec/controllers/api/v1/accounts/conversations_controller_spec.rb index f8fd446d2..bdb8117ac 100644 --- a/spec/controllers/api/v1/accounts/conversations_controller_spec.rb +++ b/spec/controllers/api/v1/accounts/conversations_controller_spec.rb @@ -141,6 +141,7 @@ RSpec.describe 'Conversations API', type: :request do expect(response).to have_http_status(:success) expect(response.parsed_body['payload']).to eq( + 'all_count' => 1, 'inboxes' => { visible_inbox.id.to_s => 1 }, 'labels' => { label.id.to_s => 1 }, 'teams' => {} diff --git a/spec/enterprise/services/conversations/unread_counts/counter_spec.rb b/spec/enterprise/services/conversations/unread_counts/counter_spec.rb index cbb2d6c98..e8f850e78 100644 --- a/spec/enterprise/services/conversations/unread_counts/counter_spec.rb +++ b/spec/enterprise/services/conversations/unread_counts/counter_spec.rb @@ -26,6 +26,7 @@ RSpec.describe Conversations::UnreadCounts::Counter do result = described_class.new(account: account, user: agent).perform + expect(result[:all_count]).to eq(2) expect(result[:inboxes]).to eq(inbox.id.to_s => 2) expect(result[:labels]).to eq(label.id.to_s => 2) expect(result[:teams]).to eq(team.id.to_s => 2) @@ -40,6 +41,7 @@ RSpec.describe Conversations::UnreadCounts::Counter do result = described_class.new(account: account, user: agent).perform + expect(result[:all_count]).to eq(2) expect(result[:inboxes]).to eq(inbox.id.to_s => 2) expect(result[:labels]).to eq(label.id.to_s => 2) expect(result[:teams]).to eq(team.id.to_s => 2) @@ -53,6 +55,7 @@ RSpec.describe Conversations::UnreadCounts::Counter do result = described_class.new(account: account, user: agent).perform + expect(result[:all_count]).to eq(1) expect(result[:inboxes]).to eq(inbox.id.to_s => 1) expect(result[:labels]).to eq(label.id.to_s => 1) expect(result[:teams]).to eq(team.id.to_s => 1) @@ -65,7 +68,7 @@ RSpec.describe Conversations::UnreadCounts::Counter do result = described_class.new(account: account, user: agent).perform - expect(result).to eq(inboxes: {}, labels: {}, teams: {}) + expect(result).to eq(all_count: 0, inboxes: {}, labels: {}, teams: {}) expect(store.base_ready?(account.id)).to be(false) expect(store.assignment_ready?(account.id)).to be(false) end diff --git a/spec/services/conversations/unread_counts/counter_spec.rb b/spec/services/conversations/unread_counts/counter_spec.rb index bfd10436e..723f2d437 100644 --- a/spec/services/conversations/unread_counts/counter_spec.rb +++ b/spec/services/conversations/unread_counts/counter_spec.rb @@ -62,6 +62,7 @@ RSpec.describe Conversations::UnreadCounts::Counter do result = described_class.new(account: account, user: agent).perform expect(result).to eq( + all_count: 1, inboxes: { visible_inbox.id.to_s => 1 }, labels: { label.id.to_s => 1 }, teams: { visible_team.id.to_s => 1 } @@ -75,6 +76,7 @@ RSpec.describe Conversations::UnreadCounts::Counter do result = described_class.new(account: account, user: admin).perform expect(result).to eq( + all_count: 2, inboxes: { visible_inbox.id.to_s => 1, hidden_inbox.id.to_s => 1 }, labels: { label.id.to_s => 2 }, teams: { visible_team.id.to_s => 2 } @@ -87,6 +89,7 @@ RSpec.describe Conversations::UnreadCounts::Counter do result = described_class.new(account: account, user: agent).perform expect(result).to eq( + all_count: 1, inboxes: { visible_inbox.id.to_s => 1 }, labels: {}, teams: { visible_team.id.to_s => 1 }