commit f86b6adbec619415f237f1c8135c81e10e80edb6
parent 755b0014c96df6414bd77ffde9137773f31036fb
Author: Yureka <yuka@yuka.dev>
Date: Mon, 5 Sep 2022 17:35:11 +0200
parent 755b0014c96df6414bd77ffde9137773f31036fb
Author: Yureka <yuka@yuka.dev>
Date: Mon, 5 Sep 2022 17:35:11 +0200
datastorage abstraction
4 files changed, 138 insertions(+), 69 deletions(-)
M
|
155
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------
diff --git a/src/app_functions.js b/src/app_functions.js @@ -13,9 +13,6 @@ subscribeSettings(async () => { if (settings.showRIL100Names) await loadDS100(); }); -export const getJourneysHistory = () => db.getAll('journeysHistory'); - -export const addHistoryEntry = newEntry => db.put('journeysHistory', newEntry); const addJourneys = async data => { if (!data) return false; @@ -30,22 +27,19 @@ const addJourneys = async data => { journeyId: '' }; - const tx = db.transaction(['journey', 'journeysOverview', 'journeysHistory'], 'readwrite'); - const journeyStore = tx.objectStore('journey'); - const journeysOverviewStore = tx.objectStore('journeysOverview'); - const journeysHistoryStore = tx.objectStore('journeysHistory'); - let proms = data.journeys.map(j => { - j.settings = data.settings; - j.slug = data.slug; - journeyStore.put(j); + const journeyEntries = data.journeys.map(j => { + return { + ...j, + settings: data.settings, + slug: data.slug, + }; }); - proms.push(journeysOverviewStore.put({ + const journeysOverviewEntry = { ...data, journeys: data.journeys.map(j => j.refreshToken), - })); - proms.push(journeysHistoryStore.put(historyEntry)); - await Promise.all(proms); - await tx.done; + }; + + await db.addJourneys(journeyEntries, journeysOverviewEntry, historyEntry); //const lastHistoryEntry = (await getJourneysHistory()).slice(-1); //if (lastHistoryEntry[0] @@ -89,13 +83,13 @@ const processJourney = journey => { }; export const getJourneys = async slug => { - let data = await db.get('journeysOverview', slug); + let data = await db.getJourneysOverview(slug); data.journeys = await Promise.all(data.journeys.map(x => getJourney(x, data.profile))); return data; }; export const getJourney = async (refreshToken, profile) => { - let data = await db.get('journey', refreshToken); + let data = await db.getJourney(refreshToken); const settings = mkSettings(); if (!data || JSON.stringify(data.settings) != JSON.stringify(settings)) { data = await refreshJourney(refreshToken, profile); @@ -105,7 +99,7 @@ export const getJourney = async (refreshToken, profile) => { }; export const getMoreJourneys = async (slug, mode) => { - const saved = await db.get('journeysOverview', slug); + const saved = await db.getJourneysOverview(slug); const params = { ...saved.params, ...mkSettings() }; params[mode+'Than'] = saved[mode+'Ref']; let { departure, arrival, from, to, ...moreOpt } = params; @@ -135,20 +129,20 @@ export const getMoreJourneys = async (slug, mode) => { await addJourneys(res); }; export const refreshJourneys = async (slug) => { - const saved = await db.get('journeysOverview', slug); + const saved = await db.getJourneysOverview(slug); await Promise.all(saved.journeys.map(x => refreshJourney(x, saved.profile || "db"))); }; export const refreshJourney = async (refreshToken, profile) => { const client = await getHafasClient(profile || settings.profile || "db"); const requestSettings = mkSettings(); const [saved, data] = await Promise.all([ - db.get('journey', refreshToken), + db.getJourney(refreshToken), client.refreshJourney(trainsearchToHafas(refreshToken), requestSettings) ]); data.settings = requestSettings; data.refreshToken = hafasToTrainsearch(data.refreshToken); if (saved) data.slug = saved.slug; - db.put('journey', data); + db.updateJourney(data); return data; };
diff --git a/src/dataStorage.js b/src/dataStorage.js @@ -5,46 +5,123 @@ const dbName = devMode ? 'trainsearch_dev' : 'trainsearch'; export let db; +class LocalStorage { + async addJourneys(journeyEntries, overviewEntry, historyEntry) { + } + async getJourneysHistory() { + } + async addHistoryEntry(newEntry) { + } + async getSettings() { + } + async modifySettings(callback) { + } + async getJourneysOverview(slug) { + } + async getJourney(refreshToken) { + } + async updateJourney(data) { + } +} + +class IDBStorage { + constructor(idb) { + this.idb = idb; + } + + static async open() { + const idb = await openDB(dbName, 2, { + upgrade: (db, oldVersion, newVersion, transaction) => { + console.log(`upgrading database from ${oldVersion} to ${newVersion}`); + 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: + //... add migrations for 2->3 here + } + }, + blocking: async () => { + db.close(); + location.reload(); + }, + }); + 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); + }); + proms.push(journeysOverviewStore.put(overviewEntry)); + proms.push(journeysHistoryStore.put(historyEntry)); + proms.push(tx.done); + await Promise.all(proms); + } + async getJourneysHistory() { + return await this.idb.getAll('journeysHistory'); + } + async addHistoryEntry(newEntry) { + await this.idb.put('journeysHistory', newEntry); + } + async getSettings() { + return await this.idb.get('settings', 'settings'); + } + async modifySettings(cb) { + const tx = this.idb.transaction('settings', 'readwrite'); + const newSettings = callback(JSON.parse(JSON.stringify(this.getSettings()))); + 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) { + return await this.idb.get('journey', refreshToken); + } + async updateJourney(data) { + return await this.idb.put('journey', data); + } +} + export const initDataStorage = async () => { - db = await openDB(dbName, 2, { - upgrade: (db, oldVersion, newVersion, transaction) => { - console.log(`upgrading database from ${oldVersion} to ${newVersion}`); - 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: - //... add migrations for 2->3 here - } - }, - blocking: async () => { - db.close(); - location.reload(); - }, - }); + try { + db = await IDBStorage.open(); + } catch(e) { + console.log("indexeddb initialization failed: ", e); + console.log("falling back to localstorage"); + db = new LocalStorage(); + } }; export const clearDataStorage = () => deleteDB(dbName);
diff --git a/src/searchView.js b/src/searchView.js @@ -1,5 +1,6 @@ import { showDiv, ElementById, padZeros, isValidDate, formatFromTo } from './helpers.js'; -import { parseName, ConsoleLog, t, loadDS100, getJourneys, getJourneysHistory, newJourneys } from './app_functions.js'; +import { db } from './dataStorage.js'; +import { parseName, ConsoleLog, t, loadDS100, getJourneys, newJourneys } from './app_functions.js'; import { modifySettings, settings } from './settings.js'; import { go } from './router.js'; import { html, render } from 'lit-html'; @@ -155,7 +156,7 @@ const journeysHistoryAction = (journeysHistory, element) => { }; export const searchView = async () => { - const journeysHistory = (await getJourneysHistory()).slice().reverse(); + const journeysHistory = (await db.getJourneysHistory()).slice().reverse(); render(searchTemplate(journeysHistory), ElementById('content')); ElementById('from').focus(); @@ -331,7 +332,7 @@ export const swapFromTo = () => { }; export const setFromHistory = async id => { - const entry = (await getJourneysHistory())[id]; + const entry = (await db.getJourneysHistory())[id]; if (!entry) return; setSuggestion(entry.fromPoint, 'from'); setSuggestion(entry.toPoint, 'to');
diff --git a/src/settings.js b/src/settings.js @@ -41,15 +41,12 @@ export const subscribeSettings = cb => { }; export const initSettings = async () => { - settings = (await db.get('settings', 'settings')) || defaultSettings; + settings = (await db.getSettings) || defaultSettings; for (const cb of subscribers) await cb(); }; export const modifySettings = async callback => { - const tx = db.transaction('settings', 'readwrite'); - const newSettings = callback(JSON.parse(JSON.stringify(settings))); - await tx.store.put(newSettings, 'settings'); - await tx.done; + const newSettings = db.modifySettings(callback); Object.freeze(newSettings); settings = newSettings; for (const cb of subscribers) await cb();