Skip to content

Commit e42382e

Browse files
committed
Partial upgrade to IndexedDB for cache and adminCache
Fixes #455
1 parent 4996abd commit e42382e

File tree

5 files changed

+142
-11
lines changed

5 files changed

+142
-11
lines changed

src/checker/admin.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,8 @@ try {
105105
if ((e.error === "Access denied.") || (e.message === "Access denied.")) return auth.admin(init);
106106
pollingOff();
107107
});
108-
const bulkLoad = storage.get("adminCache") || {};
108+
await storage.idbReady;
109+
const bulkLoad = (await storage.idbGet('adminCache')) || storage.get("adminCache") || {};
109110
courses = bulkLoad.courses || [];
110111
segments = bulkLoad.segments || [];
111112
questions = bulkLoad.questions || [];

src/checker/checker.js

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -333,7 +333,9 @@ try {
333333
}
334334
resetInputs();
335335
await auth.bulkLoad(["courses", "segments", "questions", "responses"], storage.get("code"), storage.get("password"));
336-
history = (storage.get("cache") || {}).responses || [];
336+
await storage.idbReady;
337+
const _cache = (await storage.idbGet('cache')) || storage.get('cache') || {};
338+
history = (_cache).responses || [];
337339
await updateHistory();
338340
await updateSegment(null, true);
339341
if ((typeof r.correct === 'undefined') || r.correct || (typeof r.error !== 'undefined')) {
@@ -438,7 +440,8 @@ try {
438440
const periodRange = getExtendedPeriodRange(null, Number(code.slice(0, 1)));
439441
try {
440442
await auth.bulkLoad(["courses", "segments", "questions", "responses"], storage.get("code"), storage.get("password"));
441-
const bulkLoad = storage.get("cache") || {};
443+
await storage.idbReady;
444+
const bulkLoad = (await storage.idbGet('cache')) || storage.get("cache") || {};
442445
courses = bulkLoad.courses;
443446
segmentsArray = bulkLoad.segments;
444447
questionsArray = bulkLoad.questions;
@@ -531,7 +534,8 @@ try {
531534
segments.removeEventListener("change", updateSegment);
532535
segments.addEventListener("change", updateSegment);
533536
// Update history feed
534-
history = (storage.get("cache") || {}).responses || [];
537+
await storage.idbReady;
538+
history = ((await storage.idbGet('cache')) || storage.get("cache") || {}).responses || [];
535539
await updateHistory();
536540
await updateSegment();
537541
// Show clear data fix guide

src/keybinds/keybinds.js

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { insertFromIndex } from "/src/symbols/symbols.js";
55
import { moveFromCurrent, renderExtras } from "/src/modules/island.js";
66

77
try {
8-
document.addEventListener("keydown", (e) => {
8+
document.addEventListener("keydown", async (e) => {
99
const anyDialogOpen = Array.from(document.querySelectorAll("dialog")).some(
1010
(dialog) => dialog.open,
1111
);
@@ -43,9 +43,10 @@ try {
4343
} else if (e.shiftKey) {
4444
if (e.key == "R" && !anyDialogOpen && !isTyping) {
4545
themes.resetTheme();
46-
storage.delete("cache");
46+
await storage.idbReady;
47+
storage.idbDelete("cache").catch((e) => console.error('IDB delete failed', e));
4748
storage.delete("lastBulkLoad");
48-
storage.delete("adminCache");
49+
storage.idbDelete("adminCache").catch((e) => console.error('IDB delete failed', e));
4950
storage.delete("lastAdminBulkLoad");
5051
location.reload();
5152
}

src/modules/auth.js

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -742,6 +742,7 @@ export async function loadAdminSettings(courses) {
742742

743743
export async function bulkLoad(fields = [], usr = null, pwd = null, isAdmin = false) {
744744
const startTime = Date.now();
745+
await storage.idbReady;
745746
const bulkLoadResponse = await fetch(`${domain}/bulk_load`, {
746747
method: "POST",
747748
headers: { "Content-Type": "application/json" },
@@ -750,9 +751,9 @@ export async function bulkLoad(fields = [], usr = null, pwd = null, isAdmin = fa
750751
pwd,
751752
fields,
752753
lastFetched: storage.get(isAdmin ? "lastAdminBulkLoad" : "lastBulkLoad") || null,
753-
syncDeleted: (() => {
754+
syncDeleted: (async () => {
754755
var cacheIds = {};
755-
var cache = storage.get(isAdmin ? "adminCache" : "cache") || {};
756+
var cache = (await storage.idbGet(isAdmin ? "adminCache" : "cache")) || storage.get(isAdmin ? "adminCache" : "cache") || {};
756757
for (const table in cache) {
757758
if (Array.isArray(cache[table] || [])) cacheIds[table] = (cache[table] || []).map(data => String(data.id || data.seatCode || data.period || data.key || data.username || 0));
758759
}
@@ -773,7 +774,7 @@ export async function bulkLoad(fields = [], usr = null, pwd = null, isAdmin = fa
773774
continue;
774775
}
775776
deletedData = fetchedBulkLoad.syncDeleted?.[table] || [];
776-
existingData = (storage.get(isAdmin ? "adminCache" : "cache")?.[table] || []).filter(item => {
777+
existingData = (((await storage.idbGet(isAdmin ? "adminCache" : "cache")) || storage.get(isAdmin ? "adminCache" : "cache") || {})?.[table] || []).filter(item => {
777778
return !deletedData.includes(String(item.id || item.seatCode || item.period || item.key || item.username || 0));
778779
});
779780
mergedData = [...existingData];
@@ -791,7 +792,12 @@ export async function bulkLoad(fields = [], usr = null, pwd = null, isAdmin = fa
791792
}
792793
}
793794
storage.set(isAdmin ? "lastAdminBulkLoad" : "lastBulkLoad", fetchedBulkLoad.asOf || null);
794-
storage.set(isAdmin ? "adminCache" : "cache", updatedBulkLoad || fetchedBulkLoad || {});
795+
try {
796+
console.log('Setting', updatedBulkLoad || fetchedBulkLoad || {})
797+
await storage.idbSet(isAdmin ? "adminCache" : "cache", updatedBulkLoad || fetchedBulkLoad || {});
798+
} catch (e) {
799+
storage.set(isAdmin ? "adminCache" : "cache", updatedBulkLoad || fetchedBulkLoad || {});
800+
}
795801
const loadTime = ((Date.now() - startTime) / 1000).toFixed(2);
796802
console.log(`${(loadTime < 1) ? '🟢' : ((loadTime > 5) ? '🔴' : '🟡')} Bulk load fetched in ${loadTime}s`);
797803
}

src/modules/storage.js

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,28 @@
11
class Storage {
22
constructor(id) {
33
this.id = id;
4+
this._cache = null;
5+
this._idbName = id;
6+
this._idbVersion = 1;
7+
this.idbReady = this._initIDB();
48
this.syncWithCookie();
59
}
610

711
set(key, value) {
12+
if (key === 'cache') {
13+
try {
14+
this._cache = value;
15+
this.idbSet('cache', value).catch((e) => console.error('IDB set failed', e));
16+
const temp = this.object;
17+
if (temp && temp.cache) delete temp.cache;
18+
localStorage.setItem(this.id, JSON.stringify(temp));
19+
this.syncWithCookie();
20+
} catch (e) {
21+
console.error('storage.set(cache) failed', e);
22+
}
23+
return this;
24+
}
25+
826
let temp = this.object;
927
temp[key] = value;
1028
localStorage.setItem(this.id, JSON.stringify(temp));
@@ -13,6 +31,7 @@ class Storage {
1331
}
1432

1533
get(key) {
34+
if (key === 'cache') return this._cache;
1635
return this.object[key];
1736
}
1837

@@ -21,6 +40,16 @@ class Storage {
2140
}
2241

2342
delete(key) {
43+
if (key === 'cache') {
44+
this._cache = null;
45+
this.idbDelete('cache').catch((e) => console.error('IDB delete failed', e));
46+
const temp = this.object;
47+
if (temp && temp.cache) delete temp.cache;
48+
localStorage.setItem(this.id, JSON.stringify(temp));
49+
this.syncWithCookie();
50+
return this;
51+
}
52+
2453
let temp = this.object;
2554
delete temp[key];
2655
localStorage.setItem(this.id, JSON.stringify(temp));
@@ -31,6 +60,13 @@ class Storage {
3160
obliterate() {
3261
localStorage.removeItem(this.id);
3362
setCookie(this.id, "", -1);
63+
try {
64+
const req = indexedDB.deleteDatabase(this._idbName);
65+
req.onsuccess = () => { };
66+
req.onerror = () => { console.error('Failed to delete IDB', req.error); };
67+
} catch (e) {
68+
console.error('obliterate IDB error', e);
69+
}
3470
}
3571

3672
get object() {
@@ -46,6 +82,89 @@ class Storage {
4682
localStorage.setItem(this.id, cookieData);
4783
}
4884
}
85+
86+
_initIDB() {
87+
return this._openIDB()
88+
.then((db) => {
89+
this._db = db;
90+
return this.idbGet('cache').then((v) => { this._cache = v; }).catch(() => { this._cache = null; });
91+
})
92+
.catch((e) => {
93+
console.error('IndexedDB unavailable', e);
94+
});
95+
}
96+
97+
_openIDB() {
98+
return new Promise((resolve, reject) => {
99+
if (!window.indexedDB) return reject(new Error('IndexedDB not supported'));
100+
const request = indexedDB.open(this._idbName, this._idbVersion);
101+
request.onupgradeneeded = (event) => {
102+
const db = event.target.result;
103+
if (!db.objectStoreNames.contains('virtual-falcons')) db.createObjectStore('virtual-falcons', { keyPath: 'key' });
104+
};
105+
request.onsuccess = () => resolve(request.result);
106+
request.onerror = () => reject(request.error);
107+
});
108+
}
109+
110+
idbSet(key, value) {
111+
return new Promise((resolve, reject) => {
112+
this._openIDB().then((db) => {
113+
const tx = db.transaction(['virtual-falcons'], 'readwrite');
114+
const store = tx.objectStore('virtual-falcons');
115+
const req = store.put({ key: key, value: value });
116+
req.onsuccess = () => resolve();
117+
req.onerror = () => reject(req.error);
118+
}).catch(reject);
119+
});
120+
}
121+
122+
idbGet(key) {
123+
return new Promise((resolve, reject) => {
124+
this._openIDB().then((db) => {
125+
const tx = db.transaction(['virtual-falcons'], 'readonly');
126+
const store = tx.objectStore('virtual-falcons');
127+
const req = store.get(key);
128+
req.onsuccess = () => {
129+
if (req.result) resolve(req.result.value); else resolve(undefined);
130+
};
131+
req.onerror = () => reject(req.error);
132+
}).catch(reject);
133+
});
134+
}
135+
136+
idbDelete(key) {
137+
return new Promise((resolve, reject) => {
138+
this._openIDB().then((db) => {
139+
const tx = db.transaction(['virtual-falcons'], 'readwrite');
140+
const store = tx.objectStore('virtual-falcons');
141+
const req = store.delete(key);
142+
req.onsuccess = () => resolve();
143+
req.onerror = () => reject(req.error);
144+
}).catch(reject);
145+
});
146+
}
147+
148+
idbAll() {
149+
return new Promise((resolve, reject) => {
150+
this._openIDB().then((db) => {
151+
const tx = db.transaction(['virtual-falcons'], 'readonly');
152+
const store = tx.objectStore('virtual-falcons');
153+
const req = store.openCursor();
154+
const out = {};
155+
req.onsuccess = (e) => {
156+
const cursor = e.target.result;
157+
if (cursor) {
158+
out[cursor.key] = cursor.value.value;
159+
cursor.continue();
160+
} else {
161+
resolve(out);
162+
}
163+
};
164+
req.onerror = () => reject(req.error);
165+
}).catch(reject);
166+
});
167+
}
49168
}
50169

51170
function setCookie(name, value, days) {

0 commit comments

Comments
 (0)