import * as IDB from 'idb-keyval/dist/index.js'; export const HISTORY_PREFIX = 'HOMEBREWERY-HISTORY'; export const HISTORY_SLOTS = 5; // History values in minutes const HISTORY_SAVE_DELAYS = { '0' : 0, '1' : 2, '2' : 10, '3' : 60, '4' : 12 * 60, '5' : 2 * 24 * 60 }; // const HISTORY_SAVE_DELAYS = { // '0' : 0, // '1' : 1, // '2' : 2, // '3' : 3, // '4' : 4, // '5' : 5 // }; const HB_DB = 'HOMEBREWERY-DB'; const HB_STORE = 'HISTORY'; const GARBAGE_COLLECT_DELAY = 28 * 24 * 60; // const GARBAGE_COLLECT_DELAY = 10; function getKeyBySlot(brew, slot){ // Return a string representing the key for this brew and history slot return `${HISTORY_PREFIX}-${brew.shareId}-${slot}`; }; function parseBrewForStorage(brew, slot = 0) { // Strip out unneeded object properties // Returns an array of [ key, brew ] const archiveBrew = { title : brew.title, text : brew.text, style : brew.style, version : brew.version, shareId : brew.shareId, savedAt : brew?.savedAt || new Date(), expireAt : new Date() }; archiveBrew.expireAt.setMinutes(archiveBrew.expireAt.getMinutes() + HISTORY_SAVE_DELAYS[slot]); const key = getKeyBySlot(brew, slot); return [key, archiveBrew]; } // Create a custom IDB store async function createHBStore(){ return await IDB.createStore(HB_DB, HB_STORE); } export async function loadHistory(brew){ const DEFAULT_HISTORY_ITEM = { expireAt: '2000-01-01T00:00:00.000Z', shareId: brew.shareId, noData: true }; const historyKeys = []; // Create array of all history keys for (let i = 1; i <= HISTORY_SLOTS; i++){ historyKeys.push(getKeyBySlot(brew, i)); }; // Load all keys from IDB at once const dataArray = await IDB.getMany(historyKeys, await createHBStore()); return dataArray.map((data)=>{ return data ?? DEFAULT_HISTORY_ITEM; }); } export async function updateHistory(brew) { const history = await loadHistory(brew); // Walk each version position for (let slot = HISTORY_SLOTS - 1; slot >= 0; slot--){ const storedVersion = history[slot]; // If slot has expired, update all lower slots and break if(new Date() >= new Date(storedVersion.expireAt)){ // Create array of arrays : [ [key1, value1], [key2, value2], ..., [keyN, valueN] ] // to pass to IDB.setMany const historyUpdate = []; for (let updateSlot = slot; updateSlot > 0; updateSlot--){ // Move data from updateSlot to updateSlot + 1 if(!history[updateSlot - 1]?.noData) { historyUpdate.push(parseBrewForStorage(history[updateSlot - 1], updateSlot + 1)); } }; // Update the most recent brew historyUpdate.push(parseBrewForStorage(brew, 1)); await IDB.setMany(historyUpdate, await createHBStore()); // Break out of data checks because we found an expired value break; } }; }; export async function versionHistoryGarbageCollection(){ const entries = await IDB.entries(await createHBStore()); for (const [key, value] of entries){ const expireAt = new Date(value.savedAt); expireAt.setMinutes(expireAt.getMinutes() + GARBAGE_COLLECT_DELAY); if(new Date() > expireAt){ await IDB.del(key, await createHBStore()); }; }; };