[fractal/fractal-next] login: Show errors as in-app-notification
- From: Julian Sparber <jsparber src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [fractal/fractal-next] login: Show errors as in-app-notification
- Date: Wed, 13 Oct 2021 11:35:31 +0000 (UTC)
commit f46d15e18716563ba83101f16abcc28c7b323301
Author: Julian Sparber <julian sparber net>
Date: Wed Oct 13 12:28:36 2021 +0200
login: Show errors as in-app-notification
This also improves how the error message is created and passed from the
`Session` to the `Login` widget.
data/resources/ui/login.ui | 5 ---
src/login.rs | 95 +++++++++-------------------------------------
src/matrix_error.rs | 40 ++++++++++++++++---
src/session/mod.rs | 49 +++++++++++++++---------
4 files changed, 82 insertions(+), 107 deletions(-)
---
diff --git a/data/resources/ui/login.ui b/data/resources/ui/login.ui
index 92ed8ae9..7bacd558 100644
--- a/data/resources/ui/login.ui
+++ b/data/resources/ui/login.ui
@@ -64,11 +64,6 @@
<object class="GtkBox">
<property name="orientation">vertical</property>
<property name="spacing">18</property>
- <child>
- <object class="GtkLabel" id="error_message">
- <property name="visible">False</property>
- </object>
- </child>
<child>
<object class="GtkBox">
<property name="name">devnotice</property>
diff --git a/src/login.rs b/src/login.rs
index f5a63c3b..30f1edb1 100644
--- a/src/login.rs
+++ b/src/login.rs
@@ -1,13 +1,10 @@
use crate::Session;
use adw::subclass::prelude::BinImpl;
-use gettextrs::gettext;
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 {
@@ -37,8 +34,6 @@ mod imp {
#[template_child]
pub password_entry: TemplateChild<gtk::PasswordEntry>,
#[template_child]
- pub error_message: TemplateChild<gtk::Label>,
- #[template_child]
pub back_to_session_button: TemplateChild<gtk::Button>,
}
@@ -199,22 +194,27 @@ impl Login {
}
fn set_handler_for_prepared_session(&self, session: &Session) {
- session.connect_prepared(clone!(@weak self as login => move |session| {
- if let Some(error) = session.get_error() {
- let error_message = &imp::Login::from_instance(&login).error_message;
- error_message.set_text(&error.to_string());
- error_message.show();
- debug!("Failed to create a new session: {:?}", error);
-
- login.unfreeze();
- } else {
- debug!("A new session was prepared");
- login.emit_by_name("new-session", &[&session]).unwrap();
- login.clean();
+ session.connect_prepared(clone!(@weak self as login => move |session, error| {
+ match error {
+ Some(e) => {
+ login.parent_window().append_error(&e);
+ login.unfreeze();
+ },
+ None => {
+ debug!("A new session was prepared");
+ login.emit_by_name("new-session", &[&session]).unwrap();
+ login.clean();
+ }
}
login.drop_session_reference();
}));
}
+
+ fn parent_window(&self) -> crate::Window {
+ self.root()
+ .and_then(|root| root.downcast().ok())
+ .expect("Login needs to have a parent window")
+ }
}
impl Default for Login {
@@ -230,64 +230,3 @@ 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!("⚠️ 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/matrix_error.rs b/src/matrix_error.rs
index 53f1faed..31747752 100644
--- a/src/matrix_error.rs
+++ b/src/matrix_error.rs
@@ -1,6 +1,9 @@
use matrix_sdk::{
- ruma::api::error::{FromHttpResponseError, ServerError},
- HttpError,
+ ruma::api::{
+ client::error::ErrorKind::{Forbidden, LimitExceeded, UserDeactivated},
+ error::{FromHttpResponseError, ServerError},
+ },
+ Error, HttpError,
};
use gettextrs::gettext;
@@ -14,13 +17,38 @@ impl UserFacingMatrixError for HttpError {
match self {
HttpError::Reqwest(_) => {
// TODO: Add more information based on the error
- gettext("Couldn't connect to the server.")
+ gettext("Unable to connect to the homeserver.")
}
HttpError::ClientApi(FromHttpResponseError::Http(ServerError::Known(error))) => {
- // TODO: The server may not give us pretty enough error message. We should add our own error
message.
- error.message
+ match error.kind {
+ Forbidden => gettext("The provided username or password is invalid."),
+ UserDeactivated => gettext("The user is deactivated."),
+ LimitExceeded { retry_after_ms } => {
+ if let Some(ms) = retry_after_ms {
+ gettext(format!(
+ "You exceeded the homeservers rate limit, retry in {} seconds.",
+ ms.as_secs()
+ ))
+ } else {
+ gettext("You exceeded the homeservers rate limit, try again later.")
+ }
+ }
+ _ => {
+ // TODO: The server may not give us pretty enough error message. We should add our
own error message.
+ error.message
+ }
+ }
}
- _ => gettext("An Unknown error occurred."),
+ _ => gettext("An unknown connection error occurred."),
+ }
+ }
+}
+
+impl UserFacingMatrixError for Error {
+ fn to_user_facing(self) -> String {
+ match self {
+ Error::Http(http_error) => http_error.to_user_facing(),
+ _ => gettext("An unknown error occurred."),
}
}
}
diff --git a/src/session/mod.rs b/src/session/mod.rs
index 8c0e2029..833a9166 100644
--- a/src/session/mod.rs
+++ b/src/session/mod.rs
@@ -24,7 +24,7 @@ use crate::Error;
use crate::Window;
use crate::RUNTIME;
-use crate::login::LoginError;
+use crate::matrix_error::UserFacingMatrixError;
use crate::session::content::ContentType;
use adw::subclass::prelude::BinImpl;
use futures::StreamExt;
@@ -74,8 +74,6 @@ mod imp {
pub content: TemplateChild<adw::Leaflet>,
#[template_child]
pub sidebar: TemplateChild<Sidebar>,
- /// Contains the error if something went wrong
- pub error: RefCell<Option<matrix_sdk::Error>>,
pub client: RefCell<Option<Client>>,
pub room_list: OnceCell<RoomList>,
pub user: OnceCell<User>,
@@ -211,7 +209,12 @@ mod imp {
fn signals() -> &'static [Signal] {
static SIGNALS: Lazy<Vec<Signal>> = Lazy::new(|| {
vec![
- Signal::builder("prepared", &[], <()>::static_type().into()).build(),
+ Signal::builder(
+ "prepared",
+ &[Option::<Error>::static_type().into()],
+ <()>::static_type().into(),
+ )
+ .build(),
Signal::builder("logged-out", &[], <()>::static_type().into()).build(),
]
});
@@ -372,7 +375,7 @@ impl Session {
store_session: bool,
) {
let priv_ = imp::Session::from_instance(self);
- match result {
+ let error = match result {
Ok((client, session)) => {
priv_.client.replace(Some(client.clone()));
let user = User::new(self, &session.user_id);
@@ -406,12 +409,25 @@ impl Session {
self.room_list().load();
self.sync();
+
+ None
}
Err(error) => {
- priv_.error.replace(Some(error));
+ error!("Failed to prepare the session: {}", error);
+
+ let error_string = error.to_user_facing();
+
+ Some(Error::new(move |_| {
+ let error_label = gtk::LabelBuilder::new()
+ .label(&error_string)
+ .wrap(true)
+ .build();
+ Some(error_label.upcast())
+ }))
}
- }
- self.emit_by_name("prepared", &[]).unwrap();
+ };
+
+ self.emit_by_name("prepared", &[&error]).unwrap();
}
fn sync(&self) {
@@ -501,19 +517,16 @@ impl Session {
sender
}
- /// 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<LoginError> {
- let priv_ = &imp::Session::from_instance(self);
- priv_.error.take().map(LoginError::from)
- }
-
- pub fn connect_prepared<F: Fn(&Self) + 'static>(&self, f: F) -> glib::SignalHandlerId {
+ /// Connects the prepared signals to the function f given in input
+ pub fn connect_prepared<F: Fn(&Self, Option<Error>) + 'static>(
+ &self,
+ f: F,
+ ) -> glib::SignalHandlerId {
self.connect_local("prepared", true, move |values| {
let obj = values[0].get::<Self>().unwrap();
+ let err = values[1].get::<Option<Error>>().unwrap();
- f(&obj);
+ f(&obj, err);
None
})
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]