+112
-75
src/routes/Home.tsx
+112
-75
src/routes/Home.tsx
···
39
39
profile: ProfileViewDetailed;
40
40
onLogout: () => void;
41
41
}) {
42
-
const [isDirLoading, setDirLoading] = useState(false);
43
42
const [refreshTrigger, setRefreshTrigger] = useState(0);
44
43
const [showSettings, setShowSettings] = useState(false);
45
44
···
96
95
97
96
<div className="bg-card rounded-lg p-4 mb-4">
98
97
<p className="mb-2 text-white">Backups</p>
99
-
<div className="flex gap-2">
100
-
<Button
101
-
variant="outline"
102
-
className="cursor-pointer"
103
-
onClick={async () => {
104
-
try {
105
-
setDirLoading(true);
106
-
await createBackupDir();
107
-
const appDataDirPath = await getBackupDir();
108
-
openPath(appDataDirPath);
109
-
} finally {
110
-
setDirLoading(false);
111
-
}
112
-
}}
113
-
disabled={isDirLoading}
114
-
>
115
-
{isDirLoading ? (
116
-
<LoaderCircleIcon className="w-4 h-4 animate-spin mr-2" />
117
-
) : (
118
-
<FolderOpen className="w-4 h-4 mr-2" />
119
-
)}
120
-
Open backups
121
-
</Button>
122
-
123
-
<StartBackup onBackupComplete={handleBackupComplete} />
124
-
125
-
{/* <Button
126
-
variant="outline"
127
-
className="cursor-pointer"
128
-
onClick={async () => {
129
-
await BackgroundTestService.testBackgroundBackup();
130
-
}}
131
-
>
132
-
Test Background
133
-
</Button> */}
134
-
</div>
98
+
<StartBackup onBackupComplete={handleBackupComplete} />
135
99
</div>
136
100
137
101
<Backups refreshTrigger={refreshTrigger} />
···
142
106
function StartBackup({ onBackupComplete }: { onBackupComplete: () => void }) {
143
107
const [isLoading, setIsLoading] = useState(false);
144
108
const [stage, setStage] = useState<BackupStage | null>(null);
145
-
const [_, setProgress] = useState<number | undefined>();
109
+
const [progress, setProgress] = useState<number | undefined>();
110
+
const [isDirLoading, setDirLoading] = useState(false);
146
111
const { agent } = useAuth();
147
112
113
+
const formatStage = (stage: BackupStage | null): string => {
114
+
if (!stage) return "Initializing backup...";
115
+
116
+
switch (stage) {
117
+
case "blobs":
118
+
return "Downloading blobs...";
119
+
case "cleanup":
120
+
return "Cleaning up...";
121
+
case "complete":
122
+
return "Backup complete!";
123
+
case "fetching":
124
+
return "Downloading account archive...";
125
+
case "writing":
126
+
return "Downloading account archive...";
127
+
default:
128
+
return "Processing...";
129
+
}
130
+
};
131
+
148
132
return (
149
-
<Button
150
-
variant="outline"
151
-
className="cursor-pointer"
152
-
onClick={async () => {
153
-
try {
154
-
setIsLoading(true);
155
-
if (agent == null) {
156
-
toast("Agent not initialized, try to reload the app.");
157
-
return;
158
-
}
159
-
const manager = new BackupAgent(agent!!, {
160
-
onProgress: (progress) => {
161
-
setStage(progress.stage);
162
-
setProgress(progress.progress);
163
-
},
164
-
});
165
-
await manager.startBackup();
166
-
await settingsManager.setLastBackupDate(new Date().toISOString());
167
-
toast("Backup complete!");
168
-
onBackupComplete(); // Trigger refresh
169
-
} catch (err: any) {
170
-
toast(err.toString());
171
-
console.error(err);
172
-
} finally {
173
-
setIsLoading(false);
174
-
}
175
-
}}
176
-
disabled={isLoading}
177
-
>
178
-
{isLoading ? (
179
-
<>
180
-
<LoaderCircleIcon className="animate-spin text-white/80" />
181
-
<span className="capitalize">{stage}</span>
182
-
</>
183
-
) : (
184
-
<span>Backup now</span>
133
+
<div className="space-y-4">
134
+
<div className="flex gap-2">
135
+
<Button
136
+
variant="outline"
137
+
className="cursor-pointer"
138
+
onClick={async () => {
139
+
try {
140
+
setDirLoading(true);
141
+
await createBackupDir();
142
+
const appDataDirPath = await getBackupDir();
143
+
openPath(appDataDirPath);
144
+
} finally {
145
+
setDirLoading(false);
146
+
}
147
+
}}
148
+
disabled={isDirLoading}
149
+
>
150
+
{isDirLoading ? (
151
+
<LoaderCircleIcon className="w-4 h-4 animate-spin mr-2" />
152
+
) : (
153
+
<FolderOpen className="w-4 h-4 mr-2" />
154
+
)}
155
+
Open backups
156
+
</Button>
157
+
158
+
<Button
159
+
variant="outline"
160
+
className="cursor-pointer"
161
+
onClick={async () => {
162
+
try {
163
+
setIsLoading(true);
164
+
if (agent == null) {
165
+
toast("Agent not initialized, try to reload the app.");
166
+
return;
167
+
}
168
+
169
+
const manager = new BackupAgent(agent!!, {
170
+
onProgress: (progress) => {
171
+
setStage(progress.stage);
172
+
setProgress(progress.progress);
173
+
},
174
+
});
175
+
await manager.startBackup();
176
+
await settingsManager.setLastBackupDate(new Date().toISOString());
177
+
toast("Backup complete!");
178
+
onBackupComplete();
179
+
} catch (err: any) {
180
+
toast(err.toString());
181
+
console.error(err);
182
+
} finally {
183
+
setIsLoading(false);
184
+
setStage(null);
185
+
setProgress(undefined);
186
+
}
187
+
}}
188
+
disabled={isLoading}
189
+
>
190
+
{isLoading ? (
191
+
<>
192
+
<LoaderCircleIcon className="w-4 h-4 animate-spin mr-2" />
193
+
Backing up...
194
+
</>
195
+
) : (
196
+
"Backup now"
197
+
)}
198
+
</Button>
199
+
</div>
200
+
201
+
{/* Clean backup progress card with animations */}
202
+
{isLoading && (
203
+
<div className="bg-card border rounded-lg p-4 animate-in slide-in-from-top-2 duration-300">
204
+
<div className="flex items-center justify-between mb-3">
205
+
<div className="flex items-center gap-3">
206
+
<div className="w-8 h-8 rounded-md bg-primary/10 flex items-center justify-center">
207
+
<HardDrive className="w-4 h-4 text-primary animate-pulse" />
208
+
</div>
209
+
<div>
210
+
<h3 className="font-medium">{formatStage(stage)}</h3>
211
+
</div>
212
+
</div>
213
+
</div>
214
+
215
+
<div className="space-y-1.5">
216
+
<Progress
217
+
value={progress}
218
+
className="h-2 transition-all duration-500 ease-out"
219
+
/>
220
+
</div>
221
+
</div>
185
222
)}
186
-
</Button>
223
+
</div>
187
224
);
188
225
}
189
226