Add assets/js/offline-engine.js
This commit is contained in:
@@ -0,0 +1,195 @@
|
||||
import { getDB } from './db.js';
|
||||
|
||||
function getStore(storeName, mode = 'readonly') {
|
||||
|
||||
const db = getDB();
|
||||
|
||||
const tx = db.transaction(storeName, mode);
|
||||
|
||||
return tx.objectStore(storeName);
|
||||
}
|
||||
|
||||
export async function saveWords(lang, list, words) {
|
||||
|
||||
const store = getStore('words', 'readwrite');
|
||||
|
||||
for (const word of words) {
|
||||
|
||||
store.put({
|
||||
key: `${lang}:${list}:${word.id}`,
|
||||
lang,
|
||||
list,
|
||||
...word
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export async function getWords(lang, list) {
|
||||
|
||||
return new Promise((resolve) => {
|
||||
|
||||
const store = getStore('words');
|
||||
|
||||
const req = store.getAll();
|
||||
|
||||
req.onsuccess = () => {
|
||||
|
||||
const result = req.result.filter(
|
||||
w => w.lang === lang && w.list === list
|
||||
);
|
||||
|
||||
resolve(result);
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
export async function getNextWord(lang, list) {
|
||||
|
||||
const words = await getWords(lang, list);
|
||||
|
||||
if (!words.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const today = new Date().toISOString().split('T')[0];
|
||||
|
||||
let due = words.filter(word => {
|
||||
return !word.nextReview ||
|
||||
word.nextReview <= today;
|
||||
});
|
||||
|
||||
if (!due.length) {
|
||||
due = words;
|
||||
}
|
||||
|
||||
const random =
|
||||
Math.floor(Math.random() * due.length);
|
||||
|
||||
return due[random];
|
||||
}
|
||||
|
||||
export async function answerWord(word, answer) {
|
||||
|
||||
const correct =
|
||||
word.answer.trim().toLowerCase() ===
|
||||
answer.trim().toLowerCase();
|
||||
|
||||
const store =
|
||||
getStore('words', 'readwrite');
|
||||
|
||||
const updated = { ...word };
|
||||
|
||||
if (correct) {
|
||||
|
||||
const days =
|
||||
updated.interval
|
||||
? updated.interval * 2
|
||||
: 1;
|
||||
|
||||
updated.interval = days;
|
||||
|
||||
const d = new Date();
|
||||
d.setDate(d.getDate() + days);
|
||||
|
||||
updated.nextReview =
|
||||
d.toISOString().split('T')[0];
|
||||
|
||||
} else {
|
||||
|
||||
updated.interval = 1;
|
||||
|
||||
const d = new Date();
|
||||
d.setDate(d.getDate() + 1);
|
||||
|
||||
updated.nextReview =
|
||||
d.toISOString().split('T')[0];
|
||||
}
|
||||
|
||||
store.put(updated);
|
||||
|
||||
await updateSessionStats(correct);
|
||||
|
||||
await queueSync({
|
||||
wordId: word.id,
|
||||
lang: word.lang,
|
||||
list: word.list,
|
||||
correct,
|
||||
timestamp: Date.now()
|
||||
});
|
||||
|
||||
return {
|
||||
correct
|
||||
};
|
||||
}
|
||||
|
||||
async function queueSync(item) {
|
||||
|
||||
const store =
|
||||
getStore('sync_queue', 'readwrite');
|
||||
|
||||
store.add(item);
|
||||
}
|
||||
|
||||
export async function updateSessionStats(correct) {
|
||||
|
||||
const store =
|
||||
getStore('session', 'readwrite');
|
||||
|
||||
const req =
|
||||
store.get('stats');
|
||||
|
||||
req.onsuccess = () => {
|
||||
|
||||
let stats = req.result;
|
||||
|
||||
if (!stats) {
|
||||
stats = {
|
||||
key: 'stats',
|
||||
correct: 0,
|
||||
wrong: 0
|
||||
};
|
||||
}
|
||||
|
||||
if (correct) {
|
||||
stats.correct++;
|
||||
} else {
|
||||
stats.wrong++;
|
||||
}
|
||||
|
||||
store.put(stats);
|
||||
};
|
||||
}
|
||||
|
||||
export async function getSessionStats() {
|
||||
|
||||
return new Promise(resolve => {
|
||||
|
||||
const store =
|
||||
getStore('session');
|
||||
|
||||
const req =
|
||||
store.get('stats');
|
||||
|
||||
req.onsuccess = () => {
|
||||
|
||||
resolve(
|
||||
req.result || {
|
||||
correct: 0,
|
||||
wrong: 0
|
||||
}
|
||||
);
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
export async function resetSessionStats() {
|
||||
|
||||
const store =
|
||||
getStore('session', 'readwrite');
|
||||
|
||||
store.put({
|
||||
key: 'stats',
|
||||
correct: 0,
|
||||
wrong: 0
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user