Feat: added review mode

This commit is contained in:
Richard Wong 2025-05-19 14:51:01 +08:00
parent ed95d6b388
commit ba76a8983b
5 changed files with 261 additions and 11 deletions

View File

@ -5,6 +5,8 @@
"pick_num_verses": "Pick Number of Verses:", "pick_num_verses": "Pick Number of Verses:",
"num_verses_tested": "Number of Verses Tested:", "num_verses_tested": "Number of Verses Tested:",
"note_num_verses": "(It will only give you as many verses as there are in selected packs)", "note_num_verses": "(It will only give you as many verses as there are in selected packs)",
"set_review": "Set Review Mode",
"note_set_review": "Choose between test mode (default) or review mode",
"set_shuffle": "Set Shuffle:", "set_shuffle": "Set Shuffle:",
"note_set_shuffle": "(Otherwise cards will appear in sequential order)", "note_set_shuffle": "(Otherwise cards will appear in sequential order)",
"hide_reference": "Set Hide Reference:", "hide_reference": "Set Hide Reference:",

View File

@ -5,6 +5,8 @@
"pick_num_verses": "암송 구절 선택:", "pick_num_verses": "암송 구절 선택:",
"num_verses_tested": "구절 수 선택:", "num_verses_tested": "구절 수 선택:",
"note_num_verses": "(It will only give you as many verses as there are in selected packs)", "note_num_verses": "(It will only give you as many verses as there are in selected packs)",
"set_review": "Set Review Mode",
"note_set_review": "Choose between test mode (default) or review mode",
"set_shuffle": "무작위 설정:", "set_shuffle": "무작위 설정:",
"note_set_shuffle": "(Otherwise cards will appear in sequential order)", "note_set_shuffle": "(Otherwise cards will appear in sequential order)",
"hide_reference": "Set Hide Reference:", "hide_reference": "Set Hide Reference:",

119
src/VersePrinter.css Normal file
View File

@ -0,0 +1,119 @@
.App {
max-width: 400px;
}
.VersePrinter {
padding-block: 1px 10px;
border-top: 1px solid black;
max-width: 400px;
}
.reference-box {
max-width:400px;
height: 5vh;
field-sizing: content; /* not yet implemented in firefox and safari */
display: block;
width: 99%;
border: 1px solid grey;
border-radius: 5px;
font-size: 15px;
}
.chapter-title-box {
max-width:400px;
height: 5vh;
field-sizing: content; /* not yet implemented in firefox and safari */
display: block;
width: 99%;
border: 1px solid grey;
border-radius: 5px;
font-size: 15px;
}
.title-box {
max-width: 400px;
height: 5vh;
field-sizing: content; /* not yet implemented in firefox and safari */
display: block;
width: 99%;
border: 1px solid grey;
border-radius: 5px;
font-size: 15px;
}
.reference-label .main-title-box-label .title-box-label .verse-box-label {
pointer-events: none;
}
.verse-box {
max-width:400px;
min-height: 12vh;
field-sizing: content; /* not yet implemented in firefox and safari */
display: block;
width: 99%;
border: 1px solid grey;
border-radius: 5px;
font-size: 15px;
}
.answer-button-box {
display: flex;
gap: 10px;
padding-top: 10px;
}
.answer-box {
width: 80%;
}
.diff-box {
width: 80%;
}
@media (prefers-color-scheme: light) {
.correct {
background-color: #e6ffe6; /* Change the background color as needed */
}
.partial {
background-color: #dafcff; /* Change the background color as needed */
}
.incorrect {
background-color: transparent; /* Change the background color as needed */
}
:root {
--background-color-removed: #f1ebb3;
--background-color-added: #ffd7b6;
}
}
@media (prefers-color-scheme: dark) {
.correct {
background-color: #2e5e2e; /* Change the background color as needed */
}
.partial {
background-color: #004d5c; /* Change the background color as needed */
}
.incorrect {
background-color: transparent; /* Change the background color as needed */
}
:root {
--background-color-removed: #877e2a;
--background-color-added: #7b4418;
}
.VersePrinter {
border-top: 1px solid white; /* override for dark mode */
}
}

58
src/VersePrinter.jsx Normal file
View File

@ -0,0 +1,58 @@
import { useState } from "react";
import "./VersePrinter.css";
import { StringDiff } from "react-string-diff";
import { containsKorean, jamoSubstringMatch } from './utils';
const STATE = {
INCORRECT: 0,
PARTIAL: 1,
CORRECT: 2,
};
// function to render and handle logic of each of the cells
const VersePrinter = ({ element: { pack, title, chapterTitle, reference, verse } , t, index}) => { // useful use of destructuring here
return (
<div className="VersePrinter">
<div className="verse-number">
<h3>Verse {index}</h3>
</div>
{/* This shows the difference between given and input answers*/}
<div className="diff-box">
<p></p>
<div>
<h3>Reference:</h3>
<p>{reference}</p>
</div>
<p></p>
{chapterTitle && (
<div>
<h3>Chapter Title:</h3>
<p>{chapterTitle}</p>
</div>
)}
<p></p>
<div>
<h3>Title:</h3>
<p>{title}</p>
</div>
<p></p>
<div>
<h3>Verse: </h3>
<p>{verse}</p>
</div>
</div>
</div>
);
}
export default VersePrinter

