feat(vocab): implement daily hard vocab limit handling in practice dialog
This commit is contained in:
@@ -140,6 +140,7 @@ const PRACTICE_MIN_EXPOSURES = 3;
|
|||||||
const SRS_SESSION_STORAGE_VERSION = 2;
|
const SRS_SESSION_STORAGE_VERSION = 2;
|
||||||
const HARD_REQUIRED_CONSECUTIVE_CORRECT = 5;
|
const HARD_REQUIRED_CONSECUTIVE_CORRECT = 5;
|
||||||
const MAX_DAILY_DUE = 50;
|
const MAX_DAILY_DUE = 50;
|
||||||
|
const MAX_DAILY_HARD_VOCABS = 7;
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'VocabPracticeDialog',
|
name: 'VocabPracticeDialog',
|
||||||
@@ -148,6 +149,7 @@ export default {
|
|||||||
return {
|
return {
|
||||||
openParams: null, // { languageId, chapterId, lessonId, courseId }
|
openParams: null, // { languageId, chapterId, lessonId, courseId }
|
||||||
onClose: null,
|
onClose: null,
|
||||||
|
onDailyHardLimitReached: null,
|
||||||
loading: false,
|
loading: false,
|
||||||
allVocabs: false,
|
allVocabs: false,
|
||||||
srsMode: false,
|
srsMode: false,
|
||||||
@@ -389,6 +391,19 @@ export default {
|
|||||||
}
|
}
|
||||||
this.saveHardVocabMap();
|
this.saveHardVocabMap();
|
||||||
},
|
},
|
||||||
|
maybeStartDailyHardPractice() {
|
||||||
|
if (!this.srsMode || this.hardCount < MAX_DAILY_HARD_VOCABS) return false;
|
||||||
|
const callback = this.onDailyHardLimitReached;
|
||||||
|
this.close();
|
||||||
|
this.$nextTick(() => {
|
||||||
|
try {
|
||||||
|
callback?.();
|
||||||
|
} catch (_) {
|
||||||
|
// A missing parent callback must not interrupt the dialog close.
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return true;
|
||||||
|
},
|
||||||
unmarkCurrentAsHard() {
|
unmarkCurrentAsHard() {
|
||||||
if (!this.current) return;
|
if (!this.current) return;
|
||||||
const matchingKey = this.findMatchingHardKey(this.current);
|
const matchingKey = this.findMatchingHardKey(this.current);
|
||||||
@@ -531,7 +546,7 @@ export default {
|
|||||||
}
|
}
|
||||||
this.saveSrsSession();
|
this.saveSrsSession();
|
||||||
},
|
},
|
||||||
open({ languageId, chapterId, lessonId, courseId, initialPool = null, initialHardPool = null, srsMode = false, closeOnHardCompletion = false, onClose = null }) {
|
open({ languageId, chapterId, lessonId, courseId, initialPool = null, initialHardPool = null, srsMode = false, closeOnHardCompletion = false, onDailyHardLimitReached = null, onClose = null }) {
|
||||||
if (this.autoAdvanceTimer) {
|
if (this.autoAdvanceTimer) {
|
||||||
clearTimeout(this.autoAdvanceTimer);
|
clearTimeout(this.autoAdvanceTimer);
|
||||||
this.autoAdvanceTimer = null;
|
this.autoAdvanceTimer = null;
|
||||||
@@ -542,6 +557,7 @@ export default {
|
|||||||
console.debug('[VocabPracticeDialog] open called with', { languageId, chapterId, lessonId, courseId, srsMode, hasInitialPool: Array.isArray(initialPool) });
|
console.debug('[VocabPracticeDialog] open called with', { languageId, chapterId, lessonId, courseId, srsMode, hasInitialPool: Array.isArray(initialPool) });
|
||||||
} catch (_) {}
|
} catch (_) {}
|
||||||
this.onClose = typeof onClose === 'function' ? onClose : null;
|
this.onClose = typeof onClose === 'function' ? onClose : null;
|
||||||
|
this.onDailyHardLimitReached = typeof onDailyHardLimitReached === 'function' ? onDailyHardLimitReached : null;
|
||||||
this.srsMode = Boolean(srsMode);
|
this.srsMode = Boolean(srsMode);
|
||||||
this.initialPool = Array.isArray(initialPool) ? initialPool : null;
|
this.initialPool = Array.isArray(initialPool) ? initialPool : null;
|
||||||
this.closeOnHardCompletion = Boolean(closeOnHardCompletion);
|
this.closeOnHardCompletion = Boolean(closeOnHardCompletion);
|
||||||
@@ -574,6 +590,7 @@ export default {
|
|||||||
this.hardVocabMap[key] = { learning: item.learning, reference: item.reference, itemKey: item.itemKey || item.id || null };
|
this.hardVocabMap[key] = { learning: item.learning, reference: item.reference, itemKey: item.itemKey || item.id || null };
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
if (this.maybeStartDailyHardPractice()) return;
|
||||||
this.reloadPool();
|
this.reloadPool();
|
||||||
},
|
},
|
||||||
close() {
|
close() {
|
||||||
@@ -1240,6 +1257,7 @@ export default {
|
|||||||
: [];
|
: [];
|
||||||
this.saveSrsSession();
|
this.saveSrsSession();
|
||||||
}
|
}
|
||||||
|
if (!isCorrect && this.maybeStartDailyHardPractice()) return;
|
||||||
this.autoAdvanceTimer = setTimeout(() => {
|
this.autoAdvanceTimer = setTimeout(() => {
|
||||||
this.autoAdvanceTimer = null;
|
this.autoAdvanceTimer = null;
|
||||||
this.next();
|
this.next();
|
||||||
|
|||||||
@@ -946,7 +946,12 @@ export default {
|
|||||||
this.$refs.practiceDialog?.open?.({
|
this.$refs.practiceDialog?.open?.({
|
||||||
courseId: this.courseId,
|
courseId: this.courseId,
|
||||||
initialPool: this.srsDueItems,
|
initialPool: this.srsDueItems,
|
||||||
|
initialHardPool: this.hardVocabList,
|
||||||
srsMode: true,
|
srsMode: true,
|
||||||
|
onDailyHardLimitReached: async () => {
|
||||||
|
await this.refreshHardVocabList();
|
||||||
|
this.openHardPractice();
|
||||||
|
},
|
||||||
onClose: () => this.loadSrsDueItems()
|
onClose: () => this.loadSrsDueItems()
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user