From 9a1e8af707acec9bfabd4c5be9ba6595d7f14c3e Mon Sep 17 00:00:00 2001
From: polwex <polwex@sortug.com>
Date: Sat, 16 Aug 2025 13:18:51 +0700
Subject: pretty fast gotta say

---
 src/components/tones/ToneSelectorClient.tsx | 65 +++++++++++++++++++++++------
 1 file changed, 53 insertions(+), 12 deletions(-)

(limited to 'src/components/tones/ToneSelectorClient.tsx')

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) => {
-- 
cgit v1.2.3

