mirror of
https://github.com/jamiepine/voicebox.git
synced 2026-09-15 12:50:42 -07:00
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.
This commit is contained in:
+1
-1
@@ -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';
|
||||
|
||||
@@ -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<GenerationFormValues>({
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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 (
|
||||
<div className="fixed left-0 top-0 h-full w-20 bg-sidebar border-r border-border flex flex-col items-center py-6 gap-6">
|
||||
{/* Logo */}
|
||||
<div className="mb-2">
|
||||
<img
|
||||
src={voiceboxLogo}
|
||||
alt="Voicebox"
|
||||
className="w-12 h-12 object-contain"
|
||||
/>
|
||||
<img src={voiceboxLogo} alt="Voicebox" className="w-12 h-12 object-contain" />
|
||||
</div>
|
||||
|
||||
{/* Navigation Buttons */}
|
||||
@@ -48,6 +50,21 @@ export function Sidebar({ activeTab, onTabChange }: SidebarProps) {
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
|
||||
{/* Spacer to push loader to bottom */}
|
||||
<div className="flex-1" />
|
||||
|
||||
{/* Generation Loader */}
|
||||
{isGenerating && (
|
||||
<div
|
||||
className={cn(
|
||||
'w-full flex items-center justify-center transition-all duration-200',
|
||||
isPlayerVisible ? 'mb-[120px]' : 'mb-0',
|
||||
)}
|
||||
>
|
||||
<Loader2 className="h-6 w-6 text-accent animate-spin" />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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() {
|
||||
<DialogHeader>
|
||||
<DialogTitle>Import Profile</DialogTitle>
|
||||
<DialogDescription>
|
||||
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.
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
<DialogFooter>
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
import { create } from 'zustand';
|
||||
|
||||
interface GenerationState {
|
||||
isGenerating: boolean;
|
||||
setIsGenerating: (generating: boolean) => void;
|
||||
}
|
||||
|
||||
export const useGenerationStore = create<GenerationState>((set) => ({
|
||||
isGenerating: false,
|
||||
setIsGenerating: (generating) => set({ isGenerating: generating }),
|
||||
}));
|
||||
Binary file not shown.
Reference in New Issue
Block a user