The weeb for the next gen discord boat - Wamellow wamellow.com
bot discord
3
fork

Configure Feed

Select the types of activity you want to include in your feed.

perf: optimize tts demo (#50)

authored by

Luna Seemann and committed by
GitHub
40a804a5 237fb373

+88 -74
+85 -71
app/(home)/tts-demo.component.tsx
··· 5 5 import DiscordUser from "@/components/discord/user"; 6 6 import { actor } from "@/utils/tts"; 7 7 import Image from "next/image"; 8 - import { useEffect, useRef, useState } from "react"; 8 + import { useEffect, useState } from "react"; 9 9 10 10 const DEMO_MESSAGES = [ 11 11 "Hello everyone, welcome to the voice channel!", ··· 29 29 Pausing = 2 30 30 } 31 31 32 - export function TTSDemo() { 33 - const [messageIndex, setMessageIndex] = useState(0); 32 + function Typewriter({ text, onComplete }: { text: string; onComplete: () => void; }) { 34 33 const [charIndex, setCharIndex] = useState(0); 35 - const [phase, setPhase] = useState<AnimationPhase>(AnimationPhase.Typing); 36 34 37 - const currentMessage = DEMO_MESSAGES[messageIndex]; 38 - const displayedText = currentMessage.slice(0, charIndex); 35 + useEffect( 36 + () => { 37 + if (charIndex < text.length) { 38 + const timeout = setTimeout( 39 + () => setCharIndex((c) => c + 1), 40 + 40 + Math.random() * 30 41 + ); 39 42 40 - const timeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null); 43 + return () => clearTimeout(timeout); 44 + } 41 45 42 - useEffect(() => { 43 - if (phase !== AnimationPhase.Typing) return; 46 + const timeout = setTimeout(onComplete, 100); 47 + return () => clearTimeout(timeout); 48 + }, 49 + [charIndex, text.length, onComplete] 50 + ); 44 51 45 - if (charIndex < currentMessage.length) { 46 - timeoutRef.current = setTimeout(() => { 47 - setCharIndex((c) => c + 1); 48 - }, 40 + Math.random() * 30); 49 - } else { 50 - timeoutRef.current = setTimeout(() => { 51 - setPhase(AnimationPhase.Speaking); 52 - }, 100); 53 - } 52 + return ( 53 + <> 54 + {text.slice(0, charIndex)} 55 + {charIndex < text.length && ( 56 + <span className="inline-block w-0.5 h-4 bg-white ml-0.5 animate-blink" /> 57 + )} 58 + </> 59 + ); 60 + } 54 61 55 - return () => { 56 - if (timeoutRef.current) clearTimeout(timeoutRef.current); 57 - }; 58 - }, [phase, charIndex, currentMessage.length]); 62 + function TTSDemoInterface() { 63 + const [messageIndex, setMessageIndex] = useState(0); 64 + const [phase, setPhase] = useState<AnimationPhase>(AnimationPhase.Typing); 59 65 60 - useEffect(() => { 61 - if (phase !== AnimationPhase.Speaking) return; 66 + const currentMessage = DEMO_MESSAGES[messageIndex]; 62 67 63 - const speakDuration = 2_000 + currentMessage.length * 30; 64 - timeoutRef.current = setTimeout(() => { 65 - setPhase(AnimationPhase.Pausing); 66 - }, speakDuration); 68 + useEffect( 69 + () => { 70 + if (phase === AnimationPhase.Speaking) { 71 + const speakDuration = 2_000 + currentMessage.length * 30; 72 + const timeout = setTimeout(() => setPhase(AnimationPhase.Pausing), speakDuration); 67 73 68 - return () => { 69 - if (timeoutRef.current) clearTimeout(timeoutRef.current); 70 - }; 71 - }, [phase, currentMessage.length]); 74 + return () => clearTimeout(timeout); 75 + } 72 76 73 - useEffect(() => { 74 - if (phase !== AnimationPhase.Pausing) return; 77 + if (phase === AnimationPhase.Pausing) { 78 + const timeout = setTimeout( 79 + () => { 80 + setMessageIndex((i) => (i + 1) % DEMO_MESSAGES.length); 81 + setPhase(AnimationPhase.Typing); 82 + }, 83 + 800 84 + ); 75 85 76 - timeoutRef.current = setTimeout(() => { 77 - setMessageIndex((i) => (i + 1) % DEMO_MESSAGES.length); 78 - setCharIndex(0); 79 - setPhase(AnimationPhase.Typing); 80 - }, 800); 86 + return () => clearTimeout(timeout); 87 + } 88 + }, 89 + [phase, currentMessage.length] 90 + ); 81 91 82 - return () => { 83 - if (timeoutRef.current) clearTimeout(timeoutRef.current); 84 - }; 85 - }, [phase]); 92 + return ( 93 + <> 94 + <div className="bg-discord-gray rounded-lg py-4 px-8 border border-white/5 backdrop-blur-sm"> 95 + <DiscordChannelCategory name="#/voice/dev/null"> 96 + <DiscordChannel 97 + type="voice" 98 + name="• Public" 99 + > 100 + <DiscordUser username="Luna" avatar="/luna.webp" isMuted /> 101 + <DiscordUser username="Duck" avatar="/space.webp" isMuted /> 102 + <DiscordUser username="Wamellow" avatar="/waya-v3.webp" isTalking={phase === AnimationPhase.Speaking} isBot /> 103 + </DiscordChannel> 104 + </DiscordChannelCategory> 105 + </div> 86 106 87 - const isSpeaking = phase === AnimationPhase.Speaking; 88 - const isTyping = phase === AnimationPhase.Typing && charIndex < currentMessage.length; 107 + <div className="bg-[#383a40] rounded-lg p-3 w-full border border-white/5 backdrop-blur-sm"> 108 + <div className="flex items-center gap-2"> 109 + <Image src="/luna.webp" alt="" width={64} height={64} className="size-6 rounded-full" priority /> 110 + <div className="flex-1 text-sm text-white min-h-5"> 111 + {phase === AnimationPhase.Typing ? ( 112 + <Typewriter 113 + key={messageIndex} 114 + text={currentMessage} 115 + onComplete={() => setPhase(AnimationPhase.Speaking)} 116 + /> 117 + ) : ( 118 + currentMessage 119 + )} 120 + </div> 121 + </div> 122 + </div> 123 + </> 124 + ); 125 + } 89 126 127 + export function TTSDemo() { 90 128 return ( 91 129 <div className="relative w-full"> 92 130 <div className="absolute inset-0 -z-10"> 93 - <div className="absolute -inset-4 bg-gradient-to-br from-violet-600/30 via-indigo-500/20 to-pink-500/30 rounded-3xl blur-2xl animate-pulse-slow" /> 131 + <div className="absolute -inset-4 bg-linear-to-br from-violet-600/30 via-indigo-500/20 to-pink-500/30 rounded-3xl blur-2xl animate-pulse-slow" /> 94 132 <div className="absolute -top-8 -left-8 size-32 bg-violet-500/25 rounded-full blur-3xl animate-float" /> 95 133 <div className="absolute -bottom-6 -right-6 size-24 bg-pink-500/20 rounded-full blur-2xl animate-float-delayed" /> 96 134 </div> 97 135 98 136 <div className="flex flex-col gap-3 relative"> 99 - <div className="bg-discord-gray rounded-lg py-4 px-8 border border-white/5 backdrop-blur-sm"> 100 - <DiscordChannelCategory name="#/voice/dev/null"> 101 - <DiscordChannel 102 - type="voice" 103 - name="• Public" 104 - > 105 - <DiscordUser username="Luna" avatar="/luna.webp" isMuted /> 106 - <DiscordUser username="Duck" avatar="/space.webp" isMuted /> 107 - <DiscordUser username="Wamellow" avatar="/waya-v3.webp" isTalking={isSpeaking} isBot /> 108 - </DiscordChannel> 109 - </DiscordChannelCategory> 110 - </div> 111 - 112 - {/* Chat input */} 113 - <div className="bg-[#383a40] rounded-lg p-3 w-full border border-white/5 backdrop-blur-sm"> 114 - <div className="flex items-center gap-2"> 115 - <Image src="/luna.webp" alt="" width={64} height={64} className="size-6 rounded-full" priority /> 116 - <div className="flex-1 text-sm text-white min-h-5"> 117 - {displayedText} 118 - {isTyping && ( 119 - <span className="inline-block w-0.5 h-4 bg-white ml-0.5 animate-blink" /> 120 - )} 121 - </div> 122 - </div> 123 - </div> 137 + <TTSDemoInterface /> 124 138 125 139 <div className="flex gap-1"> 126 140 {SUPPORTED_LANGS.map((lang) => (
+3 -3
components/discord/app-badge.tsx
··· 15 15 <div 16 16 className={cn( 17 17 "text-xxs! text-white bg-blurple rounded-sm py-px px-1 h-4 inline-flex items-center", 18 - variant === "large" && "h-10 px-3 text-lg! rounded-md", 18 + variant === "large" && "h-13 px-4 rounded-2xl", 19 19 className 20 20 )} 21 21 {...props} 22 22 > 23 - <HiCheck className={cn(variant === "large" && "size-6")} /> 24 - <span className={cn("ml-1 font-semibold", variant === "large" && "ml-2")}> 23 + <HiCheck className={cn(variant === "large" && "size-8")} /> 24 + <span className={cn("ml-1 font-semibold", variant === "large" && "ml-2 text-3xl")}> 25 25 {children || "APP"} 26 26 </span> 27 27 </div>