Enhanced handling of hard relearning

This commit is contained in:
Torsten Schulz (local)
2026-08-13 11:03:17 +02:00
parent 6a0b31a39a
commit 588fab1beb
2 changed files with 39 additions and 18 deletions

View File

@@ -161,7 +161,6 @@ import { normalizeComparableWithNumberWords } from '@/utils/numberAnswerVariants
const PRACTICE_MIN_EXPOSURES = 3;
const SRS_SESSION_STORAGE_VERSION = 2;
const HARD_REQUIRED_CONSECUTIVE_CORRECT = 5;
const SRS_AGAIN_REINSERT_OFFSET = 3;
const MAX_DAILY_DUE = 50;
export default {
@@ -175,6 +174,7 @@ export default {
allVocabs: false,
srsMode: false,
initialPool: null,
closeOnHardCompletion: false,
simpleMode: false,
pool: [],
@@ -450,10 +450,8 @@ export default {
const matchingKey = this.findMatchingHardKey(this.current);
if (!matchingKey) return;
this.removeHardEntriesForItem(this.current);
if (this.hardPhaseActive && this.hardRemainingCount <= 0) {
this.hardPhaseActive = false;
}
this.saveHardVocabMap();
this.maybeFinishHardPhase();
},
addCurrentToCycle() {
if (!this.current?.id) return;
@@ -467,10 +465,16 @@ export default {
this.hardPhaseActive = true;
},
maybeFinishHardPhase() {
if (!this.hardPhaseActive) return;
if (this.hardRemainingCount > 0) return;
if (!this.hardPhaseActive && !this.closeOnHardCompletion) return false;
if (this.hardRemainingCount > 0) return false;
if (this.hardCount <= 0 && this.closeOnHardCompletion) {
this.close();
return true;
}
if (!this.hardPhaseActive) return false;
this.hardPhaseActive = false;
this.cycleAskedIds = [];
return false;
},
getLocalDateKey() {
const d = new Date();
@@ -575,7 +579,7 @@ export default {
}
this.saveSrsSession();
},
open({ languageId, chapterId, lessonId, courseId, initialPool = null, srsMode = false, onClose = null }) {
open({ languageId, chapterId, lessonId, courseId, initialPool = null, srsMode = false, closeOnHardCompletion = false, onClose = null }) {
if (this.autoAdvanceTimer) {
clearTimeout(this.autoAdvanceTimer);
this.autoAdvanceTimer = null;
@@ -588,6 +592,7 @@ export default {
this.onClose = typeof onClose === 'function' ? onClose : null;
this.srsMode = Boolean(srsMode);
this.initialPool = Array.isArray(initialPool) ? initialPool : null;
this.closeOnHardCompletion = Boolean(closeOnHardCompletion);
this.allVocabs = false;
this.simpleMode = false;
this.srsSession = null;
@@ -1231,6 +1236,9 @@ export default {
delete next[mapKey];
this.hardVocabMap = next;
this.saveHardVocabMap();
if (this.maybeFinishHardPhase()) {
return;
}
}
}
} else {
@@ -1256,21 +1264,18 @@ export default {
const id = this.current?.id;
const treatAsAgain = rating === 'again' || !this.lastCorrect;
const moveToHardPractice = treatAsAgain && this.isItemMarkedHard(this.current);
if (id && this.srsSession) {
if (treatAsAgain) {
// Wrong answers are not done yet: requeue near the end so the user
// sees them again before the session finishes.
if (Array.isArray(this.srsQueueIds)) {
const remaining = this.srsQueueIds.filter((x) => x !== id);
const insertAt = Math.min(remaining.length, SRS_AGAIN_REINSERT_OFFSET);
remaining.splice(insertAt, 0, id);
this.srsQueueIds = remaining;
}
if (treatAsAgain && !moveToHardPractice) {
// Keep the card at the front of the queue. After the solution has
// been shown, the user must type it correctly before moving on.
if (Array.isArray(this.srsSession.doneIds)) {
this.srsSession.doneIds = this.srsSession.doneIds.filter((x) => x !== id);
}
} else {
// Hard cards leave the daily review after a wrong answer. They are
// practiced separately through the hard-vocab drill.
const done = Array.isArray(this.srsSession.doneIds) ? this.srsSession.doneIds : [];
if (!done.includes(id)) {
done.push(id);
@@ -1285,6 +1290,21 @@ export default {
this.saveSrsSession();
}
if (treatAsAgain && !moveToHardPractice) {
const retryItem = this.pool.find((item) => item.id === id) || this.current;
const retryDirection = this.direction;
this.resetQuestion();
this.current = retryItem;
this.direction = retryDirection;
const prompt = this.currentAnswerPrompt;
this.acceptableAnswers = this.getAnswersForPrompt(prompt, this.direction);
if (this.simpleMode) this.buildChoices();
this.$nextTick(() => {
if (!this.simpleMode) this.$refs.answerInput?.focus?.();
});
return;
}
this.next();
},
submitChoice(opt) {
@@ -1338,7 +1358,7 @@ export default {
this.addCurrentToCycle();
}
this.maybeStartHardPhase();
this.maybeFinishHardPhase();
if (this.maybeFinishHardPhase()) return;
const retryDirection = this.pendingRetry?.direction || null;
this.resetQuestion();
if (retryDirection) {
@@ -1346,7 +1366,7 @@ export default {
}
this.current = this.pickNextItem();
if (!this.current) {
this.maybeFinishHardPhase();
if (this.maybeFinishHardPhase()) return;
if (!this.hardPhaseActive) {
this.current = this.pickNextItem();
}

View File

@@ -966,6 +966,7 @@ export default {
this.$refs.practiceDialog?.open?.({
courseId: this.courseId,
initialPool: this.hardVocabList,
closeOnHardCompletion: true,
onClose: () => this.refreshHardVocabList()
});
},