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