keep working on cli shortcuts, unify visibility management
This commit is contained in:
		@@ -19,6 +19,7 @@ use crate::{
 | 
			
		||||
    ipc,
 | 
			
		||||
    server::Server,
 | 
			
		||||
    errors::*,
 | 
			
		||||
    shortcuts,
 | 
			
		||||
    state::AppState,
 | 
			
		||||
    tray,
 | 
			
		||||
};
 | 
			
		||||
@@ -99,7 +100,7 @@ async fn setup(app: &mut App) -> Result<(), Box<dyn Error>> {
 | 
			
		||||
    if let Err(_e) = config::set_auto_launch(conf.start_on_login) {
 | 
			
		||||
        setup_errors.push("Error: Failed to manage autolaunch.".into());
 | 
			
		||||
    }
 | 
			
		||||
    if let Err(e) = config::register_hotkeys(&conf.hotkeys) {
 | 
			
		||||
    if let Err(e) = shortcuts::register_hotkeys(&conf.hotkeys) {
 | 
			
		||||
        setup_errors.push(format!("{e}"));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -21,7 +21,8 @@ fn main() {
 | 
			
		||||
        None | Some(("run", _)) => launch_gui(),
 | 
			
		||||
        Some(("get", m)) => cli::get(m),
 | 
			
		||||
        Some(("exec", m)) => cli::exec(m),
 | 
			
		||||
        _ => unreachable!(),
 | 
			
		||||
        Some(("shortcut", m)) => cli::invoke_shortcut(m),
 | 
			
		||||
        _ => unreachable!("Unknown subcommand"),
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    if let Err(e) = res {
 | 
			
		||||
 
 | 
			
		||||
@@ -6,13 +6,15 @@ use clap::{
 | 
			
		||||
    Command,
 | 
			
		||||
    Arg,
 | 
			
		||||
    ArgMatches,
 | 
			
		||||
     ArgAction
 | 
			
		||||
    ArgAction,
 | 
			
		||||
    builder::PossibleValuesParser,
 | 
			
		||||
 };
 | 
			
		||||
use tokio::io::{AsyncReadExt, AsyncWriteExt};
 | 
			
		||||
 | 
			
		||||
use crate::credentials::Credentials;
 | 
			
		||||
use crate::errors::*;
 | 
			
		||||
use crate::server::{Request, Response};
 | 
			
		||||
use crate::shortcuts::ShortcutAction;
 | 
			
		||||
 | 
			
		||||
#[cfg(unix)]
 | 
			
		||||
use {
 | 
			
		||||
@@ -63,6 +65,16 @@ pub fn parser() -> Command<'static> {
 | 
			
		||||
                        .multiple_values(true)
 | 
			
		||||
                )
 | 
			
		||||
        )
 | 
			
		||||
        .subcommand(
 | 
			
		||||
            Command::new("shortcut")
 | 
			
		||||
                .about("Invoke an action normally trigged by hotkey (e.g. launch terminal)")
 | 
			
		||||
                .arg(
 | 
			
		||||
                    Arg::new("action")
 | 
			
		||||
                        .value_parser(
 | 
			
		||||
                            PossibleValuesParser::new(["show_window", "launch_terminal"])
 | 
			
		||||
                        )
 | 
			
		||||
                )
 | 
			
		||||
        )
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -129,10 +141,35 @@ pub fn exec(args: &ArgMatches) -> Result<(), CliError> {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#[tokio::main]
 | 
			
		||||
async fn get_credentials(base: bool) -> Result<Credentials, RequestError> {
 | 
			
		||||
pub fn invoke_shortcut(args: &ArgMatches) -> Result<(), CliError> {
 | 
			
		||||
    let action = match args.get_one::<String>("action").map(|s| s.as_str()) {
 | 
			
		||||
        Some("show_window") => ShortcutAction::ShowWindow,
 | 
			
		||||
        Some("launch_terminal") => ShortcutAction::LaunchTerminal,
 | 
			
		||||
        Some(&_) | None => unreachable!("Unknown shortcut action"), // guaranteed by clap
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    let req = Request::InvokeShortcut(action);
 | 
			
		||||
    match make_request(&req) {
 | 
			
		||||
        Ok(Response::Empty) => Ok(()),
 | 
			
		||||
        Ok(r) => Err(RequestError::Unexpected(r).into()),
 | 
			
		||||
        Err(e) => Err(e.into()),
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
fn get_credentials(base: bool) -> Result<Credentials, RequestError> {
 | 
			
		||||
    let req = Request::GetAwsCredentials { base };
 | 
			
		||||
    let mut data = serde_json::to_string(&req).unwrap();
 | 
			
		||||
    match make_request(&req) {
 | 
			
		||||
        Ok(Response::Aws(creds)) => Ok(creds),
 | 
			
		||||
        Ok(r) => Err(RequestError::Unexpected(r)),
 | 
			
		||||
        Err(e) => Err(e),
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#[tokio::main]
 | 
			
		||||
async fn make_request(req: &Request) -> Result<Response, RequestError> {
 | 
			
		||||
    let mut data = serde_json::to_string(req).unwrap();
 | 
			
		||||
    // server expects newline marking end of request
 | 
			
		||||
    data.push('\n');
 | 
			
		||||
 | 
			
		||||
@@ -142,12 +179,7 @@ async fn get_credentials(base: bool) -> Result<Credentials, RequestError> {
 | 
			
		||||
    let mut buf = Vec::with_capacity(1024);
 | 
			
		||||
    stream.read_to_end(&mut buf).await?;
 | 
			
		||||
    let res: Result<Response, ServerError> = serde_json::from_slice(&buf)?;
 | 
			
		||||
    match res {
 | 
			
		||||
        Ok(Response::Aws(creds)) => Ok(creds),
 | 
			
		||||
        // Eventually we will want this
 | 
			
		||||
        // Ok(r) => Err(RequestError::Unexpected(r)),
 | 
			
		||||
        Err(e) => Err(RequestError::Server(e)),
 | 
			
		||||
    }
 | 
			
		||||
    Ok(res?)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -26,12 +26,14 @@ use serde::{
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
pub trait ErrorPopup {
 | 
			
		||||
pub trait ShowError {
 | 
			
		||||
    fn error_popup(self, title: &str);
 | 
			
		||||
    fn error_popup_nowait(self, title: &str);
 | 
			
		||||
    fn error_print(self);
 | 
			
		||||
    fn error_print_prefix(self, prefix: &str);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<E: std::fmt::Display> ErrorPopup for Result<(), E> {
 | 
			
		||||
impl<E: std::fmt::Display> ShowError for Result<(), E> {
 | 
			
		||||
    fn error_popup(self, title: &str) {
 | 
			
		||||
        if let Err(e) = self {
 | 
			
		||||
            let (tx, rx) = mpsc::channel();
 | 
			
		||||
@@ -50,6 +52,18 @@ impl<E: std::fmt::Display> ErrorPopup for Result<(), E> {
 | 
			
		||||
                .show(|_| {})
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn error_print(self) {
 | 
			
		||||
        if let Err(e) = self {
 | 
			
		||||
            eprintln!("{e}");
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn error_print_prefix(self, prefix: &str) {
 | 
			
		||||
        if let Err(e) = self {
 | 
			
		||||
            eprintln!("{prefix}: {e}");
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -164,6 +178,15 @@ pub enum HandlerError {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, ThisError, AsRefStr)]
 | 
			
		||||
pub enum WindowError {
 | 
			
		||||
    #[error("Failed to find main application window")]
 | 
			
		||||
    NoMainWindow,
 | 
			
		||||
    #[error(transparent)]
 | 
			
		||||
    ManageFailure(#[from] tauri::Error),
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, ThisError, AsRefStr)]
 | 
			
		||||
pub enum GetCredentialsError {
 | 
			
		||||
    #[error("Credentials are currently locked")]
 | 
			
		||||
@@ -324,6 +347,7 @@ impl Serialize for SerializeWrapper<&GetSessionTokenError> {
 | 
			
		||||
impl_serialize_basic!(SetupError);
 | 
			
		||||
impl_serialize_basic!(GetCredentialsError);
 | 
			
		||||
impl_serialize_basic!(ClientInfoError);
 | 
			
		||||
impl_serialize_basic!(WindowError);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
impl Serialize for HandlerError {
 | 
			
		||||
 
 | 
			
		||||
@@ -7,5 +7,6 @@ mod clientinfo;
 | 
			
		||||
mod ipc;
 | 
			
		||||
mod state;
 | 
			
		||||
mod server;
 | 
			
		||||
mod shortcuts;
 | 
			
		||||
mod terminal;
 | 
			
		||||
mod tray;
 | 
			
		||||
 
 | 
			
		||||
@@ -6,7 +6,7 @@
 | 
			
		||||
use creddy::{
 | 
			
		||||
    app,
 | 
			
		||||
    cli,
 | 
			
		||||
    errors::ErrorPopup,
 | 
			
		||||
    errors::ShowError,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,3 @@
 | 
			
		||||
use std::time::Duration;
 | 
			
		||||
 | 
			
		||||
#[cfg(windows)]
 | 
			
		||||
use tokio::net::windows::named_pipe::{
 | 
			
		||||
    NamedPipeServer,
 | 
			
		||||
@@ -21,6 +19,7 @@ use crate::clientinfo::{self, Client};
 | 
			
		||||
use crate::credentials::Credentials;
 | 
			
		||||
use crate::ipc::{Approval, AwsRequestNotification};
 | 
			
		||||
use crate::state::AppState;
 | 
			
		||||
use crate::shortcuts::{self, ShortcutAction};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#[derive(Serialize, Deserialize)]
 | 
			
		||||
@@ -28,12 +27,14 @@ pub enum Request {
 | 
			
		||||
    GetAwsCredentials{ 
 | 
			
		||||
        base: bool,
 | 
			
		||||
    },
 | 
			
		||||
    InvokeShortcut(ShortcutAction),
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Serialize, Deserialize)]
 | 
			
		||||
pub enum Response {
 | 
			
		||||
    Aws(Credentials)
 | 
			
		||||
    Aws(Credentials),
 | 
			
		||||
    Empty,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -102,17 +103,25 @@ async fn handle(stream: &mut NamedPipeServer, app_handle: AppHandle) -> Result<R
 | 
			
		||||
    let req: Request = serde_json::from_slice(&buf)?;
 | 
			
		||||
    match req {
 | 
			
		||||
        Request::GetAwsCredentials{ base } => get_aws_credentials(base, client, app_handle).await,
 | 
			
		||||
        // etc
 | 
			
		||||
        Request::InvokeShortcut(action) => invoke_shortcut(action).await,
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
async fn invoke_shortcut(action: ShortcutAction) -> Result<Response, HandlerError> {
 | 
			
		||||
    shortcuts::exec_shortcut(action);
 | 
			
		||||
    Ok(Response::Empty)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
async fn get_aws_credentials(base: bool, client: Client, app_handle: AppHandle) -> Result<Response, HandlerError> {
 | 
			
		||||
    let state = app_handle.state::<AppState>();
 | 
			
		||||
 | 
			
		||||
    let main_window = app_handle.get_window("main").ok_or(HandlerError::NoMainWindow)?;
 | 
			
		||||
    let is_currently_visible = main_window.is_visible()?;
 | 
			
		||||
    let rehide_after = state.get_or_set_rehide(!is_currently_visible).await;
 | 
			
		||||
    let rehide_ms = {
 | 
			
		||||
        let config = state.config.read().await;
 | 
			
		||||
        config.rehide_ms
 | 
			
		||||
    };
 | 
			
		||||
    let lease = state.acquire_visibility_lease(rehide_ms).await
 | 
			
		||||
        .map_err(|_e| HandlerError::NoMainWindow)?; // automate this conversion eventually?
 | 
			
		||||
 | 
			
		||||
    let (chan_send, chan_recv) = oneshot::channel();
 | 
			
		||||
    let request_id = state.register_request(chan_send).await;
 | 
			
		||||
@@ -124,12 +133,6 @@ async fn get_aws_credentials(base: bool, client: Client, app_handle: AppHandle)
 | 
			
		||||
        let notification = AwsRequestNotification {id: request_id, client, base};
 | 
			
		||||
        app_handle.emit_all("credentials-request", ¬ification)?;
 | 
			
		||||
 | 
			
		||||
        if !main_window.is_visible()? {
 | 
			
		||||
            main_window.unminimize()?;
 | 
			
		||||
            main_window.show()?;
 | 
			
		||||
        }
 | 
			
		||||
        main_window.set_focus()?;
 | 
			
		||||
 | 
			
		||||
        match chan_recv.await {
 | 
			
		||||
            Ok(Approval::Approved) => {
 | 
			
		||||
                if base {
 | 
			
		||||
@@ -154,31 +157,6 @@ async fn get_aws_credentials(base: bool, client: Client, app_handle: AppHandle)
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    rt::spawn(
 | 
			
		||||
        handle_rehide(rehide_after, app_handle.app_handle())
 | 
			
		||||
    );
 | 
			
		||||
    lease.release();
 | 
			
		||||
    result
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
async fn handle_rehide(rehide_after: bool, app_handle: AppHandle) {
 | 
			
		||||
    let state = app_handle.state::<AppState>();
 | 
			
		||||
    let delay = {
 | 
			
		||||
        let config = state.config.read().await;
 | 
			
		||||
        Duration::from_millis(config.rehide_ms)
 | 
			
		||||
    };
 | 
			
		||||
    tokio::time::sleep(delay).await;
 | 
			
		||||
 | 
			
		||||
    // if there are no other pending requests, set rehide status back to None
 | 
			
		||||
    if state.req_count().await == 0 {
 | 
			
		||||
        state.clear_rehide().await;
 | 
			
		||||
        // and hide the window if necessary
 | 
			
		||||
        if rehide_after {
 | 
			
		||||
            app_handle.get_window("main").map(|w| {
 | 
			
		||||
                if let Err(e) = w.hide() {
 | 
			
		||||
                    eprintln!("{e}");
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,12 +1,14 @@
 | 
			
		||||
use serde::{Serialize, Deserialize};
 | 
			
		||||
 | 
			
		||||
use tauri::{
 | 
			
		||||
    AppHandle,
 | 
			
		||||
    GlobalShortcutManager,
 | 
			
		||||
    Manager,
 | 
			
		||||
    async_runtime as rt,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
use crate::app::APP;
 | 
			
		||||
use crate::config::HotkeysConfig;
 | 
			
		||||
use crate::errors::*;
 | 
			
		||||
use crate::terminal;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -19,11 +21,18 @@ pub enum ShortcutAction {
 | 
			
		||||
 | 
			
		||||
pub fn exec_shortcut(action: ShortcutAction) {
 | 
			
		||||
    match action {
 | 
			
		||||
        ShowWindow => {
 | 
			
		||||
        ShortcutAction::ShowWindow => {
 | 
			
		||||
            let app = APP.get().unwrap();
 | 
			
		||||
            app.get_window("main").map(|w| w.show());
 | 
			
		||||
            app.get_window("main")
 | 
			
		||||
                .ok_or("Couldn't find application main window")
 | 
			
		||||
                .map(|w| w.show().error_popup("Failed to show window"))
 | 
			
		||||
                .error_popup("Failed to show window");
 | 
			
		||||
        },
 | 
			
		||||
        ShortcutAction::LaunchTerminal => {
 | 
			
		||||
            rt::spawn(async {
 | 
			
		||||
                terminal::launch(false).await.error_popup("Failed to launch terminal");
 | 
			
		||||
            });
 | 
			
		||||
        },
 | 
			
		||||
        LaunchTerminal => terminal::launch(false),
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -35,7 +44,7 @@ pub fn register_hotkeys(hotkeys: &HotkeysConfig) -> tauri::Result<()> {
 | 
			
		||||
 | 
			
		||||
    if hotkeys.show_window.enabled {
 | 
			
		||||
        manager.register(
 | 
			
		||||
            hotkeys.show_window.keys,
 | 
			
		||||
            &hotkeys.show_window.keys,
 | 
			
		||||
            || exec_shortcut(ShortcutAction::ShowWindow)
 | 
			
		||||
        )?;
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -1,10 +1,15 @@
 | 
			
		||||
use std::collections::HashMap;
 | 
			
		||||
use std::time::Duration;
 | 
			
		||||
 | 
			
		||||
use tokio::{
 | 
			
		||||
    sync::RwLock,
 | 
			
		||||
    sync::oneshot::Sender,
 | 
			
		||||
    sync::oneshot::{self, Sender},
 | 
			
		||||
};
 | 
			
		||||
use sqlx::SqlitePool;
 | 
			
		||||
use tauri::{
 | 
			
		||||
    Manager,
 | 
			
		||||
    async_runtime as rt,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
use crate::credentials::{
 | 
			
		||||
    Session,
 | 
			
		||||
@@ -14,6 +19,73 @@ use crate::credentials::{
 | 
			
		||||
use crate::{config, config::AppConfig};
 | 
			
		||||
use crate::ipc::{self, Approval};
 | 
			
		||||
use crate::errors::*;
 | 
			
		||||
use crate::shortcuts;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#[derive(Debug)]
 | 
			
		||||
struct Visibility {
 | 
			
		||||
    leases: usize,
 | 
			
		||||
    original: Option<bool>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Visibility {
 | 
			
		||||
    fn new() -> Self {
 | 
			
		||||
        Visibility { leases: 0, original: None }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn acquire(&mut self, delay_ms: u64) -> Result<VisibilityLease, WindowError> {
 | 
			
		||||
        let app = crate::app::APP.get().unwrap();
 | 
			
		||||
        let window = app.get_window("main")
 | 
			
		||||
            .ok_or(WindowError::NoMainWindow)?;
 | 
			
		||||
 | 
			
		||||
        self.leases += 1;
 | 
			
		||||
        if self.original.is_none() {
 | 
			
		||||
            let is_visible = window.is_visible()?;
 | 
			
		||||
            self.original = Some(is_visible);
 | 
			
		||||
            if !is_visible {
 | 
			
		||||
                window.show()?;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        window.set_focus()?;
 | 
			
		||||
 | 
			
		||||
        let (tx, rx) = oneshot::channel();
 | 
			
		||||
        let lease = VisibilityLease { notify: tx };
 | 
			
		||||
 | 
			
		||||
        let delay = Duration::from_millis(delay_ms);
 | 
			
		||||
        let handle = app.app_handle();
 | 
			
		||||
        rt::spawn(async move {
 | 
			
		||||
            // We don't care if it's an error; lease being dropped should be handled identically
 | 
			
		||||
            let _ = rx.await;
 | 
			
		||||
            tokio::time::sleep(delay).await;
 | 
			
		||||
            // we can't use `self` here because we would have to move it into the async block
 | 
			
		||||
            let state = handle.state::<AppState>();
 | 
			
		||||
            let mut visibility = state.visibility.write().await;
 | 
			
		||||
            visibility.leases -= 1;
 | 
			
		||||
            if visibility.leases == 0 {
 | 
			
		||||
                if let Some(false) = visibility.original {
 | 
			
		||||
                    window.hide().error_print();
 | 
			
		||||
                }
 | 
			
		||||
                visibility.original = None;
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        Ok(lease)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub struct VisibilityLease {
 | 
			
		||||
    notify: Sender<()>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl VisibilityLease {
 | 
			
		||||
    pub fn release(self) {
 | 
			
		||||
        rt::spawn(async move {
 | 
			
		||||
            if let Err(_) = self.notify.send(()) {
 | 
			
		||||
                eprintln!("Error releasing visibility lease")
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#[derive(Debug)]
 | 
			
		||||
@@ -22,11 +94,11 @@ pub struct AppState {
 | 
			
		||||
    pub session: RwLock<Session>,
 | 
			
		||||
    pub request_count: RwLock<u64>,
 | 
			
		||||
    pub waiting_requests: RwLock<HashMap<u64, Sender<Approval>>>,
 | 
			
		||||
    pub current_rehide_status: RwLock<Option<bool>>,
 | 
			
		||||
    pub pending_terminal_request: RwLock<bool>,
 | 
			
		||||
    // setup_errors is never modified and so doesn't need to be wrapped in RwLock
 | 
			
		||||
    pub setup_errors: Vec<String>,
 | 
			
		||||
    pool: sqlx::SqlitePool,
 | 
			
		||||
    visibility: RwLock<Visibility>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl AppState {
 | 
			
		||||
@@ -41,10 +113,10 @@ impl AppState {
 | 
			
		||||
            session: RwLock::new(session),
 | 
			
		||||
            request_count: RwLock::new(0),
 | 
			
		||||
            waiting_requests: RwLock::new(HashMap::new()),
 | 
			
		||||
            current_rehide_status: RwLock::new(None),
 | 
			
		||||
            pending_terminal_request: RwLock::new(false),
 | 
			
		||||
            setup_errors,
 | 
			
		||||
            pool,
 | 
			
		||||
            visibility: RwLock::new(Visibility::new()),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -69,7 +141,7 @@ impl AppState {
 | 
			
		||||
        if new_config.hotkeys.show_window != live_config.hotkeys.show_window
 | 
			
		||||
            || new_config.hotkeys.launch_terminal != live_config.hotkeys.launch_terminal
 | 
			
		||||
        {
 | 
			
		||||
            config::register_hotkeys(&new_config.hotkeys)?;
 | 
			
		||||
            shortcuts::register_hotkeys(&new_config.hotkeys)?;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        new_config.save(&self.pool).await?;
 | 
			
		||||
@@ -94,25 +166,9 @@ impl AppState {
 | 
			
		||||
        waiting_requests.remove(&id);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub async fn req_count(&self) -> usize {
 | 
			
		||||
        let waiting_requests = self.waiting_requests.read().await;
 | 
			
		||||
        waiting_requests.len()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub async fn get_or_set_rehide(&self, new_value: bool) -> bool {
 | 
			
		||||
        let mut rehide = self.current_rehide_status.write().await;
 | 
			
		||||
        match *rehide {
 | 
			
		||||
            Some(original) => original,
 | 
			
		||||
            None => {
 | 
			
		||||
                *rehide = Some(new_value);
 | 
			
		||||
                new_value
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub async fn clear_rehide(&self) {
 | 
			
		||||
        let mut rehide = self.current_rehide_status.write().await;
 | 
			
		||||
        *rehide = None;
 | 
			
		||||
    pub async fn acquire_visibility_lease(&self, delay: u64) -> Result<VisibilityLease, WindowError> {
 | 
			
		||||
        let mut visibility = self.visibility.write().await;
 | 
			
		||||
        visibility.acquire(delay)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub async fn send_response(&self, response: ipc::RequestResponse) -> Result<(), SendResponseError> {
 | 
			
		||||
 
 | 
			
		||||
@@ -26,13 +26,8 @@ pub async fn launch(use_base: bool) -> Result<(), LaunchTerminalError> {
 | 
			
		||||
    // if session is unlocked or empty, wait for credentials from frontend
 | 
			
		||||
    if !state.is_unlocked().await {
 | 
			
		||||
        app.emit_all("launch-terminal-request", ())?;
 | 
			
		||||
        let window = app.get_window("main")
 | 
			
		||||
            .ok_or(LaunchTerminalError::NoMainWindow)?;
 | 
			
		||||
        if !window.is_visible()? {
 | 
			
		||||
            window.unminimize()?;
 | 
			
		||||
            window.show()?;
 | 
			
		||||
        }
 | 
			
		||||
        window.set_focus()?;
 | 
			
		||||
        let lease = state.acquire_visibility_lease(0).await
 | 
			
		||||
            .map_err(|_e| LaunchTerminalError::NoMainWindow)?; // automate conversion eventually?
 | 
			
		||||
 | 
			
		||||
        let (tx, rx) = tokio::sync::oneshot::channel();
 | 
			
		||||
        app.once_global("credentials-event", move |e| {
 | 
			
		||||
@@ -47,6 +42,7 @@ pub async fn launch(use_base: bool) -> Result<(), LaunchTerminalError> {
 | 
			
		||||
            state.unregister_terminal_request().await;
 | 
			
		||||
            return Ok(()); // request was canceled by user
 | 
			
		||||
        }
 | 
			
		||||
        lease.release();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // more lock-management
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user