app autostart on boot (havent tested, hopefully works)

This commit is contained in:
2026-09-04 15:04:04 +08:00
parent b3a74d45c1
commit 927f82e8c9
10 changed files with 175 additions and 23 deletions
Generated
+74 -8
View File
@@ -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",
+3 -2
View File
@@ -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",
+3 -2
View File
@@ -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": "保留好友",
+1
View File
@@ -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"] }
+6
View File
@@ -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<tauri::Wry> {
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,
+23
View File
@@ -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<bool, String> {
handle.autolaunch().is_enabled().map_err(db::command_error)
}
#[tauri::command]
#[specta::specta]
pub fn set_autostart_enabled(handle: AppHandle, enabled: bool) -> Result<bool, String> {
let manager = handle.autolaunch();
if enabled {
manager.enable()
} else {
manager.disable()
}
.map_err(db::command_error)?;
manager.is_enabled().map_err(db::command_error)
}
+2
View File
@@ -1,3 +1,5 @@
pub mod autostart;
use std::sync::RwLock;
use serde::{Deserialize, Serialize};
+6
View File
@@ -89,6 +89,12 @@ async completeOnboarding() : Promise<null> {
async requestAccessibilityPermission() : Promise<boolean> {
return await TAURI_INVOKE("request_accessibility_permission");
},
async getAutostartEnabled() : Promise<boolean> {
return await TAURI_INVOKE("get_autostart_enabled");
},
async setAutostartEnabled(enabled: boolean) : Promise<boolean> {
return await TAURI_INVOKE("set_autostart_enabled", { enabled });
},
async getLocaleSettings() : Promise<LocaleChanged> {
return await TAURI_INVOKE("get_locale_settings");
},
@@ -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 @@
</fieldset>
<fieldset class="fieldset border border-base-300 bg-base-100 p-3">
<legend class="fieldset-legend px-1">{$messages.account_ui_label()}</legend>
<legend class="fieldset-legend px-1"
>{$messages.account_system_label()}</legend
>
<label class="fieldset-label mb-2" for="autostart">
<input
id="autostart"
type="checkbox"
class="checkbox checkbox-sm"
bind:checked={autostartEnabled}
disabled={!autostartLoaded || busy}
onchange={() => {
error = "";
}}
/>
{$messages.autostart_label()}
</label>
<label class="fieldset-label" for="language"
>{$messages.language_label()}</label
>
+1 -1
View File
@@ -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;