Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 22 additions & 9 deletions src-tauri/src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
use crate::utils::defender::is_defender_excluded;
use crate::utils::path;
use crate::utils::path::{get_app_base_path, get_app_working_dir_path};
use anyhow::{anyhow, Context};
use anyhow::Context;
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use std::fs;
Expand Down Expand Up @@ -241,8 +241,9 @@ pub(crate) async fn load_app_config_from_json(app_name: &str) -> anyhow::Result<
let json_data = tokio::fs::read_to_string(&config_path)
.await
.with_context(|| format!("Failed to read app.json for {}", app_name))?;
let normalized_json_data = json_data.trim_start_matches('\u{feff}');

match serde_json::from_str::<App>(&json_data) {
match serde_json::from_str::<App>(normalized_json_data) {
Ok(mut app) => {
if app.name != app_name {
warn!("App name mismatch in app.json ('{}') and directory ('{}'). Correcting to directory name: '{}'.", app.name, app_name, app_name);
Expand All @@ -266,17 +267,29 @@ pub(crate) async fn load_app_config_from_json(app_name: &str) -> anyhow::Result<
Ok(Some(app))
}
Err(e) => {
error!(
"Failed to deserialize app.json for {}: {}. Content sample: {}",
let bad_path = config_path.with_extension("json.bad");
warn!(
"Failed to deserialize app.json for {}: {}. Backing up invalid file to {} and rebuilding from embedded config.",
app_name,
e,
json_data.chars().take(200).collect::<String>()
bad_path.display()
);
Err(anyhow!(
"Failed to deserialize app.json for {}: {}",
warn!(
"Invalid app.json content sample for {}: {}",
app_name,
e
))
normalized_json_data.chars().take(200).collect::<String>()
);

if let Err(copy_err) = tokio::fs::copy(&config_path, &bad_path).await {
warn!(
"Failed to back up invalid app.json for {} to {}: {}",
app_name,
bad_path.display(),
copy_err
);
}

Ok(None)
}
}
}
100 changes: 41 additions & 59 deletions src-tauri/src/app_service.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
//src/app_service.rs
use crate::app::App;
use crate::config_manager::{
GLOBAL_CONFIG_STATE, UPDATE_METHOD_OPTION_AUTO, UPDATE_METHOD_OPTION_IGNORE,
};
use crate::emitter::get_app_handle;
use crate::config_manager::{GLOBAL_CONFIG_STATE, UPDATE_METHOD_OPTION_AUTO};
use crate::git::ensure_repository;
use crate::runas;
use crate::utils::error::Error;
Expand Down Expand Up @@ -340,6 +337,13 @@ pub async fn load_apps() -> Result<Vec<App>, Error> {
.find(|version| git::is_release_version(version))
.cloned();
let current_version_missing = app.current_version_missing;
let has_pending_version_change_notice = app
.app_starting_version
.as_ref()
.zip(app.current_version.as_ref())
.is_some_and(|(starting_version, current_version)| {
starting_version != current_version && !app.update_note.is_empty()
});
let release_update_available =
latest_release_version
.as_ref()
Expand All @@ -355,77 +359,55 @@ pub async fn load_apps() -> Result<Vec<App>, Error> {
let is_latest = !release_update_available;

info!(
"First load, checking for auto-start conditions. update_method:{}, is_latest:{}, current_version_missing:{}",
update_method, is_latest, current_version_missing
"First load, checking for auto-start conditions. update_method:{}, is_latest:{}, current_version_missing:{}, has_pending_version_change_notice:{}",
update_method, is_latest, current_version_missing, has_pending_version_change_notice
);

let mut needs_autostart = false;
info!("locale is {}", get_locale());
if app.installed && !app.available_versions.is_empty() {
if release_update_available {
if current_version_missing {
info!(
"Current version is no longer available upstream. Forcing update to latest available release."
);
} else {
info!("App is not the latest release version.");
}
let app_name_clone = app.name.clone();
let latest_version = latest_release_version
.expect("release_update_available requires latest release");
if current_version_missing || update_method == UPDATE_METHOD_OPTION_AUTO {
if current_version_missing {
info!(
"{}",
t!(
"message.new_version_update",
version = latest_version.clone()
)
);
send_notification(
app_name_clone.clone(),
t!("message.new_version_update", version = latest_version),
);
update_to_version(&app_name_clone, &latest_version).await?;
info!("Auto Update to version {} success.", &latest_version);
send_notification(
app_name_clone,
t!("message.version_update_success", version = latest_version),
"Current version is no longer available upstream. Waiting for explicit user action before updating to {}.",
latest_version
);
needs_autostart = true;
} else {
send_notification(
app_name_clone.clone(),
t!("message.new_version", version = latest_version),
info!(
"App is not the latest release version. Waiting for explicit user action before updating to {}.",
latest_version
);
if update_method == UPDATE_METHOD_OPTION_IGNORE {
info!("Auto-update is UPDATE_METHOD_OPTION_IGNORE set auto_start to true.");
needs_autostart = true;
}
}

let app_name_clone = app.name.clone();
let notification_key =
if current_version_missing || update_method == UPDATE_METHOD_OPTION_AUTO {
"message.new_version_update"
} else {
"message.new_version"
};
send_notification(
app_name_clone,
t!(notification_key, version = latest_version),
);
} else if has_pending_version_change_notice {
info!(
"Pending version-change notice detected for app '{}'. Waiting for user action before any further operation.",
app.name
);
} else {
needs_autostart = true;
info!("App is the latest version and installed. set auto start to true");
info!(
"App '{}' is installed and up to date. Waiting for explicit user action instead of auto-starting.",
app.name
);
}
}

if needs_autostart {
info!("Auto-starting app '{}'.", app.name);
let app_name_clone = app.name.clone();
drop(auto_start_guard);
if let Some(app_handle) = get_app_handle() {
let app_handle_clone = app_handle.clone();
tokio::spawn(async move {
if let Err(e) = start_app(app_handle_clone, app_name_clone.clone()).await {
error!("Auto-start for app '{}' failed: {:?}", app_name_clone, e);
}
});
}
} else {
info!(
"Auto-start conditions not met for app '{}' (installed: {}, is_latest: {}).",
app.name, app.installed, is_latest
);
}
info!(
"First-load automation is disabled for app '{}' (installed: {}, is_latest: {}).",
app.name, app.installed, is_latest
);
}
}

Expand Down
8 changes: 6 additions & 2 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,7 @@ function App() {
version: string;
actionType: string;
} | null>(null);
const activeVersionChangeAppRef = useRef<string | null>(null);
const [isVersionChangeProcessRunning, setIsVersionChangeProcessRunning] = useState<boolean>(false);
const [isRunningAppConsoleOpen, setIsRunningAppConsoleOpen] = useState<boolean>(false);
const [themeMode, setThemeMode] = useState<ThemeModeSetting>(() => {
Expand Down Expand Up @@ -336,6 +337,9 @@ function App() {
error?: boolean;
}>("app-log", (event) => {
const {app_name, finished, error} = event.payload;
if (activeVersionChangeAppRef.current !== app_name) {
return;
}
setInlineUpdateLogs(prev => {
const entry = prev[app_name];
if (!entry || entry.completed) return prev;
Expand All @@ -347,8 +351,6 @@ function App() {
completedAppsRef.current.add(app_name); // mark completed immediately
return {...prev, [app_name]: {...entry, isConfirming: false, completed: true, failed: false}};
}
} else if (!entry.isConfirming) {
return {...prev, [app_name]: {...entry, isConfirming: true, failed: false}};
}
return prev;
});
Expand Down Expand Up @@ -438,6 +440,7 @@ function App() {
const handleConfirmVersionChange = async (params: { appName: string, version: string, actionType: string }) => {
clearMessages();
setAppActionLoading(prev => ({...prev, [params.appName]: true}));
activeVersionChangeAppRef.current = params.appName;
// Mark as confirming so the inline log shows a spinner
setInlineUpdateLogs(prev => ({
...prev,
Expand Down Expand Up @@ -490,6 +493,7 @@ function App() {
setStartingAppName(null);
setVersionChangeConsoleData(null);
setProfileChangeData(null);
activeVersionChangeAppRef.current = null;

// After a version change: clear version selector and mark log as completed (shows success state)
if (wasVersionChange && appNameForVersionChange) {
Expand Down