start on login

This commit is contained in:
Joseph Montanaro 2023-04-27 14:24:08 -07:00
parent ebc00a5df6
commit 741169d807
9 changed files with 101 additions and 27 deletions

32
src-tauri/Cargo.lock generated
View File

@ -68,6 +68,7 @@ checksum = "bb07d2053ccdbe10e2af2995a2f116c1330396493dc1269f6a91d0ae82e19704"
name = "app" name = "app"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"auto-launch",
"aws-config", "aws-config",
"aws-sdk-sts", "aws-sdk-sts",
"aws-smithy-types", "aws-smithy-types",
@ -138,6 +139,17 @@ dependencies = [
"wildmatch", "wildmatch",
] ]
[[package]]
name = "auto-launch"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5904a4d734f0235edf29aab320a14899f3e090446e594ff96508a6215f76f89c"
dependencies = [
"dirs",
"thiserror",
"winreg",
]
[[package]] [[package]]
name = "autocfg" name = "autocfg"
version = "1.1.0" version = "1.1.0"
@ -915,6 +927,15 @@ dependencies = [
"subtle", "subtle",
] ]
[[package]]
name = "dirs"
version = "4.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca3aa72a6f96ea37bbc5aa912f6788242832f75369bdfdadcb0e38423f100059"
dependencies = [
"dirs-sys",
]
[[package]] [[package]]
name = "dirs-next" name = "dirs-next"
version = "2.0.0" version = "2.0.0"
@ -925,6 +946,17 @@ dependencies = [
"dirs-sys-next", "dirs-sys-next",
] ]
[[package]]
name = "dirs-sys"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6"
dependencies = [
"libc",
"redox_users",
"winapi",
]
[[package]] [[package]]
name = "dirs-sys-next" name = "dirs-sys-next"
version = "0.1.2" version = "0.1.2"

View File

@ -31,6 +31,7 @@ thiserror = "1.0.38"
once_cell = "1.16.0" once_cell = "1.16.0"
strum = "0.24" strum = "0.24"
strum_macros = "0.24" strum_macros = "0.24"
auto-launch = "0.4.0"
[features] [features]
# by default Tauri runs in production mode # by default Tauri runs in production mode

View File

