214 lines
3.8 KiB
JavaScript
214 lines
3.8 KiB
JavaScript
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
|
|
});
|
|
}
|
|
|
|
import { saveWords } from './offline-engine.js';
|
|
|
|
export async function importList(lang, list) {
|
|
|
|
const res = await fetch(
|
|
`/api/list.php?lang=${lang}&list=${list}`
|
|
);
|
|
|
|
const data = await res.json();
|
|
|
|
if (!data.words) {
|
|
throw new Error('No words');
|
|
}
|
|
|
|
await saveWords(lang, list, data.words);
|
|
|
|
return data.words.length;
|
|
} |