View File

@ -10,6 +10,7 @@ import 'react-checkbox-tree/lib/react-checkbox-tree.css';
import _ from 'underscore'; import _ from 'underscore';
import './VerseSampler.css' import './VerseSampler.css'
import VerseValidator from "./VerseValidator"; import VerseValidator from "./VerseValidator";
import VersePrinter from "./VersePrinter";
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import logo from './assets/droplet.svg'; import logo from './assets/droplet.svg';
import { Suspense } from "react"; import { Suspense } from "react";
@ -44,6 +45,36 @@ const ArrayTester = ({ array, toHideReference, translate}) => {
return list return list
} }
const GenerateReviewList = ({ VerseData, packs, testCount, toShuffle, translate}) => {
let testList = packs.reduce(
// grab all elements included checked in "packs"
(accumulator, currentValue) => accumulator.concat(VerseData[currentValue]),
new Array()
);
testList = toShuffle ? _.sample(testList, testCount) : _.first(testList, testCount);
return (
<ArrayPrinter
array={testList}
translate={translate}
/>
)
}
const ArrayPrinter = ({ array, translate}) => {
const list = array.map((element, index) => (
// key needs to be unique; chose 3 elements that will separate all elements
<VersePrinter
key={element.pack + element.title + element.reference}
element={element}
t={translate} // this passes the t i18 object to the function
index={index + 1}
/>
))
return list
}
const CheckboxWidget = ({nodes, checked, expanded, setChecked, setExpanded}) => { const CheckboxWidget = ({nodes, checked, expanded, setChecked, setExpanded}) => {
return ( return (
<div className="CheckboxTree"> <div className="CheckboxTree">
@ -136,18 +167,36 @@ function Page() {
const [toShuffle, setShuffle] = useState(false); const [toShuffle, setShuffle] = useState(false);
// Function to handle checkbox change // Function to handle checkbox change
const handleShuffleCheckboxChange = () => { const handleShuffleCheckboxChange = () => {
// Toggle the state when the checkbox is changed
setShuffle(!toShuffle);
// additional state change to disable HideReference when shuffling // additional state change to disable HideReference when shuffling
if (!toShuffle) { if (!toShuffle) {
setHideReference(false); setHideReference(false);
} }
// Toggle the state when the checkbox is changed
// modify state at the end
setShuffle(!toShuffle);
}; };
// state for toReview
const [toReview, setReview] = useState(false);
// Function to handle checkbox change
const handleReviewCheckboxChange = () => {
// additional state change to disable HideReference when reviewing
if (!toReview) {
setHideReference(false);
}
// Toggle the state when the checkbox is changed
// modify state at the end
setReview(!toReview);
};
// state for toHideReference // state for toHideReference
const [toHideReference, setHideReference] = useState(false); const [toHideReference, setHideReference] = useState(false);
// Function to handle checkbox change // Function to handle checkbox change
const handleHideReferenceCheckboxChange = () => { const handleHideReferenceCheckboxChange = () => {
if (!toHideReference) {
setHideReference(false);
}
// Toggle the state when the checkbox is changed // Toggle the state when the checkbox is changed
setHideReference(!toHideReference); setHideReference(!toHideReference);
}; };
@ -176,6 +225,17 @@ function Page() {
<p>{t('main.note_num_verses')}</p> <p>{t('main.note_num_verses')}</p>
<h2>
{t('main.set_review')}
<input
type="checkbox"
checked={toReview}
onChange={handleReviewCheckboxChange}
/>
</h2>
<p>{t('main.note_set_review')}</p>
<h2> <h2>
{t('main.set_shuffle')} {t('main.set_shuffle')}
<input <input
@ -187,7 +247,7 @@ function Page() {
<p>{t('main.note_set_shuffle')}</p> <p>{t('main.note_set_shuffle')}</p>
<div> <div>
{!toShuffle ? {!(toShuffle || toReview) ?
<> <>
<h2> <h2>
{t('main.hide_reference')} {t('main.hide_reference')}
@ -222,14 +282,23 @@ function Page() {
</div> </div>
<h1>{t('main.verses')}</h1> <h1>{t('main.verses')}</h1>
<GenerateTestList {toReview ?
VerseData={VerseData} <GenerateReviewList
packs={checked} VerseData={VerseData}
testCount={testCount} packs={checked}
toShuffle={toShuffle} testCount={testCount}
toHideReference={toHideReference} toShuffle={toShuffle}
translate={t} translate={t}
/> /> :
<GenerateTestList
VerseData={VerseData}
packs={checked}
testCount={testCount}
toShuffle={toShuffle}
toHideReference={toHideReference}
translate={t}
/>
}
<hr /> <hr />