2023-05-06 12:01:56 -07:00
|
|
|
use std::error::Error;
|
2024-01-31 13:14:08 -08:00
|
|
|
use std::time::Duration;
|
2023-05-06 12:01:56 -07:00
|
|
|
|
|
|
|
use once_cell::sync::OnceCell;
|
2024-06-03 01:21:43 -04:00
|
|
|
use rfd::{
|
|
|
|
MessageDialog,
|
|
|
|
MessageLevel,
|
|
|
|
};
|
2023-05-06 12:01:56 -07:00
|
|
|
use sqlx::{
|
|
|
|
SqlitePool,
|
|
|
|
sqlite::SqlitePoolOptions,
|
|
|
|
sqlite::SqliteConnectOptions,
|
|
|
|
};
|
|
|
|
use tauri::{
|
|
|
|
App,
|
|
|
|
AppHandle,
|
|
|
|
async_runtime as rt,
|
2024-06-03 01:21:43 -04:00
|
|
|
Manager,
|
|
|
|
RunEvent,
|
|
|
|
WindowEvent,
|
2023-05-06 12:01:56 -07:00
|
|
|
};
|
2024-06-03 01:21:43 -04:00
|
|
|
use tauri::menu::MenuItem;
|
2023-05-06 12:01:56 -07:00
|
|
|
|
|
|
|
use crate::{
|
|
|
|
config::{self, AppConfig},
|
2024-06-19 05:10:55 -04:00
|
|
|
credentials::AppSession,
|
2023-05-06 12:01:56 -07:00
|
|
|
ipc,
|
|
|
|
server::Server,
|
|
|
|
errors::*,
|
2023-09-21 10:44:35 -07:00
|
|
|
shortcuts,
|
2023-05-06 12:01:56 -07:00
|
|
|
state::AppState,
|
|
|
|
tray,
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
pub static APP: OnceCell<AppHandle> = OnceCell::new();
|
|
|
|
|
|
|
|
|
|
|
|
pub fn run() -> tauri::Result<()> {
|
|
|
|
tauri::Builder::default()
|
|
|
|
.plugin(tauri_plugin_single_instance::init(|app, _argv, _cwd| {
|
2024-01-26 21:03:45 -08:00
|
|
|
show_main_window(app)
|
2024-06-03 01:21:43 -04:00
|
|
|
.error_popup("Failed to show main window")
|
2023-05-06 12:01:56 -07:00
|
|
|
}))
|
2024-06-01 20:17:50 -04:00
|
|
|
.plugin(tauri_plugin_global_shortcut::Builder::default().build())
|
2023-05-06 12:01:56 -07:00
|
|
|
.invoke_handler(tauri::generate_handler![
|
|
|
|
ipc::unlock,
|
|
|
|
ipc::respond,
|
|
|
|
ipc::get_session_status,
|
2024-01-31 13:14:08 -08:00
|
|
|
ipc::signal_activity,
|
2024-06-19 05:10:55 -04:00
|
|
|
ipc::save_aws_credential,
|
2023-05-06 12:01:56 -07:00
|
|
|
ipc::get_config,
|
|
|
|
ipc::save_config,
|
2023-08-02 19:57:37 -07:00
|
|
|
ipc::launch_terminal,
|
2023-09-14 15:04:25 -07:00
|
|
|
ipc::get_setup_errors,
|
2023-05-06 12:01:56 -07:00
|
|
|
])
|
2024-06-03 01:21:43 -04:00
|
|
|
.setup(|app| {
|
|
|
|
let res = rt::block_on(setup(app));
|
|
|
|
if let Err(ref e) = res {
|
|
|
|
MessageDialog::new()
|
|
|
|
.set_level(MessageLevel::Error)
|
|
|
|
.set_title("Creddy failed to start")
|
|
|
|
.set_description(format!("{e}"))
|
|
|
|
.show();
|
|
|
|
}
|
|
|
|
res
|
|
|
|
})
|
2023-05-06 12:01:56 -07:00
|
|
|
.build(tauri::generate_context!())?
|
2024-06-03 01:21:43 -04:00
|
|
|
.run(|app, run_event| {
|
|
|
|
if let RunEvent::WindowEvent { event, .. } = run_event {
|
|
|
|
if let WindowEvent::CloseRequested { api, .. } = event {
|
2024-01-26 21:03:45 -08:00
|
|
|
let _ = hide_main_window(app);
|
2023-05-06 12:01:56 -07:00
|
|
|
api.prevent_close();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
Ok(())
|
2023-05-06 16:56:45 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
pub async fn connect_db() -> Result<SqlitePool, SetupError> {
|
|
|
|
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?;
|
2023-05-06 21:59:24 -07:00
|
|
|
sqlx::migrate!().run(&pool).await?;
|
2023-05-06 16:56:45 -07:00
|
|
|
Ok(pool)
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
async fn setup(app: &mut App) -> Result<(), Box<dyn Error>> {
|
2024-06-01 20:17:50 -04:00
|
|
|
APP.set(app.handle().clone()).unwrap();
|
|
|
|
tray::setup(app)?;
|
2023-09-13 11:06:40 -07:00
|
|
|
// get_or_create_db_path doesn't create the actual db file, just the directory
|
|
|
|
let is_first_launch = !config::get_or_create_db_path()?.exists();
|
2023-05-06 16:56:45 -07:00
|
|
|
let pool = connect_db().await?;
|
2023-09-14 15:04:25 -07:00
|
|
|
let mut setup_errors: Vec<String> = vec![];
|
2023-09-13 11:06:40 -07:00
|
|
|
|
2023-10-09 08:50:31 -07:00
|
|
|
let mut conf = match AppConfig::load(&pool).await {
|
2023-09-13 11:06:40 -07:00
|
|
|
Ok(c) => c,
|
2024-06-16 07:08:10 -04:00
|
|
|
Err(LoadKvError::Invalid(_)) => {
|
2023-09-14 15:04:25 -07:00
|
|
|
setup_errors.push(
|
|
|
|
"Could not load configuration from database. Reverting to defaults.".into()
|
|
|
|
);
|
2023-09-13 11:06:40 -07:00
|
|
|
AppConfig::default()
|
|
|
|
},
|
|
|
|
err => err?,
|
|
|
|
};
|
|
|
|
|
2024-06-19 05:10:55 -04:00
|
|
|
let app_session = AppSession::load(&pool).await?;
|
2024-06-01 20:17:50 -04:00
|
|
|
Server::start(app.handle().clone())?;
|
2023-05-06 16:56:45 -07:00
|
|
|
|
|
|
|
config::set_auto_launch(conf.start_on_login)?;
|
2023-09-14 15:04:25 -07:00
|
|
|
if let Err(_e) = config::set_auto_launch(conf.start_on_login) {
|
|
|
|
setup_errors.push("Error: Failed to manage autolaunch.".into());
|
|
|
|
}
|
2023-10-09 08:50:31 -07:00
|
|
|
|
|
|
|
// if hotkeys fail to register, disable them so that this error doesn't have to keep showing up
|
|
|
|
if let Err(_e) = shortcuts::register_hotkeys(&conf.hotkeys) {
|
|
|
|
conf.hotkeys.disable_all();
|
|
|
|
conf.save(&pool).await?;
|
|
|
|
setup_errors.push("Failed to register hotkeys. Hotkey settings have been disabled.".into());
|
2023-09-14 15:04:25 -07:00
|
|
|
}
|
2023-09-13 11:06:40 -07:00
|
|
|
|
2023-11-09 14:24:44 -08:00
|
|
|
let desktop_is_gnome = std::env::var("XDG_CURRENT_DESKTOP")
|
|
|
|
.map(|names| names.split(':').any(|n| n == "GNOME"))
|
|
|
|
.unwrap_or(false);
|
|
|
|
|
2023-07-11 16:13:20 -07:00
|
|
|
if !conf.start_minimized || is_first_launch {
|
2024-01-26 21:03:45 -08:00
|
|
|
show_main_window(&app.handle())?;
|
2023-05-06 16:56:45 -07:00
|
|
|
}
|
|
|
|
|
2024-06-19 05:10:55 -04:00
|
|
|
let state = AppState::new(conf, app_session, pool, setup_errors, desktop_is_gnome);
|
2023-05-06 16:56:45 -07:00
|
|
|
app.manage(state);
|
2024-01-31 13:14:08 -08:00
|
|
|
|
|
|
|
// make sure we do this after managing app state, so that it doesn't panic
|
2024-06-01 20:17:50 -04:00
|
|
|
start_auto_locker(app.app_handle().clone());
|
2024-01-31 13:14:08 -08:00
|
|
|
|
2023-05-06 16:56:45 -07:00
|
|
|
Ok(())
|
|
|
|
}
|
2024-01-26 21:03:45 -08:00
|
|
|
|
|
|
|
|
2024-01-31 13:14:08 -08:00
|
|
|
fn start_auto_locker(app: AppHandle) {
|
|
|
|
rt::spawn(async move {
|
|
|
|
let state = app.state::<AppState>();
|
|
|
|
loop {
|
|
|
|
// this gives our session-timeout a minimum resolution of 10s, which seems fine?
|
|
|
|
let delay = Duration::from_secs(10);
|
|
|
|
tokio::time::sleep(delay).await;
|
|
|
|
|
|
|
|
if state.should_auto_lock().await {
|
2024-06-03 01:21:43 -04:00
|
|
|
state.lock().await.error_popup("Failed to lock Creddy");
|
2024-01-31 13:14:08 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2024-01-26 21:03:45 -08:00
|
|
|
pub fn show_main_window(app: &AppHandle) -> Result<(), WindowError> {
|
2024-06-01 20:17:50 -04:00
|
|
|
let w = app.get_webview_window("main").ok_or(WindowError::NoMainWindow)?;
|
2024-01-26 21:03:45 -08:00
|
|
|
w.show()?;
|
2024-06-03 01:21:43 -04:00
|
|
|
let show_hide = app.state::<MenuItem<tauri::Wry>>();
|
|
|
|
show_hide.set_text("Hide")?;
|
2024-01-26 21:03:45 -08:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
pub fn hide_main_window(app: &AppHandle) -> Result<(), WindowError> {
|
2024-06-01 20:17:50 -04:00
|
|
|
let w = app.get_webview_window("main").ok_or(WindowError::NoMainWindow)?;
|
2024-01-26 21:03:45 -08:00
|
|
|
w.hide()?;
|
2024-06-03 01:21:43 -04:00
|
|
|
let show_hide = app.state::<MenuItem<tauri::Wry>>();
|
|
|
|
show_hide.set_text("Show")?;
|
2024-01-26 21:03:45 -08:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
pub fn toggle_main_window(app: &AppHandle) -> Result<(), WindowError> {
|
2024-06-01 20:17:50 -04:00
|
|
|
let w = app.get_webview_window("main").ok_or(WindowError::NoMainWindow)?;
|
2024-01-26 21:03:45 -08:00
|
|
|
if w.is_visible()? {
|
|
|
|
hide_main_window(app)
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
show_main_window(app)
|
|
|
|
}
|
|
|
|
}
|