可以乱序记忆
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
2025-11-20 10:40:57 +08:00
parent 4eb44422d2
commit a2e579cb7b
3 changed files with 46 additions and 65 deletions

View File

@@ -83,48 +83,22 @@
"githubLogin": "GitHub Login" "githubLogin": "GitHub Login"
}, },
"memorize": { "memorize": {
"choose": {
"back": "Back",
"choose": "Choose"
},
"edit": {
"back": "Back",
"save": "Save Word Pairs",
"locale1": "Locale 1",
"locale2": "Locale 2"
},
"folder_selector": { "folder_selector": {
"selectFolder": "Select a folder", "selectFolder": "Select a folder",
"noFolders": "No folders found", "noFolders": "No folders found",
"folderInfo": "{id}. {name} ({count})" "folderInfo": "{id}. {name} ({count})"
}, },
"main": {
"title": "Memorize",
"locale1": "Your selected locale 1 is {locale}",
"locale2": "Your selected locale 2 is {locale}",
"total": "There are {total} word pairs in total",
"start": "Start",
"import": "Import",
"export": "Export",
"edit": "Edit"
},
"memorize": { "memorize": {
"showAnswer": "Show Answer", "showAnswer": "Show Answer",
"next": "Next", "next": "Next",
"reverse": "Reverse", "reverse": "Reverse",
"dictation": "Dictation", "dictation": "Dictation",
"noTextPairs": "No text pairs available", "noTextPairs": "No text pairs available",
"progress": "{current}/{total}" "progress": "{current}/{total}",
"disorder": "Disorder"
}, },
"page": { "page": {
"unauthorized": "You are not authorized to access this folder" "unauthorized": "You are not authorized to access this folder"
},
"start": {
"show": "Show",
"reverse": "Reverse",
"dictation": "Dictation",
"back": "Back",
"next": "Next"
} }
}, },
"navbar": { "navbar": {

View File

@@ -87,44 +87,22 @@
"back": "返回", "back": "返回",
"choose": "选择" "choose": "选择"
}, },
"edit": {
"back": "返回",
"save": "保存单词对",
"locale1": "区域1",
"locale2": "区域2"
},
"folder_selector": { "folder_selector": {
"selectFolder": "选择文件夹", "selectFolder": "选择文件夹",
"noFolders": "未找到文件夹", "noFolders": "未找到文件夹",
"folderInfo": "{id}. {name} ({count})" "folderInfo": "{id}. {name} ({count})"
}, },
"main": {
"title": "记忆",
"locale1": "您选择的区域一是{locale}",
"locale2": "您选择的区域二是{locale}",
"total": "总计有{total}个单词对",
"start": "开始",
"import": "导入",
"export": "导出",
"edit": "编辑"
},
"memorize": { "memorize": {
"showAnswer": "显示答案", "showAnswer": "显示答案",
"next": "下一个", "next": "下一个",
"reverse": "反向", "reverse": "反向",
"dictation": "听写", "dictation": "听写",
"noTextPairs": "没有可用的文本对", "noTextPairs": "没有可用的文本对",
"progress": "{current}/{total}" "progress": "{current}/{total}",
"disorder": "乱序"
}, },
"page": { "page": {
"unauthorized": "您无权访问该文件夹" "unauthorized": "您无权访问该文件夹"
},
"start": {
"show": "显示",
"reverse": "反向",
"dictation": "听写",
"back": "返回",
"next": "下个"
} }
}, },
"navbar": { "navbar": {

View File

@@ -3,7 +3,7 @@
import { Center } from "@/components/Center"; import { Center } from "@/components/Center";
import { text_pair } from "../../../../generated/prisma/browser"; import { text_pair } from "../../../../generated/prisma/browser";
import Container from "@/components/cards/Container"; import Container from "@/components/cards/Container";
import { useState } from "react"; import { useEffect, useRef, useState } from "react";
import LightButton from "@/components/buttons/LightButton"; import LightButton from "@/components/buttons/LightButton";
import { useAudioPlayer } from "@/hooks/useAudioPlayer"; import { useAudioPlayer } from "@/hooks/useAudioPlayer";
import { getTTSAudioUrl } from "@/lib/browser/tts"; import { getTTSAudioUrl } from "@/lib/browser/tts";
@@ -18,18 +18,37 @@ const Memorize: React.FC<MemorizeProps> = ({ textPairs }) => {
const t = useTranslations("memorize.memorize"); const t = useTranslations("memorize.memorize");
const [reverse, setReverse] = useState(false); const [reverse, setReverse] = useState(false);
const [dictation, setDictation] = useState(false); const [dictation, setDictation] = useState(false);
const [disorder, setDisorder] = useState(false);
const [index, setIndex] = useState(0); const [index, setIndex] = useState(0);
const [show, setShow] = useState<"question" | "answer">("question"); const [show, setShow] = useState<"question" | "answer">("question");
const { load, play } = useAudioPlayer(); const { load, play } = useAudioPlayer();
const [disorderedTextPairs, setDisorderedTextPairs] = useState<text_pair[]>(
[],
);
useEffect(() => {
setDisorderedTextPairs(textPairs.toSorted(() => Math.random() - 0.5));
}, [textPairs]);
const getTextPairs = () => {
if (disorder) {
return disorderedTextPairs;
}
return textPairs.toSorted((a, b) => a.id - b.id);
};
return ( return (
<Center> <Center>
<Container className="p-6 flex flex-col gap-8 h-96 justify-center items-center"> <Container className="p-6 flex flex-col gap-8 h-96 justify-center items-center">
{(textPairs.length > 0 && ( {(getTextPairs().length > 0 && (
<> <>
<div className="h-36 flex flex-col gap-2 justify-start items-center font-serif text-3xl"> <div className="h-36 flex flex-col gap-2 justify-start items-center font-serif text-3xl">
<div className="text-sm text-gray-500"> <div className="text-sm text-gray-500">
{t("progress", { current: index + 1, total: textPairs.length })} {t("progress", {
current: index + 1,
total: getTextPairs().length,
})}
</div> </div>
{dictation ? ( {dictation ? (
show === "question" ? ( show === "question" ? (
@@ -38,26 +57,28 @@ const Memorize: React.FC<MemorizeProps> = ({ textPairs }) => {
<> <>
<div> <div>
{reverse {reverse
? textPairs[index].text2 ? getTextPairs()[index].text2
: textPairs[index].text1} : getTextPairs()[index].text1}
</div> </div>
<div> <div>
{reverse {reverse
? textPairs[index].text1 ? getTextPairs()[index].text1
: textPairs[index].text2} : getTextPairs()[index].text2}
</div> </div>
</> </>
) )
) : ( ) : (
<> <>
<div> <div>
{reverse ? textPairs[index].text2 : textPairs[index].text1} {reverse
? getTextPairs()[index].text2
: getTextPairs()[index].text1}
</div> </div>
<div> <div>
{show === "answer" {show === "answer"
? reverse ? reverse
? textPairs[index].text1 ? getTextPairs()[index].text1
: textPairs[index].text2 : getTextPairs()[index].text2
: ""} : ""}
</div> </div>
</> </>
@@ -68,15 +89,15 @@ const Memorize: React.FC<MemorizeProps> = ({ textPairs }) => {
className="w-32" className="w-32"
onClick={async () => { onClick={async () => {
if (show === "answer") { if (show === "answer") {
const newIndex = (index + 1) % textPairs.length; const newIndex = (index + 1) % getTextPairs().length;
setIndex(newIndex); setIndex(newIndex);
if (dictation) if (dictation)
getTTSAudioUrl( getTTSAudioUrl(
textPairs[newIndex][reverse ? "text2" : "text1"], getTextPairs()[newIndex][reverse ? "text2" : "text1"],
VOICES.find( VOICES.find(
(v) => (v) =>
v.locale === v.locale ===
textPairs[newIndex][ getTextPairs()[newIndex][
reverse ? "locale2" : "locale1" reverse ? "locale2" : "locale1"
], ],
)!.short_name, )!.short_name,
@@ -106,6 +127,14 @@ const Memorize: React.FC<MemorizeProps> = ({ textPairs }) => {
> >
{t("dictation")} {t("dictation")}
</LightButton> </LightButton>
<LightButton
onClick={() => {
setDisorder(!disorder);
}}
selected={disorder}
>
{t("disorder")}
</LightButton>
</div> </div>
</> </>
)) || <p>{t("noTextPairs")}</p>} )) || <p>{t("noTextPairs")}</p>}