From 04fbed2c3ac08500f3162bd0fdbbcf85e29d3b48 Mon Sep 17 00:00:00 2001 From: Alexander Rosenberg Date: Wed, 25 Oct 2023 02:55:33 -0700 Subject: [PATCH] Work on logging out --- simplelogin-gui/data/settings_panel.blp | 2 + simplelogin-gui/src/application.rs | 36 +++++++++++--- simplelogin-gui/src/settings_panel.rs | 66 ++++++++++++++++++++++++- simplelogin/src/lib.rs | 22 +++++++++ 4 files changed, 118 insertions(+), 8 deletions(-) diff --git a/simplelogin-gui/data/settings_panel.blp b/simplelogin-gui/data/settings_panel.blp index 34ef259..857c1ea 100644 --- a/simplelogin-gui/data/settings_panel.blp +++ b/simplelogin-gui/data/settings_panel.blp @@ -104,6 +104,7 @@ template $SettingsPanel : Box { halign: start; label: "Logout"; valign: center; + clicked => $logout_clicked() swapped; layout { column: "2"; @@ -231,6 +232,7 @@ template $SettingsPanel : Box { halign: center; icon-name: "window-close-symbolic"; valign: end; + clicked => $close_error_clicked() swapped; styles [ "loading-label", diff --git a/simplelogin-gui/src/application.rs b/simplelogin-gui/src/application.rs index 9de1610..6f02428 100644 --- a/simplelogin-gui/src/application.rs +++ b/simplelogin-gui/src/application.rs @@ -48,25 +48,39 @@ mod imp { login_window.present(); } + fn close_window_for_widget>(widget: &P) { + if let Some(win) = get_window_ancestor(widget) { + win.close(); + } + } + fn activate_has_api_key(&self) { let obj = &self.obj(); let main_panel = MainPanel::new(obj); - let settings_panel = SettingsPanel::new(obj); + let settings_panel: gtk::Widget = SettingsPanel::new(obj).upcast(); let window = TabbedWindow::new(obj, &[("Aliases", &main_panel.upcast()), - ("Settings", &settings_panel.clone().upcast())]); - let window_closure = window.clone(); + ("Settings", &settings_panel)]); settings_panel.connect_closure( "api-key-cleared", false, closure_local!(@weak-allow-none self as opt_this - => move |_: SettingsPanel| { - window_closure.close(); + => move |panel: SettingsPanel| { + Self::close_window_for_widget(&panel); if let Some(this) = opt_this { - this.context.borrow_mut().set_api_key(None); this.set_api_key(None); this.activate_no_api_key(); } })); + settings_panel.connect_closure( + "logged-out", false, + closure_local!(@weak-allow-none self as opt_this + => move |panel: SettingsPanel| { + Self::close_window_for_widget(&panel); + if let Some(this) = opt_this { + this.set_api_key(None); + this.activate_no_api_key(); + } + })); window.present(); } } @@ -130,3 +144,13 @@ impl Default for Application { Self::new(crate::APP_ID) } } + +pub fn get_window_ancestor>( + widget: &P +) -> Option { + let root_opt = widget.root()?; + match root_opt.downcast::() { + Ok(win) => Some(win), + Err(_) => None, + } +} diff --git a/simplelogin-gui/src/settings_panel.rs b/simplelogin-gui/src/settings_panel.rs index 341b3c4..90eae90 100644 --- a/simplelogin-gui/src/settings_panel.rs +++ b/simplelogin-gui/src/settings_panel.rs @@ -4,6 +4,7 @@ use gtk::{glib, prelude::*, subclass::prelude::*}; mod imp { use super::*; + use glib::clone; use std::cell::RefCell; use glib::subclass::Signal; use once_cell::sync::Lazy; @@ -60,8 +61,69 @@ mod imp { impl SettingsPanel { #[template_callback] fn clear_api_key_clicked(&self, _: >k::Button) { - // TODO ensure user is prompted before clear - self.obj().emit_by_name::<()>("api-key-cleared", &[]); + let main_loop = glib::MainContext::default(); + main_loop.spawn_local(clone!(@weak self as this => async move { + let alert_diag = gtk::AlertDialog::builder() + .message("Are you sure you would like to clear the API key?") + .buttons(["Yes", "No"]).build(); + let widget_this = application::get_window_ancestor( + &this.obj().clone().upcast::()); + let resp = alert_diag.choose_future(widget_this.as_ref()).await; + if let Ok(resp_num) = resp { + if resp_num == 0 { + let app = this.application.borrow(); + app.context().borrow_mut().set_api_key(None); + this.obj().emit_by_name::<()>("api-key-cleared", &[]); + } + } + })); + } + + fn set_is_loading(&self, loading: bool) { + if loading { + self.set_error(None); + } + self.loading_overlay.get().set_visible(loading); + self.content_wrapper.get().set_sensitive(!loading); + } + + fn set_error(&self, error: Option<&str>) { + if let Some(e) = error { + self.set_is_loading(false); + self.error_label.get().set_text(e); + } + self.content_wrapper.get().set_sensitive(error.is_none()); + self.error_overlay.get().set_visible(error.is_some()); + } + + #[template_callback] + fn logout_clicked(&self, _: >k::Button) { + let main_loop = glib::MainContext::default(); + main_loop.spawn_local(clone!(@weak self as this => async move { + let alert_diag = gtk::AlertDialog::builder() + .message("Are you sure you would like to log out?") + .buttons(["Yes", "No"]).build(); + let widget_this = application::get_window_ancestor( + &this.obj().clone().upcast::()); + let resp = alert_diag.choose_future(widget_this.as_ref()).await; + if let Ok(resp_num) = resp { + if resp_num == 0 { + let app = this.application.borrow(); + let mut context = app.context().borrow_mut(); + let res = context.logout().await; + drop(context); + match res { + Ok(_) => this.obj().emit_by_name::<()>("logged-out", &[]), + Err(err) => this.set_error(Some(err.to_string().as_str())), + } + } + } + })); + } + + #[template_callback] + fn close_error_clicked(&self, _: >k::Button) { + self.set_error(None); } } diff --git a/simplelogin/src/lib.rs b/simplelogin/src/lib.rs index a71e657..3b044ba 100644 --- a/simplelogin/src/lib.rs +++ b/simplelogin/src/lib.rs @@ -161,6 +161,16 @@ impl Context { } } + async fn get_request( + &mut self, endpoint: &str, headers: &[(&str, &str)] + ) -> Result { + let mut builder = self.client.get(self.url.to_owned() + endpoint); + for header in headers { + builder = builder.header(header.0, header.1); + } + Self::perform_request(builder).await + } + async fn post_request( &mut self, endpoint: &str, body: &str ) -> Result { @@ -228,4 +238,16 @@ impl Context { Err(Error::new(ErrorKind::State, "No login operation in progress")) } } + + pub async fn logout(&mut self) -> Result<(), Error> { + match self.api_key.as_ref() { + Some(api_key) => { + self.get_request("api/logout", + &[("Authentication", api_key.clone().as_str())]).await?; + self.api_key = None; + Ok(()) + }, + None => Err(Error::new(ErrorKind::State, "Not logged in")), + } + } }