From 9210549ddcb7510af05082f28631ebd85447f23f Mon Sep 17 00:00:00 2001 From: Hocky Yudhiono Date: Thu, 4 Jul 2024 08:31:41 +0800 Subject: [PATCH] Update srs quiz display and fetching algo --- main/handler/learning.ts | 49 +++++---- renderer/components/Meaning/QuizDisplay.tsx | 5 +- renderer/hooks/useSRS.tsx | 43 -------- renderer/pages/srs.tsx | 112 ++++++++++---------- 4 files changed, 83 insertions(+), 126 deletions(-) delete mode 100644 renderer/hooks/useSRS.tsx diff --git a/main/handler/learning.ts b/main/handler/learning.ts index 255cdf9..00f3121 100644 --- a/main/handler/learning.ts +++ b/main/handler/learning.ts @@ -181,7 +181,7 @@ class SRSDatabase { static db: Level; static async setup(lang: string) { - if (this.srsData.get(lang)) return; + if (this.srsData.has(lang)) return; for (const key of Object.keys(SkillConstant)) { this.learningTrees.set( getPair(lang, SkillConstant[key]), @@ -207,36 +207,26 @@ class SRSDatabase { } } - static storeOrUpdate(lang: string, ch: string, srsData: SRSData) { + static storeOrUpdateToDB(lang: string, ch: string, srsData: SRSData) { this.db.put(`srs/${lang}/${ch}`, srsData.toJSON()).then(r => console.log(r)); } - static insertNew(lang: string, character: string) { + static storeOrUpdate(lang: string, character: string, srsData?: SRSData) { if (!this.srsData.has(lang)) { console.warn(`ERROR: srsData ${lang} not initted`) return; } - if (this.srsData.get(lang).has(character)) { + if (!srsData && this.srsData.get(lang).has(character)) { return; } - const newSrs = new SRSData(character, lang); - this.storeOrUpdate(lang, character, newSrs); - this.srsData.get(lang).set(character, newSrs); - } - - static updateLearningTrees(lang: string, character: string, skillName: SkillConstant, newLevel: number) { - const ptrToSRSData = this.srsData.get(lang).get(character); - if (!ptrToSRSData) return; - - this.learningTrees.get(getPair(lang, skillName)).erase(ptrToSRSData); - ptrToSRSData.skills.get(skillName).level = newLevel; - ptrToSRSData.skills.get(skillName).lastUpdated = now(); - ptrToSRSData.lastUpdated = now(); - - this.learningTrees.get(getPair(lang, skillName)).insert(ptrToSRSData); - this.storeOrUpdate(lang, character, ptrToSRSData); + if(!srsData) srsData = new SRSData(character, lang); + this.storeOrUpdateToDB(lang, character, srsData); + this.srsData.get(lang).set(character, srsData); + for(const key of Object.keys(SkillConstant)) { + this.learningTrees.get(getPair(lang, SkillConstant[key])).insert(srsData); + } } static classicKeyGen(srsData: SRSData, skillType: SkillConstant) { @@ -252,19 +242,17 @@ class SRSDatabase { sm2Algorithm(skill, grade); - this.learningTrees.get(getPair(lang, skillType)).insert(ptrToSRSData); this.storeOrUpdate(lang, character, ptrToSRSData); } static getQuestion(lang: string, skillType: SkillConstant, optionNumber: number = 3) { const learningTree = this.learningTrees.get(getPair(lang, skillType)); const treeSize = learningTree.container.size(); + console.log(treeSize) if (treeSize < 1) return null; // Not enough characters to generate a question - const nextCharacter = learningTree.findByOrder(0); // Get the closest character to be learned // Ensure optionNumber does not exceed the available distinct characters optionNumber = Math.min(optionNumber, treeSize - 1); - let selectedIndices: number[] = [] if (optionNumber > 100) { @@ -347,7 +335,7 @@ class Learning { await this.db.put(key, JSON.stringify(parsedValue)); } if (parsedValue.level) for (const ch of strippedKey) { - SRSDatabase.insertNew(lang, ch); + SRSDatabase.storeOrUpdate(lang, ch); } learningState[strippedKey] = parsedValue; } @@ -415,7 +403,7 @@ class Learning { } }); - ipcMain.handle('getOneQuestion', async (_event, lang, skillType) => { + ipcMain.handle('learn-getOneQuestion', async (_event, lang, skillType) => { try { return SRSDatabase.getQuestion(lang, skillType); } catch (error) { @@ -423,6 +411,17 @@ class Learning { return null; } }); + + ipcMain.handle('learn-updateOneCharacter', async (_event, skillType, lang, character, isCorrect) => { + try { + const grade = isCorrect ? 5 : 2; // SM2 grading: 5 for correct, 2 for incorrect + SRSDatabase.updateSkillLevel(lang, character, skillType, grade); + return true; + } catch (error) { + console.error('Error updating one character:', error); + return false; + } + }); } } diff --git a/renderer/components/Meaning/QuizDisplay.tsx b/renderer/components/Meaning/QuizDisplay.tsx index d7dc0b1..17ccbb3 100644 --- a/renderer/components/Meaning/QuizDisplay.tsx +++ b/renderer/components/Meaning/QuizDisplay.tsx @@ -2,7 +2,7 @@ import React, {useEffect, useRef, useState} from "react"; import HanziWriter from "hanzi-writer"; import {AwesomeButton} from "react-awesome-button"; -const QuizDisplay = ({character, mode = 'plain'}) => { +const QuizDisplay = ({character, mode = 'plain', onAnswer}) => { const writingRef = useRef(null); const [hanziWriter, setHanziWriter] = useState(null); const [changer, setChanger] = useState(0); @@ -31,6 +31,7 @@ const QuizDisplay = ({character, mode = 'plain'}) => { showHintAfterMisses: mode === 'plain' ? 3 : false, onComplete: () => { console.log('Quiz complete') + onAnswer(true); }, }).then(r => console.log(r)).catch(error => { console.log(error) @@ -44,7 +45,7 @@ const QuizDisplay = ({character, mode = 'plain'}) => { console.log(e); } } - }, [character, mode, hanziWriter, changer]); + }, [character, mode, hanziWriter, changer, onAnswer]); return (
{ - const [currentCharacter, setCurrentCharacter] = useState(initialCharacter); - const [hanziProgress, setHanziProgress] = useState(null); - const [mode, setMode] = useState('help'); - - const fetchProgress = useCallback(async (char) => { - const progress = await ipcRenderer.invoke('loadSRSState', videoConstants.chineseLang); - setHanziProgress(progress[char] || { - level: {reading: 0, meaning: 0, writing: 0}, - timeCreated: Date.now(), - timeUpdated: {reading: Date.now(), meaning: Date.now(), writing: Date.now()} - }); - }, []); - - const updateProgress = useCallback(async (char) => { - await ipcRenderer.invoke('updateSRSContent', char, 'chinese', { - level: {reading: 0, meaning: 0, writing: 0}, - timeCreated: Date.now(), - timeUpdated: {reading: Date.now(), meaning: Date.now(), writing: Date.now()} - }); - }, []); - - useEffect(() => { - if (currentCharacter) { - fetchProgress(currentCharacter); - } - }, [currentCharacter, fetchProgress]); - - return { - currentCharacter, - setCurrentCharacter, - hanziProgress, - mode, - setMode, - updateProgress - }; -}; - -export default useSRS; diff --git a/renderer/pages/srs.tsx b/renderer/pages/srs.tsx index d818f1f..53215af 100644 --- a/renderer/pages/srs.tsx +++ b/renderer/pages/srs.tsx @@ -1,9 +1,7 @@ import React, {useCallback, useEffect, useState} from "react"; -import {ipcRenderer} from 'electron'; +import {ipcRenderer} from "electron"; import QuizDisplay from "../components/Meaning/QuizDisplay"; -import {videoConstants} from "../utils/constants"; -import {AwesomeButton} from "react-awesome-button"; -import 'react-awesome-button/dist/styles.css'; +import "react-awesome-button/dist/styles.css"; import Head from "next/head"; import useMeaning from "../hooks/useMeaning"; import useLearningKeyBind from "../hooks/useLearningKeyBind"; @@ -13,8 +11,14 @@ import {LearningSidebar} from "../components/VideoPlayer/LearningSidebar"; import {defaultLearningStyling} from "../utils/CJKStyling"; import {useStoreData} from "../hooks/useStoreData"; -const SRS = () => { +// Define the types +enum SkillConstant { + Writing, + Conveyance, + Translation, +} +const SRS = () => { const { meaning, setMeaning, @@ -25,37 +29,42 @@ const SRS = () => { lang } = useMiteiruTokenizer(); const [showSidebar, setShowSidebar] = useState(0); - useLearningKeyBind(setMeaning, setShowSidebar, undo) + useLearningKeyBind(setMeaning, setShowSidebar, undo); + + const [mode, setMode] = useState("help"); + const [questionData, setQuestionData] = useState<{ + question: string; + options: string[] + } | null>(null); + const [primaryStyling, setPrimaryStyling] = useStoreData( + "user.styling.learning", + defaultLearningStyling + ); - const [currentCharacter, setCurrentCharacter] = useState('学'); - const [mode, setMode] = useState('help'); - const [hanziProgress, setHanziProgress] = useState(null); - const [primaryStyling, setPrimaryStyling] = useStoreData('user.styling.learning', defaultLearningStyling); + const fetchQuestion = useCallback(async () => { + const question = await ipcRenderer.invoke("learn-getOneQuestion", lang, SkillConstant.Writing, 0); + console.log(question) + setQuestionData(question); + }, [lang]); useEffect(() => { - if (currentCharacter) { - const fetchProgress = async () => { - const progress = {currentCharacter: null}; - setHanziProgress(progress[currentCharacter] || { - level: {reading: 0, meaning: 0, writing: 0}, - timeCreated: Date.now(), - timeUpdated: {reading: Date.now(), meaning: Date.now(), writing: Date.now()} - }); - }; - fetchProgress(); - } - }, [currentCharacter]); + fetchQuestion().then(r => console.log("OK")); + }, [fetchQuestion]); - const handleStartPractice = useCallback(async (char) => { - setCurrentCharacter(char); - await ipcRenderer.invoke('updateSRSContent', char, 'chinese', { - level: {reading: 0, meaning: 0, writing: 0}, - timeCreated: Date.now(), - timeUpdated: {reading: Date.now(), meaning: Date.now(), writing: Date.now()} - }); - }, []); + const handleAnswer = async (isCorrect: boolean) => { + if (questionData) { + await ipcRenderer.invoke( + "learn-updateOneCharacter", + SkillConstant.Writing, + lang, + questionData.question, + isCorrect + ); + fetchQuestion(); // Fetch the next question + } + }; - const handleModeChange = useCallback((newMode) => { + const handleModeChange = useCallback((newMode: string) => { setMode(newMode); }, []); @@ -66,22 +75,9 @@ const SRS = () => {
-

Hanzi/Kanji Practice

-
- setCurrentCharacter(e.target.value)} - /> - handleStartPractice(currentCharacter)} - className="ml-4"> - Start Practice - -
-
- -
-
-

Progress for {currentCharacter}

-

Reading Level: {hanziProgress ? hanziProgress.level.reading : 0}

-

Meaning Level: {hanziProgress ? hanziProgress.level.meaning : 0}

-

Writing Level: {hanziProgress ? hanziProgress.level.writing : 0}

-
- + {questionData && ( +
+ +
+ )} +
);