mirror of
https://github.com/zhom/donutbrowser.git
synced 2026-07-25 05:40:50 +02:00
refactor: reduce table re-renders
This commit is contained in:
@@ -470,14 +470,6 @@ pub async fn check_for_browser_updates() -> Result<Vec<UpdateNotification>, Stri
|
||||
Ok(grouped)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn is_browser_disabled_for_update(browser: String) -> Result<bool, String> {
|
||||
let updater = AutoUpdater::instance();
|
||||
updater
|
||||
.is_browser_disabled(&browser)
|
||||
.map_err(|e| format!("Failed to check browser status: {e}"))
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn dismiss_update_notification(notification_id: String) -> Result<(), String> {
|
||||
let updater = AutoUpdater::instance();
|
||||
|
||||
+178
-20
@@ -2,6 +2,7 @@ use crate::platform_browser;
|
||||
use crate::profile::{BrowserProfile, ProfileManager};
|
||||
use crate::proxy_manager::PROXY_MANAGER;
|
||||
use directories::BaseDirs;
|
||||
use serde::Serialize;
|
||||
use std::collections::HashSet;
|
||||
use std::fs::create_dir_all;
|
||||
use std::path::{Path, PathBuf};
|
||||
@@ -151,11 +152,6 @@ impl BrowserRunner {
|
||||
pub fn list_profiles(&self) -> Result<Vec<BrowserProfile>, Box<dyn std::error::Error>> {
|
||||
let profile_manager = ProfileManager::instance();
|
||||
let profiles = profile_manager.list_profiles();
|
||||
if let Ok(ref ps) = profiles {
|
||||
let _ = crate::tag_manager::TAG_MANAGER.lock().map(|tm| {
|
||||
let _ = tm.rebuild_from_profiles(ps);
|
||||
});
|
||||
}
|
||||
profiles
|
||||
}
|
||||
|
||||
@@ -271,11 +267,35 @@ impl BrowserRunner {
|
||||
updated_profile.name
|
||||
);
|
||||
|
||||
println!(
|
||||
"Emitting profile events for successful Camoufox launch: {}",
|
||||
updated_profile.name
|
||||
);
|
||||
|
||||
// Emit profile update event to frontend
|
||||
if let Err(e) = app_handle.emit("profile-updated", &updated_profile) {
|
||||
println!("Warning: Failed to emit profile update event: {e}");
|
||||
}
|
||||
|
||||
// Emit minimal running changed event to frontend with a small delay
|
||||
#[derive(Serialize)]
|
||||
struct RunningChangedPayload {
|
||||
id: String,
|
||||
is_running: bool,
|
||||
}
|
||||
|
||||
let payload = RunningChangedPayload {
|
||||
id: updated_profile.id.to_string(),
|
||||
is_running: updated_profile.process_id.is_some(),
|
||||
};
|
||||
|
||||
if let Err(e) = app_handle.emit("profile-running-changed", &payload) {
|
||||
println!("Warning: Failed to emit profile running changed event: {e}");
|
||||
} else {
|
||||
println!("Emitted profile update event for: {}", updated_profile.name);
|
||||
println!(
|
||||
"Successfully emitted profile-running-changed event for Camoufox {}: running={}",
|
||||
updated_profile.name, payload.is_running
|
||||
);
|
||||
}
|
||||
|
||||
return Ok(updated_profile);
|
||||
@@ -484,11 +504,36 @@ impl BrowserRunner {
|
||||
// which is already handled in the profile creation process
|
||||
}
|
||||
|
||||
println!(
|
||||
"Emitting profile events for successful launch: {}",
|
||||
updated_profile.name
|
||||
);
|
||||
|
||||
// Emit profile update event to frontend
|
||||
if let Err(e) = app_handle.emit("profile-updated", &updated_profile) {
|
||||
println!("Warning: Failed to emit profile update event: {e}");
|
||||
}
|
||||
|
||||
// Emit minimal running changed event to frontend with a small delay to ensure UI consistency
|
||||
#[derive(Serialize)]
|
||||
struct RunningChangedPayload {
|
||||
id: String,
|
||||
is_running: bool,
|
||||
}
|
||||
let payload = RunningChangedPayload {
|
||||
id: updated_profile.id.to_string(),
|
||||
is_running: updated_profile.process_id.is_some(),
|
||||
};
|
||||
|
||||
if let Err(e) = app_handle.emit("profile-running-changed", &payload) {
|
||||
println!("Warning: Failed to emit profile running changed event: {e}");
|
||||
} else {
|
||||
println!(
|
||||
"Successfully emitted profile-running-changed event for {}: running={}",
|
||||
updated_profile.name, payload.is_running
|
||||
);
|
||||
}
|
||||
|
||||
Ok(updated_profile)
|
||||
}
|
||||
|
||||
@@ -705,20 +750,27 @@ impl BrowserRunner {
|
||||
url: Option<String>,
|
||||
internal_proxy_settings: Option<&ProxySettings>,
|
||||
) -> Result<BrowserProfile, Box<dyn std::error::Error + Send + Sync>> {
|
||||
println!("launch_or_open_url called for profile: {}", profile.name);
|
||||
|
||||
// Get the most up-to-date profile data
|
||||
let profiles = self.list_profiles().expect("Failed to list profiles");
|
||||
let profiles = self.list_profiles()
|
||||
.map_err(|e| format!("Failed to list profiles in launch_or_open_url: {}", e))?;
|
||||
let updated_profile = profiles
|
||||
.into_iter()
|
||||
.find(|p| p.name == profile.name)
|
||||
.unwrap_or_else(|| profile.clone());
|
||||
|
||||
println!("Checking browser status for profile: {}", updated_profile.name);
|
||||
|
||||
// Check if browser is already running
|
||||
let is_running = self
|
||||
.check_browser_status(app_handle.clone(), &updated_profile)
|
||||
.await?;
|
||||
.await
|
||||
.map_err(|e| format!("Failed to check browser status: {}", e))?;
|
||||
|
||||
// Get the updated profile again after status check (PID might have been updated)
|
||||
let profiles = self.list_profiles().expect("Failed to list profiles");
|
||||
let profiles = self.list_profiles()
|
||||
.map_err(|e| format!("Failed to list profiles after status check: {}", e))?;
|
||||
let final_profile = profiles
|
||||
.into_iter()
|
||||
.find(|p| p.name == profile.name)
|
||||
@@ -927,11 +979,36 @@ impl BrowserRunner {
|
||||
.save_process_info(&updated_profile)
|
||||
.map_err(|e| format!("Failed to update profile: {e}"))?;
|
||||
|
||||
println!(
|
||||
"Emitting profile events for successful Camoufox kill: {}",
|
||||
updated_profile.name
|
||||
);
|
||||
|
||||
// Emit profile update event to frontend
|
||||
if let Err(e) = app_handle.emit("profile-updated", &updated_profile) {
|
||||
println!("Warning: Failed to emit profile update event: {e}");
|
||||
}
|
||||
|
||||
// Emit minimal running changed event to frontend immediately
|
||||
#[derive(Serialize)]
|
||||
struct RunningChangedPayload {
|
||||
id: String,
|
||||
is_running: bool,
|
||||
}
|
||||
let payload = RunningChangedPayload {
|
||||
id: updated_profile.id.to_string(),
|
||||
is_running: false, // Explicitly set to false since we just killed it
|
||||
};
|
||||
|
||||
if let Err(e) = app_handle.emit("profile-running-changed", &payload) {
|
||||
println!("Warning: Failed to emit profile running changed event: {e}");
|
||||
} else {
|
||||
println!(
|
||||
"Successfully emitted profile-running-changed event for Camoufox {}: running={}",
|
||||
updated_profile.name, payload.is_running
|
||||
);
|
||||
}
|
||||
|
||||
println!(
|
||||
"Camoufox process cleanup completed for profile: {}",
|
||||
profile.name
|
||||
@@ -1036,6 +1113,36 @@ impl BrowserRunner {
|
||||
.save_process_info(&updated_profile)
|
||||
.map_err(|e| format!("Failed to update profile: {e}"))?;
|
||||
|
||||
println!(
|
||||
"Emitting profile events for successful kill: {}",
|
||||
updated_profile.name
|
||||
);
|
||||
|
||||
// Emit profile update event to frontend
|
||||
if let Err(e) = app_handle.emit("profile-updated", &updated_profile) {
|
||||
println!("Warning: Failed to emit profile update event: {e}");
|
||||
}
|
||||
|
||||
// Emit minimal running changed event to frontend immediately
|
||||
#[derive(Serialize)]
|
||||
struct RunningChangedPayload {
|
||||
id: String,
|
||||
is_running: bool,
|
||||
}
|
||||
let payload = RunningChangedPayload {
|
||||
id: updated_profile.id.to_string(),
|
||||
is_running: false, // Explicitly set to false since we just killed it
|
||||
};
|
||||
|
||||
if let Err(e) = app_handle.emit("profile-running-changed", &payload) {
|
||||
println!("Warning: Failed to emit profile running changed event: {e}");
|
||||
} else {
|
||||
println!(
|
||||
"Successfully emitted profile-running-changed event for {}: running={}",
|
||||
updated_profile.name, payload.is_running
|
||||
);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1515,18 +1622,28 @@ pub async fn launch_browser_profile(
|
||||
profile: BrowserProfile,
|
||||
url: Option<String>,
|
||||
) -> Result<BrowserProfile, String> {
|
||||
println!("Launch request received for profile: {}", profile.name);
|
||||
|
||||
let browser_runner = BrowserRunner::instance();
|
||||
|
||||
// Store the internal proxy settings for passing to launch_browser
|
||||
let mut internal_proxy_settings: Option<ProxySettings> = None;
|
||||
|
||||
// Resolve the most up-to-date profile from disk by name to avoid using stale proxy_id/browser state
|
||||
let profile_for_launch = browser_runner
|
||||
let profile_for_launch = match browser_runner
|
||||
.list_profiles()
|
||||
.map_err(|e| format!("Failed to list profiles: {e}"))?
|
||||
.into_iter()
|
||||
.find(|p| p.name == profile.name)
|
||||
.unwrap_or_else(|| profile.clone());
|
||||
.map_err(|e| format!("Failed to list profiles: {e}"))
|
||||
{
|
||||
Ok(profiles) => profiles
|
||||
.into_iter()
|
||||
.find(|p| p.name == profile.name)
|
||||
.unwrap_or_else(|| profile.clone()),
|
||||
Err(e) => {
|
||||
return Err(e);
|
||||
}
|
||||
};
|
||||
|
||||
println!("Resolved profile for launch: {}", profile_for_launch.name);
|
||||
|
||||
// Always start a local proxy before launching (non-Camoufox handled here; Camoufox has its own flow)
|
||||
if profile.browser != "camoufox" {
|
||||
@@ -1585,9 +1702,6 @@ pub async fn launch_browser_profile(
|
||||
.map(|p| format!("{}:{}", p.host, p.port))
|
||||
.unwrap_or_else(|| "DIRECT".to_string())
|
||||
);
|
||||
|
||||
// Give the proxy a moment to fully start up
|
||||
tokio::time::sleep(tokio::time::Duration::from_millis(300)).await;
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("Failed to start local proxy (will launch without it): {e}");
|
||||
@@ -1595,11 +1709,27 @@ pub async fn launch_browser_profile(
|
||||
}
|
||||
}
|
||||
|
||||
println!("Starting browser launch for profile: {}", profile_for_launch.name);
|
||||
|
||||
// Launch browser or open URL in existing instance
|
||||
let updated_profile = browser_runner
|
||||
.launch_or_open_url(app_handle.clone(), &profile_for_launch, url, internal_proxy_settings.as_ref())
|
||||
.await
|
||||
.map_err(|e| {
|
||||
println!("Browser launch failed for profile: {}, error: {}", profile_for_launch.name, e);
|
||||
|
||||
// Emit a failure event to clear loading states in the frontend
|
||||
#[derive(serde::Serialize)]
|
||||
struct RunningChangedPayload {
|
||||
id: String,
|
||||
is_running: bool,
|
||||
}
|
||||
let payload = RunningChangedPayload {
|
||||
id: profile_for_launch.id.to_string(),
|
||||
is_running: false,
|
||||
};
|
||||
let _ = app_handle.emit("profile-running-changed", &payload);
|
||||
|
||||
// Check if this is an architecture compatibility issue
|
||||
if let Some(io_error) = e.downcast_ref::<std::io::Error>() {
|
||||
if io_error.kind() == std::io::ErrorKind::Other
|
||||
@@ -1610,6 +1740,8 @@ pub async fn launch_browser_profile(
|
||||
format!("Failed to launch browser or open URL: {e}")
|
||||
})?;
|
||||
|
||||
println!("Browser launch completed for profile: {}", updated_profile.name);
|
||||
|
||||
// Now update the proxy with the correct PID if we have one
|
||||
if let Some(actual_pid) = updated_profile.process_id {
|
||||
// Update the proxy manager with the correct PID (we always started with temp pid 1 for non-Camoufox)
|
||||
@@ -1797,11 +1929,37 @@ pub async fn kill_browser_profile(
|
||||
app_handle: tauri::AppHandle,
|
||||
profile: BrowserProfile,
|
||||
) -> Result<(), String> {
|
||||
println!("Kill request received for profile: {}", profile.name);
|
||||
|
||||
let browser_runner = BrowserRunner::instance();
|
||||
browser_runner
|
||||
.kill_browser_process(app_handle, &profile)
|
||||
|
||||
match browser_runner
|
||||
.kill_browser_process(app_handle.clone(), &profile)
|
||||
.await
|
||||
.map_err(|e| format!("Failed to kill browser: {e}"))
|
||||
{
|
||||
Ok(()) => {
|
||||
println!("Successfully killed browser profile: {}", profile.name);
|
||||
Ok(())
|
||||
}
|
||||
Err(e) => {
|
||||
println!("Failed to kill browser profile {}: {}", profile.name, e);
|
||||
|
||||
// Emit a failure event to clear loading states in the frontend
|
||||
#[derive(serde::Serialize)]
|
||||
struct RunningChangedPayload {
|
||||
id: String,
|
||||
is_running: bool,
|
||||
}
|
||||
// On kill failure, we assume the process is still running
|
||||
let payload = RunningChangedPayload {
|
||||
id: profile.id.to_string(),
|
||||
is_running: true,
|
||||
};
|
||||
let _ = app_handle.emit("profile-running-changed", &payload);
|
||||
|
||||
Err(format!("Failed to kill browser: {e}"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
|
||||
+79
-3
@@ -53,7 +53,6 @@ use version_updater::{
|
||||
|
||||
use auto_updater::{
|
||||
check_for_browser_updates, complete_browser_update_with_auto_update, dismiss_update_notification,
|
||||
is_browser_disabled_for_update,
|
||||
};
|
||||
|
||||
use app_auto_updater::{
|
||||
@@ -489,6 +488,84 @@ pub fn run() {
|
||||
}
|
||||
});
|
||||
|
||||
// Periodically broadcast browser running status to the frontend
|
||||
let app_handle_status = app.handle().clone();
|
||||
tauri::async_runtime::spawn(async move {
|
||||
let mut interval = tokio::time::interval(tokio::time::Duration::from_millis(500));
|
||||
let mut last_running_states: std::collections::HashMap<String, bool> =
|
||||
std::collections::HashMap::new();
|
||||
|
||||
loop {
|
||||
interval.tick().await;
|
||||
|
||||
let runner = crate::browser_runner::BrowserRunner::instance();
|
||||
// If listing profiles fails, skip this tick
|
||||
let profiles = match runner.list_profiles() {
|
||||
Ok(p) => p,
|
||||
Err(e) => {
|
||||
println!("Warning: Failed to list profiles in status checker: {}", e);
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
for profile in profiles {
|
||||
// Check browser status and track changes
|
||||
match runner
|
||||
.check_browser_status(app_handle_status.clone(), &profile)
|
||||
.await
|
||||
{
|
||||
Ok(is_running) => {
|
||||
let profile_id = profile.id.to_string();
|
||||
let last_state = last_running_states
|
||||
.get(&profile_id)
|
||||
.copied()
|
||||
.unwrap_or(false);
|
||||
|
||||
// Only emit event if state actually changed
|
||||
if last_state != is_running {
|
||||
println!(
|
||||
"Status checker detected change for profile {}: {} -> {}",
|
||||
profile.name, last_state, is_running
|
||||
);
|
||||
|
||||
#[derive(serde::Serialize)]
|
||||
struct RunningChangedPayload {
|
||||
id: String,
|
||||
is_running: bool,
|
||||
}
|
||||
|
||||
let payload = RunningChangedPayload {
|
||||
id: profile_id.clone(),
|
||||
is_running,
|
||||
};
|
||||
|
||||
if let Err(e) = app_handle_status.emit("profile-running-changed", &payload) {
|
||||
println!("Warning: Failed to emit profile running changed event: {e}");
|
||||
} else {
|
||||
println!(
|
||||
"Status checker emitted profile-running-changed event for {}: running={}",
|
||||
profile.name, is_running
|
||||
);
|
||||
}
|
||||
|
||||
last_running_states.insert(profile_id, is_running);
|
||||
} else {
|
||||
// Update the state even if unchanged to ensure we have it tracked
|
||||
last_running_states.insert(profile_id, is_running);
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
println!(
|
||||
"Warning: Status check failed for profile {}: {}",
|
||||
profile.name, e
|
||||
);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Nodecar warm-up is now triggered from the frontend to allow UI blocking overlay
|
||||
|
||||
// Start API server if enabled in settings
|
||||
@@ -574,7 +651,6 @@ pub fn run() {
|
||||
trigger_manual_version_update,
|
||||
get_version_update_status,
|
||||
check_for_browser_updates,
|
||||
is_browser_disabled_for_update,
|
||||
dismiss_update_notification,
|
||||
complete_browser_update_with_auto_update,
|
||||
check_for_app_updates,
|
||||
@@ -603,7 +679,7 @@ pub fn run() {
|
||||
warm_up_nodecar,
|
||||
start_api_server,
|
||||
stop_api_server,
|
||||
get_api_server_status,
|
||||
get_api_server_status
|
||||
])
|
||||
.run(tauri::generate_context!())
|
||||
.expect("error while running tauri application");
|
||||
|
||||
Reference in New Issue
Block a user