ui: add launch-at-login (autostart) toggle for the UI

Add an Autostart Wails service wrapping app.Autostart and a toggle in
the General settings tab. The OS login-item registration is the single
source of truth (nothing mirrored to the preferences file). Affects the
graphical UI only, not the daemon. The toggle hides itself on platforms
where autostart is unsupported.
This commit is contained in:
Zoltan Papp
2026-05-31 02:01:02 +02:00
parent b830a45333
commit 8b03c96851
7 changed files with 117 additions and 1 deletions
@@ -1,5 +1,6 @@
import { useEffect, useRef } from "react";
import { useEffect, useRef, useState } from "react";
import { useTranslation } from "react-i18next";
import { Dialogs } from "@wailsio/runtime";
import { Button } from "@/components/buttons/Button";
import FancyToggleSwitch from "@/components/switches/FancyToggleSwitch";
import { HelpText } from "@/components/typography/HelpText";
@@ -10,12 +11,46 @@ import { useSettings } from "@/contexts/SettingsContext.tsx";
import { ManagementServerSwitch } from "@/components/ManagementServerSwitch.tsx";
import { ManagementMode, useManagementUrl } from "@/hooks/useManagementUrl.ts";
import { LanguagePicker } from "@/components/LanguagePicker.tsx";
import { Autostart } from "@bindings/services";
import i18next from "@/lib/i18n";
export function SettingsGeneral() {
const { t } = useTranslation();
const { config, setField } = useSettings();
const { mode, setMode, setUrl, displayUrl, showError, canSave, save } = useManagementUrl();
// Autostart lives in the OS login-item registry, not the daemon config, so
// it has its own read-on-mount state. supported gates whether we render the
// toggle at all (false on server/mobile builds).
const [autostartSupported, setAutostartSupported] = useState(false);
const [autostartEnabled, setAutostartEnabled] = useState(false);
useEffect(() => {
let cancelled = false;
(async () => {
const supported = await Autostart.Supported();
if (cancelled) return;
setAutostartSupported(supported);
if (!supported) return;
setAutostartEnabled(await Autostart.IsEnabled());
})().catch(() => {});
return () => {
cancelled = true;
};
}, []);
const onAutostartChange = async (enabled: boolean) => {
setAutostartEnabled(enabled);
try {
await Autostart.SetEnabled(enabled);
} catch (e) {
setAutostartEnabled(!enabled);
await Dialogs.Error({
Title: i18next.t("settings.general.autostart.errorTitle"),
Message: String(e),
});
}
};
const inputRef = useRef<HTMLInputElement>(null);
const prevMode = useRef(mode);
useEffect(() => {
@@ -44,6 +79,14 @@ export function SettingsGeneral() {
label={t("settings.general.notifications.label")}
helpText={t("settings.general.notifications.help")}
/>
{autostartSupported && (
<FancyToggleSwitch
value={autostartEnabled}
onChange={onAutostartChange}
label={t("settings.general.autostart.label")}
helpText={t("settings.general.autostart.help")}
/>
)}
</SectionGroup>
<SectionGroup title={t("settings.general.section.connection")}>