From d9de8f04f2f66b503a75aa2a20d880457e49f024 Mon Sep 17 00:00:00 2001 From: Jamie Pine Date: Sun, 25 Jan 2026 19:13:23 -0800 Subject: [PATCH] Refactor components and implement generation state management - Updated import order in App component for consistency. - Enhanced Sidebar component with loading indicator for audio generation state. - Integrated generation state management using Zustand in GenerationForm and Sidebar. - Improved ProfileList component formatting for better readability. - Added new generationStore for managing audio generation state across components. --- app/src/App.tsx | 2 +- .../components/Generation/GenerationForm.tsx | 5 +++ app/src/components/Sidebar.tsx | 31 ++++++++++++++---- .../components/VoiceProfiles/ProfileList.tsx | 5 +-- app/src/stores/generationStore.ts | 11 +++++++ tauri/src-tauri/gen/Assets.car | Bin 3847048 -> 3847048 bytes 6 files changed, 44 insertions(+), 10 deletions(-) create mode 100644 app/src/stores/generationStore.ts diff --git a/app/src/App.tsx b/app/src/App.tsx index 96f75043..deeb6c17 100644 --- a/app/src/App.tsx +++ b/app/src/App.tsx @@ -1,4 +1,4 @@ -import { useState, useEffect } from 'react'; +import { useEffect, useState } from 'react'; import voiceboxLogo from '@/assets/voicebox-logo.png'; import { AudioPlayer } from '@/components/AudioPlayer/AudioPlayer'; import { GenerationForm } from '@/components/Generation/GenerationForm'; diff --git a/app/src/components/Generation/GenerationForm.tsx b/app/src/components/Generation/GenerationForm.tsx index 0106684d..c029f05b 100644 --- a/app/src/components/Generation/GenerationForm.tsx +++ b/app/src/components/Generation/GenerationForm.tsx @@ -26,6 +26,7 @@ import { useToast } from '@/components/ui/use-toast'; import { apiClient } from '@/lib/api/client'; import { useGeneration } from '@/lib/hooks/useGeneration'; import { useProfile } from '@/lib/hooks/useProfiles'; +import { useGenerationStore } from '@/stores/generationStore'; import { usePlayerStore } from '@/stores/playerStore'; import { useUIStore } from '@/stores/uiStore'; @@ -45,6 +46,7 @@ export function GenerationForm() { const generation = useGeneration(); const { toast } = useToast(); const setAudio = usePlayerStore((state) => state.setAudio); + const setIsGenerating = useGenerationStore((state) => state.setIsGenerating); const form = useForm({ resolver: zodResolver(generationSchema), @@ -68,6 +70,7 @@ export function GenerationForm() { } try { + setIsGenerating(true); const result = await generation.mutateAsync({ profile_id: selectedProfileId, text: data.text, @@ -93,6 +96,8 @@ export function GenerationForm() { description: error instanceof Error ? error.message : 'Failed to generate audio', variant: 'destructive', }); + } finally { + setIsGenerating(false); } } diff --git a/app/src/components/Sidebar.tsx b/app/src/components/Sidebar.tsx index 9b690c24..4526f1a5 100644 --- a/app/src/components/Sidebar.tsx +++ b/app/src/components/Sidebar.tsx @@ -1,6 +1,8 @@ -import { Home, Settings } from 'lucide-react'; -import { cn } from '@/lib/utils/cn'; +import { Home, Loader2, Settings } from 'lucide-react'; import voiceboxLogo from '@/assets/voicebox-logo.png'; +import { cn } from '@/lib/utils/cn'; +import { useGenerationStore } from '@/stores/generationStore'; +import { usePlayerStore } from '@/stores/playerStore'; interface SidebarProps { activeTab: string; @@ -13,15 +15,15 @@ const tabs = [ ]; export function Sidebar({ activeTab, onTabChange }: SidebarProps) { + const isGenerating = useGenerationStore((state) => state.isGenerating); + const audioUrl = usePlayerStore((state) => state.audioUrl); + const isPlayerVisible = !!audioUrl; + return (
{/* Logo */}
- Voicebox + Voicebox
{/* Navigation Buttons */} @@ -48,6 +50,21 @@ export function Sidebar({ activeTab, onTabChange }: SidebarProps) { ); })}
+ + {/* Spacer to push loader to bottom */} +
+ + {/* Generation Loader */} + {isGenerating && ( +
+ +
+ )}
); } diff --git a/app/src/components/VoiceProfiles/ProfileList.tsx b/app/src/components/VoiceProfiles/ProfileList.tsx index a415f933..0aea78ad 100644 --- a/app/src/components/VoiceProfiles/ProfileList.tsx +++ b/app/src/components/VoiceProfiles/ProfileList.tsx @@ -10,7 +10,7 @@ import { DialogHeader, DialogTitle, } from '@/components/ui/dialog'; -import { useProfiles, useImportProfile } from '@/lib/hooks/useProfiles'; +import { useImportProfile, useProfiles } from '@/lib/hooks/useProfiles'; import { useUIStore } from '@/stores/uiStore'; import { ProfileCard } from './ProfileCard'; import { ProfileForm } from './ProfileForm'; @@ -128,7 +128,8 @@ export function ProfileList() { Import Profile - Import the profile from "{selectedFile?.name}". This will create a new profile with all samples. + Import the profile from "{selectedFile?.name}". This will create a new profile with + all samples. diff --git a/app/src/stores/generationStore.ts b/app/src/stores/generationStore.ts new file mode 100644 index 00000000..c507f5f3 --- /dev/null +++ b/app/src/stores/generationStore.ts @@ -0,0 +1,11 @@ +import { create } from 'zustand'; + +interface GenerationState { + isGenerating: boolean; + setIsGenerating: (generating: boolean) => void; +} + +export const useGenerationStore = create((set) => ({ + isGenerating: false, + setIsGenerating: (generating) => set({ isGenerating: generating }), +})); diff --git a/tauri/src-tauri/gen/Assets.car b/tauri/src-tauri/gen/Assets.car index 67fa90c815a4703b31f6cd513efeb6f509b0d7c1..0795abd4efed96fc65d18a761a220c016d9b6ada 100644 GIT binary patch delta 820 zcmZwFy-rj?6bJBKR%H2HQ9y7-KwZBN%*>scJ4110K0|5A8`v333kwsArIZk3;{%Y; zm{43wAHalHuybRK(TdhTK^oUdewj=$|8r(?cE5dZ?!u>&-tu&#(U^h`%)mIbVG<^e ze)mo~du(+7nP&T+)w`oNy3?wJ@X2y5iOK9pGMmU`qbPVOln*j`?#VLCiJh$?a+6ff zilz0JFP`tMb>9wbe_)$10xh@%qi`9<>Va*i(Mqe}v$K)3%!*{nns6#9un?2930;Vx zGD&-H&&w>=yHDHiTU8OIEztrQ<2}io3-TsrN|6__)Jik=pLVs2;Uec%XwNq*CM8pf z<6>rvq;`S4XNs6I#!2Zc6MVRzoOyYXGl=b_Ru!XGuGE~OaLAE?lhaw?nO=wBov?-d zH%I3nv^vjO>hEl~VH_r45~g4pW}pLCU>2^zHJF2WSb*!W2xmDrpbIx)8E(M}til@H zhC6TH23?FpDuroRSk5LGB#6tToxvSRbR|Osb)JoVno7|FGLX p-YCI6Cm2_zvmNCKGqAJ2Vf+-zocc9ciR8_*$23JN3&yCkv@4G$10 z4N{7A$pcv873i=KLMf2aaw6i&D}5SmvFFVEnjQT7RUg3D@15pityWtYjys#De>>y$ z5o-?5^vB^h%}(E&b|cR$^FDAxnNfmNVv=n19+i=unQ01 zAv}T(JccLm6rREJ>hR#j`FXA|`IK})g(w^;Wg~eLO9~>1GRaJ6mdzYW24`4#c`y3M z`!9iD54ymBg8&H%balLMsy}(eJxJ076bej{>qx<)KMRU6o?M7h%@$grgP0bMx>Lv|6j_ANT?0u%2OE{yaYEZP(YT-zU8<^Zx)Xp5qSy