mirror of
https://github.com/zhom/donutbrowser.git
synced 2026-04-22 20:06:18 +02:00
refactor: better state control for browser download
This commit is contained in:
@@ -1,8 +1,10 @@
|
||||
use crate::proxy_manager::PROXY_MANAGER;
|
||||
use directories::BaseDirs;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashSet;
|
||||
use std::fs::{self, create_dir_all};
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
use sysinfo::{Pid, System};
|
||||
use tauri::Emitter;
|
||||
@@ -35,6 +37,11 @@ fn default_release_type() -> String {
|
||||
"stable".to_string()
|
||||
}
|
||||
|
||||
// Global state to track currently downloading browsers
|
||||
lazy_static::lazy_static! {
|
||||
static ref DOWNLOADING_BROWSERS: Arc<Mutex<HashSet<String>>> = Arc::new(Mutex::new(HashSet::new()));
|
||||
}
|
||||
|
||||
// Platform-specific modules
|
||||
#[cfg(target_os = "macos")]
|
||||
mod macos {
|
||||
@@ -2345,6 +2352,18 @@ impl BrowserRunner {
|
||||
browser_str: String,
|
||||
version: String,
|
||||
) -> Result<String, Box<dyn std::error::Error + Send + Sync>> {
|
||||
// Check if this browser type is already being downloaded
|
||||
{
|
||||
let mut downloading = DOWNLOADING_BROWSERS.lock().unwrap();
|
||||
if downloading.contains(&browser_str) {
|
||||
return Err(format!(
|
||||
"Browser '{browser_str}' is already being downloaded. Please wait for the current download to complete."
|
||||
).into());
|
||||
}
|
||||
// Mark this browser as being downloaded
|
||||
downloading.insert(browser_str.clone());
|
||||
}
|
||||
|
||||
let browser_type =
|
||||
BrowserType::from_str(&browser_str).map_err(|e| format!("Invalid browser type: {e}"))?;
|
||||
let browser = create_browser(browser_type.clone());
|
||||
@@ -2428,6 +2447,11 @@ impl BrowserRunner {
|
||||
// Clean up failed download
|
||||
let _ = registry.cleanup_failed_download(&browser_str, &version);
|
||||
let _ = registry.save();
|
||||
// Remove browser from downloading set on error
|
||||
{
|
||||
let mut downloading = DOWNLOADING_BROWSERS.lock().unwrap();
|
||||
downloading.remove(&browser_str);
|
||||
}
|
||||
return Err(format!("Failed to download browser: {e}").into());
|
||||
}
|
||||
};
|
||||
@@ -2455,6 +2479,11 @@ impl BrowserRunner {
|
||||
// Clean up failed download
|
||||
let _ = registry.cleanup_failed_download(&browser_str, &version);
|
||||
let _ = registry.save();
|
||||
// Remove browser from downloading set on error
|
||||
{
|
||||
let mut downloading = DOWNLOADING_BROWSERS.lock().unwrap();
|
||||
downloading.remove(&browser_str);
|
||||
}
|
||||
return Err(format!("Failed to extract browser: {e}").into());
|
||||
}
|
||||
}
|
||||
@@ -2484,6 +2513,11 @@ impl BrowserRunner {
|
||||
if !browser.is_version_downloaded(&version, &binaries_dir) {
|
||||
let _ = registry.cleanup_failed_download(&browser_str, &version);
|
||||
let _ = registry.save();
|
||||
// Remove browser from downloading set on verification failure
|
||||
{
|
||||
let mut downloading = DOWNLOADING_BROWSERS.lock().unwrap();
|
||||
downloading.remove(&browser_str);
|
||||
}
|
||||
return Err("Browser download completed but verification failed".into());
|
||||
}
|
||||
|
||||
@@ -2514,6 +2548,12 @@ impl BrowserRunner {
|
||||
};
|
||||
let _ = app_handle.emit("download-progress", &progress);
|
||||
|
||||
// Remove browser from downloading set
|
||||
{
|
||||
let mut downloading = DOWNLOADING_BROWSERS.lock().unwrap();
|
||||
downloading.remove(&browser_str);
|
||||
}
|
||||
|
||||
Ok(version)
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -27,9 +27,9 @@ export default function RootLayout({
|
||||
className={`${geistSans.variable} ${geistMono.variable} antialiased`}
|
||||
>
|
||||
<CustomThemeProvider>
|
||||
<WindowDragArea />
|
||||
<TooltipProvider>{children}</TooltipProvider>
|
||||
<Toaster />
|
||||
<WindowDragArea />
|
||||
</CustomThemeProvider>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+15
-5
@@ -79,17 +79,27 @@ export default function Home() {
|
||||
if (missingBinaries.length > 0) {
|
||||
console.log("Found missing binaries:", missingBinaries);
|
||||
|
||||
// Group missing binaries by browser type to avoid concurrent downloads
|
||||
const browserMap = new Map<string, string[]>();
|
||||
for (const [profileName, browser, version] of missingBinaries) {
|
||||
if (!browserMap.has(browser)) {
|
||||
browserMap.set(browser, []);
|
||||
}
|
||||
const versions = browserMap.get(browser);
|
||||
if (versions) {
|
||||
versions.push(`${version} (for ${profileName})`);
|
||||
}
|
||||
}
|
||||
|
||||
// Show a toast notification about missing binaries and auto-download them
|
||||
const missingList = missingBinaries
|
||||
.map(
|
||||
([profileName, browser, version]) =>
|
||||
`${browser} ${version} (for ${profileName})`,
|
||||
)
|
||||
const missingList = Array.from(browserMap.entries())
|
||||
.map(([browser, versions]) => `${browser}: ${versions.join(", ")}`)
|
||||
.join(", ");
|
||||
|
||||
console.log(`Downloading missing binaries: ${missingList}`);
|
||||
|
||||
try {
|
||||
// Download missing binaries sequentially by browser type to prevent conflicts
|
||||
const downloaded = await invoke<string[]>(
|
||||
"ensure_all_binaries_exist",
|
||||
);
|
||||
|
||||
@@ -41,7 +41,7 @@ export function WindowDragArea() {
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
className="fixed top-0 right-0 left-0 h-10 bg-transparent border-0 pointer-events-none z-9999"
|
||||
className="fixed top-0 right-0 left-0 h-10 bg-transparent border-0 z-9999"
|
||||
style={{
|
||||
// Ensure it's above all other content
|
||||
zIndex: 9999,
|
||||
|
||||
@@ -48,7 +48,9 @@ export function useBrowserDownload() {
|
||||
[],
|
||||
);
|
||||
const [downloadedVersions, setDownloadedVersions] = useState<string[]>([]);
|
||||
const [isDownloading, setIsDownloading] = useState(false);
|
||||
const [downloadingBrowsers, setDownloadingBrowsers] = useState<Set<string>>(
|
||||
new Set(),
|
||||
);
|
||||
const [downloadProgress, setDownloadProgress] =
|
||||
useState<DownloadProgress | null>(null);
|
||||
|
||||
@@ -181,7 +183,7 @@ export function useBrowserDownload() {
|
||||
suppressNotifications = false,
|
||||
) => {
|
||||
const browserName = getBrowserDisplayName(browserStr);
|
||||
setIsDownloading(true);
|
||||
setDownloadingBrowsers((prev) => new Set(prev).add(browserStr));
|
||||
|
||||
try {
|
||||
// Check browser compatibility before attempting download
|
||||
@@ -215,7 +217,11 @@ export function useBrowserDownload() {
|
||||
}
|
||||
throw error;
|
||||
} finally {
|
||||
setIsDownloading(false);
|
||||
setDownloadingBrowsers((prev) => {
|
||||
const next = new Set(prev);
|
||||
next.delete(browserStr);
|
||||
return next;
|
||||
});
|
||||
}
|
||||
},
|
||||
[loadDownloadedVersions],
|
||||
@@ -228,6 +234,17 @@ export function useBrowserDownload() {
|
||||
[downloadedVersions],
|
||||
);
|
||||
|
||||
// Check if a browser type is currently downloading
|
||||
const isBrowserDownloading = useCallback(
|
||||
(browserStr: string) => {
|
||||
return downloadingBrowsers.has(browserStr);
|
||||
},
|
||||
[downloadingBrowsers],
|
||||
);
|
||||
|
||||
// Legacy isDownloading for backwards compatibility
|
||||
const isDownloading = downloadingBrowsers.size > 0;
|
||||
|
||||
// Listen for download progress events
|
||||
useEffect(() => {
|
||||
const unlisten = listen<DownloadProgress>("download-progress", (event) => {
|
||||
@@ -272,6 +289,8 @@ export function useBrowserDownload() {
|
||||
availableVersions,
|
||||
downloadedVersions,
|
||||
isDownloading,
|
||||
isBrowserDownloading,
|
||||
downloadingBrowsers,
|
||||
downloadProgress,
|
||||
loadVersions,
|
||||
loadVersionsWithNewCount,
|
||||
|
||||
@@ -32,22 +32,21 @@ export function useUpdateNotifications(
|
||||
|
||||
// Add refs to track ongoing operations to prevent duplicates
|
||||
const isCheckingForUpdates = useRef(false);
|
||||
const activeDownloads = useRef<Set<string>>(new Set()); // Track "browser-version" keys
|
||||
// Track browser types being downloaded (not browser-version pairs)
|
||||
const activeDownloads = useRef<Set<string>>(new Set()); // Track browser types
|
||||
|
||||
const handleAutoUpdate = useCallback(
|
||||
async (browser: string, newVersion: string, notificationId: string) => {
|
||||
const downloadKey = `${browser}-${newVersion}`;
|
||||
|
||||
// Check if this download is already in progress
|
||||
if (activeDownloads.current.has(downloadKey)) {
|
||||
// Check if this browser type is already being downloaded
|
||||
if (activeDownloads.current.has(browser)) {
|
||||
console.log(
|
||||
`Download already in progress for ${downloadKey}, skipping duplicate`,
|
||||
`Download already in progress for browser type ${browser}, skipping duplicate auto-update`,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// Mark download as active and disable browser
|
||||
activeDownloads.current.add(downloadKey);
|
||||
// Mark browser type as active and disable browser
|
||||
activeDownloads.current.add(browser);
|
||||
setUpdatingBrowsers((prev) => new Set(prev).add(browser));
|
||||
|
||||
try {
|
||||
@@ -158,8 +157,8 @@ export function useUpdateNotifications(
|
||||
console.error("Failed to start auto-update:", error);
|
||||
throw error;
|
||||
} finally {
|
||||
// Clean up
|
||||
activeDownloads.current.delete(downloadKey);
|
||||
// Clean up - remove browser type from active downloads
|
||||
activeDownloads.current.delete(browser);
|
||||
setUpdatingBrowsers((prev) => {
|
||||
const next = new Set(prev);
|
||||
next.delete(browser);
|
||||
|
||||
Reference in New Issue
Block a user