This commit is contained in:
@@ -2,16 +2,14 @@ import Button from "@/components/Button";
|
||||
import IconClick from "@/components/IconClick";
|
||||
import IMAGES from "@/config/images";
|
||||
import { Letter, SupportedAlphabets } from "@/interfaces";
|
||||
import { Dispatch, KeyboardEvent, SetStateAction, useEffect, useRef, useState } from "react";
|
||||
import { Dispatch, KeyboardEvent, SetStateAction, useEffect, useState } from "react";
|
||||
|
||||
export default function MemoryCard(
|
||||
{
|
||||
alphabet,
|
||||
language,
|
||||
setChosenAlphabet
|
||||
}: {
|
||||
alphabet: Letter[],
|
||||
language: string,
|
||||
setChosenAlphabet: Dispatch<SetStateAction<SupportedAlphabets | null>>
|
||||
}
|
||||
) {
|
||||
@@ -34,7 +32,7 @@ export default function MemoryCard(
|
||||
}
|
||||
return (
|
||||
<div className="w-full flex justify-center items-center" onKeyDown={(e: KeyboardEvent<HTMLDivElement>) => e.preventDefault()}>
|
||||
{/* <div className="m-4 p-4 w-full md:w-[60dvw] flex-col rounded-2xl shadow border-gray-200 border flex justify-center items-center">
|
||||
<div className="m-4 p-4 w-full md:w-[60dvw] flex-col rounded-2xl shadow border-gray-200 border flex justify-center items-center">
|
||||
<div className="w-full flex justify-end items-center">
|
||||
<IconClick size={32} alt="close" src={IMAGES.close} onClick={() => setChosenAlphabet(null)}></IconClick>
|
||||
</div>
|
||||
@@ -47,12 +45,12 @@ export default function MemoryCard(
|
||||
<IconClick size={48} alt="more" src={IMAGES.more_horiz} onClick={() => setMore(!more)}></IconClick>
|
||||
{
|
||||
more ? (<>
|
||||
<Button className="w-20" label={letterDisplay ? '隐藏字母' : '显示字母'} onClick={() => { setLetterDisplay(!letterDisplay) }}></Button>
|
||||
<Button className="w-20" label={ipaDisplay ? '隐藏IPA' : '显示IPA'} onClick={() => { setIPADisplay(!ipaDisplay) }}></Button>
|
||||
<Button className="w-20" onClick={() => { setLetterDisplay(!letterDisplay) }}>{letterDisplay ? '隐藏字母' : '显示字母'}</Button>
|
||||
<Button className="w-20" onClick={() => { setIPADisplay(!ipaDisplay) }}>{ipaDisplay ? '隐藏IPA' : '显示IPA'}</Button>
|
||||
</>) : (<></>)
|
||||
}
|
||||
</div>
|
||||
</div> */}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -75,7 +75,6 @@ export default function Alphabet() {
|
||||
return (<>
|
||||
<Navbar></Navbar>
|
||||
<MemoryCard
|
||||
language={chosenAlphabet}
|
||||
alphabet={alphabetData[chosenAlphabet]}
|
||||
setChosenAlphabet={setChosenAlphabet}>
|
||||
</MemoryCard>
|
||||
|
||||
@@ -10,6 +10,7 @@ import SaveList from "./SaveList";
|
||||
import { TextSpeakerItemSchema } from "@/interfaces";
|
||||
import z from "zod";
|
||||
import { Navbar } from "@/components/Navbar";
|
||||
import { VOICES } from "@/config/locales";
|
||||
|
||||
export default function TextSpeaker() {
|
||||
const textareaRef = useRef<HTMLTextAreaElement>(null);
|
||||
@@ -25,19 +26,7 @@ export default function TextSpeaker() {
|
||||
const [ipa, setIPA] = useState<string>('');
|
||||
const objurlRef = useRef<string | null>(null);
|
||||
const [processing, setProcessing] = useState(false);
|
||||
const [voicesData, setVoicesData] = useState<{
|
||||
locale: string,
|
||||
short_name: string
|
||||
}[] | null>(null);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const { playAudio, stopAudio, audioRef } = useAudioPlayer();
|
||||
useEffect(() => {
|
||||
fetch('/list_of_voices.json')
|
||||
.then(res => res.json())
|
||||
.then(setVoicesData)
|
||||
.catch(() => setVoicesData(null))
|
||||
.finally(() => setLoading(false));
|
||||
}, []);
|
||||
useEffect(() => {
|
||||
const audio = audioRef.current;
|
||||
if (!audio) return;
|
||||
@@ -56,11 +45,6 @@ export default function TextSpeaker() {
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [audioRef, autopause]);
|
||||
|
||||
|
||||
if (loading) return <div>加载中...</div>;
|
||||
if (!voicesData) return <div>加载失败</div>;
|
||||
|
||||
|
||||
const speak = async () => {
|
||||
if (processing) return;
|
||||
setProcessing(true);
|
||||
@@ -103,7 +87,7 @@ export default function TextSpeaker() {
|
||||
theLocale = textinfo.locale as string;
|
||||
}
|
||||
|
||||
const voice = voicesData.find(v => v.locale.startsWith(theLocale));
|
||||
const voice = VOICES.find(v => v.locale.startsWith(theLocale));
|
||||
if (!voice) throw 'Voice not found.';
|
||||
|
||||
objurlRef.current = await getTTSAudioUrl(
|
||||
|
||||
@@ -7,15 +7,11 @@ import { useAudioPlayer } from "@/hooks/useAudioPlayer";
|
||||
import IMAGES from "@/config/images";
|
||||
import { getTTSAudioUrl } from "@/utils";
|
||||
import { Navbar } from "@/components/Navbar";
|
||||
import { VOICES } from "@/config/locales";
|
||||
|
||||
export default function Translator() {
|
||||
const [ipaEnabled, setIPAEnabled] = useState(true);
|
||||
const [voicesData, setVoicesData] = useState<{
|
||||
locale: string,
|
||||
short_name: string
|
||||
}[] | null>(null);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [targetLang, setTargetLang] = useState('Italian');
|
||||
const [targetLang, setTargetLang] = useState('Chinese');
|
||||
|
||||
const [sourceText, setSourceText] = useState('');
|
||||
const [targetText, setTargetText] = useState('');
|
||||
@@ -23,21 +19,10 @@ export default function Translator() {
|
||||
const [targetIPA, setTargetIPA] = useState('');
|
||||
const [sourceLocale, setSourceLocale] = useState<string | null>(null);
|
||||
const [targetLocale, setTargetLocale] = useState<string | null>(null);
|
||||
|
||||
const [translating, setTranslating] = useState(false);
|
||||
const { playAudio } = useAudioPlayer();
|
||||
|
||||
useEffect(() => {
|
||||
fetch('/list_of_voices.json')
|
||||
.then(res => res.json())
|
||||
.then(setVoicesData)
|
||||
.catch(() => setVoicesData(null))
|
||||
.finally(() => setLoading(false));
|
||||
}, []);
|
||||
if (loading) return <div>加载中...</div>;
|
||||
if (!voicesData) return <div>加载失败</div>;
|
||||
|
||||
const tl = ['English', 'Italian', 'Japanese'];
|
||||
const tl = ['Chinese', 'English', 'Italian'];
|
||||
|
||||
const inputLanguage = () => {
|
||||
const lang = prompt('Input a language.')?.trim();
|
||||
@@ -136,7 +121,7 @@ export default function Translator() {
|
||||
const info = await res.json();
|
||||
setSourceLocale(info.locale);
|
||||
|
||||
const voice = voicesData.find(v => v.locale.startsWith(info.locale));
|
||||
const voice = VOICES.find(v => v.locale.startsWith(info.locale));
|
||||
if (!voice) {
|
||||
return;
|
||||
}
|
||||
@@ -150,7 +135,7 @@ export default function Translator() {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
const voice = voicesData.find(v => v.locale.startsWith(sourceLocale!));
|
||||
const voice = VOICES.find(v => v.locale.startsWith(sourceLocale!));
|
||||
if (!voice) {
|
||||
return;
|
||||
}
|
||||
@@ -178,7 +163,7 @@ export default function Translator() {
|
||||
})
|
||||
}
|
||||
|
||||
const voice = voicesData.find(v => v.locale.startsWith(targetLocale!));
|
||||
const voice = VOICES.find(v => v.locale.startsWith(targetLocale!));
|
||||
if (!voice) return;
|
||||
|
||||
const url = await getTTSAudioUrl(targetText, voice.short_name);
|
||||
@@ -227,9 +212,9 @@ export default function Translator() {
|
||||
</div>
|
||||
<div className="option2 w-full flex gap-1 items-center flex-wrap">
|
||||
<span>translate into</span>
|
||||
<Button onClick={() => { setTargetLang('Chinese') }} selected={targetLang === 'Chinese'}>Chinese</Button>
|
||||
<Button onClick={() => { setTargetLang('English') }} selected={targetLang === 'English'}>English</Button>
|
||||
<Button onClick={() => { setTargetLang('Italian') }} selected={targetLang === 'Italian'}>Italian</Button>
|
||||
<Button onClick={() => { setTargetLang('Japanese') }} selected={targetLang === 'Japanese'}>Japanese</Button>
|
||||
<Button onClick={inputLanguage} selected={!(tl.includes(targetLang))}>{'Other' + (tl.includes(targetLang) ? '' : ': ' + targetLang)}</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user