use std::error::Error; use once_cell::sync::OnceCell; use sqlx::{ SqlitePool, sqlite::SqlitePoolOptions, sqlite::SqliteConnectOptions, }; use tauri::{ App, AppHandle, Manager, async_runtime as rt, }; use crate::{ config::{self, AppConfig}, credentials::Session, ipc, server::Server, errors::*, state::AppState, tray, }; pub static APP: OnceCell = OnceCell::new(); pub fn run() -> tauri::Result<()> { tauri::Builder::default() .plugin(tauri_plugin_single_instance::init(|app, _argv, _cwd| { app.get_window("main") .map(|w| w.show().error_popup("Failed to show main window")); })) .system_tray(tray::create()) .on_system_tray_event(tray::handle_event) .invoke_handler(tauri::generate_handler![ ipc::unlock, ipc::respond, ipc::get_session_status, ipc::save_credentials, ipc::get_config, ipc::save_config, ]) .setup(|app| rt::block_on(setup(app))) .build(tauri::generate_context!())? .run(|app, run_event| match run_event { tauri::RunEvent::WindowEvent { label, event, .. } => match event { tauri::WindowEvent::CloseRequested { api, .. } => { let _ = app.get_window(&label).map(|w| w.hide()); api.prevent_close(); } _ => () } _ => () }); Ok(()) } pub async fn connect_db() -> Result { let conn_opts = SqliteConnectOptions::new() .filename(config::get_or_create_db_path()?) .create_if_missing(true); let pool_opts = SqlitePoolOptions::new(); let pool: SqlitePool = pool_opts.connect_with(conn_opts).await?; Ok(pool) } async fn setup(app: &mut App) -> Result<(), Box> { APP.set(app.handle()).unwrap(); let pool = connect_db().await?; sqlx::migrate!().run(&pool).await?; let conf = AppConfig::load(&pool).await?; let session = Session::load(&pool).await?; let srv = Server::new(conf.listen_addr, conf.listen_port, app.handle()).await?; config::set_auto_launch(conf.start_on_login)?; if !conf.start_minimized { app.get_window("main") .ok_or(HandlerError::NoMainWindow)? .show()?; } let state = AppState::new(conf, session, srv, pool); app.manage(state); Ok(()) }