From 25d659e4e21afc50ef1cc42ed7597d1bc1a155f3 Mon Sep 17 00:00:00 2001 From: "Torsten Schulz (local)" Date: Mon, 17 Aug 2026 10:28:13 +0200 Subject: [PATCH] Auto switch to hard vocabulary --- .../socialnetwork/VocabPracticeDialog.vue | 23 ++++++++++++++++++- frontend/src/views/social/VocabCourseView.vue | 4 ++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/frontend/src/dialogues/socialnetwork/VocabPracticeDialog.vue b/frontend/src/dialogues/socialnetwork/VocabPracticeDialog.vue index 9229572..c22a933 100755 --- a/frontend/src/dialogues/socialnetwork/VocabPracticeDialog.vue +++ b/frontend/src/dialogues/socialnetwork/VocabPracticeDialog.vue @@ -162,6 +162,7 @@ const PRACTICE_MIN_EXPOSURES = 3; const SRS_SESSION_STORAGE_VERSION = 2; const HARD_REQUIRED_CONSECUTIVE_CORRECT = 5; const MAX_DAILY_DUE = 50; +const MAX_DAILY_HARD_VOCABS = 7; export default { name: 'VocabPracticeDialog', @@ -170,6 +171,7 @@ export default { return { openParams: null, // { languageId, chapterId, lessonId, courseId } onClose: null, + onDailyHardLimitReached: null, loading: false, allVocabs: false, srsMode: false, @@ -207,6 +209,7 @@ export default { hardPhaseActive: false, hardMasteryByKey: {}, // { [hardKey]: consecutiveCorrect } cycleAskedIds: [], + dailyHardMarkedCount: 0, }; }, computed: { @@ -444,6 +447,22 @@ export default { this.hardMasteryByKey[key] = 0; } this.saveHardVocabMap(); + if (this.srsMode) { + this.dailyHardMarkedCount += 1; + this.maybeStartDailyHardPractice(); + } + }, + maybeStartDailyHardPractice() { + if (this.dailyHardMarkedCount < MAX_DAILY_HARD_VOCABS) return; + const callback = this.onDailyHardLimitReached; + this.close(); + this.$nextTick(() => { + try { + callback?.(); + } catch (_) { + // A missing parent callback must not interrupt the dialog close. + } + }); }, unmarkCurrentAsHard() { if (!this.current) return; @@ -587,7 +606,7 @@ export default { } this.saveSrsSession(); }, - open({ languageId, chapterId, lessonId, courseId, initialPool = null, srsMode = false, closeOnHardCompletion = false, onClose = null }) { + open({ languageId, chapterId, lessonId, courseId, initialPool = null, srsMode = false, closeOnHardCompletion = false, onDailyHardLimitReached = null, onClose = null }) { if (this.autoAdvanceTimer) { clearTimeout(this.autoAdvanceTimer); this.autoAdvanceTimer = null; @@ -598,6 +617,7 @@ export default { console.debug('[VocabPracticeDialog] open called with', { languageId, chapterId, lessonId, courseId, srsMode, hasInitialPool: Array.isArray(initialPool) }); } catch (_) {} this.onClose = typeof onClose === 'function' ? onClose : null; + this.onDailyHardLimitReached = typeof onDailyHardLimitReached === 'function' ? onDailyHardLimitReached : null; this.srsMode = Boolean(srsMode); this.initialPool = Array.isArray(initialPool) ? initialPool : null; this.closeOnHardCompletion = Boolean(closeOnHardCompletion); @@ -616,6 +636,7 @@ export default { this.hardPhaseActive = false; this.hardMasteryByKey = {}; this.cycleAskedIds = []; + this.dailyHardMarkedCount = 0; this.locked = false; this.resetQuestion(); this.$refs.dialog.open(); diff --git a/frontend/src/views/social/VocabCourseView.vue b/frontend/src/views/social/VocabCourseView.vue index b0c096f..fc28fed 100755 --- a/frontend/src/views/social/VocabCourseView.vue +++ b/frontend/src/views/social/VocabCourseView.vue @@ -978,6 +978,10 @@ export default { courseId: this.courseId, initialPool: this.srsDueItems, srsMode: true, + onDailyHardLimitReached: () => { + this.refreshHardVocabList(); + this.$nextTick(() => this.openHardPractice()); + }, onClose: () => this.loadSrsDueItems() }); },