Update assets/js/offline-engine.js
This commit is contained in:
+78
-92
@@ -1,91 +1,113 @@
|
|||||||
import { getDB } from './db.js';
|
import { getDB } from './db.js';
|
||||||
|
|
||||||
function getStore(storeName, mode = 'readonly') {
|
/**
|
||||||
|
* HELPERS
|
||||||
|
*/
|
||||||
|
function store(storeName, mode = 'readonly') {
|
||||||
const db = getDB();
|
const db = getDB();
|
||||||
|
|
||||||
const tx = db.transaction(storeName, mode);
|
const tx = db.transaction(storeName, mode);
|
||||||
|
|
||||||
return tx.objectStore(storeName);
|
return tx.objectStore(storeName);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function saveWords(lang, list, words) {
|
/**
|
||||||
|
* IMPORT WORD LIST FROM SERVER → INDEXEDDB
|
||||||
|
*/
|
||||||
|
export async function importList(lang, list) {
|
||||||
|
|
||||||
const store = getStore('words', 'readwrite');
|
const res = await fetch(`/api/list.php?lang=${lang}&list=${list}`);
|
||||||
|
const data = await res.json();
|
||||||
|
|
||||||
for (const word of words) {
|
if (!data.words || !Array.isArray(data.words)) {
|
||||||
|
console.error("Import failed:", data);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
store.put({
|
const s = store('words', 'readwrite');
|
||||||
|
|
||||||
|
data.words.forEach(word => {
|
||||||
|
|
||||||
|
s.put({
|
||||||
key: `${lang}:${list}:${word.id}`,
|
key: `${lang}:${list}:${word.id}`,
|
||||||
|
id: word.id,
|
||||||
lang,
|
lang,
|
||||||
list,
|
list,
|
||||||
...word
|
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) {
|
export async function getWords(lang, list) {
|
||||||
|
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
|
|
||||||
const store = getStore('words');
|
const s = store('words');
|
||||||
|
|
||||||
const req = store.getAll();
|
const req = s.getAll();
|
||||||
|
|
||||||
req.onsuccess = () => {
|
req.onsuccess = () => {
|
||||||
|
|
||||||
const result = req.result.filter(
|
const filtered = req.result.filter(
|
||||||
w => w.lang === lang && w.list === list
|
w => w.lang === lang && w.list === list
|
||||||
);
|
);
|
||||||
|
|
||||||
resolve(result);
|
resolve(filtered);
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GET NEXT WORD (SRS SIMPLE)
|
||||||
|
*/
|
||||||
export async function getNextWord(lang, list) {
|
export async function getNextWord(lang, list) {
|
||||||
|
|
||||||
const words = await getWords(lang, list);
|
const words = await getWords(lang, list);
|
||||||
|
|
||||||
if (!words.length) {
|
if (!words.length) {
|
||||||
|
console.warn("No words in IndexedDB");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const today = new Date().toISOString().split('T')[0];
|
const today = new Date().toISOString().split('T')[0];
|
||||||
|
|
||||||
let due = words.filter(word => {
|
let due = words.filter(w =>
|
||||||
return !word.nextReview ||
|
!w.nextReview || w.nextReview <= today
|
||||||
word.nextReview <= today;
|
);
|
||||||
});
|
|
||||||
|
|
||||||
if (!due.length) {
|
if (!due.length) {
|
||||||
due = words;
|
due = words;
|
||||||
}
|
}
|
||||||
|
|
||||||
const random =
|
return due[Math.floor(Math.random() * due.length)];
|
||||||
Math.floor(Math.random() * due.length);
|
|
||||||
|
|
||||||
return due[random];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function answerWord(word, answer) {
|
/**
|
||||||
|
* ANSWER WORD
|
||||||
|
*/
|
||||||
|
export async function answerWord(word, input) {
|
||||||
|
|
||||||
const correct =
|
const correct =
|
||||||
word.answer.trim().toLowerCase() ===
|
word.answer.trim().toLowerCase() ===
|
||||||
answer.trim().toLowerCase();
|
input.trim().toLowerCase();
|
||||||
|
|
||||||
const store =
|
const s = store('words', 'readwrite');
|
||||||
getStore('words', 'readwrite');
|
|
||||||
|
|
||||||
const updated = { ...word };
|
const updated = { ...word };
|
||||||
|
|
||||||
if (correct) {
|
if (correct) {
|
||||||
|
|
||||||
const days =
|
updated.correct++;
|
||||||
updated.interval
|
|
||||||
? updated.interval * 2
|
|
||||||
: 1;
|
|
||||||
|
|
||||||
|
const days = updated.interval * 2;
|
||||||
updated.interval = days;
|
updated.interval = days;
|
||||||
|
|
||||||
const d = new Date();
|
const d = new Date();
|
||||||
@@ -96,6 +118,8 @@ export async function answerWord(word, answer) {
|
|||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
|
updated.wrong++;
|
||||||
|
|
||||||
updated.interval = 1;
|
updated.interval = 1;
|
||||||
|
|
||||||
const d = new Date();
|
const d = new Date();
|
||||||
@@ -105,38 +129,29 @@ export async function answerWord(word, answer) {
|
|||||||
d.toISOString().split('T')[0];
|
d.toISOString().split('T')[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
store.put(updated);
|
s.put(updated);
|
||||||
|
|
||||||
await updateSessionStats(correct);
|
updateSessionStats(correct);
|
||||||
|
|
||||||
await queueSync({
|
queueSync({
|
||||||
wordId: word.id,
|
wordId: word.id,
|
||||||
lang: word.lang,
|
lang: word.lang,
|
||||||
list: word.list,
|
list: word.list,
|
||||||
correct,
|
correct,
|
||||||
timestamp: Date.now()
|
ts: Date.now()
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return { correct };
|
||||||
correct
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function queueSync(item) {
|
/**
|
||||||
|
* SESSION STATS
|
||||||
|
*/
|
||||||
|
export function updateSessionStats(correct) {
|
||||||
|
|
||||||
const store =
|
const s = store('session', 'readwrite');
|
||||||
getStore('sync_queue', 'readwrite');
|
|
||||||
|
|
||||||
store.add(item);
|
const req = s.get('stats');
|
||||||
}
|
|
||||||
|
|
||||||
export async function updateSessionStats(correct) {
|
|
||||||
|
|
||||||
const store =
|
|
||||||
getStore('session', 'readwrite');
|
|
||||||
|
|
||||||
const req =
|
|
||||||
store.get('stats');
|
|
||||||
|
|
||||||
req.onsuccess = () => {
|
req.onsuccess = () => {
|
||||||
|
|
||||||
@@ -150,13 +165,10 @@ export async function updateSessionStats(correct) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (correct) {
|
if (correct) stats.correct++;
|
||||||
stats.correct++;
|
else stats.wrong++;
|
||||||
} else {
|
|
||||||
stats.wrong++;
|
|
||||||
}
|
|
||||||
|
|
||||||
store.put(stats);
|
s.put(stats);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -164,51 +176,25 @@ export async function getSessionStats() {
|
|||||||
|
|
||||||
return new Promise(resolve => {
|
return new Promise(resolve => {
|
||||||
|
|
||||||
const store =
|
const s = store('session');
|
||||||
getStore('session');
|
|
||||||
|
|
||||||
const req =
|
const req = s.get('stats');
|
||||||
store.get('stats');
|
|
||||||
|
|
||||||
req.onsuccess = () => {
|
req.onsuccess = () => {
|
||||||
|
|
||||||
resolve(
|
resolve(req.result || {
|
||||||
req.result || {
|
|
||||||
correct: 0,
|
correct: 0,
|
||||||
wrong: 0
|
wrong: 0
|
||||||
}
|
});
|
||||||
);
|
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function resetSessionStats() {
|
/**
|
||||||
|
* SYNC QUEUE
|
||||||
|
*/
|
||||||
|
function queueSync(item) {
|
||||||
|
|
||||||
const store =
|
const s = store('sync_queue', 'readwrite');
|
||||||
getStore('session', 'readwrite');
|
s.add(item);
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user