Files
woordjes/assets/js/offline-engine.js
T

200 lines
3.7 KiB
JavaScript

import { getDB } from './db.js';
/**
* HELPERS
*/
function store(storeName, mode = 'readonly') {
const db = getDB();
const tx = db.transaction(storeName, mode);
return tx.objectStore(storeName);
}
/**
* IMPORT WORD LIST FROM SERVER → INDEXEDDB
*/
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 || !Array.isArray(data.words)) {
console.error("Import failed:", data);
return;
}
const s = store('words', 'readwrite');
data.words.forEach(word => {
s.put({
key: `${lang}:${list}:${word.id}`,
id: word.id,
lang,
list,
question: word.question,
answer: word.answer,
correct: word.correct || 0,
wrong: word.wrong || 0,
nextReview: word.nextReview || null,
interval: word.interval || 1
});
});
console.log(`Imported ${data.words.length} words`);
}
/**
* GET WORDS
*/
export async function getWords(lang, list) {
return new Promise((resolve) => {
const s = store('words');
const req = s.getAll();
req.onsuccess = () => {
const filtered = req.result.filter(
w => w.lang === lang && w.list === list
);
resolve(filtered);
};
});
}
/**
* GET NEXT WORD (SRS SIMPLE)
*/
export async function getNextWord(lang, list) {
const words = await getWords(lang, list);
if (!words.length) {
console.warn("No words in IndexedDB");
return null;
}
const today = new Date().toISOString().split('T')[0];
let due = words.filter(w =>
!w.nextReview || w.nextReview <= today
);
if (!due.length) {
due = words;
}
return due[Math.floor(Math.random() * due.length)];
}
/**
* ANSWER WORD
*/
export async function answerWord(word, input) {
const correct =
word.answer.trim().toLowerCase() ===
input.trim().toLowerCase();
const s = store('words', 'readwrite');
const updated = { ...word };
if (correct) {
updated.correct++;
const days = updated.interval * 2;
updated.interval = days;
const d = new Date();
d.setDate(d.getDate() + days);
updated.nextReview =
d.toISOString().split('T')[0];
} else {
updated.wrong++;
updated.interval = 1;
const d = new Date();
d.setDate(d.getDate() + 1);
updated.nextReview =
d.toISOString().split('T')[0];
}
s.put(updated);
updateSessionStats(correct);
queueSync({
wordId: word.id,
lang: word.lang,
list: word.list,
correct,
ts: Date.now()
});
return { correct };
}
/**
* SESSION STATS
*/
export function updateSessionStats(correct) {
const s = store('session', 'readwrite');
const req = s.get('stats');
req.onsuccess = () => {
let stats = req.result;
if (!stats) {
stats = {
key: 'stats',
correct: 0,
wrong: 0
};
}
if (correct) stats.correct++;
else stats.wrong++;
s.put(stats);
};
}
export async function getSessionStats() {
return new Promise(resolve => {
const s = store('session');
const req = s.get('stats');
req.onsuccess = () => {
resolve(req.result || {
correct: 0,
wrong: 0
});
};
});
}
/**
* SYNC QUEUE
*/
function queueSync(item) {
const s = store('sync_queue', 'readwrite');
s.add(item);
}