summaryrefslogtreecommitdiff
path: root/src/components
diff options
context:
space:
mode:
authorpolwex <polwex@sortug.com>2025-08-16 13:18:51 +0700
committerpolwex <polwex@sortug.com>2025-08-16 13:18:51 +0700
commit9a1e8af707acec9bfabd4c5be9ba6595d7f14c3e (patch)
treeba345111027c359cf76501781236b4d8f1581ade /src/components
parentf6f8e791fc95d5efb585177071ba0328d3c3b17f (diff)
pretty fast gotta say
Diffstat (limited to 'src/components')
-rw-r--r--src/components/Main.tsx6
-rw-r--r--src/components/tones/ToneSelectorClient.tsx65
2 files changed, 57 insertions, 14 deletions
diff --git a/src/components/Main.tsx b/src/components/Main.tsx
index 3e6f3e7..6be61a3 100644
--- a/src/components/Main.tsx
+++ b/src/components/Main.tsx
@@ -18,8 +18,9 @@ import {
CardHeader,
CardTitle,
} from "@/components/ui/card";
-import { Loader2 } from "lucide-react"; // Loading spinner
import { useRouter } from "waku";
+import { Spinner } from "./ui/spinner";
+import { Loader2 } from "lucide-react";
const SorlangPage: React.FC = () => {
const [textValue, setTextValue] = useState<string>("");
@@ -192,7 +193,8 @@ const SorlangPage: React.FC = () => {
>
{isExtracting ? (
<>
- <Loader2 className="mr-2 h-4 w-4 animate-spin" />
+ {/*<Loader2 className="mr-2 h-4 w-4 animate-spin" />*/}
+ <Spinner />
Extracting...
</>
) : (
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) => {