diff --git a/app/javascript/dashboard/helper/CacheHelper/cacheableModels.js b/app/javascript/dashboard/helper/CacheHelper/cacheableModels.js index 1c126975c..5ff42bbb5 100644 --- a/app/javascript/dashboard/helper/CacheHelper/cacheableModels.js +++ b/app/javascript/dashboard/helper/CacheHelper/cacheableModels.js @@ -6,9 +6,8 @@ // // `dispatchPath` is the full Vuex dispatch path for the revalidate action. // `setMutation` is the full commit path used by paintStoresFromCache to seed -// Vuex from IDB. `clearMutation` (optional) is committed BEFORE `setMutation` -// for modules whose SET_* mutation merges-by-id instead of replacing — without -// it, rows deleted server-side between sessions would survive as phantoms. +// Vuex from IDB. Every SET_* mutation must REPLACE its records (not merge) so +// rows deleted server-side never survive as phantoms. export const cacheableModels = [ { name: 'inbox', @@ -24,7 +23,6 @@ export const cacheableModels = [ name: 'team', dispatchPath: 'teams/revalidate', setMutation: 'teams/SET_TEAMS', - clearMutation: 'teams/CLEAR_TEAMS', }, { name: 'canned_response', diff --git a/app/javascript/dashboard/helper/CacheHelper/paintStoresFromCache.js b/app/javascript/dashboard/helper/CacheHelper/paintStoresFromCache.js index 73ca7ea2b..cd96208ac 100644 --- a/app/javascript/dashboard/helper/CacheHelper/paintStoresFromCache.js +++ b/app/javascript/dashboard/helper/CacheHelper/paintStoresFromCache.js @@ -25,7 +25,6 @@ export default async function paintStoresFromCache(store, accountId) { cacheableModels.map(async model => { const localData = await dm.get({ modelName: model.name }); if (localData.length === 0) return; - if (model.clearMutation) store.commit(model.clearMutation); store.commit(model.setMutation, localData); }) ); diff --git a/app/javascript/dashboard/helper/specs/CacheHelper/paintStoresFromCache.spec.js b/app/javascript/dashboard/helper/specs/CacheHelper/paintStoresFromCache.spec.js index 3ee385be0..31305127e 100644 --- a/app/javascript/dashboard/helper/specs/CacheHelper/paintStoresFromCache.spec.js +++ b/app/javascript/dashboard/helper/specs/CacheHelper/paintStoresFromCache.spec.js @@ -64,7 +64,7 @@ describe('paintStoresFromCache', () => { expect(storeMock.dispatch).not.toHaveBeenCalled(); }); - it('commits CLEAR_TEAMS before SET_TEAMS to drop phantom rows', async () => { + it('seeds teams via SET_TEAMS', async () => { await dm.push({ modelName: 'team', data: [{ id: 1, name: 'Sales' }], @@ -72,10 +72,8 @@ describe('paintStoresFromCache', () => { await paintStoresFromCache(storeMock, accountId); - const teamCommits = storeMock.commit.mock.calls.filter(call => - call[0].startsWith('teams/') - ); - expect(teamCommits[0][0]).toBe('teams/CLEAR_TEAMS'); - expect(teamCommits[1][0]).toBe('teams/SET_TEAMS'); + expect(storeMock.commit).toHaveBeenCalledWith('teams/SET_TEAMS', [ + { id: 1, name: 'Sales' }, + ]); }); }); diff --git a/app/javascript/dashboard/store/modules/specs/teams/actions.spec.js b/app/javascript/dashboard/store/modules/specs/teams/actions.spec.js index 5c1b5f1ce..07e8ac6ab 100644 --- a/app/javascript/dashboard/store/modules/specs/teams/actions.spec.js +++ b/app/javascript/dashboard/store/modules/specs/teams/actions.spec.js @@ -2,7 +2,6 @@ import axios from 'axios'; import { actions } from '../../teams/actions'; import { SET_TEAM_UI_FLAG, - CLEAR_TEAMS, SET_TEAMS, SET_TEAM_ITEM, EDIT_TEAM, @@ -41,7 +40,6 @@ describe('#actions', () => { await actions.get({ commit }); expect(commit.mock.calls).toEqual([ [SET_TEAM_UI_FLAG, { isFetching: true }], - [CLEAR_TEAMS], [SET_TEAMS, teamsList[1]], [SET_TEAM_UI_FLAG, { isFetching: false }], ]); diff --git a/app/javascript/dashboard/store/modules/specs/teams/mutations.spec.js b/app/javascript/dashboard/store/modules/specs/teams/mutations.spec.js index f20ea1df9..3a0cc2f7d 100644 --- a/app/javascript/dashboard/store/modules/specs/teams/mutations.spec.js +++ b/app/javascript/dashboard/store/modules/specs/teams/mutations.spec.js @@ -1,5 +1,4 @@ import { - CLEAR_TEAMS, SET_TEAMS, SET_TEAM_ITEM, EDIT_TEAM, @@ -11,10 +10,15 @@ describe('#mutations', () => { describe('#SET_teams', () => { it('set teams records', () => { const state = { records: {} }; - mutations[SET_TEAMS](state, [teams[1]]); - mutations[SET_TEAMS](state, [teams[2]]); + mutations[SET_TEAMS](state, [teams[1], teams[2]]); expect(state.records).toEqual(teams); }); + + it('drops records absent from the new list', () => { + const state = { records: { ...teams } }; + mutations[SET_TEAMS](state, [teams[1]]); + expect(state.records).toEqual({ 1: teams[1] }); + }); }); describe('#ADD_TEAM', () => { @@ -43,12 +47,4 @@ describe('#mutations', () => { expect(state.records).toEqual({}); }); }); - - describe('#CLEAR_TEAMS', () => { - it('delete teams record', () => { - const state = { records: { 1: teams[1] } }; - mutations[CLEAR_TEAMS](state); - expect(state.records).toEqual({}); - }); - }); }); diff --git a/app/javascript/dashboard/store/modules/teams/actions.js b/app/javascript/dashboard/store/modules/teams/actions.js index baf285822..adfd57f3a 100644 --- a/app/javascript/dashboard/store/modules/teams/actions.js +++ b/app/javascript/dashboard/store/modules/teams/actions.js @@ -1,6 +1,5 @@ import { SET_TEAM_UI_FLAG, - CLEAR_TEAMS, SET_TEAMS, SET_TEAM_ITEM, EDIT_TEAM, @@ -26,13 +25,11 @@ export const actions = { revalidate: createCacheRevalidateAction({ api: TeamsAPI, mutation: SET_TEAMS, - clearMutation: CLEAR_TEAMS, }), get: async ({ commit }) => { commit(SET_TEAM_UI_FLAG, { isFetching: true }); try { const { data } = await TeamsAPI.get(true); - commit(CLEAR_TEAMS); commit(SET_TEAMS, data); } catch (error) { throw new Error(error); diff --git a/app/javascript/dashboard/store/modules/teams/mutations.js b/app/javascript/dashboard/store/modules/teams/mutations.js index 1c1c3fed2..fd27b252e 100644 --- a/app/javascript/dashboard/store/modules/teams/mutations.js +++ b/app/javascript/dashboard/store/modules/teams/mutations.js @@ -1,6 +1,5 @@ import { SET_TEAM_UI_FLAG, - CLEAR_TEAMS, SET_TEAMS, SET_TEAM_ITEM, EDIT_TEAM, @@ -15,19 +14,14 @@ export const mutations = { }; }, - [CLEAR_TEAMS]: $state => { - $state.records = {}; - }, - + // Replaces (not merges) so rows deleted server-side never survive as + // phantoms — SET_TEAMS only ever receives the full list. [SET_TEAMS]: ($state, data) => { - const updatedRecords = { ...$state.records }; + const records = {}; data.forEach(team => { - updatedRecords[team.id] = { - ...(updatedRecords[team.id] || {}), - ...team, - }; + records[team.id] = team; }); - $state.records = updatedRecords; + $state.records = records; }, [SET_TEAM_ITEM]: ($state, data) => { diff --git a/app/javascript/dashboard/store/modules/teams/types.js b/app/javascript/dashboard/store/modules/teams/types.js index 7c0a3e8f4..256031ebe 100644 --- a/app/javascript/dashboard/store/modules/teams/types.js +++ b/app/javascript/dashboard/store/modules/teams/types.js @@ -1,5 +1,4 @@ export const SET_TEAM_UI_FLAG = 'SET_TEAM_UI_FLAG'; -export const CLEAR_TEAMS = 'CLEAR_TEAMS'; export const SET_TEAMS = 'SET_TEAMS'; export const SET_TEAM_ITEM = 'SET_TEAM_ITEM'; export const EDIT_TEAM = 'EDIT_TEAM';