[fractal/fractal-next] auth_dialog: Add AuthError



commit d82b7ed19cb489fc2524bc4c0f82d17059d630f8
Author: Kai A. Hiller <V02460 gmail com>
Date:   Sat Feb 12 08:15:28 2022 +0100

    auth_dialog: Add AuthError

 src/components/auth_dialog.rs                      | 31 +++++++++++++++-------
 src/components/mod.rs                              |  2 +-
 .../account_settings/devices_page/device.rs        | 29 +++++++++-----------
 .../account_settings/devices_page/device_row.rs    | 26 +++++++++++++-----
 .../content/verification/session_verification.rs   | 14 +++++-----
 5 files changed, 60 insertions(+), 42 deletions(-)
---
diff --git a/src/components/auth_dialog.rs b/src/components/auth_dialog.rs
index 04168565..60abf0db 100644
--- a/src/components/auth_dialog.rs
+++ b/src/components/auth_dialog.rs
@@ -64,6 +64,15 @@ impl AuthData {
     }
 }
 
+#[derive(Debug)]
+pub enum AuthError {
+    ServerResponse(Box<Error>),
+    MalformedResponse,
+    StageFailed,
+    NoStageToChoose,
+    UserCancelled,
+}
+
 mod imp {
     use std::cell::RefCell;
 
@@ -223,7 +232,7 @@ impl AuthDialog {
     >(
         &self,
         callback: FN,
-    ) -> Option<Result<Response, Error>> {
+    ) -> Result<Response, AuthError> {
         let priv_ = self.imp();
         let client = self.session().client();
         let mut auth_data = None;
@@ -235,11 +244,11 @@ impl AuthDialog {
             let response = handle.await.unwrap();
 
             let uiaa_info: UiaaInfo = match response {
-                Ok(result) => return Some(Ok(result)),
+                Ok(result) => return Ok(result),
                 Err(Error::Http(UiaaError(FromHttpResponseError::Http(ServerError::Known(
                     UiaaResponse::AuthResponse(uiaa_info),
                 ))))) => uiaa_info,
-                Err(error) => return Some(Err(error)),
+                Err(error) => return Err(AuthError::ServerResponse(Box::new(error))),
             };
 
             self.show_auth_error(&uiaa_info.auth_error);
@@ -248,12 +257,13 @@ impl AuthDialog {
             let flow = uiaa_info
                 .flows
                 .iter()
-                .find(|flow| flow.stages.starts_with(&uiaa_info.completed))?;
+                .find(|flow| flow.stages.starts_with(&uiaa_info.completed))
+                .ok_or(AuthError::NoStageToChoose)?;
 
             match flow.stages[uiaa_info.completed.len()].as_ref() {
                 "m.login.password" => {
                     priv_.stack.set_visible_child_name("m.login.password");
-                    if self.show_and_wait_for_response().await {
+                    if self.show_and_wait_for_response().await.is_ok() {
                         let user_id = self.session().user().unwrap().user_id().to_string();
                         let password = priv_.password.text().to_string();
                         let session = uiaa_info.session;
@@ -277,12 +287,13 @@ impl AuthDialog {
                             spawn_tokio!(async move { client_clone.homeserver().await })
                                 .await
                                 .unwrap();
+                        let first_stage = flow.stages.first();
                         self.setup_fallback_page(
                             homeserver.as_str(),
-                            flow.stages.first()?.as_ref(),
+                            first_stage.ok_or(AuthError::NoStageToChoose)?.as_ref(),
                             &session,
                         );
-                        if self.show_and_wait_for_response().await {
+                        if self.show_and_wait_for_response().await.is_ok() {
                             auth_data =
                                 Some(AuthData::FallbackAcknowledgement(FallbackAcknowledgement {
                                     session,
@@ -294,12 +305,12 @@ impl AuthDialog {
                 }
             }
 
-            return None;
+            return Err(AuthError::UserCancelled);
         }
     }
 
     /// Lets the user complete the current stage.
-    async fn show_and_wait_for_response(&self) -> bool {
+    async fn show_and_wait_for_response(&self) -> Result<(), AuthError> {
         let (sender, receiver) = futures::channel::oneshot::channel();
         let sender = Cell::new(Some(sender));
 
@@ -315,7 +326,7 @@ impl AuthDialog {
         self.disconnect(handler_id);
         self.close();
 
-        result
+        result.then(|| ()).ok_or(AuthError::UserCancelled)
     }
 
     fn show_auth_error(&self, auth_error: &Option<ErrorBody>) {
diff --git a/src/components/mod.rs b/src/components/mod.rs
index 8e7df87c..7ad75e41 100644
--- a/src/components/mod.rs
+++ b/src/components/mod.rs
@@ -16,7 +16,7 @@ mod video_player_renderer;
 
 pub use self::{
     audio_player::AudioPlayer,
-    auth_dialog::{AuthData, AuthDialog},
+    auth_dialog::{AuthData, AuthDialog, AuthError},
     avatar::Avatar,
     badge::Badge,
     context_menu_bin::{ContextMenuBin, ContextMenuBinExt, ContextMenuBinImpl},
diff --git a/src/session/account_settings/devices_page/device.rs 
b/src/session/account_settings/devices_page/device.rs
index a5075ecc..1a6a30c5 100644
--- a/src/session/account_settings/devices_page/device.rs
+++ b/src/session/account_settings/devices_page/device.rs
@@ -1,5 +1,4 @@
 use gtk::{glib, prelude::*, subclass::prelude::*};
-use log::error;
 use matrix_sdk::{
     encryption::identities::Device as CryptoDevice,
     ruma::{
@@ -9,7 +8,10 @@ use matrix_sdk::{
     },
 };
 
-use crate::{components::AuthDialog, session::Session};
+use crate::{
+    components::{AuthDialog, AuthError},
+    session::Session,
+};
 
 mod imp {
     use glib::object::WeakRef;
@@ -173,16 +175,17 @@ impl Device {
             })
     }
 
-    /// Delete the `Device`
-    ///
-    /// Returns `true` for success
-    pub async fn delete(&self, transient_for: Option<&impl IsA<gtk::Window>>) -> bool {
+    /// Deletes the `Device`.
+    pub async fn delete(
+        &self,
+        transient_for: Option<&impl IsA<gtk::Window>>,
+    ) -> Result<(), AuthError> {
         let session = self.session();
         let device_id = self.device_id().to_owned();
 
         let dialog = AuthDialog::new(transient_for, &session);
 
-        let result = dialog
+        dialog
             .authenticate(move |client, auth_data| {
                 let device_id = device_id.clone();
                 async move {
@@ -196,16 +199,8 @@ impl Device {
                     }
                 }
             })
-            .await;
-        match result {
-            Some(Ok(_)) => true,
-            Some(Err(err)) => {
-                // TODO: show error message to the user
-                error!("Failed to delete device: {}", err);
-                false
-            }
-            None => false,
-        }
+            .await?;
+        Ok(())
     }
 
     pub fn is_verified(&self) -> bool {
diff --git a/src/session/account_settings/devices_page/device_row.rs 
b/src/session/account_settings/devices_page/device_row.rs
index 3525b58e..e5682af7 100644
--- a/src/session/account_settings/devices_page/device_row.rs
+++ b/src/session/account_settings/devices_page/device_row.rs
@@ -1,8 +1,13 @@
+use adw::{self, prelude::*};
 use gettextrs::gettext;
-use gtk::{glib, glib::clone, prelude::*, subclass::prelude::*, CompositeTemplate};
+use gtk::{glib, glib::clone, subclass::prelude::*, CompositeTemplate};
+use log::error;
 
 use super::Device;
-use crate::{components::SpinnerButton, spawn};
+use crate::{
+    components::{AuthError, SpinnerButton},
+    spawn,
+};
 
 mod imp {
     use std::cell::RefCell;
@@ -155,12 +160,19 @@ impl DeviceRow {
         if let Some(device) = self.device() {
             spawn!(clone!(@weak self as obj => async move {
                 let window: Option<gtk::Window> = obj.root().and_then(|root| root.downcast().ok());
-                let success = device.delete(window.as_ref()).await;
-                obj.imp().delete_button.set_loading(false);
-
-                if success {
-                    obj.hide();
+                match device.delete(window.as_ref()).await {
+                    Ok(_) => obj.hide(),
+                    Err(AuthError::UserCancelled) => {},
+                    Err(err) => {
+                        error!("Failed to delete device {}: {err:?}", device.device_id());
+                        if let Some(adw_window) = window.and_then(|w| 
w.downcast::<adw::PreferencesWindow>().ok()) {
+                            let device_name = device.display_name();
+                            let error_message = gettext!("Failed to delete device “{}”", device_name);
+                            adw_window.add_toast(&adw::Toast::new(&error_message));
+                        }
+                    },
                 }
+                obj.imp().delete_button.set_loading(false);
             }));
         }
     }
diff --git a/src/session/content/verification/session_verification.rs 
b/src/session/content/verification/session_verification.rs
index 522c6b4d..3dc72ab0 100644
--- a/src/session/content/verification/session_verification.rs
+++ b/src/session/content/verification/session_verification.rs
@@ -5,7 +5,7 @@ use log::error;
 
 use super::IdentityVerificationWidget;
 use crate::{
-    components::{AuthDialog, SpinnerButton},
+    components::{AuthDialog, AuthError, SpinnerButton},
     session::verification::{IdentityVerification, VerificationState},
     spawn, Error, Session, Window,
 };
@@ -303,15 +303,15 @@ impl SessionVerification {
 
 
             let error_message = match result {
-                Some(Ok(_)) => None,
-                Some(Err(error)) => {
-                    error!("Failed to bootstrap cross-signing: {}", error);
-                    Some(gettext("An error occurred during the creation of the encryption keys."))
-                },
-                None => {
+                Ok(_) => None,
+                Err(AuthError::UserCancelled) => {
                     error!("Failed to bootstrap cross-signing: User cancelled the authentication");
                     Some(gettext("You cancelled the authentication needed to create the encryption keys."))
                 },
+                Err(error) => {
+                    error!("Failed to bootstrap cross-signing: {:?}", error);
+                    Some(gettext("An error occurred during the creation of the encryption keys."))
+                },
             };
 
             if let Some(error_message) = error_message {


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]