refactor: better state control for browser download

This commit is contained in:
zhom
2025-06-22 06:23:27 +04:00
parent 3d3a3b3816
commit f51aa9ed85
6 changed files with 88 additions and 20 deletions
+40
View File
@@ -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
View File
@@ -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
View File
@@ -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",
);
+1 -1
View File
@@ -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,
+22 -3
View File
@@ -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,
+9 -10
View File
@@ -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);