@ -1,6 +1,7 @@
use std::net::Ipv4Addr; use std::net::Ipv4Addr;
use std::path::PathBuf; use std::path::PathBuf;
use auto_launch::AutoLaunchBuilder;
use serde::{Serialize, Deserialize}; use serde::{Serialize, Deserialize};
use sqlx::SqlitePool; use sqlx::SqlitePool;
@ -17,6 +18,8 @@ pub struct AppConfig {
pub rehide_ms: u64, pub rehide_ms: u64,
#[serde(default = "default_start_minimized")] #[serde(default = "default_start_minimized")]
pub start_minimized: bool, pub start_minimized: bool,
#[serde(default = "default_start_on_login")]
pub start_on_login: bool,
} }
@ -27,6 +30,7 @@ impl Default for AppConfig {
listen_port: default_listen_port(), listen_port: default_listen_port(),
rehide_ms: default_rehide_ms(), rehide_ms: default_rehide_ms(),
start_minimized: default_start_minimized(), start_minimized: default_start_minimized(),
start_on_login: default_start_on_login(),
} }
} }
} }
@ -62,6 +66,28 @@ impl AppConfig {
} }
pub fn set_auto_launch(enable: bool) -> Result<(), SetupError> {
let path_buf = std::env::current_exe()
.map_err(|e| auto_launch::Error::Io(e))?;
let path = path_buf
.to_string_lossy();
let auto = AutoLaunchBuilder::new()
.set_app_name("Creddy")
.set_app_path(&path)
.build()?;
if enable {
auto.enable()?;
}
else {
auto.disable()?;
}
Ok(())
}
pub fn get_or_create_db_path() -> PathBuf { pub fn get_or_create_db_path() -> PathBuf {
if cfg!(debug_assertions) { if cfg!(debug_assertions) {
return PathBuf::from("./creddy.db"); return PathBuf::from("./creddy.db");
@ -90,7 +116,7 @@ fn default_listen_port() -> u16 {
} }
fn default_listen_addr() -> Ipv4Addr { Ipv4Addr::LOCALHOST } fn default_listen_addr() -> Ipv4Addr { Ipv4Addr::LOCALHOST }
fn default_rehide_ms() -> u64 { 1000 } fn default_rehide_ms() -> u64 { 1000 }
// start minimized and on login only in production mode
fn default_start_minimized() -> bool { !cfg!(debug_assertions) } // default to start-minimized in production only fn default_start_minimized() -> bool { !cfg!(debug_assertions) }
fn default_start_on_login() -> bool { !cfg!(debug_assertions) }

View File

@ -87,6 +87,8 @@ pub enum SetupError {
MigrationError(#[from] MigrateError), MigrationError(#[from] MigrateError),
#[error("Error parsing configuration from database")] #[error("Error parsing configuration from database")]
ConfigParseError(#[from] serde_json::Error), ConfigParseError(#[from] serde_json::Error),
#[error("Failed to set up start-on-login: {0}")]
AutoLaunchError(#[from] auto_launch::Error),
} }

View File

@ -21,7 +21,7 @@ use state::AppState;
pub static APP: OnceCell<AppHandle> = OnceCell::new(); pub static APP: OnceCell<AppHandle> = OnceCell::new();
fn main() { fn main() {
let initial_state = match rt::block_on(state::AppState::load()) { let initial_state = match rt::block_on(AppState::load()) {
Ok(state) => state, Ok(state) => state,
Err(e) => {eprintln!("{}", e); return;} Err(e) => {eprintln!("{}", e); return;}
}; };
@ -42,6 +42,8 @@ fn main() {
APP.set(app.handle()).unwrap(); APP.set(app.handle()).unwrap();
let state = app.state::<AppState>(); let state = app.state::<AppState>();
let config = state.config.read().unwrap(); let config = state.config.read().unwrap();
config::set_auto_launch(config.start_on_login)?;
let addr = std::net::SocketAddrV4::new(config.listen_addr, config.listen_port); let addr = std::net::SocketAddrV4::new(config.listen_addr, config.listen_port);
tauri::async_runtime::spawn(server::serve(addr, app.handle())); tauri::async_runtime::spawn(server::serve(addr, app.handle()));

View File

@ -150,13 +150,14 @@ impl AppState {
Ok(()) Ok(())
} }
pub async fn update_config(&self, new_config: AppConfig) -> Result<(), sqlx::error::Error> { pub async fn update_config(&self, new_config: AppConfig) -> Result<(), SetupError> {
let config = { new_config.save(&self.pool).await?;
let mut live_config = self.config.write().unwrap();
*live_config = new_config; let mut live_config = self.config.write().unwrap();
live_config.clone() if new_config.start_on_login != live_config.start_on_login {
}; config::set_auto_launch(new_config.start_on_login)?;
config.save(&self.pool).await?; }
*live_config = new_config;
Ok(()) Ok(())
} }

View File

@ -10,27 +10,28 @@
export let max = null; export let max = null;
export let decimal = false; export let decimal = false;
console.log('min:', min);
const dispatch = createEventDispatcher(); const dispatch = createEventDispatcher();
let error = null; let error = null;
let localValue = value.toString();
function validate(event) { function validate(event) {
let v = event.target.value; localValue = localValue.replace(/[^-0-9.]/g, '');
// Don't update the value, but also don't error, if it's empty
if (v === '') { // or if it could be the start of a negative or decimal number
if (localValue.match(/^$|^-$|^\.$/) !== null) {
error = null; error = null;
return; return;
} }
let num = parseFloat(v); let num = parseFloat(localValue);
if (Number.isNaN(num)) { if (num % 1 !== 0 && !decimal) {
error = `"${v}" is not a number`;
}
else if (num % 1 !== 0 && !decimal) {
error = `${num} is not a whole number`; error = `${num} is not a whole number`;
} }
else if (min && num < min) { else if (min !== null && num < min) {
error = `Too low (minimum ${min})`; error = `Too low (minimum ${min})`;
} }
else if (max && num > max) { else if (max !== null && num > max) {
error = `Too large (maximum ${max})` error = `Too large (maximum ${max})`
} }
else { else {
@ -47,12 +48,13 @@
{#if unit} {#if unit}
<span class="mr-2">{unit}:</span> <span class="mr-2">{unit}:</span>
{/if} {/if}
<div class="tooltip tooltip-error" class:tooltip-open={error !== null} data-tip={error}> <div class="tooltip tooltip-error" class:tooltip-open={error !== null} data-tip="{error}">
<input <input
type="text" type="text"
class="input input-sm input-bordered text-right max-w-[4rem]" class="input input-sm input-bordered text-right"
size="{Math.max(5, localValue.length)}"
class:input-error={error} class:input-error={error}
value={value} bind:value={localValue}
on:input="{validate}" on:input="{validate}"
/> />
</div> </div>

View File

@ -12,6 +12,8 @@
<slot name="input"></slot> <slot name="input"></slot>
</div> </div>
<p class="mt-3"> {#if $$slots.description}
<slot name="description"></slot> <p class="mt-3">
</p> <slot name="description"></slot>
</p>
{/if}

View File

@ -20,6 +20,12 @@
<div class="max-w-md mx-auto mt-1.5 p-4"> <div class="max-w-md mx-auto mt-1.5 p-4">
<h2 class="text-2xl font-bold text-center">Settings</h2> <h2 class="text-2xl font-bold text-center">Settings</h2>
<ToggleSetting title="Start on login" bind:value={$appState.config.start_on_login} on:update={save}>
<svelte:fragment slot="description">
Start Creddy when you log in to your computer.
</svelte:fragment>
</ToggleSetting>
<ToggleSetting title="Start minimized" bind:value={$appState.config.start_minimized} on:update={save}> <ToggleSetting title="Start minimized" bind:value={$appState.config.start_minimized} on:update={save}>
<svelte:fragment slot="description"> <svelte:fragment slot="description">
Minimize to the system tray at startup. Minimize to the system tray at startup.