mirror of
https://github.com/jamiepine/voicebox.git
synced 2026-09-15 12:50:42 -07:00
Enhance update status display and audio sample components
- Improved the UpdateStatus component to show download progress and total bytes downloaded during updates. - Refactored AudioSampleRecording and AudioSampleSystem components for cleaner button rendering and consistent layout. - Updated import order in SampleUpload component for better organization.
This commit is contained in:
@@ -71,11 +71,23 @@ export function UpdateStatus() {
|
||||
|
||||
{status.downloading && (
|
||||
<div className="space-y-2">
|
||||
<div className="flex items-center gap-2 text-sm">
|
||||
<Download className="h-4 w-4" />
|
||||
Downloading update...
|
||||
<div className="flex items-center justify-between text-sm">
|
||||
<div className="flex items-center gap-2">
|
||||
<Download className="h-4 w-4" />
|
||||
Downloading update...
|
||||
</div>
|
||||
{status.downloadProgress !== undefined && (
|
||||
<span className="text-muted-foreground">
|
||||
{status.downloadProgress}%
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<Progress />
|
||||
<Progress value={status.downloadProgress} />
|
||||
{status.downloadedBytes !== undefined && status.totalBytes !== undefined && status.totalBytes > 0 && (
|
||||
<div className="text-xs text-muted-foreground">
|
||||
{(status.downloadedBytes / 1024 / 1024).toFixed(1)} MB / {(status.totalBytes / 1024 / 1024).toFixed(1)} MB
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Mic, Square, Play, Pause } from 'lucide-react';
|
||||
import { Mic, Pause, Play, Square } from 'lucide-react';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { FormControl, FormItem, FormLabel, FormMessage } from '@/components/ui/form';
|
||||
import { formatAudioDuration } from '@/lib/utils/audio';
|
||||
@@ -35,12 +35,7 @@ export function AudioSampleRecording({
|
||||
<div className="space-y-4">
|
||||
{!isRecording && !file && (
|
||||
<div className="flex flex-col items-center justify-center gap-4 p-4 border-2 border-dashed rounded-lg min-h-[180px]">
|
||||
<Button
|
||||
type="button"
|
||||
onClick={onStart}
|
||||
size="lg"
|
||||
className="flex items-center gap-2"
|
||||
>
|
||||
<Button type="button" onClick={onStart} size="lg" className="flex items-center gap-2">
|
||||
<Mic className="h-5 w-5" />
|
||||
Start Recording
|
||||
</Button>
|
||||
@@ -81,16 +76,9 @@ export function AudioSampleRecording({
|
||||
<Mic className="h-5 w-5 text-primary" />
|
||||
<span className="font-medium">Recording complete</span>
|
||||
</div>
|
||||
<p className="text-sm text-muted-foreground text-center">
|
||||
File: {file.name}
|
||||
</p>
|
||||
<p className="text-sm text-muted-foreground text-center">File: {file.name}</p>
|
||||
<div className="flex gap-2">
|
||||
<Button
|
||||
type="button"
|
||||
size="icon"
|
||||
variant="outline"
|
||||
onClick={onPlayPause}
|
||||
>
|
||||
<Button type="button" size="icon" variant="outline" onClick={onPlayPause}>
|
||||
{isPlaying ? <Pause className="h-4 w-4" /> : <Play className="h-4 w-4" />}
|
||||
</Button>
|
||||
<Button
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Monitor, Square, Play, Pause, Mic } from 'lucide-react';
|
||||
import { Mic, Monitor, Pause, Play, Square } from 'lucide-react';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { FormControl, FormItem, FormLabel, FormMessage } from '@/components/ui/form';
|
||||
import { formatAudioDuration } from '@/lib/utils/audio';
|
||||
@@ -35,12 +35,7 @@ export function AudioSampleSystem({
|
||||
<div className="space-y-4">
|
||||
{!isRecording && !file && (
|
||||
<div className="flex flex-col items-center justify-center gap-4 p-4 border-2 border-dashed rounded-lg min-h-[180px]">
|
||||
<Button
|
||||
type="button"
|
||||
onClick={onStart}
|
||||
size="lg"
|
||||
className="flex items-center gap-2"
|
||||
>
|
||||
<Button type="button" onClick={onStart} size="lg" className="flex items-center gap-2">
|
||||
<Monitor className="h-5 w-5" />
|
||||
Start Capture
|
||||
</Button>
|
||||
@@ -81,16 +76,9 @@ export function AudioSampleSystem({
|
||||
<Monitor className="h-5 w-5 text-primary" />
|
||||
<span className="font-medium">Capture complete</span>
|
||||
</div>
|
||||
<p className="text-sm text-muted-foreground text-center">
|
||||
File: {file.name}
|
||||
</p>
|
||||
<p className="text-sm text-muted-foreground text-center">File: {file.name}</p>
|
||||
<div className="flex gap-2">
|
||||
<Button
|
||||
type="button"
|
||||
size="icon"
|
||||
variant="outline"
|
||||
onClick={onPlayPause}
|
||||
>
|
||||
<Button type="button" size="icon" variant="outline" onClick={onPlayPause}>
|
||||
{isPlaying ? <Pause className="h-4 w-4" /> : <Play className="h-4 w-4" />}
|
||||
</Button>
|
||||
<Button
|
||||
|
||||
@@ -22,15 +22,15 @@ import {
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
|
||||
import { Textarea } from '@/components/ui/textarea';
|
||||
import { useToast } from '@/components/ui/use-toast';
|
||||
import { useAudioRecording } from '@/lib/hooks/useAudioRecording';
|
||||
import { useAudioPlayer } from '@/lib/hooks/useAudioPlayer';
|
||||
import { useAudioRecording } from '@/lib/hooks/useAudioRecording';
|
||||
import { useAddSample, useProfile } from '@/lib/hooks/useProfiles';
|
||||
import { useSystemAudioCapture } from '@/lib/hooks/useSystemAudioCapture';
|
||||
import { useTranscription } from '@/lib/hooks/useTranscription';
|
||||
import { isTauri } from '@/lib/tauri';
|
||||
import { AudioSampleUpload } from './AudioSampleUpload';
|
||||
import { AudioSampleRecording } from './AudioSampleRecording';
|
||||
import { AudioSampleSystem } from './AudioSampleSystem';
|
||||
import { AudioSampleUpload } from './AudioSampleUpload';
|
||||
|
||||
const sampleSchema = z.object({
|
||||
file: z.instanceof(File, { message: 'Please select an audio file' }),
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
import { check, type Update } from '@tauri-apps/plugin-updater';
|
||||
import { relaunch } from '@tauri-apps/plugin-process';
|
||||
|
||||
@@ -9,6 +9,9 @@ export interface UpdateStatus {
|
||||
downloading: boolean;
|
||||
installing: boolean;
|
||||
error?: string;
|
||||
downloadProgress?: number; // 0-100 percentage
|
||||
downloadedBytes?: number;
|
||||
totalBytes?: number;
|
||||
}
|
||||
|
||||
const isTauri = () => {
|
||||
@@ -25,7 +28,7 @@ export function useAutoUpdater(checkOnMount = false) {
|
||||
|
||||
const [update, setUpdate] = useState<Update | null>(null);
|
||||
|
||||
const checkForUpdates = async () => {
|
||||
const checkForUpdates = useCallback(async () => {
|
||||
if (!isTauri()) {
|
||||
return;
|
||||
}
|
||||
@@ -61,7 +64,7 @@ export function useAutoUpdater(checkOnMount = false) {
|
||||
error: error instanceof Error ? error.message : 'Failed to check for updates',
|
||||
});
|
||||
}
|
||||
};
|
||||
}, []);
|
||||
|
||||
const downloadAndInstall = async () => {
|
||||
if (!update || !isTauri()) return;
|
||||
@@ -69,19 +72,40 @@ export function useAutoUpdater(checkOnMount = false) {
|
||||
try {
|
||||
setStatus((prev) => ({ ...prev, downloading: true, error: undefined }));
|
||||
|
||||
let downloadedBytes = 0;
|
||||
let totalBytes = 0;
|
||||
|
||||
await update.downloadAndInstall((event) => {
|
||||
switch (event.event) {
|
||||
case 'Started':
|
||||
setStatus((prev) => ({ ...prev, downloading: true }));
|
||||
totalBytes = event.data.contentLength || 0;
|
||||
downloadedBytes = 0;
|
||||
setStatus((prev) => ({
|
||||
...prev,
|
||||
downloading: true,
|
||||
totalBytes,
|
||||
downloadedBytes: 0,
|
||||
downloadProgress: 0
|
||||
}));
|
||||
break;
|
||||
case 'Progress':
|
||||
console.log(`Downloaded ${event.data.chunkLength} bytes`);
|
||||
case 'Progress': {
|
||||
downloadedBytes += event.data.chunkLength;
|
||||
const progress = totalBytes > 0
|
||||
? Math.round((downloadedBytes / totalBytes) * 100)
|
||||
: undefined;
|
||||
setStatus((prev) => ({
|
||||
...prev,
|
||||
downloadedBytes,
|
||||
downloadProgress: progress
|
||||
}));
|
||||
break;
|
||||
}
|
||||
case 'Finished':
|
||||
setStatus((prev) => ({
|
||||
...prev,
|
||||
downloading: false,
|
||||
installing: true,
|
||||
downloadProgress: 100
|
||||
}));
|
||||
break;
|
||||
}
|
||||
@@ -93,6 +117,9 @@ export function useAutoUpdater(checkOnMount = false) {
|
||||
...prev,
|
||||
downloading: false,
|
||||
installing: false,
|
||||
downloadProgress: undefined,
|
||||
downloadedBytes: undefined,
|
||||
totalBytes: undefined,
|
||||
error: error instanceof Error ? error.message : 'Failed to install update',
|
||||
}));
|
||||
}
|
||||
@@ -102,7 +129,7 @@ export function useAutoUpdater(checkOnMount = false) {
|
||||
if (checkOnMount && isTauri()) {
|
||||
checkForUpdates();
|
||||
}
|
||||
}, [checkOnMount]);
|
||||
}, [checkOnMount, checkForUpdates]);
|
||||
|
||||
return {
|
||||
status,
|
||||
|
||||
Reference in New Issue
Block a user