From 44b40d5a4613662a91f0eb11e145bfee3e8f1461 Mon Sep 17 00:00:00 2001 From: "Torsten Schulz (local)" Date: Tue, 21 Apr 2026 15:57:21 +0200 Subject: [PATCH] feat(VocabPracticeDialog, localization): add hard vocabulary marking feature - Introduced functionality to mark vocabulary items as difficult, allowing users to track and manage challenging words during practice sessions. - Added buttons for marking and unmarking current vocabulary as hard, enhancing user engagement and learning effectiveness. - Updated the UI to display the count of hard-marked vocabulary items, providing users with better insights into their learning progress. - Enhanced localization files for German and English to support new vocabulary marking features, ensuring a consistent user experience across languages. --- .../socialnetwork/VocabPracticeDialog.vue | 75 +++++++++++++++++++ .../src/i18n/locales/de/socialnetwork.json | 3 + .../src/i18n/locales/en/socialnetwork.json | 3 + 3 files changed, 81 insertions(+) diff --git a/frontend/src/dialogues/socialnetwork/VocabPracticeDialog.vue b/frontend/src/dialogues/socialnetwork/VocabPracticeDialog.vue index a596073..f7eee55 100644 --- a/frontend/src/dialogues/socialnetwork/VocabPracticeDialog.vue +++ b/frontend/src/dialogues/socialnetwork/VocabPracticeDialog.vue @@ -94,6 +94,12 @@ + +
@@ -137,6 +143,10 @@ {{ $t('socialnetwork.vocab.practice.fail') }} {{ wrongCount }} ({{ failPercent }}%)
+
+ {{ $t('socialnetwork.vocab.practice.hardCount') }} + {{ hardCount }} +
@@ -188,6 +198,7 @@ export default { lastCorrect: false, locked: false, autoAdvanceTimer: null, + hardVocabMap: {}, // { [normalizedPairKey]: { learning, reference, markedAt } } }; }, computed: { @@ -277,8 +288,70 @@ export default { const total = this.srsTotalDue || 0; return Math.max(0, total - this.srsDoneCount); }, + hardCount() { + return Object.keys(this.hardVocabMap || {}).length; + }, + isCurrentMarkedHard() { + if (!this.current) return false; + return Boolean(this.hardVocabMap[this.getHardKey(this.current)]); + } }, methods: { + hardStorageKey() { + const courseId = this.openParams?.courseId; + if (!courseId) return null; + return `yourpart:vocab:hardList:${courseId}`; + }, + loadHardVocabMap() { + const key = this.hardStorageKey(); + if (!key) { + this.hardVocabMap = {}; + return; + } + try { + const raw = localStorage.getItem(key); + const parsed = raw ? JSON.parse(raw) : {}; + this.hardVocabMap = parsed && typeof parsed === 'object' ? parsed : {}; + } catch (_) { + this.hardVocabMap = {}; + } + }, + saveHardVocabMap() { + const key = this.hardStorageKey(); + if (!key) return; + try { + localStorage.setItem(key, JSON.stringify(this.hardVocabMap || {})); + } catch (_) { + // ignore quota/private-mode issues + } + }, + getHardKey(item) { + const learning = this.normalize(item?.learning || ''); + const reference = this.normalize(item?.reference || ''); + return `${learning}|${reference}`; + }, + markCurrentAsHard() { + if (!this.current) return; + const key = this.getHardKey(this.current); + this.hardVocabMap = { + ...this.hardVocabMap, + [key]: { + learning: this.current.learning, + reference: this.current.reference, + markedAt: new Date().toISOString() + } + }; + this.saveHardVocabMap(); + }, + unmarkCurrentAsHard() { + if (!this.current) return; + const key = this.getHardKey(this.current); + if (!this.hardVocabMap[key]) return; + const next = { ...this.hardVocabMap }; + delete next[key]; + this.hardVocabMap = next; + this.saveHardVocabMap(); + }, getLocalDateKey() { const d = new Date(); const y = d.getFullYear(); @@ -381,6 +454,7 @@ export default { this.lastWrongReview = null; this.pendingRetry = null; this.pool = []; + this.hardVocabMap = {}; this.locked = false; this.resetQuestion(); this.$refs.dialog.open(); @@ -388,6 +462,7 @@ export default { document.addEventListener('keydown', this.handleKeyDown); }); this.reloadPool(); + this.loadHardVocabMap(); }, close() { if (this.autoAdvanceTimer) { diff --git a/frontend/src/i18n/locales/de/socialnetwork.json b/frontend/src/i18n/locales/de/socialnetwork.json index e52e9ef..e44f98b 100644 --- a/frontend/src/i18n/locales/de/socialnetwork.json +++ b/frontend/src/i18n/locales/de/socialnetwork.json @@ -461,6 +461,9 @@ "remaining": "Noch offen", "success": "Erfolg", "fail": "Misserfolg", + "hardCount": "Schwer markiert", + "markHard": "Als schwer markieren", + "unmarkHard": "Aus schwer entfernen", "srsRateTitle": "Wie sicher war das?", "srsAgain": "Nochmal", "srsAgainHint": "sehr bald wiederholen", diff --git a/frontend/src/i18n/locales/en/socialnetwork.json b/frontend/src/i18n/locales/en/socialnetwork.json index d2f4f2a..e444dff 100644 --- a/frontend/src/i18n/locales/en/socialnetwork.json +++ b/frontend/src/i18n/locales/en/socialnetwork.json @@ -461,6 +461,9 @@ "remaining": "Remaining", "success": "Success", "fail": "Fail", + "hardCount": "Marked difficult", + "markHard": "Mark as difficult", + "unmarkHard": "Remove difficult mark", "srsRateTitle": "How solid did it feel?", "srsAgain": "Again", "srsAgainHint": "repeat very soon",