diff --git a/app/javascript/dashboard/helper/CacheHelper/DataManager.js b/app/javascript/dashboard/helper/CacheHelper/DataManager.js index 23beaa971..909301e70 100644 --- a/app/javascript/dashboard/helper/CacheHelper/DataManager.js +++ b/app/javascript/dashboard/helper/CacheHelper/DataManager.js @@ -1,9 +1,10 @@ import { openDB } from 'idb'; import { DATA_VERSION } from './version'; +import { cacheableModels, cacheableModelNames } from './cacheableModels'; export class DataManager { constructor(accountId) { - this.modelsToSync = ['inbox', 'label', 'team']; + this.modelsToSync = cacheableModelNames; this.accountId = accountId; this.db = null; } @@ -11,12 +12,16 @@ export class DataManager { async initDb() { if (this.db) return this.db; const dbName = `cw-store-${this.accountId}`; - this.db = await openDB(`cw-store-${this.accountId}`, DATA_VERSION, { + this.db = await openDB(dbName, DATA_VERSION, { upgrade(db) { - db.createObjectStore('cache-keys'); - db.createObjectStore('inbox', { keyPath: 'id' }); - db.createObjectStore('label', { keyPath: 'id' }); - db.createObjectStore('team', { keyPath: 'id' }); + if (!db.objectStoreNames.contains('cache-keys')) { + db.createObjectStore('cache-keys'); + } + cacheableModels.forEach(model => { + if (!db.objectStoreNames.contains(model.name)) { + db.createObjectStore(model.name, { keyPath: 'id' }); + } + }); }, }); @@ -41,7 +46,7 @@ export class DataManager { async replace({ modelName, data }) { this.validateModel(modelName); - this.db.clear(modelName); + await this.db.clear(modelName); return this.push({ modelName, data }); } @@ -65,9 +70,11 @@ export class DataManager { } async setCacheKeys(cacheKeys) { - Object.keys(cacheKeys).forEach(async modelName => { - this.db.put('cache-keys', cacheKeys[modelName], modelName); - }); + await Promise.all( + Object.entries(cacheKeys).map(([modelName, value]) => + this.db.put('cache-keys', value, modelName) + ) + ); } async getCacheKey(modelName) { diff --git a/app/javascript/dashboard/helper/CacheHelper/cacheableModels.js b/app/javascript/dashboard/helper/CacheHelper/cacheableModels.js new file mode 100644 index 000000000..9eec7f354 --- /dev/null +++ b/app/javascript/dashboard/helper/CacheHelper/cacheableModels.js @@ -0,0 +1,41 @@ +// Single source of truth for IDB-cached workspace config. +// +// Each entry must keep `name` equal to the Rails `Model.name.underscore` value +// so the server's `cache_keys` payload (and the IDB object store name) lines up +// with what the client looks up. +// +// `dispatchPath` is the full Vuex dispatch path for the revalidate action. +// `setMutation` is the full commit path used by hydrateStoresFromCache 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. +export const cacheableModels = [ + { + name: 'inbox', + dispatchPath: 'inboxes/revalidate', + setMutation: 'inboxes/SET_INBOXES', + }, + { + name: 'label', + dispatchPath: 'labels/revalidate', + setMutation: 'labels/SET_LABELS', + }, + { + name: 'team', + dispatchPath: 'teams/revalidate', + setMutation: 'teams/SET_TEAMS', + clearMutation: 'teams/CLEAR_TEAMS', + }, + { + name: 'canned_response', + dispatchPath: 'revalidateCannedResponses', + setMutation: 'SET_CANNED', + }, + { + name: 'account_user', + dispatchPath: 'agents/revalidate', + setMutation: 'agents/SET_AGENTS', + }, +]; + +export const cacheableModelNames = cacheableModels.map(model => model.name); diff --git a/app/javascript/dashboard/helper/CacheHelper/version.js b/app/javascript/dashboard/helper/CacheHelper/version.js index 07bd897f5..8cd958863 100644 --- a/app/javascript/dashboard/helper/CacheHelper/version.js +++ b/app/javascript/dashboard/helper/CacheHelper/version.js @@ -1,3 +1,9 @@ -// Monday, 13 March 2023 -// Change this version if you want to invalidate old data -export const DATA_VERSION = '1678706392'; +// Bump DATA_VERSION whenever a new object store is added to the IDB schema +// (the `upgrade()` callback in DataManager only runs when the stored DB +// version is less than the requested version). The upgrade body itself is +// idempotent — it conditionally creates each store via +// `objectStoreNames.contains(name)` — so a single bump is enough to add any +// number of new stores without losing existing data. +// +// Wednesday, 21 May 2026 — bumped to add canned_response + account_user stores +export const DATA_VERSION = '1747785600';