mirror of
https://github.com/jamiepine/voicebox.git
synced 2026-10-02 16:45:15 -07:00
Fix review findings: toggle logic, preset saving, version lookup, async audio ops
- Fix inverted effects toggle in FloatingGenerateBox - Add Save button + API method for editing custom effect presets - Return early on effects save failure in ProfileForm - Fix no-op ternary in effectsStore - Handle duplicate preset names with proper 400 response - Use effects_chain is None instead of label for clean version lookup - Move blocking audio ops to asyncio.to_thread in async endpoints - Log warnings instead of silently swallowing parse errors
This commit is contained in:
@@ -142,6 +142,29 @@ export function EffectsDetail() {
|
||||
}
|
||||
}
|
||||
|
||||
async function handleSaveExisting() {
|
||||
if (!selectedPresetId || !name.trim()) return;
|
||||
setSaving(true);
|
||||
try {
|
||||
await apiClient.updateEffectPreset(selectedPresetId, {
|
||||
name: name.trim(),
|
||||
description: description.trim() || undefined,
|
||||
effects_chain: workingChain,
|
||||
});
|
||||
queryClient.invalidateQueries({ queryKey: ['effect-presets'] });
|
||||
queryClient.invalidateQueries({ queryKey: ['effect-preset', selectedPresetId] });
|
||||
toast({ title: 'Preset updated' });
|
||||
} catch (error) {
|
||||
toast({
|
||||
title: 'Failed to save',
|
||||
description: error instanceof Error ? error.message : 'Unknown error',
|
||||
variant: 'destructive',
|
||||
});
|
||||
} finally {
|
||||
setSaving(false);
|
||||
}
|
||||
}
|
||||
|
||||
async function handleSaveAsNew() {
|
||||
await handleSaveNew();
|
||||
}
|
||||
@@ -186,16 +209,27 @@ export function EffectsDetail() {
|
||||
</h2>
|
||||
<div className="flex items-center gap-2">
|
||||
{!isBuiltIn && !isCreatingNew && (
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
className="h-8 text-destructive hover:text-destructive gap-1.5"
|
||||
onClick={handleDelete}
|
||||
disabled={deleting}
|
||||
>
|
||||
<Trash2 className="h-3.5 w-3.5" />
|
||||
{deleting ? 'Deleting...' : 'Delete'}
|
||||
</Button>
|
||||
<>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
className="h-8 text-destructive hover:text-destructive gap-1.5"
|
||||
onClick={handleDelete}
|
||||
disabled={deleting}
|
||||
>
|
||||
<Trash2 className="h-3.5 w-3.5" />
|
||||
{deleting ? 'Deleting...' : 'Delete'}
|
||||
</Button>
|
||||
<Button
|
||||
size="sm"
|
||||
className="h-8 gap-1.5"
|
||||
onClick={handleSaveExisting}
|
||||
disabled={saving || workingChain.length === 0}
|
||||
>
|
||||
<Save className="h-3.5 w-3.5" />
|
||||
{saving ? 'Saving...' : 'Save'}
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
{isCreatingNew && (
|
||||
<Button
|
||||
|
||||
@@ -393,8 +393,9 @@ export function FloatingGenerateBox({
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
onClick={() => {
|
||||
setIsEffectsMode(!isEffectsMode);
|
||||
if (isEffectsMode) setIsInstructMode(false);
|
||||
const next = !isEffectsMode;
|
||||
setIsEffectsMode(next);
|
||||
if (next) setIsInstructMode(false);
|
||||
}}
|
||||
className={cn(
|
||||
'h-10 w-10 rounded-full transition-all duration-200',
|
||||
|
||||
@@ -456,6 +456,7 @@ export function ProfileForm() {
|
||||
fxError instanceof Error ? fxError.message : 'Failed to save effects chain',
|
||||
variant: 'destructive',
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -606,6 +606,16 @@ class ApiClient {
|
||||
});
|
||||
}
|
||||
|
||||
async updateEffectPreset(
|
||||
presetId: string,
|
||||
data: { name?: string; description?: string; effects_chain?: EffectConfig[] },
|
||||
): Promise<EffectPresetResponse> {
|
||||
return this.request<EffectPresetResponse>(`/effects/presets/${presetId}`, {
|
||||
method: 'PUT',
|
||||
body: JSON.stringify(data),
|
||||
});
|
||||
}
|
||||
|
||||
async deleteEffectPreset(presetId: string): Promise<void> {
|
||||
await this.request<void>(`/effects/presets/${presetId}`, {
|
||||
method: 'DELETE',
|
||||
|
||||
@@ -296,6 +296,12 @@ export interface EffectPresetCreate {
|
||||
effects_chain: EffectConfig[];
|
||||
}
|
||||
|
||||
export interface EffectPresetUpdate {
|
||||
name?: string;
|
||||
description?: string;
|
||||
effects_chain?: EffectConfig[];
|
||||
}
|
||||
|
||||
export interface AvailableEffectParam {
|
||||
default: number;
|
||||
min: number;
|
||||
|
||||
@@ -22,5 +22,5 @@ export const useEffectsStore = create<EffectsStore>((set) => ({
|
||||
setWorkingChain: (chain) => set({ workingChain: chain }),
|
||||
|
||||
isCreatingNew: false,
|
||||
setIsCreatingNew: (v) => set({ isCreatingNew: v, selectedPresetId: v ? null : null }),
|
||||
setIsCreatingNew: (v) => set({ isCreatingNew: v, ...(v && { selectedPresetId: null }) }),
|
||||
}));
|
||||
|
||||
Reference in New Issue
Block a user