import { openDB, deleteDB } from 'idb'; export let db; const dbName = !isDevServer ? 'trainsearch' : 'trainsearch_dev'; class IDBStorage { constructor(idb) { this.idb = idb; } static async open() { const idb = await openDB(dbName, 3, { upgrade: async (db, oldVersion, newVersion, transaction) => { console.log(`upgrading database from ${oldVersion} to ${newVersion}`); let settings; switch (oldVersion) { case 0: /* * in database scheme v1, the `journeys` object store stores whole searches, including journey data */ db.createObjectStore('journeys', {keyPath: 'slug'}); db.createObjectStore('journeysHistory', {autoIncrement: true}); db.createObjectStore('settings'); case 1: /* * in database scheme v2, there are the following data stores: * `journey` for storing individual journeys with their refreshToken as index * `journeysOverview`t contains the search results with the `.journeys` field being an array of refreshTokens. * * in comparison to v1, there are these additional changes: * `journeys` object store is deleted. * `journeysHistory` object store is cleared/recreated. */ /* clean up old stuff */ db.deleteObjectStore('journeys'); db.deleteObjectStore('journeysHistory'); /* create new stores */ db.createObjectStore('journey', {keyPath: 'refreshToken'}); db.createObjectStore('journeysOverview', {keyPath: 'slug'}); db.createObjectStore('journeysHistory', {autoIncrement: true}); case 2: settings = await transaction.objectStore('settings').get('settings'); if (settings !== undefined && settings.profile === 'vbb') { settings.profile = 'db'; } await transaction.objectStore('settings').put(settings, 'settings'); case 3: settings = await transaction.objectStore('settings').get('settings'); if (settings !== undefined && settings.bikeFriendly === undefined) { settings.bikeFriendly = false; } await transaction.objectStore('settings').put(settings, 'settings'); case 4: settings = await transaction.objectStore('settings').get('settings'); if (settings !== undefined && settings.loyaltyCard === undefined) { settings.loyaltyCard = 'NONE'; } await transaction.objectStore('settings').put(settings, 'settings'); case 5: settings = await transaction.objectStore('settings').get('settings'); if (settings !== undefined && settings.walkingSpeed === undefined) { settings.walkingSpeed = 'normal'; } await transaction.objectStore('settings').put(settings, 'settings'); case 5: settings = await transaction.objectStore('settings').get('settings'); if (settings !== undefined && settings.ageGroup === undefined) { settings.ageGroup = 'E'; } await transaction.objectStore('settings').put(settings, 'settings'); } } }); return new IDBStorage(idb); } async addJourneys(journeyEntries, overviewEntry, historyEntry) { const tx = this.idb.transaction(['journey', 'journeysOverview', 'journeysHistory'], 'readwrite'); const journeyStore = tx.objectStore('journey'); const journeysOverviewStore = tx.objectStore('journeysOverview'); const journeysHistoryStore = tx.objectStore('journeysHistory'); let proms = journeyEntries.map(j => { journeyStore.put(j); }); if (overviewEntry.historyEntryId === undefined) overviewEntry.historyEntryId = await journeysHistoryStore.put(historyEntry); proms.push(journeysOverviewStore.put(overviewEntry)); proms.push(tx.done); await Promise.all(proms); } async getHistory(profile) { const tx = this.idb.transaction('journeysHistory'); const history = []; for await (const cursor of tx.store) { if (profile !== undefined && profile !== cursor.value.profile) continue; history.push({ ...cursor.value, key: cursor.key, }); } return history; } async addHistoryEntry(entry) { await this.idb.put('journeysHistory', entry); } async updateHistoryEntry(key, entry) { await this.idb.put('journeysHistory', entry, key); } async getHistoryEntry(key) { return await this.idb.get('journeysHistory', key); } async getSettings() { return await this.idb.get('settings', 'settings'); } async modifySettings(prevSettings, callback) { const tx = this.idb.transaction('settings', 'readwrite'); const newSettings = callback(prevSettings); await Promise.all([ tx.store.put(newSettings, 'settings'), tx.done ]); return newSettings; } async getJourneysOverview(slug) { return await this.idb.get('journeysOverview', slug); } async getJourney(refreshToken) { const journeyObject = await this.idb.get('journey', refreshToken); return journeyObject; } async updateJourney(data) { return await this.idb.put('journey', data); } } export const clearDataStorage = async () => await deleteDB(dbName); export const initDataStorage = async () => { try { db = await IDBStorage.open(); } catch(e) { console.log("IndexedDB initialization failed: ", e); alert("IndexedDB initialization failed!"); } };