diff options
author | polwex <polwex@sortug.com> | 2025-08-16 13:18:51 +0700 |
---|---|---|
committer | polwex <polwex@sortug.com> | 2025-08-16 13:18:51 +0700 |
commit | 9a1e8af707acec9bfabd4c5be9ba6595d7f14c3e (patch) | |
tree | ba345111027c359cf76501781236b4d8f1581ade /src/components/tones/ToneSelectorClient.tsx | |
parent | f6f8e791fc95d5efb585177071ba0328d3c3b17f (diff) |
pretty fast gotta say
Diffstat (limited to 'src/components/tones/ToneSelectorClient.tsx')
-rw-r--r-- | src/components/tones/ToneSelectorClient.tsx | 65 |
1 files changed, 53 insertions, 12 deletions
diff --git a/src/components/tones/ToneSelectorClient.tsx b/src/components/tones/ToneSelectorClient.tsx index 8a0327c..48580a4 100644 --- a/src/components/tones/ToneSelectorClient.tsx +++ b/src/components/tones/ToneSelectorClient.tsx @@ -1,6 +1,7 @@ "use client"; -import { useState, useEffect, useTransition, useRef } from "react"; +import { Spinner } from "@/components/ui/spinner"; +import { useState, useEffect, useTransition, useRef, useCallback } from "react"; import { WordData } from "@/zoom/logic/types"; import { fetchWordsByToneAndSyllables, @@ -26,7 +27,7 @@ import { Label } from "@/components/ui/label"; import { Skeleton } from "@/components/ui/skeleton"; // For loading state import { MutationOrder, ToneQuery } from "@/lib/types/phonetics"; import { ProsodySyllable } from "@/lib/types/cards"; -import { ArrowLeft, ArrowRight, Loader2, Volume2 } from "lucide-react"; +import { ArrowLeft, ArrowRight, Volume2 } from "lucide-react"; function getColorByTone(tone: string): string { if (tone === "mid") return "blue"; @@ -38,6 +39,7 @@ function getColorByTone(tone: string): string { } // Helper to display tones prominently const ProminentToneDisplay = ({ word }: { word: any }) => { + const [isLoading, setLoading] = useState(false); const tones: string[] = word.tone_sequence.split(","); const syls: string[] = word.syl_seq.split(","); const [isPending, startTransition] = useTransition(); @@ -59,7 +61,7 @@ const ProminentToneDisplay = ({ word }: { word: any }) => { const audioRef = useRef<HTMLAudioElement>(null); async function playAudio() { - // setLoading(true); + setLoading(true); // const audioContext = new (window.AudioContext || // (window as any).webkitAudioContext)(); // const response = await fetch(audioUrl); @@ -76,11 +78,22 @@ const ProminentToneDisplay = ({ word }: { word: any }) => { const res = await fetch(`/api/tts?word=${word.spelling}&lang=thai`); const audioBlob = await res.blob(); const audioURL = URL.createObjectURL(audioBlob); + setLoading(false); if (audioRef.current) { audioRef.current.src = audioURL; audioRef.current.play(); } } + useEffect(() => { + const onKeyDown = (e: KeyboardEvent) => { + if (e.key === " ") { + e.preventDefault(); + playAudio(); + } + }; + window.addEventListener("keydown", onKeyDown); + return () => window.removeEventListener("keydown", onKeyDown); + }, [playAudio]); return ( <div className="flex flex-col items-center mb-4"> @@ -105,7 +118,7 @@ const ProminentToneDisplay = ({ word }: { word: any }) => { > <Volume2 size={20} /> </button> - {isPending && <Loader2 />} + {(isPending || isLoading) && <Spinner />} <audio ref={audioRef} /> <p className="ipa text-xl text-gray-700 mt-2">{word.frequency}</p> <p className="ipa text-xl text-gray-700 mt-2">{word.word_id}</p> @@ -126,12 +139,26 @@ export default function ToneSelectorClient({ const [isLoading, startTransition] = useTransition(); const [selectedTones, setTones] = useState<ToneQuery>(initialTones); - function goPrev() { + const goPrev = useCallback(() => { setCurrentIdx((i) => (i === 0 ? 0 : i - 1)); - } - function goNext() { + }, []); + const goNext = useCallback(() => { setCurrentIdx((i) => (i === data.length - 1 ? data.length - 1 : i + 1)); - } + }, [data.length]); + + useEffect(() => { + const onKeyDown = (e: KeyboardEvent) => { + if (e.key === "ArrowLeft") { + e.preventDefault(); + goPrev(); + } else if (e.key === "ArrowRight") { + e.preventDefault(); + goNext(); + } + }; + window.addEventListener("keydown", onKeyDown); + return () => window.removeEventListener("keydown", onKeyDown); + }, [goPrev, goNext]); const handleFetch = () => { startTransition(async () => { @@ -225,12 +252,12 @@ function ToneForm({ { value: "rising", label: "5 (Rising)" }, ]; const [syllableCount, setSyllableCount] = useState<number>(2); - function decrSyl() { + const decrSyl = useCallback(() => { setSyllableCount((s) => (s <= 1 ? 1 : s - 1)); - } - function incrSyl() { + }, []); + const incrSyl = useCallback(() => { setSyllableCount((s) => (s >= 5 ? 5 : s + 1)); - } + }, []); useEffect(() => { // Adjust selectedTones array length when syllableCount changes @@ -251,6 +278,20 @@ function ToneForm({ } }; + useEffect(() => { + const onKeyDown = (e: KeyboardEvent) => { + if (e.key === "ArrowUp") { + e.preventDefault(); + incrSyl(); + } else if (e.key === "ArrowDown") { + e.preventDefault(); + decrSyl(); + } + }; + window.addEventListener("keydown", onKeyDown); + return () => window.removeEventListener("keydown", onKeyDown); + }, [incrSyl, decrSyl]); + const handleToneChange = (syllableIndex: number, value: string) => { const tone = value === "any" ? null : value; setTones((prevTones) => { |