From 927f82e8c99742d8f1b4869aaac9214136ad3441 Mon Sep 17 00:00:00 2001 From: Wind-Explorer Date: Fri, 4 Sep 2026 15:04:04 +0800 Subject: [PATCH] app autostart on boot (havent tested, hopefully works) --- Cargo.lock | 82 +++++++++++++++++-- messages/en.json | 5 +- messages/zh-CN.json | 5 +- src-tauri/Cargo.toml | 1 + src-tauri/src/lib.rs | 6 ++ src-tauri/src/settings/autostart.rs | 23 ++++++ src-tauri/src/settings/mod.rs | 2 + src/lib/bindings.ts | 6 ++ .../control-panel/general-panel.svelte | 66 ++++++++++++--- src/routes/control-panel/+page.svelte | 2 +- 10 files changed, 175 insertions(+), 23 deletions(-) create mode 100644 src-tauri/src/settings/autostart.rs diff --git a/Cargo.lock b/Cargo.lock index 8d2b198..aeb5286 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -27,6 +27,7 @@ dependencies = [ "sys-locale", "tauri", "tauri-build", + "tauri-plugin-autostart", "tauri-plugin-dialog", "tauri-plugin-opener", "tauri-plugin-updater", @@ -273,6 +274,17 @@ version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" +[[package]] +name = "auto-launch" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f012b8cc0c850f34117ec8252a44418f2e34a2cf501de89e29b241ae5f79471" +dependencies = [ + "dirs 4.0.0", + "thiserror 1.0.69", + "winreg 0.10.1", +] + [[package]] name = "autocfg" version = "1.5.1" @@ -991,13 +1003,33 @@ dependencies = [ "crypto-common 0.2.2", ] +[[package]] +name = "dirs" +version = "4.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3aa72a6f96ea37bbc5aa912f6788242832f75369bdfdadcb0e38423f100059" +dependencies = [ + "dirs-sys 0.3.7", +] + [[package]] name = "dirs" version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3e8aa94d75141228480295a7d0e7feb620b1a5ad9f12bc40be62411e38cce4e" dependencies = [ - "dirs-sys", + "dirs-sys 0.5.0", +] + +[[package]] +name = "dirs-sys" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" +dependencies = [ + "libc", + "redox_users 0.4.6", + "winapi", ] [[package]] @@ -1008,7 +1040,7 @@ checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab" dependencies = [ "libc", "option-ext", - "redox_users", + "redox_users 0.5.2", "windows-sys 0.61.2", ] @@ -1172,7 +1204,7 @@ dependencies = [ "rustc_version", "toml 1.1.2+spec-1.1.0", "vswhom", - "winreg", + "winreg 0.55.0", ] [[package]] @@ -3496,6 +3528,17 @@ dependencies = [ "bitflags 2.13.0", ] +[[package]] +name = "redox_users" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" +dependencies = [ + "getrandom 0.2.17", + "libredox", + "thiserror 1.0.69", +] + [[package]] name = "redox_users" version = "0.5.2" @@ -4718,7 +4761,7 @@ dependencies = [ "anyhow", "bytes", "cookie", - "dirs", + "dirs 6.0.0", "dunce", "embed_plist", "getrandom 0.3.4", @@ -4769,7 +4812,7 @@ checksum = "4aa1f9055fc23919a54e4e125052bed16ed04aef0487086e758fe01a67b451c7" dependencies = [ "anyhow", "cargo_toml", - "dirs", + "dirs 6.0.0", "glob", "heck 0.5.0", "json-patch", @@ -4839,6 +4882,20 @@ dependencies = [ "walkdir", ] +[[package]] +name = "tauri-plugin-autostart" +version = "2.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "459383cebc193cdd03d1ba4acc40f2c408a7abce419d64bdcd2d745bc2886f70" +dependencies = [ + "auto-launch", + "serde", + "serde_json", + "tauri", + "tauri-plugin", + "thiserror 2.0.18", +] + [[package]] name = "tauri-plugin-dialog" version = "2.7.2" @@ -4910,7 +4967,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b28d8cabdeb0564f03ae261963de4bc3d98321cd3d213e76a81b7d344e5df606" dependencies = [ "base64 0.22.1", - "dirs", + "dirs 6.0.0", "flate2", "futures-util", "http", @@ -5487,7 +5544,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "15edbb0d80583e85ee8df283410038e17314df5cba30da2087a54a85216c0773" dependencies = [ "crossbeam-channel", - "dirs", + "dirs 6.0.0", "libappindicator", "muda", "objc2", @@ -6593,6 +6650,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "winreg" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d" +dependencies = [ + "winapi", +] + [[package]] name = "winreg" version = "0.55.0" @@ -6625,7 +6691,7 @@ dependencies = [ "block2", "cookie", "crossbeam-channel", - "dirs", + "dirs 6.0.0", "dom_query", "dpi", "dunce", diff --git a/messages/en.json b/messages/en.json index a91204a..6098668 100644 --- a/messages/en.json +++ b/messages/en.json @@ -3,6 +3,7 @@ "language_system": "Use system setting", "language_english": "English", "language_chinese": "Chinese (中文)", + "autostart_label": "Start Friendolls when I turn on the computer", "language_label": "Language", "common_ok": "OK", "common_cancel": "Cancel", @@ -120,7 +121,7 @@ "onboarding_next": "Next >", "control_window_title": "Friendolls Properties", "control_tab_scene": "Scene", - "control_tab_account": "Account", + "control_tab_general": "General", "control_tab_friends": "Friends", "control_tab_network": "Network", "account_identity": "Identity", @@ -129,7 +130,7 @@ "account_public_key": "Identification Key", "account_copy_key": "Copy Identification Key", "account_loading_key": "Loading Identification Key", - "account_ui_label": "UI", + "account_system_label": "System", "friends_remove_prefix": "Applying will remove ", "friends_remove_suffix": ". They will immediately go offline and stop receiving your activity.", "friends_keep": "Keep friend", diff --git a/messages/zh-CN.json b/messages/zh-CN.json index fc63836..109b504 100644 --- a/messages/zh-CN.json +++ b/messages/zh-CN.json @@ -3,6 +3,7 @@ "language_system": "跟随系统", "language_english": "英语(English)", "language_chinese": "中文", + "autostart_label": "打开电脑时自动启动 Friendolls", "language_label": "语言", "common_ok": "确定", "common_cancel": "取消", @@ -120,7 +121,7 @@ "onboarding_next": "下一步 >", "control_window_title": "Friendolls 属性", "control_tab_scene": "场景", - "control_tab_account": "账户", + "control_tab_general": "通用", "control_tab_friends": "好友", "control_tab_network": "网络", "account_identity": "身份信息", @@ -129,7 +130,7 @@ "account_public_key": "身份密钥", "account_copy_key": "复制身份密钥", "account_loading_key": "正在加载身份密钥", - "account_ui_label": "界面", + "account_system_label": "系统", "friends_remove_prefix": "应用更改将删除好友“", "friends_remove_suffix": "”。对方会立即离线,并停止接收你的活动信息。", "friends_keep": "保留好友", diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index dd06844..fabde57 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -41,6 +41,7 @@ uuid = { version = "1", features = ["v4"] } webp = { version = "0.3.1", default-features = false } friendolls-common = { path = "../src-common" } sys-locale = "0.3.2" +tauri-plugin-autostart = "2" [dev-dependencies] tokio = { version = "1.53.1", features = ["macros", "rt-multi-thread"] } diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 4bf2b61..628d6de 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -50,6 +50,10 @@ pub fn run() { tauri::Builder::default() .plugin(tauri_plugin_updater::Builder::new().build()) + .plugin(tauri_plugin_autostart::init( + tauri_plugin_autostart::MacosLauncher::LaunchAgent, + None, + )) .plugin(tauri_plugin_dialog::init()) .plugin(tauri_plugin_opener::init()) .invoke_handler(specta_builder.invoke_handler()) @@ -110,6 +114,8 @@ fn specta_builder() -> tauri_specta::Builder { onboarding::get_onboarding_status, onboarding::complete_onboarding, macos::request_accessibility_permission, + settings::autostart::get_autostart_enabled, + settings::autostart::set_autostart_enabled, settings::get_locale_settings, settings::set_locale_preference, ui::control_panel::open_action_window, diff --git a/src-tauri/src/settings/autostart.rs b/src-tauri/src/settings/autostart.rs new file mode 100644 index 0000000..0c7d847 --- /dev/null +++ b/src-tauri/src/settings/autostart.rs @@ -0,0 +1,23 @@ +use tauri::AppHandle; +use tauri_plugin_autostart::ManagerExt; + +use crate::db; + +#[tauri::command] +#[specta::specta] +pub fn get_autostart_enabled(handle: AppHandle) -> Result { + handle.autolaunch().is_enabled().map_err(db::command_error) +} + +#[tauri::command] +#[specta::specta] +pub fn set_autostart_enabled(handle: AppHandle, enabled: bool) -> Result { + let manager = handle.autolaunch(); + if enabled { + manager.enable() + } else { + manager.disable() + } + .map_err(db::command_error)?; + manager.is_enabled().map_err(db::command_error) +} diff --git a/src-tauri/src/settings/mod.rs b/src-tauri/src/settings/mod.rs index 43484e8..972ec21 100644 --- a/src-tauri/src/settings/mod.rs +++ b/src-tauri/src/settings/mod.rs @@ -1,3 +1,5 @@ +pub mod autostart; + use std::sync::RwLock; use serde::{Deserialize, Serialize}; diff --git a/src/lib/bindings.ts b/src/lib/bindings.ts index 191f050..d58c05f 100644 --- a/src/lib/bindings.ts +++ b/src/lib/bindings.ts @@ -89,6 +89,12 @@ async completeOnboarding() : Promise { async requestAccessibilityPermission() : Promise { return await TAURI_INVOKE("request_accessibility_permission"); }, +async getAutostartEnabled() : Promise { + return await TAURI_INVOKE("get_autostart_enabled"); +}, +async setAutostartEnabled(enabled: boolean) : Promise { + return await TAURI_INVOKE("set_autostart_enabled", { enabled }); +}, async getLocaleSettings() : Promise { return await TAURI_INVOKE("get_locale_settings"); }, diff --git a/src/lib/components/control-panel/general-panel.svelte b/src/lib/components/control-panel/general-panel.svelte index 3fdbc81..04578b7 100644 --- a/src/lib/components/control-panel/general-panel.svelte +++ b/src/lib/components/control-panel/general-panel.svelte @@ -16,10 +16,31 @@ let { register }: { register: RegisterPanel } = $props(); let displayName = $state(""); - let dirty = $state(false); + let nameDirty = $state(false); let busy = $state(false); let error = $state(""); let languageBusy = $state(false); + let autostartEnabled = $state(false); + let savedAutostartEnabled = $state(false); + let autostartLoaded = $state(false); + let dirty = $derived( + nameDirty || (autostartLoaded && autostartEnabled !== savedAutostartEnabled), + ); + + async function loadAutostart() { + autostartLoaded = false; + try { + savedAutostartEnabled = await commands.getAutostartEnabled(); + autostartEnabled = savedAutostartEnabled; + autostartLoaded = true; + } catch (cause) { + error = errorMessage(cause); + } + } + + onMount(() => { + void loadAutostart(); + }); async function changeLanguage(event: Event) { languageBusy = true; @@ -36,27 +57,30 @@ } $effect(() => { - if (!dirty) displayName = $profile?.displayName ?? ""; + if (!nameDirty) displayName = $profile?.displayName ?? ""; register("general", { apply, reset }, { dirty, busy }); }); onMount(() => register("general", { apply, reset }, { dirty, busy })); - function updateDirty() { - dirty = displayName.trim() !== ($profile?.displayName ?? ""); + function updateDirty(event: Event) { + displayName = (event.currentTarget as HTMLInputElement).value; + nameDirty = displayName.trim() !== ($profile?.displayName ?? ""); error = ""; } function reset() { displayName = $profile?.displayName ?? ""; - dirty = false; + nameDirty = false; error = ""; + autostartEnabled = savedAutostartEnabled; + void loadAutostart(); } async function apply() { const nextName = displayName.trim(); if (!dirty) return true; - if (!nextName) { + if (nameDirty && !nextName) { error = $messages.error_display_name_empty(); return false; } @@ -64,9 +88,16 @@ busy = true; error = ""; try { - await commands.updateProfile(nextName, null); - displayName = nextName; - dirty = false; + if (nameDirty) { + await commands.updateProfile(nextName, null); + displayName = nextName; + nameDirty = false; + } + if (autostartLoaded && autostartEnabled !== savedAutostartEnabled) { + savedAutostartEnabled = + await commands.setAutostartEnabled(autostartEnabled); + autostartEnabled = savedAutostartEnabled; + } return true; } catch (cause) { error = errorMessage(cause); @@ -147,7 +178,22 @@
- {$messages.account_ui_label()} + {$messages.account_system_label()} + diff --git a/src/routes/control-panel/+page.svelte b/src/routes/control-panel/+page.svelte index f4c4b7f..0093f3d 100644 --- a/src/routes/control-panel/+page.svelte +++ b/src/routes/control-panel/+page.svelte @@ -14,7 +14,7 @@ const tabs = [ { id: "scene", label: "control_tab_scene" }, - { id: "general", label: "control_tab_account" }, + { id: "general", label: "control_tab_general" }, { id: "friends", label: "control_tab_friends" }, { id: "network", label: "control_tab_network" }, ] as const;