[fractal/fractal-next] login: add specific error logins based on login with password



commit 9636f746ae54e66e3104715a90723fc448689dfe
Author: giusdp <depalma gsp gmail com>
Date:   Mon Jul 12 19:20:48 2021 +0200

    login: add specific error logins based on login with password
    
    Add LoginError struct to model the possible error results from login
    with password and implement From<matrix_sdk::Error> to map the session.get_error
    to a LoginError and get the relevant display message to show in the login screen.
    
    Fixes https://gitlab.gnome.org/GNOME/fractal/-/issues/729

 src/login.rs       | 69 ++++++++++++++++++++++++++++++++++++++++++++++++++++--
 src/session/mod.rs |  5 ++--
 2 files changed, 70 insertions(+), 4 deletions(-)
---
diff --git a/src/login.rs b/src/login.rs
index 3c04239e..98a7bdc9 100644
--- a/src/login.rs
+++ b/src/login.rs
@@ -7,6 +7,8 @@ use gtk::subclass::prelude::*;
 use gtk::{self, prelude::*};
 use gtk::{glib, glib::clone, CompositeTemplate};
 use log::debug;
+use std::fmt;
+use std::time::Duration;
 use url::{ParseError, Url};
 
 mod imp {
@@ -128,8 +130,7 @@ impl Login {
         session.connect_prepared(clone!(@weak self as obj, @strong session => move |_| {
             if let Some(error) = session.get_error() {
                 let error_message = &imp::Login::from_instance(&obj).error_message;
-                // TODO: show more specific error
-                error_message.set_text(&gettext("⚠️ The Login failed."));
+                error_message.set_text(&error.to_string());
                 error_message.show();
                 debug!("Failed to create a new session: {:?}", error);
 
@@ -201,3 +202,67 @@ fn build_homeserver_url(server: &str) -> Result<Url, ParseError> {
         Url::parse(&format!("https://{}";, server))
     }
 }
+
+#[derive(Debug)]
+pub enum LoginError {
+    ServerNotFound,
+    Forbidden,
+    UserDeactivated,
+    LimitExceeded(Option<Duration>),
+    Unknown(String),
+}
+
+impl fmt::Display for LoginError {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        let error_msg = match &self {
+            LoginError::ServerNotFound => gettext("⚠️ Homeserver not found."),
+            LoginError::Forbidden => gettext("⚠️ Invalid credentials."),
+            LoginError::UserDeactivated => gettext("⚠️ The user is deactivated."),
+            LoginError::LimitExceeded(retry_ms) => {
+                if let Some(ms) = retry_ms {
+                    gettext(format!(
+                        "⚠️ Exceeded rate limit, retry in {} seconds.",
+                        ms.as_secs()
+                    ))
+                } else {
+                    gettext("⚠️ Exceeded rate limit, try again later.")
+                }
+            }
+            LoginError::Unknown(info) => {
+                debug!("Unknown error occurred during login: {}", info);
+                gettext("⚠️ Login Failed! Unknown error.")
+            }
+        };
+        f.write_str(&error_msg)
+    }
+}
+
+impl From<matrix_sdk::Error> for LoginError {
+    /// Transform a matrix_sdk error into a LoginError based on the login with password
+    /// Logging in can result in the following errors:
+    /// M_FORBIDDEN: The provided authentication data was incorrect.
+    /// M_USER_DEACTIVATED: The user has been deactivated.
+    /// M_LIMIT_EXCEEDED: This request was rate-limited.
+    /// M_UNKNOWN: An unknown error occurred
+    /// or the home server was not found/unavailable (a Reqwest error)
+    fn from(error: matrix_sdk::Error) -> Self {
+        use matrix_sdk::ruma::api::client::error::ErrorKind::{
+            Forbidden, LimitExceeded, UserDeactivated,
+        };
+        use matrix_sdk::ruma::api::error::{FromHttpResponseError, ServerError};
+        use matrix_sdk::Error::Http;
+        use matrix_sdk::HttpError::{ClientApi, Reqwest};
+        match error {
+            Http(Reqwest(_)) => LoginError::ServerNotFound,
+            Http(ClientApi(FromHttpResponseError::Http(ServerError::Known(server_err)))) => {
+                match server_err.kind {
+                    Forbidden => LoginError::Forbidden,
+                    UserDeactivated => LoginError::UserDeactivated,
+                    LimitExceeded { retry_after_ms } => LoginError::LimitExceeded(retry_after_ms),
+                    e => LoginError::Unknown(e.to_string()),
+                }
+            }
+            e => LoginError::Unknown(e.to_string()),
+        }
+    }
+}
diff --git a/src/session/mod.rs b/src/session/mod.rs
index 73eb6847..10512fc5 100644
--- a/src/session/mod.rs
+++ b/src/session/mod.rs
@@ -20,6 +20,7 @@ use crate::utils::do_async;
 use crate::Error;
 use crate::RUNTIME;
 
+use crate::login::LoginError;
 use crate::session::content::ContentType;
 use adw;
 use adw::subclass::prelude::BinImpl;
@@ -412,9 +413,9 @@ impl Session {
     /// Returns and consumes the `error` that was generated when the session failed to login,
     /// on a successful login this will be `None`.
     /// Unfortunately it's not possible to connect the Error directly to the `prepared` signals.
-    pub fn get_error(&self) -> Option<matrix_sdk::Error> {
+    pub fn get_error(&self) -> Option<LoginError> {
         let priv_ = &imp::Session::from_instance(self);
-        priv_.error.take()
+        priv_.error.take().map(LoginError::from)
     }
 
     pub fn connect_prepared<F: Fn(&Self) + 'static>(&self, f: F) -> glib::SignalHandlerId {


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