[fractal] App: use trait for errors



commit 19c77a6f6f89d0bc0f79db0d05b69bd3654b4219
Author: Alejandro Domínguez <adomu net-c com>
Date:   Tue Jun 30 19:59:55 2020 +0200

    App: use trait for errors
    
    There is a big match block in `fn dispatch_error` to handle all error
    cases, which are modeled with a big enum with many variants. Since there
    is an error case for each request, this can be changed to isolated data
    structures that implement a common trait. Done this way, extending and
    modifying existing error cases becomes easier. Moreover, there is no
    need to check among so many variants and then run the proper piece of
    code; traits pass a pointer directly to the function.

 fractal-gtk/src/actions/account_settings.rs |   6 +-
 fractal-gtk/src/actions/message.rs          |  13 +-
 fractal-gtk/src/actions/room_settings.rs    |   5 +-
 fractal-gtk/src/app/backend_loop.rs         | 175 ----------------
 fractal-gtk/src/app/connect/language.rs     |   7 +-
 fractal-gtk/src/app/mod.rs                  |   4 -
 fractal-gtk/src/appop/account.rs            |  17 +-
 fractal-gtk/src/appop/directory.rs          |   8 +-
 fractal-gtk/src/appop/invite.rs             |  10 +-
 fractal-gtk/src/appop/login.rs              |   7 +-
 fractal-gtk/src/appop/member.rs             |   6 +-
 fractal-gtk/src/appop/message.rs            |  14 +-
 fractal-gtk/src/appop/room.rs               |  47 +++--
 fractal-gtk/src/appop/start_chat.rs         |   6 +-
 fractal-gtk/src/appop/sync.rs               |  21 +-
 fractal-gtk/src/appop/user.rs               |   8 +-
 fractal-gtk/src/backend/directory.rs        |  46 ++++-
 fractal-gtk/src/backend/media.rs            |   3 +-
 fractal-gtk/src/backend/mod.rs              |  47 ++++-
 fractal-gtk/src/backend/register.rs         |  42 +++-
 fractal-gtk/src/backend/room.rs             | 308 +++++++++++++++++++++++++---
 fractal-gtk/src/backend/sync.rs             |  77 ++++++-
 fractal-gtk/src/backend/user.rs             | 297 +++++++++++++++++++++++++--
 fractal-gtk/src/error.rs                    |  52 -----
 fractal-gtk/src/meson.build                 |   1 -
 fractal-gtk/src/widgets/address.rs          |   9 +-
 fractal-gtk/src/widgets/image.rs            |   6 +-
 fractal-gtk/src/widgets/room.rs             |   6 +-
 fractal-gtk/src/widgets/room_settings.rs    |  10 +-
 29 files changed, 854 insertions(+), 404 deletions(-)
---
diff --git a/fractal-gtk/src/actions/account_settings.rs b/fractal-gtk/src/actions/account_settings.rs
index aee5d8ec..d1c240d8 100644
--- a/fractal-gtk/src/actions/account_settings.rs
+++ b/fractal-gtk/src/actions/account_settings.rs
@@ -1,4 +1,4 @@
-use crate::backend::user;
+use crate::backend::{user, HandleError};
 use crate::i18n::i18n;
 use fractal_api::identifiers::UserId;
 use fractal_api::r0::AccessToken;
@@ -8,9 +8,7 @@ use gio::SimpleAction;
 use gio::SimpleActionGroup;
 use std::thread;
 
-use crate::app::dispatch_error;
 use crate::app::App;
-use crate::error::BKError;
 
 use crate::widgets::FileDialog::open;
 
@@ -47,7 +45,7 @@ pub fn new(
                         APPOP!(show_new_avatar, (path));
                     }
                     Err(err) => {
-                        dispatch_error(BKError::SetUserAvatarError(err));
+                        err.handle_error();
                     }
                 }
             });
diff --git a/fractal-gtk/src/actions/message.rs b/fractal-gtk/src/actions/message.rs
index dba19ac8..d3092b5b 100644
--- a/fractal-gtk/src/actions/message.rs
+++ b/fractal-gtk/src/actions/message.rs
@@ -13,8 +13,7 @@ use std::sync::mpsc::{Receiver, Sender};
 use std::thread;
 
 use crate::actions::AppState;
-use crate::app::dispatch_error;
-use crate::error::BKError;
+use crate::backend::HandleError;
 use crate::i18n::i18n;
 use crate::types::Message;
 use crate::uibuilder::UI;
@@ -130,7 +129,7 @@ pub fn new(
                             .expect("failed to execute process");
                     }
                     Err(err) => {
-                        dispatch_error(BKError::MediaError(err));
+                        err.handle_error()
                     }
                 }
             });
@@ -247,7 +246,7 @@ pub fn new(
             thread::spawn(move || {
                 let query = room::redact_msg(server, access_token, msg);
                 if let Err(err) = query {
-                    dispatch_error(BKError::SentMsgRedactionError(err));
+                    err.handle_error();
                 }
             });
         }
@@ -281,7 +280,7 @@ fn request_more_messages(
                     APPOP!(show_room_messages_top, (msgs, room, prev_batch));
                 }
                 Err(err) => {
-                    dispatch_error(BKError::RoomMessagesToError(err));
+                    err.handle_error();
                 }
             }
         });
@@ -293,7 +292,7 @@ fn request_more_messages(
                     APPOP!(show_room_messages_top, (msgs, room, prev_batch));
                 }
                 Err(err) => {
-                    dispatch_error(BKError::RoomMessagesToError(err));
+                    err.handle_error();
                 }
             }
         });
@@ -305,7 +304,7 @@ fn request_more_messages(
                     APPOP!(show_room_messages_top, (msgs, room, prev_batch));
                 }
                 Err(err) => {
-                    dispatch_error(BKError::RoomMessagesToError(err));
+                    err.handle_error();
                 }
             },
         );
diff --git a/fractal-gtk/src/actions/room_settings.rs b/fractal-gtk/src/actions/room_settings.rs
index 263dba53..0f912f78 100644
--- a/fractal-gtk/src/actions/room_settings.rs
+++ b/fractal-gtk/src/actions/room_settings.rs
@@ -8,9 +8,8 @@ use gio::SimpleActionGroup;
 use std::convert::TryFrom;
 use std::thread;
 
-use crate::app::dispatch_error;
 use crate::app::App;
-use crate::error::BKError;
+use crate::backend::HandleError;
 use crate::i18n::i18n;
 
 use crate::widgets::ErrorDialog;
@@ -55,7 +54,7 @@ pub fn new(
                                 APPOP!(show_new_room_avatar);
                             }
                             Err(err) => {
-                                dispatch_error(BKError::SetRoomAvatarError(err));
+                                err.handle_error();
                             }
                         }
                     });
diff --git a/fractal-gtk/src/app/connect/language.rs b/fractal-gtk/src/app/connect/language.rs
index 0f8a3145..0b402b71 100644
--- a/fractal-gtk/src/app/connect/language.rs
+++ b/fractal-gtk/src/app/connect/language.rs
@@ -1,8 +1,5 @@
-use crate::app::dispatch_error;
 use crate::app::App;
-use crate::error::BKError;
-
-use crate::backend::room;
+use crate::backend::{room, HandleError};
 use glib::object::Cast;
 use gtk::prelude::*;
 use std::thread;
@@ -37,7 +34,7 @@ impl App {
                             thread::spawn(move || {
                                 let query = room::set_language(access_token, server, uid, room_id, 
lang_code);
                                 if let Err(err) = query {
-                                    dispatch_error(BKError::ChangeLanguageError(err));
+                                    err.handle_error();
                                 }
                             });
                         }
diff --git a/fractal-gtk/src/app/mod.rs b/fractal-gtk/src/app/mod.rs
index 04128a09..603cbb12 100644
--- a/fractal-gtk/src/app/mod.rs
+++ b/fractal-gtk/src/app/mod.rs
@@ -37,10 +37,6 @@ macro_rules! APPOP {
     }}
 }
 
-mod backend_loop;
-
-pub use backend_loop::dispatch_error;
-
 // Our application struct for containing all the state we have to carry around.
 // TODO: subclass gtk::Application once possible
 pub struct App {
diff --git a/fractal-gtk/src/appop/account.rs b/fractal-gtk/src/appop/account.rs
index 15f73310..31acb8f0 100644
--- a/fractal-gtk/src/appop/account.rs
+++ b/fractal-gtk/src/appop/account.rs
@@ -4,12 +4,11 @@ use log::info;
 use std::path::PathBuf;
 use std::thread;
 
-use crate::app::dispatch_error;
 use crate::app::App;
 use crate::appop::AppOp;
 use crate::appop::AppState;
+use crate::backend::HandleError;
 
-use crate::error::BKError;
 use crate::i18n::i18n;
 use crate::widgets;
 use crate::widgets::AvatarExt;
@@ -34,7 +33,7 @@ impl AppOp {
                     APPOP!(set_three_pid, (l));
                 }
                 Err(err) => {
-                    dispatch_error(BKError::GetThreePIDError(err));
+                    err.handle_error();
                 }
             }
         });
@@ -60,7 +59,7 @@ impl AppOp {
                             APPOP!(added_three_pid);
                         }
                         Err(err) => {
-                            dispatch_error(BKError::AddThreePIDError(err));
+                            err.handle_error();
                         }
                     }
                 });
@@ -127,7 +126,7 @@ impl AppOp {
                                 APPOP!(valid_phone_token, (sid, secret));
                             }
                             Err(err) => {
-                                dispatch_error(BKError::SubmitPhoneTokenError(err));
+                                err.handle_error();
                             }
                         }
                     });
@@ -175,7 +174,7 @@ impl AppOp {
                             APPOP!(added_three_pid);
                         }
                         Err(err) => {
-                            dispatch_error(BKError::AddThreePIDError(err));
+                            err.handle_error();
                         }
                     }
                 });
@@ -618,7 +617,7 @@ impl AppOp {
                         APPOP!(show_new_username, (u));
                     }
                     Err(err) => {
-                        dispatch_error(BKError::SetUserNameError(err));
+                        err.handle_error();
                     }
                 }
             });
@@ -689,7 +688,7 @@ impl AppOp {
                                 APPOP!(password_changed);
                             }
                             Err(err) => {
-                                dispatch_error(BKError::ChangePasswordError(err));
+                                err.handle_error();
                             }
                         }
                     });
@@ -805,7 +804,7 @@ impl AppOp {
                                 APPOP!(account_destruction_logoff);
                             }
                             Err(err) => {
-                                dispatch_error(BKError::AccountDestructionError(err));
+                                err.handle_error();
                             }
                         }
                     });
diff --git a/fractal-gtk/src/appop/directory.rs b/fractal-gtk/src/appop/directory.rs
index 0220b0ce..b34d91c6 100644
--- a/fractal-gtk/src/appop/directory.rs
+++ b/fractal-gtk/src/appop/directory.rs
@@ -2,13 +2,11 @@ use gtk::prelude::*;
 use libhandy::Column;
 use std::thread;
 
-use crate::backend::directory;
+use crate::backend::{directory, HandleError};
 
-use crate::app::dispatch_error;
 use crate::app::App;
 use crate::appop::AppOp;
 
-use crate::error::BKError;
 use crate::widgets;
 
 use super::RoomSearchPagination;
@@ -24,7 +22,7 @@ impl AppOp {
                     APPOP!(set_protocols, (protocols));
                 }
                 Err(err) => {
-                    dispatch_error(BKError::DirectoryProtocolsError(err));
+                    err.handle_error();
                 }
             }
         });
@@ -152,7 +150,7 @@ impl AppOp {
                     APPOP!(append_directory_rooms, (rooms, rooms_since));
                 }
                 Err(err) => {
-                    dispatch_error(BKError::DirectorySearchError(err));
+                    err.handle_error();
                 }
             }
         });
diff --git a/fractal-gtk/src/appop/invite.rs b/fractal-gtk/src/appop/invite.rs
index cd6c66d5..cf9f5ba2 100644
--- a/fractal-gtk/src/appop/invite.rs
+++ b/fractal-gtk/src/appop/invite.rs
@@ -5,12 +5,10 @@ use fractal_api::identifiers::{RoomId, UserId};
 use gtk::prelude::*;
 use std::thread;
 
-use crate::app::dispatch_error;
 use crate::app::App;
 use crate::appop::member::SearchType;
 use crate::appop::AppOp;
-
-use crate::error::BKError;
+use crate::backend::HandleError;
 
 use crate::globals;
 
@@ -174,7 +172,7 @@ impl AppOp {
                 thread::spawn(move || {
                     let query = room::invite(server, access_token, room_id, user_id);
                     if let Err(err) = query {
-                        dispatch_error(BKError::InviteError(err));
+                        err.handle_error();
                     }
                 });
             }
@@ -237,7 +235,7 @@ impl AppOp {
                             APPOP!(reload_rooms);
                         }
                         Err(err) => {
-                            dispatch_error(BKError::JoinRoomError(err));
+                            err.handle_error();
                         }
                     }
                 });
@@ -246,7 +244,7 @@ impl AppOp {
                     let query =
                         room::leave_room(login_data.server_url, login_data.access_token, room_id);
                     if let Err(err) = query {
-                        dispatch_error(BKError::LeaveRoomError(err));
+                        err.handle_error();
                     }
                 });
             }
diff --git a/fractal-gtk/src/appop/login.rs b/fractal-gtk/src/appop/login.rs
index 78a67656..e1da398d 100644
--- a/fractal-gtk/src/appop/login.rs
+++ b/fractal-gtk/src/appop/login.rs
@@ -6,12 +6,11 @@ use fractal_api::r0::AccessToken;
 
 use fractal_api::url::Url;
 
-use crate::app::dispatch_error;
 use crate::app::App;
 use crate::appop::AppOp;
 
+use crate::backend::HandleError;
 use crate::cache;
-use crate::error::BKError;
 
 use std::thread;
 
@@ -85,7 +84,7 @@ impl AppOp {
                     APPOP!(bk_login, (uid, tk, dev, server, identity));
                 }
                 Err(err) => {
-                    dispatch_error(BKError::LoginError(err));
+                    err.handle_error();
                 }
             },
         );
@@ -103,7 +102,7 @@ impl AppOp {
                     APPOP!(bk_logout);
                 }
                 Err(err) => {
-                    dispatch_error(BKError::LogoutError(err));
+                    err.handle_error();
                 }
             }
         });
diff --git a/fractal-gtk/src/appop/member.rs b/fractal-gtk/src/appop/member.rs
index 51d7bcc6..771e9f57 100644
--- a/fractal-gtk/src/appop/member.rs
+++ b/fractal-gtk/src/appop/member.rs
@@ -1,4 +1,4 @@
-use crate::backend::user;
+use crate::backend::{user, HandleError};
 use crate::clone;
 use fractal_api::identifiers::{RoomId, UserId};
 use gtk::prelude::*;
@@ -8,9 +8,7 @@ use std::convert::TryFrom;
 use std::thread;
 
 use crate::actions::AppState;
-use crate::app::dispatch_error;
 use crate::appop::AppOp;
-use crate::error::BKError;
 use crate::widgets;
 use crate::App;
 
@@ -197,7 +195,7 @@ impl AppOp {
                     APPOP!(user_search_finished, (users));
                 }
                 Err(err) => {
-                    dispatch_error(BKError::UserSearchError(err));
+                    err.handle_error();
                 }
             }
         });
diff --git a/fractal-gtk/src/appop/message.rs b/fractal-gtk/src/appop/message.rs
index 93e60b9b..16e8a6ee 100644
--- a/fractal-gtk/src/appop/message.rs
+++ b/fractal-gtk/src/appop/message.rs
@@ -1,4 +1,4 @@
-use crate::backend::room;
+use crate::backend::{room, HandleError};
 use crate::clone;
 use crate::types::ExtraContent;
 use comrak::{markdown_to_html, ComrakOptions};
@@ -19,12 +19,10 @@ use std::fs;
 use std::path::PathBuf;
 use std::thread;
 
-use crate::app::dispatch_error;
 use crate::appop::room::Force;
 use crate::appop::AppOp;
 use crate::App;
 
-use crate::error::BKError;
 use crate::uitypes::MessageContent;
 use crate::uitypes::RowType;
 use crate::widgets;
@@ -169,7 +167,7 @@ impl AppOp {
                         APPOP!(clear_room_notifications, (r));
                     }
                     Err(err) => {
-                        dispatch_error(BKError::MarkedAsReadError(err));
+                        err.handle_error();
                     }
                 }
             });
@@ -227,7 +225,7 @@ impl AppOp {
                                 APPOP!(sync, (initial, number_tries));
                             }
                             Err(err) => {
-                                dispatch_error(BKError::SentMsgError(err));
+                                err.handle_error();
                             }
                         }
                     });
@@ -671,7 +669,7 @@ fn attach_file(baseu: Url, tk: AccessToken, mut msg: Message) {
                 msg.extra_content = serde_json::to_value(&extra_content).ok();
             }
             Err(err) => {
-                dispatch_error(BKError::AttachedFileError(err));
+                err.handle_error();
             }
         }
 
@@ -692,7 +690,7 @@ fn attach_file(baseu: Url, tk: AccessToken, mut msg: Message) {
             APPOP!(attached_file, (msg));
         }
         Err(err) => {
-            dispatch_error(BKError::AttachedFileError(err));
+            err.handle_error();
         }
     };
 }
@@ -706,7 +704,7 @@ fn send_msg_and_manage(baseu: Url, tk: AccessToken, msg: Message) {
             APPOP!(sync, (initial, number_tries));
         }
         Err(err) => {
-            dispatch_error(BKError::SentMsgError(err));
+            err.handle_error();
         }
     };
 }
diff --git a/fractal-gtk/src/appop/room.rs b/fractal-gtk/src/appop/room.rs
index 6a6f6e6c..d8b6a1bb 100644
--- a/fractal-gtk/src/appop/room.rs
+++ b/fractal-gtk/src/appop/room.rs
@@ -3,18 +3,17 @@ use crate::i18n::{i18n, i18n_k, ni18n_f};
 use fractal_api::identifiers::RoomId;
 use fractal_api::url::Url;
 use log::{error, warn};
-use std::convert::TryFrom;
+use std::convert::TryInto;
 use std::fs::remove_file;
 use std::os::unix::fs;
 use std::thread;
 
 use gtk::prelude::*;
 
-use crate::app::dispatch_error;
 use crate::app::App;
 use crate::appop::AppOp;
+use crate::backend::HandleError;
 
-use crate::error::BKError;
 use crate::util::cache_dir_path;
 
 use crate::actions;
@@ -86,7 +85,7 @@ impl AppOp {
                             APPOP!(set_room_members, (room, members));
                         }
                         Err(err) => {
-                            dispatch_error(BKError::RoomMembersError(err));
+                            err.handle_error();
                         }
                     }
                 });
@@ -101,7 +100,7 @@ impl AppOp {
                             APPOP!(set_room_avatar, (room, avatar));
                         }
                         Err(err) => {
-                            dispatch_error(BKError::RoomAvatarError(err));
+                            err.handle_error();
                         }
                     },
                 );
@@ -154,7 +153,7 @@ impl AppOp {
                             APPOP!(added_to_fav, (r, tofav));
                         }
                         Err(err) => {
-                            dispatch_error(BKError::AddedToFavError(err));
+                            err.handle_error();
                         }
                     }
                 });
@@ -252,7 +251,7 @@ impl AppOp {
                     APPOP!(set_room_avatar, (room, avatar));
                 }
                 Err(err) => {
-                    dispatch_error(BKError::RoomAvatarError(err));
+                    err.handle_error();
                 }
             },
         );
@@ -267,7 +266,7 @@ impl AppOp {
                     APPOP!(set_room_detail, (room, key, v));
                 }
                 Err(err) => {
-                    dispatch_error(BKError::RoomDetailError(err));
+                    err.handle_error();
                 }
             }
         });
@@ -333,7 +332,7 @@ impl AppOp {
         thread::spawn(move || {
             let query = room::leave_room(login_data.server_url, login_data.access_token, room_id);
             if let Err(err) = query {
-                dispatch_error(BKError::LeaveRoomError(err));
+                err.handle_error();
             }
         });
         self.rooms.remove(&r);
@@ -414,7 +413,8 @@ impl AppOp {
                     APPOP!(new_room, (r, id));
                 }
                 Err(err) => {
-                    dispatch_error(BKError::NewRoomError(err, int_id));
+                    APPOP!(remove_room, (int_id));
+                    err.handle_error();
                 }
             }
         });
@@ -550,27 +550,34 @@ impl AppOp {
 
     pub fn join_to_room(&mut self) {
         let login_data = unwrap_or_unit_return!(self.login_data.clone());
-        let name = self
+        let try_room_id = self
             .ui
             .builder
             .get_object::<gtk::Entry>("join_room_name")
             .expect("Can't find join_room_name in ui file.")
             .get_text()
-            .map_or(String::new(), |gstr| gstr.to_string());
+            .map_or(String::new(), |gstr| gstr.to_string())
+            .trim()
+            .try_into();
 
         thread::spawn(move || {
-            match RoomId::try_from(name.trim())
-                .map_err(Into::into)
-                .and_then(|room_id| {
-                    room::join_room(login_data.server_url, login_data.access_token, room_id)
-                }) {
+            let room_id = match try_room_id {
+                Ok(rid) => rid,
+                Err(_) => {
+                    let error = i18n("The room ID is malformed");
+                    APPOP!(show_error, (error));
+                    return;
+                }
+            };
+
+            match room::join_room(login_data.server_url, login_data.access_token, room_id) {
                 Ok(jtr) => {
                     let jtr = Some(jtr);
                     APPOP!(set_join_to_room, (jtr));
                     APPOP!(reload_rooms);
                 }
                 Err(err) => {
-                    dispatch_error(BKError::JoinRoomError(err));
+                    err.handle_error();
                 }
             }
         });
@@ -714,7 +721,7 @@ impl AppOp {
                     APPOP!(set_room_avatar, (room, avatar));
                 }
                 Err(err) => {
-                    dispatch_error(BKError::RoomAvatarError(err));
+                    err.handle_error();
                 }
             }
         });
@@ -768,7 +775,7 @@ impl AppOp {
                 active_room,
             );
             if let Err(err) = query {
-                dispatch_error(BKError::SendTypingError(err));
+                err.handle_error();
             }
         });
     }
diff --git a/fractal-gtk/src/appop/start_chat.rs b/fractal-gtk/src/appop/start_chat.rs
index 0e280ea7..6ad155fe 100644
--- a/fractal-gtk/src/appop/start_chat.rs
+++ b/fractal-gtk/src/appop/start_chat.rs
@@ -4,12 +4,11 @@ use gtk::prelude::*;
 use std::thread;
 
 use crate::actions::AppState;
-use crate::app::dispatch_error;
 use crate::app::App;
 use crate::appop::AppOp;
 use crate::appop::SearchType;
+use crate::backend::HandleError;
 
-use crate::error::BKError;
 use crate::types::{Room, RoomMembership, RoomTag};
 
 impl AppOp {
@@ -38,7 +37,8 @@ impl AppOp {
                     APPOP!(new_room, (r, id));
                 }
                 Err(err) => {
-                    dispatch_error(BKError::NewRoomError(err, int_id));
+                    APPOP!(remove_room, (int_id));
+                    err.handle_error();
                 }
             }
         });
diff --git a/fractal-gtk/src/appop/sync.rs b/fractal-gtk/src/appop/sync.rs
index 79fe77ce..7f522756 100644
--- a/fractal-gtk/src/appop/sync.rs
+++ b/fractal-gtk/src/appop/sync.rs
@@ -3,11 +3,12 @@ use std::thread;
 
 use crate::i18n::i18n;
 
-use crate::app::dispatch_error;
 use crate::app::App;
 use crate::appop::AppOp;
-use crate::backend::sync::{self, RoomElement, SyncRet};
-use crate::error::BKError;
+use crate::backend::{
+    sync::{self, RoomElement, SyncRet},
+    HandleError,
+};
 
 impl AppOp {
     pub fn initial_sync(&self, show: bool) {
@@ -51,7 +52,7 @@ impl AppOp {
                                 }
                             }
                             Err(err) => {
-                                dispatch_error(BKError::RoomsError(err));
+                                err.handle_error();
                             }
                         };
 
@@ -73,7 +74,7 @@ impl AppOp {
                                 APPOP!(set_rooms, (rooms, clear_room_list));
                             }
                             Err(err) => {
-                                dispatch_error(BKError::UpdateRoomsError(err));
+                                err.handle_error();
                             }
                         }
 
@@ -82,7 +83,7 @@ impl AppOp {
                                 APPOP!(show_room_messages, (msgs));
                             }
                             Err(err) => {
-                                dispatch_error(BKError::RoomMessagesError(err));
+                                err.handle_error();
                             }
                         }
 
@@ -92,7 +93,7 @@ impl AppOp {
                                 APPOP!(set_rooms, (rooms, clear_room_list));
                             }
                             Err(err) => {
-                                dispatch_error(BKError::UpdateRoomsError(err));
+                                err.handle_error();
                             }
                         }
 
@@ -128,7 +129,7 @@ impl AppOp {
                                 }
                             }
                             Err(err) => {
-                                dispatch_error(BKError::RoomElementError(err));
+                                err.handle_error();
                             }
                         }
 
@@ -136,8 +137,8 @@ impl AppOp {
                         let s = Some(next_batch);
                         APPOP!(synced, (s));
                     }
-                    Err((err, n_tries)) => {
-                        dispatch_error(BKError::SyncError(err, n_tries));
+                    Err(err) => {
+                        err.handle_error();
                     }
                 }
             });
diff --git a/fractal-gtk/src/appop/user.rs b/fractal-gtk/src/appop/user.rs
index 14c9bd81..096735c1 100644
--- a/fractal-gtk/src/appop/user.rs
+++ b/fractal-gtk/src/appop/user.rs
@@ -1,18 +1,16 @@
 use gtk::prelude::*;
 
-use crate::backend::user;
+use crate::backend::{user, HandleError};
 use crate::clone;
 
 use std::path::PathBuf;
 use std::thread;
 
-use crate::app::dispatch_error;
 use crate::app::App;
 use crate::appop::AppOp;
 
 use crate::cache::download_to_cache;
 
-use crate::error::BKError;
 use crate::widgets;
 use crate::widgets::AvatarExt;
 
@@ -28,7 +26,7 @@ impl AppOp {
                     APPOP!(set_username, (username));
                 }
                 Err(err) => {
-                    dispatch_error(BKError::NameError(err));
+                    err.handle_error();
                 }
             }
         }));
@@ -39,7 +37,7 @@ impl AppOp {
                     APPOP!(set_avatar, (path));
                 }
                 Err(err) => {
-                    dispatch_error(BKError::AvatarError(err));
+                    err.handle_error();
                 }
             }
         }));
diff --git a/fractal-gtk/src/backend/directory.rs b/fractal-gtk/src/backend/directory.rs
index eaf28f5d..c2902f6e 100644
--- a/fractal-gtk/src/backend/directory.rs
+++ b/fractal-gtk/src/backend/directory.rs
@@ -20,9 +20,32 @@ use fractal_api::r0::thirdparty::get_supported_protocols::ProtocolInstance;
 use fractal_api::r0::thirdparty::get_supported_protocols::Response as SupportedProtocolsResponse;
 use fractal_api::r0::AccessToken;
 
-use super::{dw_media, ContentType};
+use super::{dw_media, ContentType, HandleError};
+use crate::app::App;
+use crate::i18n::i18n;
+use crate::APPOP;
 
-pub fn protocols(base: Url, access_token: AccessToken) -> Result<Vec<ProtocolInstance>, Error> {
+#[derive(Debug)]
+pub struct DirectoryProtocolsError;
+
+impl<T: Into<Error>> From<T> for DirectoryProtocolsError {
+    fn from(_: T) -> Self {
+        Self
+    }
+}
+
+impl HandleError for DirectoryProtocolsError {
+    fn handle_error(&self) {
+        let error = i18n("Error searching for rooms");
+        APPOP!(reset_directory_state);
+        APPOP!(show_error, (error));
+    }
+}
+
+pub fn protocols(
+    base: Url,
+    access_token: AccessToken,
+) -> Result<Vec<ProtocolInstance>, DirectoryProtocolsError> {
     let params = SupportedProtocolsParameters { access_token };
     let request = get_supported_protocols(base, &params)?;
     let response: SupportedProtocolsResponse =
@@ -34,6 +57,23 @@ pub fn protocols(base: Url, access_token: AccessToken) -> Result<Vec<ProtocolIns
         .collect())
 }
 
+#[derive(Debug)]
+pub struct DirectorySearchError;
+
+impl<T: Into<Error>> From<T> for DirectorySearchError {
+    fn from(_: T) -> Self {
+        Self
+    }
+}
+
+impl HandleError for DirectorySearchError {
+    fn handle_error(&self) {
+        let error = i18n("Error searching for rooms");
+        APPOP!(reset_directory_state);
+        APPOP!(show_error, (error));
+    }
+}
+
 pub fn room_search(
     base: Url,
     access_token: AccessToken,
@@ -41,7 +81,7 @@ pub fn room_search(
     generic_search_term: String,
     third_party: String,
     rooms_since: Option<String>,
-) -> Result<(Vec<Room>, Option<String>), Error> {
+) -> Result<(Vec<Room>, Option<String>), DirectorySearchError> {
     let homeserver = Some(homeserver).filter(|hs| !hs.is_empty());
     let generic_search_term = Some(generic_search_term).filter(|q| !q.is_empty());
     let third_party = Some(third_party).filter(|tp| !tp.is_empty());
diff --git a/fractal-gtk/src/backend/media.rs b/fractal-gtk/src/backend/media.rs
index 44c2ba61..1ae3cd8e 100644
--- a/fractal-gtk/src/backend/media.rs
+++ b/fractal-gtk/src/backend/media.rs
@@ -1,3 +1,4 @@
+use super::MediaError;
 use crate::error::Error;
 use crate::globals;
 use fractal_api::identifiers::{EventId, RoomId};
@@ -17,7 +18,7 @@ use fractal_api::r0::message::get_message_events::Response as GetMessagesEventsR
 
 use super::{dw_media, get_prev_batch_from, ContentType, ThreadPool};
 
-pub type MediaResult = Result<String, Error>;
+pub type MediaResult = Result<String, MediaError>;
 pub type MediaList = (Vec<Message>, String);
 
 pub fn get_thumb_async(
diff --git a/fractal-gtk/src/backend/mod.rs b/fractal-gtk/src/backend/mod.rs
index e9a29830..06c6d173 100644
--- a/fractal-gtk/src/backend/mod.rs
+++ b/fractal-gtk/src/backend/mod.rs
@@ -1,6 +1,9 @@
 use fractal_api::identifiers::{EventId, RoomId};
 use fractal_api::url::Url;
 use lazy_static::lazy_static;
+use log::error;
+use regex::Regex;
+use std::fmt::Debug;
 use std::fs::write;
 use std::io::Read;
 use std::path::Path;
@@ -111,16 +114,27 @@ pub fn get_prev_batch_from(
     Ok(prev_batch)
 }
 
+#[derive(Debug)]
+pub struct MediaError(pub(self) Error);
+
+impl<T: Into<Error>> From<T> for MediaError {
+    fn from(err: T) -> Self {
+        Self(err.into())
+    }
+}
+
+impl HandleError for MediaError {}
+
 pub fn dw_media(
     base: Url,
     mxc: &str,
     media_type: ContentType,
     dest: Option<String>,
-) -> Result<String, Error> {
+) -> Result<String, MediaError> {
     let mxc_url = Url::parse(mxc)?;
 
     if mxc_url.scheme() != "mxc" {
-        return Err(Error::BackendError);
+        return Err(MediaError(Error::BackendError));
     }
 
     let server = mxc_url.host().ok_or(Error::BackendError)?.to_owned();
@@ -167,3 +181,32 @@ pub fn dw_media(
             .map_err(Into::into)
     }
 }
+
+pub trait HandleError: Debug {
+    fn handle_error(&self) {
+        let err_str = format!("{:?}", self);
+        error!(
+            "Query error: {}",
+            remove_matrix_access_token_if_present(&err_str).unwrap_or(err_str)
+        );
+    }
+}
+
+/// This function removes the value of the `access_token` query from a URL used for accessing the Matrix API.
+/// The primary use case is the removing of sensitive information for logging.
+/// Specifically, the URL is expected to be contained within quotes and the token is replaced with 
`<redacted>`.
+/// Returns `Some` on removal, otherwise `None`.
+pub fn remove_matrix_access_token_if_present(message: &str) -> Option<String> {
+    lazy_static! {
+    static ref RE: Regex =
+        Regex::new(r#""((http)|(https))://([^"]+)/_matrix/([^"]+)\?access_token=(?P<token>[^&"]+)([^"]*)""#,)
+        .expect("Malformed regular expression.");
+    }
+    // If the supplied string doesn't contain a match for the regex, we return `None`.
+    let cap = RE.captures(message)?;
+    let captured_token = cap
+        .name("token")
+        .expect("'token' capture group not present.")
+        .as_str();
+    Some(message.replace(captured_token, "<redacted>"))
+}
diff --git a/fractal-gtk/src/backend/register.rs b/fractal-gtk/src/backend/register.rs
index c1ca8d4b..ec02ff55 100644
--- a/fractal-gtk/src/backend/register.rs
+++ b/fractal-gtk/src/backend/register.rs
@@ -3,6 +3,7 @@ use fractal_api::url::Url;
 
 use crate::error::Error;
 
+use crate::actions::AppState;
 use crate::backend::HTTP_CLIENT;
 use crate::globals;
 use fractal_api::r0::account::login::request as login_req;
@@ -18,11 +19,35 @@ use fractal_api::r0::server::domain_info::Response as DomainInfoResponse;
 use fractal_api::r0::AccessToken;
 use fractal_api::r0::Medium;
 
+use super::HandleError;
+use crate::app::App;
+use crate::i18n::i18n;
+use crate::APPOP;
+
+#[derive(Debug)]
+pub struct LoginError;
+
+impl<T: Into<Error>> From<T> for LoginError {
+    fn from(_: T) -> Self {
+        Self
+    }
+}
+
+impl HandleError for LoginError {
+    fn handle_error(&self) {
+        let error = i18n("Can’t login, try again");
+        let st = AppState::Login;
+        APPOP!(show_error, (error));
+        APPOP!(logout);
+        APPOP!(set_state, (st));
+    }
+}
+
 pub fn login(
     user: String,
     password: String,
     server: Url,
-) -> Result<(UserId, AccessToken, Option<DeviceId>), Error> {
+) -> Result<(UserId, AccessToken, Option<DeviceId>), LoginError> {
     let body = if globals::EMAIL_RE.is_match(&user) {
         LoginBody {
             auth: Auth::Password { password },
@@ -48,11 +73,22 @@ pub fn login(
     if let (Some(tk), Some(uid)) = (response.access_token, response.user_id) {
         Ok((uid, tk, response.device_id))
     } else {
-        Err(Error::BackendError)
+        Err(LoginError)
+    }
+}
+
+#[derive(Debug)]
+pub struct LogoutError(Error);
+
+impl<T: Into<Error>> From<T> for LogoutError {
+    fn from(err: T) -> Self {
+        Self(err.into())
     }
 }
 
-pub fn logout(server: Url, access_token: AccessToken) -> Result<(), Error> {
+impl HandleError for LogoutError {}
+
+pub fn logout(server: Url, access_token: AccessToken) -> Result<(), LogoutError> {
     let params = LogoutParameters { access_token };
 
     let request = logout_req(server, &params)?;
diff --git a/fractal-gtk/src/backend/room.rs b/fractal-gtk/src/backend/room.rs
index ec9fb996..fe86ef9a 100644
--- a/fractal-gtk/src/backend/room.rs
+++ b/fractal-gtk/src/backend/room.rs
@@ -12,6 +12,7 @@ use std::time::Duration;
 use crate::error::Error;
 use crate::globals;
 
+use crate::actions::AppState;
 use crate::backend::HTTP_CLIENT;
 use crate::util::cache_dir_path;
 
@@ -75,14 +76,30 @@ use fractal_api::r0::AccessToken;
 
 use serde_json::Value as JsonValue;
 
-use super::{dw_media, get_prev_batch_from, ContentType};
+use super::{
+    dw_media, get_prev_batch_from, remove_matrix_access_token_if_present, ContentType, HandleError,
+};
+use crate::app::App;
+use crate::i18n::i18n;
+use crate::APPOP;
+
+#[derive(Debug)]
+pub struct RoomDetailError(Error);
+
+impl<T: Into<Error>> From<T> for RoomDetailError {
+    fn from(err: T) -> Self {
+        Self(err.into())
+    }
+}
+
+impl HandleError for RoomDetailError {}
 
 pub fn get_room_detail(
     base: Url,
     access_token: AccessToken,
     room_id: RoomId,
     keys: String,
-) -> Result<(RoomId, String, String), Error> {
+) -> Result<(RoomId, String, String), RoomDetailError> {
     let params = GetStateEventsForKeyParameters { access_token };
 
     let request = get_state_events_for_key(base, &params, &room_id, &keys)?;
@@ -94,11 +111,22 @@ pub fn get_room_detail(
     Ok((room_id, keys, value))
 }
 
+#[derive(Debug)]
+pub struct RoomAvatarError(Error);
+
+impl<T: Into<Error>> From<T> for RoomAvatarError {
+    fn from(err: T) -> Self {
+        Self(err.into())
+    }
+}
+
+impl HandleError for RoomAvatarError {}
+
 pub fn get_room_avatar(
     base: Url,
     access_token: AccessToken,
     room_id: RoomId,
-) -> Result<(RoomId, Option<Url>), Error> {
+) -> Result<(RoomId, Option<Url>), RoomAvatarError> {
     let params = GetStateEventsForKeyParameters { access_token };
 
     get_state_events_for_key(base.clone(), &params, &room_id, "m.room.avatar")
@@ -126,13 +154,25 @@ pub fn get_room_avatar(
             Error::MatrixError(errcode, _) if errcode == "M_NOT_FOUND" => Ok((room_id, None)),
             error => Err(error),
         })
+        .map_err(Into::into)
+}
+
+#[derive(Debug)]
+pub struct RoomMembersError(Error);
+
+impl<T: Into<Error>> From<T> for RoomMembersError {
+    fn from(err: T) -> Self {
+        Self(err.into())
+    }
 }
 
+impl HandleError for RoomMembersError {}
+
 pub fn get_room_members(
     base: Url,
     access_token: AccessToken,
     room_id: RoomId,
-) -> Result<(RoomId, Vec<Member>), Error> {
+) -> Result<(RoomId, Vec<Member>), RoomMembersError> {
     let params = JoinedMembersParameters { access_token };
 
     let request = get_joined_members(base, &room_id, &params)?;
@@ -143,6 +183,17 @@ pub fn get_room_members(
     Ok((room_id, ms))
 }
 
+#[derive(Debug)]
+pub struct RoomMessagesToError(Error);
+
+impl<T: Into<Error>> From<T> for RoomMessagesToError {
+    fn from(err: T) -> Self {
+        Self(err.into())
+    }
+}
+
+impl HandleError for RoomMessagesToError {}
+
 /* Load older messages starting by prev_batch
  * https://matrix.org/docs/spec/client_server/latest.html#get-matrix-client-r0-rooms-roomid-messages
  */
@@ -151,7 +202,7 @@ pub fn get_room_messages(
     access_token: AccessToken,
     room_id: RoomId,
     from: String,
-) -> Result<(Vec<Message>, RoomId, Option<String>), Error> {
+) -> Result<(Vec<Message>, RoomId, Option<String>), RoomMessagesToError> {
     let params = GetMessagesEventsParams {
         access_token,
         from,
@@ -179,7 +230,7 @@ pub fn get_room_messages_from_msg(
     access_token: AccessToken,
     room_id: RoomId,
     msg: Message,
-) -> Result<(Vec<Message>, RoomId, Option<String>), Error> {
+) -> Result<(Vec<Message>, RoomId, Option<String>), RoomMessagesToError> {
     let event_id = msg.id.as_ref().ok_or(Error::BackendError)?;
 
     // first of all, we calculate the from param using the context api, then we call the
@@ -189,11 +240,21 @@ pub fn get_room_messages_from_msg(
     get_room_messages(base, access_token, room_id, from)
 }
 
+#[derive(Debug)]
+pub struct SendMsgError(String);
+
+impl HandleError for SendMsgError {
+    fn handle_error(&self) {
+        error!("sending {}: retrying send", self.0);
+        APPOP!(retry_send);
+    }
+}
+
 pub fn send_msg(
     base: Url,
     access_token: AccessToken,
     msg: Message,
-) -> Result<(String, Option<EventId>), Error> {
+) -> Result<(String, Option<EventId>), SendMsgError> {
     let room_id: RoomId = msg.room.clone();
 
     let params = CreateMessageEventParameters { access_token };
@@ -235,15 +296,26 @@ pub fn send_msg(
 
             Ok((txn_id.clone(), response.event_id))
         })
-        .or(Err(Error::SendMsgError(txn_id)))
+        .or(Err(SendMsgError(txn_id)))
 }
 
+#[derive(Debug)]
+pub struct SendTypingError(Error);
+
+impl<T: Into<Error>> From<T> for SendTypingError {
+    fn from(err: T) -> Self {
+        Self(err.into())
+    }
+}
+
+impl HandleError for SendTypingError {}
+
 pub fn send_typing(
     base: Url,
     access_token: AccessToken,
     user_id: UserId,
     room_id: RoomId,
-) -> Result<(), Error> {
+) -> Result<(), SendTypingError> {
     let params = TypingNotificationParameters { access_token };
     let body = TypingNotificationBody::Typing(Duration::from_secs(4));
 
@@ -253,14 +325,24 @@ pub fn send_typing(
     Ok(())
 }
 
+#[derive(Debug)]
+pub struct SendMsgRedactionError;
+
+impl HandleError for SendMsgRedactionError {
+    fn handle_error(&self) {
+        let error = i18n("Error deleting message");
+        APPOP!(show_error, (error));
+    }
+}
+
 pub fn redact_msg(
     base: Url,
     access_token: AccessToken,
     msg: Message,
-) -> Result<(EventId, Option<EventId>), Error> {
+) -> Result<(EventId, Option<EventId>), SendMsgRedactionError> {
     let room_id = &msg.room;
     let txn_id = msg.get_txn_id();
-    let event_id = msg.id.clone().ok_or(Error::BackendError)?;
+    let event_id = msg.id.clone().ok_or(SendMsgRedactionError)?;
 
     let params = RedactEventParameters { access_token };
 
@@ -278,10 +360,37 @@ pub fn redact_msg(
 
             Ok((event_id.clone(), response.event_id))
         })
-        .or(Err(Error::SendMsgRedactionError(event_id)))
+        .or(Err(SendMsgRedactionError))
 }
 
-pub fn join_room(base: Url, access_token: AccessToken, room_id: RoomId) -> Result<RoomId, Error> {
+#[derive(Debug)]
+pub struct JoinRoomError(Error);
+
+impl<T: Into<Error>> From<T> for JoinRoomError {
+    fn from(err: T) -> Self {
+        Self(err.into())
+    }
+}
+
+impl HandleError for JoinRoomError {
+    fn handle_error(&self) {
+        let err_str = format!("{:?}", self);
+        error!(
+            "{}",
+            remove_matrix_access_token_if_present(&err_str).unwrap_or(err_str)
+        );
+        let error = i18n("Can’t join the room, try again.").to_string();
+        let state = AppState::NoRoom;
+        APPOP!(show_error, (error));
+        APPOP!(set_state, (state));
+    }
+}
+
+pub fn join_room(
+    base: Url,
+    access_token: AccessToken,
+    room_id: RoomId,
+) -> Result<RoomId, JoinRoomError> {
     let room_id_or_alias_id = room_id.clone().into();
 
     let params = JoinRoomParameters {
@@ -295,7 +404,22 @@ pub fn join_room(base: Url, access_token: AccessToken, room_id: RoomId) -> Resul
     Ok(room_id)
 }
 
-pub fn leave_room(base: Url, access_token: AccessToken, room_id: RoomId) -> Result<(), Error> {
+#[derive(Debug)]
+pub struct LeaveRoomError(Error);
+
+impl<T: Into<Error>> From<T> for LeaveRoomError {
+    fn from(err: T) -> Self {
+        Self(err.into())
+    }
+}
+
+impl HandleError for LeaveRoomError {}
+
+pub fn leave_room(
+    base: Url,
+    access_token: AccessToken,
+    room_id: RoomId,
+) -> Result<(), LeaveRoomError> {
     let params = LeaveRoomParameters { access_token };
 
     let request = leave_room_req(base, &room_id, &params)?;
@@ -304,12 +428,23 @@ pub fn leave_room(base: Url, access_token: AccessToken, room_id: RoomId) -> Resu
     Ok(())
 }
 
+#[derive(Debug)]
+pub struct MarkedAsReadError(Error);
+
+impl<T: Into<Error>> From<T> for MarkedAsReadError {
+    fn from(err: T) -> Self {
+        Self(err.into())
+    }
+}
+
+impl HandleError for MarkedAsReadError {}
+
 pub fn mark_as_read(
     base: Url,
     access_token: AccessToken,
     room_id: RoomId,
     event_id: EventId,
-) -> Result<(RoomId, EventId), Error> {
+) -> Result<(RoomId, EventId), MarkedAsReadError> {
     let params = SetReadMarkerParameters { access_token };
 
     let body = SetReadMarkerBody {
@@ -322,13 +457,23 @@ pub fn mark_as_read(
 
     Ok((room_id, event_id))
 }
+#[derive(Debug)]
+pub struct SetRoomNameError(Error);
+
+impl<T: Into<Error>> From<T> for SetRoomNameError {
+    fn from(err: T) -> Self {
+        Self(err.into())
+    }
+}
+
+impl HandleError for SetRoomNameError {}
 
 pub fn set_room_name(
     base: Url,
     access_token: AccessToken,
     room_id: RoomId,
     name: String,
-) -> Result<(), Error> {
+) -> Result<(), SetRoomNameError> {
     let params = CreateStateEventsForKeyParameters { access_token };
 
     let body = json!({
@@ -341,12 +486,23 @@ pub fn set_room_name(
     Ok(())
 }
 
+#[derive(Debug)]
+pub struct SetRoomTopicError(Error);
+
+impl<T: Into<Error>> From<T> for SetRoomTopicError {
+    fn from(err: T) -> Self {
+        Self(err.into())
+    }
+}
+
+impl HandleError for SetRoomTopicError {}
+
 pub fn set_room_topic(
     base: Url,
     access_token: AccessToken,
     room_id: RoomId,
     topic: String,
-) -> Result<(), Error> {
+) -> Result<(), SetRoomTopicError> {
     let params = CreateStateEventsForKeyParameters { access_token };
 
     let body = json!({
@@ -359,12 +515,29 @@ pub fn set_room_topic(
     Ok(())
 }
 
+#[derive(Debug)]
+pub struct SetRoomAvatarError(Error);
+
+impl<T: Into<Error>> From<T> for SetRoomAvatarError {
+    fn from(err: T) -> Self {
+        Self(err.into())
+    }
+}
+
+impl From<AttachedFileError> for SetRoomAvatarError {
+    fn from(err: AttachedFileError) -> Self {
+        Self(err.0)
+    }
+}
+
+impl HandleError for SetRoomAvatarError {}
+
 pub fn set_room_avatar(
     base: Url,
     access_token: AccessToken,
     room_id: RoomId,
     avatar: String,
-) -> Result<(), Error> {
+) -> Result<(), SetRoomAvatarError> {
     let params = CreateStateEventsForKeyParameters {
         access_token: access_token.clone(),
     };
@@ -378,11 +551,31 @@ pub fn set_room_avatar(
     Ok(())
 }
 
+#[derive(Debug)]
+pub struct AttachedFileError(Error);
+
+impl<T: Into<Error>> From<T> for AttachedFileError {
+    fn from(err: T) -> Self {
+        Self(err.into())
+    }
+}
+
+impl HandleError for AttachedFileError {
+    fn handle_error(&self) {
+        let err_str = format!("{:?}", self);
+        error!(
+            "attaching {}: retrying send",
+            remove_matrix_access_token_if_present(&err_str).unwrap_or(err_str)
+        );
+        APPOP!(retry_send);
+    }
+}
+
 pub fn upload_file(
     base: Url,
     access_token: AccessToken,
     fname: &str,
-) -> Result<CreateContentResponse, Error> {
+) -> Result<CreateContentResponse, AttachedFileError> {
     let params_upload = CreateContentParameters {
         access_token,
         filename: None,
@@ -404,12 +597,36 @@ pub enum RoomType {
     Private,
 }
 
+#[derive(Debug)]
+pub struct NewRoomError(Error);
+
+impl<T: Into<Error>> From<T> for NewRoomError {
+    fn from(err: T) -> Self {
+        Self(err.into())
+    }
+}
+
+impl HandleError for NewRoomError {
+    fn handle_error(&self) {
+        let err_str = format!("{:?}", self);
+        error!(
+            "{}",
+            remove_matrix_access_token_if_present(&err_str).unwrap_or(err_str)
+        );
+
+        let error = i18n("Can’t create the room, try again");
+        let state = AppState::NoRoom;
+        APPOP!(show_error, (error));
+        APPOP!(set_state, (state));
+    }
+}
+
 pub fn new_room(
     base: Url,
     access_token: AccessToken,
     name: String,
     privacy: RoomType,
-) -> Result<Room, Error> {
+) -> Result<Room, NewRoomError> {
     let params = CreateRoomParameters { access_token };
 
     let (visibility, preset) = match privacy {
@@ -481,7 +698,7 @@ pub fn direct_chat(
     access_token: AccessToken,
     user_id: UserId,
     user: Member,
-) -> Result<Room, Error> {
+) -> Result<Room, NewRoomError> {
     let params = CreateRoomParameters {
         access_token: access_token.clone(),
     };
@@ -513,7 +730,7 @@ pub fn direct_chat(
 
     if let Err(err) = directs {
         error!("Can't set m.direct: {:?}", err);
-        return Err(err);
+        return Err(NewRoomError(err));
     }
 
     Ok(Room {
@@ -523,13 +740,24 @@ pub fn direct_chat(
     })
 }
 
+#[derive(Debug)]
+pub struct AddedToFavError(Error);
+
+impl<T: Into<Error>> From<T> for AddedToFavError {
+    fn from(err: T) -> Self {
+        Self(err.into())
+    }
+}
+
+impl HandleError for AddedToFavError {}
+
 pub fn add_to_fav(
     base: Url,
     access_token: AccessToken,
     user_id: UserId,
     room_id: RoomId,
     tofav: bool,
-) -> Result<(RoomId, bool), Error> {
+) -> Result<(RoomId, bool), AddedToFavError> {
     let request = if tofav {
         let params = CreateTagParameters { access_token };
         let body = CreateTagBody { order: Some(0.5) };
@@ -544,12 +772,23 @@ pub fn add_to_fav(
     Ok((room_id, tofav))
 }
 
+#[derive(Debug)]
+pub struct InviteError(Error);
+
+impl<T: Into<Error>> From<T> for InviteError {
+    fn from(err: T) -> Self {
+        Self(err.into())
+    }
+}
+
+impl HandleError for InviteError {}
+
 pub fn invite(
     base: Url,
     access_token: AccessToken,
     room_id: RoomId,
     user_id: UserId,
-) -> Result<(), Error> {
+) -> Result<(), InviteError> {
     let params = InviteUserParameters { access_token };
     let body = InviteUserBody { user_id };
 
@@ -559,13 +798,32 @@ pub fn invite(
     Ok(())
 }
 
+#[derive(Debug)]
+pub struct ChangeLanguageError(Error);
+
+impl<T: Into<Error>> From<T> for ChangeLanguageError {
+    fn from(err: T) -> Self {
+        Self(err.into())
+    }
+}
+
+impl HandleError for ChangeLanguageError {
+    fn handle_error(&self) {
+        let err_str = format!("{:?}", self);
+        error!(
+            "Error forming url to set room language: {}",
+            remove_matrix_access_token_if_present(&err_str).unwrap_or(err_str)
+        );
+    }
+}
+
 pub fn set_language(
     access_token: AccessToken,
     base: Url,
     user_id: UserId,
     room_id: RoomId,
     input_language: String,
-) -> Result<(), Error> {
+) -> Result<(), ChangeLanguageError> {
     let params = SetRoomAccountDataParameters { access_token };
 
     let body = json!(Language { input_language });
diff --git a/fractal-gtk/src/backend/sync.rs b/fractal-gtk/src/backend/sync.rs
index 9fbe2544..c6abd4de 100644
--- a/fractal-gtk/src/backend/sync.rs
+++ b/fractal-gtk/src/backend/sync.rs
@@ -31,6 +31,10 @@ use std::{
     time::{self, Duration},
 };
 
+use super::{remove_matrix_access_token_if_present, HandleError};
+use crate::app::App;
+use crate::APPOP;
+
 pub enum RoomElement {
     Name(RoomId, String),
     Topic(RoomId, String),
@@ -39,17 +43,76 @@ pub enum RoomElement {
     RemoveMessage(RoomId, EventId),
 }
 
+#[derive(Debug)]
+pub struct SyncError(Error, u64);
+
+impl HandleError for SyncError {
+    fn handle_error(&self) {
+        let err_str = format!("{:?}", self.0);
+        error!(
+            "SYNC Error: {}",
+            remove_matrix_access_token_if_present(&err_str).unwrap_or(err_str)
+        );
+        let new_number_tries = self.1 + 1;
+        APPOP!(sync_error, (new_number_tries));
+    }
+}
+
+#[derive(Debug)]
+pub struct RoomsError(Error);
+
+impl<T: Into<Error>> From<T> for RoomsError {
+    fn from(err: T) -> Self {
+        Self(err.into())
+    }
+}
+
+impl HandleError for RoomsError {}
+
+#[derive(Debug)]
+pub struct UpdateRoomsError(Error);
+
+impl<T: Into<Error>> From<T> for UpdateRoomsError {
+    fn from(err: T) -> Self {
+        Self(err.into())
+    }
+}
+
+impl HandleError for UpdateRoomsError {}
+
+#[derive(Debug)]
+pub struct RoomMessagesError(Error);
+
+impl<T: Into<Error>> From<T> for RoomMessagesError {
+    fn from(err: T) -> Self {
+        Self(err.into())
+    }
+}
+
+impl HandleError for RoomMessagesError {}
+
+#[derive(Debug)]
+pub struct RoomElementError(Error);
+
+impl<T: Into<Error>> From<T> for RoomElementError {
+    fn from(err: T) -> Self {
+        Self(err.into())
+    }
+}
+
+impl HandleError for RoomElementError {}
+
 pub enum SyncRet {
     NoSince {
-        rooms: Result<(Vec<Room>, Option<Room>), Error>,
+        rooms: Result<(Vec<Room>, Option<Room>), RoomsError>,
         next_batch: String,
     },
     WithSince {
-        update_rooms: Result<Vec<Room>, Error>,
-        room_messages: Result<Vec<Message>, Error>,
+        update_rooms: Result<Vec<Room>, UpdateRoomsError>,
+        room_messages: Result<Vec<Message>, RoomMessagesError>,
         room_notifications: HashMap<RoomId, UnreadNotificationsCount>,
-        update_rooms_2: Result<Vec<Room>, Error>,
-        other: Result<Vec<RoomElement>, Error>,
+        update_rooms_2: Result<Vec<Room>, UpdateRoomsError>,
+        other: Result<Vec<RoomElement>, RoomElementError>,
         next_batch: String,
     },
 }
@@ -62,7 +125,7 @@ pub fn sync(
     since: Option<String>,
     initial: bool,
     number_tries: u64,
-) -> Result<SyncRet, (Error, u64)> {
+) -> Result<SyncRet, SyncError> {
     let (timeout, filter) = if !initial {
         (time::Duration::from_secs(30), Default::default())
     } else {
@@ -294,7 +357,7 @@ pub fn sync(
             );
             thread::sleep(time::Duration::from_secs(waiting_time));
 
-            Err((err, number_tries))
+            Err(SyncError(err, number_tries))
         }
     }
 }
diff --git a/fractal-gtk/src/backend/user.rs b/fractal-gtk/src/backend/user.rs
index 3d5f3a75..51f450bd 100644
--- a/fractal-gtk/src/backend/user.rs
+++ b/fractal-gtk/src/backend/user.rs
@@ -2,12 +2,15 @@ use fractal_api::identifiers::UserId;
 use fractal_api::url::Url;
 use std::fs;
 
+use super::MediaError;
+use crate::actions::global::activate_action;
 use crate::backend::ThreadPool;
 use crate::backend::HTTP_CLIENT;
 use crate::cache::CacheMap;
 use crate::error::Error;
 use crate::util::cache_dir_path;
 use crate::util::ResultExpectLog;
+use log::error;
 use std::convert::TryInto;
 use std::path::PathBuf;
 use std::sync::mpsc::Sender;
@@ -68,9 +71,25 @@ use fractal_api::r0::ThreePIDCredentials;
 
 use super::{dw_media, ContentType};
 
+use super::{remove_matrix_access_token_if_present, HandleError};
+use crate::app::App;
+use crate::i18n::i18n;
+use crate::APPOP;
+
 pub type UserInfo = (String, String);
 
-pub fn get_username(base: Url, uid: UserId) -> Result<Option<String>, Error> {
+#[derive(Debug)]
+pub struct NameError(Error);
+
+impl<T: Into<Error>> From<T> for NameError {
+    fn from(err: T) -> Self {
+        Self(err.into())
+    }
+}
+
+impl HandleError for NameError {}
+
+pub fn get_username(base: Url, uid: UserId) -> Result<Option<String>, NameError> {
     let request = get_display_name(base, &uid)?;
     let response: GetDisplayNameResponse = HTTP_CLIENT.get_client()?.execute(request)?.json()?;
 
@@ -94,12 +113,23 @@ pub fn get_username_async(base: Url, uid: UserId) -> String {
         .unwrap_or_else(|| uid.to_string())
 }
 
+#[derive(Debug)]
+pub struct SetUserNameError(Error);
+
+impl<T: Into<Error>> From<T> for SetUserNameError {
+    fn from(err: T) -> Self {
+        Self(err.into())
+    }
+}
+
+impl HandleError for SetUserNameError {}
+
 pub fn set_username(
     base: Url,
     access_token: AccessToken,
     uid: UserId,
     username: String,
-) -> Result<String, Error> {
+) -> Result<String, SetUserNameError> {
     let params = SetDisplayNameParameters { access_token };
     let body = SetDisplayNameBody {
         displayname: Some(username.clone()),
@@ -111,10 +141,30 @@ pub fn set_username(
     Ok(username)
 }
 
+#[derive(Debug)]
+pub struct GetThreePIDError;
+
+impl<T: Into<Error>> From<T> for GetThreePIDError {
+    fn from(_: T) -> Self {
+        Self
+    }
+}
+
+impl HandleError for GetThreePIDError {
+    fn handle_error(&self) {
+        let error = i18n("Sorry, account settings can’t be loaded.");
+        APPOP!(show_load_settings_error_dialog, (error));
+        let ctx = glib::MainContext::default();
+        ctx.invoke(move || {
+            activate_action("app", "back");
+        })
+    }
+}
+
 pub fn get_threepid(
     base: Url,
     access_token: AccessToken,
-) -> Result<Vec<ThirdPartyIdentifier>, Error> {
+) -> Result<Vec<ThirdPartyIdentifier>, GetThreePIDError> {
     let params = ThirdPartyIDParameters { access_token };
 
     let request = get_identifiers(base, &params)?;
@@ -123,13 +173,50 @@ pub fn get_threepid(
     Ok(response.threepids)
 }
 
+#[derive(Debug)]
+pub enum GetTokenEmailError {
+    TokenUsed,
+    Denied,
+    Other(Error),
+}
+
+impl<T: Into<Error>> From<T> for GetTokenEmailError {
+    fn from(err: T) -> Self {
+        Self::Other(err.into())
+    }
+}
+
+impl HandleError for GetTokenEmailError {
+    fn handle_error(&self) {
+        match self {
+            Self::TokenUsed => {
+                let error = i18n("Email is already in use");
+                APPOP!(show_error_dialog_in_settings, (error));
+            }
+            Self::Denied => {
+                let error = i18n("Please enter a valid email address.");
+                APPOP!(show_error_dialog_in_settings, (error));
+            }
+            Self::Other(err) => {
+                let error = i18n("Couldn’t add the email address.");
+                let err_str = format!("{:?}", err);
+                error!(
+                    "{}",
+                    remove_matrix_access_token_if_present(&err_str).unwrap_or(err_str)
+                );
+                APPOP!(show_error_dialog_in_settings, (error));
+            }
+        }
+    }
+}
+
 pub fn get_email_token(
     base: Url,
     access_token: AccessToken,
     identity: Url,
     email: String,
     client_secret: String,
-) -> Result<(String, String), Error> {
+) -> Result<(String, String), GetTokenEmailError> {
     use EmailTokenResponse::*;
 
     let params = EmailTokenParameters { access_token };
@@ -149,8 +236,47 @@ pub fn get_email_token(
         .json::<EmailTokenResponse>()?
     {
         Passed(info) => Ok((info.sid, client_secret)),
-        Failed(info) if info.errcode == "M_THREEPID_IN_USE" => Err(Error::TokenUsed),
-        Failed(_) => Err(Error::Denied),
+        Failed(info) if info.errcode == "M_THREEPID_IN_USE" => Err(GetTokenEmailError::TokenUsed),
+        Failed(_) => Err(GetTokenEmailError::Denied),
+    }
+}
+
+#[derive(Debug)]
+pub enum GetTokenPhoneError {
+    TokenUsed,
+    Denied,
+    Other(Error),
+}
+
+impl<T: Into<Error>> From<T> for GetTokenPhoneError {
+    fn from(err: T) -> Self {
+        Self::Other(err.into())
+    }
+}
+
+impl HandleError for GetTokenPhoneError {
+    fn handle_error(&self) {
+        match self {
+            Self::TokenUsed => {
+                let error = i18n("Phone number is already in use");
+                APPOP!(show_error_dialog_in_settings, (error));
+            }
+            Self::Denied => {
+                let error = i18n(
+                    "Please enter your phone number in the format: \n + your country code and your phone 
number.",
+                );
+                APPOP!(show_error_dialog_in_settings, (error));
+            }
+            Self::Other(err) => {
+                let error = i18n("Couldn’t add the phone number.");
+                let err_str = format!("{:?}", err);
+                error!(
+                    "{}",
+                    remove_matrix_access_token_if_present(&err_str).unwrap_or(err_str)
+                );
+                APPOP!(show_error_dialog_in_settings, (error));
+            }
+        }
     }
 }
 
@@ -160,7 +286,7 @@ pub fn get_phone_token(
     identity: Url,
     phone: String,
     client_secret: String,
-) -> Result<(String, String), Error> {
+) -> Result<(String, String), GetTokenPhoneError> {
     use PhoneTokenResponse::*;
 
     let params = PhoneTokenParameters { access_token };
@@ -181,18 +307,29 @@ pub fn get_phone_token(
         .json::<PhoneTokenResponse>()?
     {
         Passed(info) => Ok((info.sid, client_secret)),
-        Failed(info) if info.errcode == "M_THREEPID_IN_USE" => Err(Error::TokenUsed),
-        Failed(_) => Err(Error::Denied),
+        Failed(info) if info.errcode == "M_THREEPID_IN_USE" => Err(GetTokenPhoneError::TokenUsed),
+        Failed(_) => Err(GetTokenPhoneError::Denied),
+    }
+}
+
+#[derive(Debug)]
+pub struct AddedToFavError(Error);
+
+impl<T: Into<Error>> From<T> for AddedToFavError {
+    fn from(err: T) -> Self {
+        Self(err.into())
     }
 }
 
+impl HandleError for AddedToFavError {}
+
 pub fn add_threepid(
     base: Url,
     access_token: AccessToken,
     identity: Url,
     client_secret: String,
     sid: String,
-) -> Result<(), Error> {
+) -> Result<(), AddedToFavError> {
     let params = AddThreePIDParameters { access_token };
     let body = AddThreePIDBody {
         three_pid_creds: ThreePIDCredentials {
@@ -209,12 +346,23 @@ pub fn add_threepid(
     Ok(())
 }
 
+#[derive(Debug)]
+pub struct SubmitPhoneTokenError(Error);
+
+impl<T: Into<Error>> From<T> for SubmitPhoneTokenError {
+    fn from(err: T) -> Self {
+        Self(err.into())
+    }
+}
+
+impl HandleError for SubmitPhoneTokenError {}
+
 pub fn submit_phone_token(
     base: Url,
     client_secret: String,
     sid: String,
     token: String,
-) -> Result<(Option<String>, String), Error> {
+) -> Result<(Option<String>, String), SubmitPhoneTokenError> {
     let body = SubmitPhoneTokenBody {
         sid: sid.clone(),
         client_secret: client_secret.clone(),
@@ -227,12 +375,23 @@ pub fn submit_phone_token(
     Ok((Some(sid).filter(|_| response.success), client_secret))
 }
 
+#[derive(Debug)]
+pub struct DeleteThreePIDError(Error);
+
+impl<T: Into<Error>> From<T> for DeleteThreePIDError {
+    fn from(err: T) -> Self {
+        Self(err.into())
+    }
+}
+
+impl HandleError for DeleteThreePIDError {}
+
 pub fn delete_three_pid(
     base: Url,
     access_token: AccessToken,
     medium: Medium,
     address: String,
-) -> Result<(), Error> {
+) -> Result<(), DeleteThreePIDError> {
     let params = DeleteThreePIDParameters { access_token };
     let body = DeleteThreePIDBody { address, medium };
 
@@ -242,13 +401,34 @@ pub fn delete_three_pid(
     Ok(())
 }
 
+#[derive(Debug)]
+pub struct ChangePasswordError(Error);
+
+impl<T: Into<Error>> From<T> for ChangePasswordError {
+    fn from(err: T) -> Self {
+        Self(err.into())
+    }
+}
+
+impl HandleError for ChangePasswordError {
+    fn handle_error(&self) {
+        let error = i18n("Couldn’t change the password");
+        let err_str = format!("{:?}", self);
+        error!(
+            "{}",
+            remove_matrix_access_token_if_present(&err_str).unwrap_or(err_str)
+        );
+        APPOP!(show_password_error_dialog, (error));
+    }
+}
+
 pub fn change_password(
     base: Url,
     access_token: AccessToken,
     user: String,
     old_password: String,
     new_password: String,
-) -> Result<(), Error> {
+) -> Result<(), ChangePasswordError> {
     let params = ChangePasswordParameters { access_token };
     let body = ChangePasswordBody {
         new_password,
@@ -265,12 +445,33 @@ pub fn change_password(
     Ok(())
 }
 
+#[derive(Debug)]
+pub struct AccountDestructionError(Error);
+
+impl<T: Into<Error>> From<T> for AccountDestructionError {
+    fn from(err: T) -> Self {
+        Self(err.into())
+    }
+}
+
+impl HandleError for AccountDestructionError {
+    fn handle_error(&self) {
+        let error = i18n("Couldn’t delete the account");
+        let err_str = format!("{:?}", self.0);
+        error!(
+            "{}",
+            remove_matrix_access_token_if_present(&err_str).unwrap_or(err_str)
+        );
+        APPOP!(show_error_dialog_in_settings, (error));
+    }
+}
+
 pub fn account_destruction(
     base: Url,
     access_token: AccessToken,
     user: String,
     password: String,
-) -> Result<(), Error> {
+) -> Result<(), AccountDestructionError> {
     let params = DeactivateParameters { access_token };
     let body = DeactivateBody {
         auth: Some(AuthenticationData::Password {
@@ -286,16 +487,46 @@ pub fn account_destruction(
     Ok(())
 }
 
-pub fn get_avatar(base: Url, userid: UserId) -> Result<PathBuf, Error> {
-    get_user_avatar(base, &userid).map(|(_, fname)| fname.into())
+#[derive(Debug)]
+pub struct AvatarError(Error);
+
+impl<T: Into<Error>> From<T> for AvatarError {
+    fn from(err: T) -> Self {
+        Self(err.into())
+    }
+}
+
+impl From<GetUserAvatarError> for AvatarError {
+    fn from(err: GetUserAvatarError) -> Self {
+        Self(err.0)
+    }
+}
+
+impl HandleError for AvatarError {}
+
+pub fn get_avatar(base: Url, userid: UserId) -> Result<PathBuf, AvatarError> {
+    get_user_avatar(base, &userid)
+        .map(|(_, fname)| fname.into())
+        .map_err(Into::into)
+}
+
+#[derive(Debug)]
+pub struct SetUserAvatarError(Error);
+
+impl<T: Into<Error>> From<T> for SetUserAvatarError {
+    fn from(err: T) -> Self {
+        Self(err.into())
+    }
 }
 
+impl HandleError for SetUserAvatarError {}
+
 pub fn set_user_avatar(
     base: Url,
     access_token: AccessToken,
     uid: UserId,
     avatar: PathBuf,
-) -> Result<PathBuf, Error> {
+) -> Result<PathBuf, SetUserAvatarError> {
     let params_upload = CreateContentParameters {
         access_token: access_token.clone(),
         filename: None,
@@ -343,11 +574,22 @@ pub fn get_user_info_async(
     });
 }
 
+#[derive(Debug)]
+pub struct UserSearchError(Error);
+
+impl<T: Into<Error>> From<T> for UserSearchError {
+    fn from(err: T) -> Self {
+        Self(err.into())
+    }
+}
+
+impl HandleError for UserSearchError {}
+
 pub fn search(
     base: Url,
     access_token: AccessToken,
     search_term: String,
-) -> Result<Vec<Member>, Error> {
+) -> Result<Vec<Member>, UserSearchError> {
     let params = UserDirectoryParameters { access_token };
     let body = UserDirectoryBody {
         search_term,
@@ -360,7 +602,24 @@ pub fn search(
     Ok(response.results.into_iter().map(Into::into).collect())
 }
 
-pub fn get_user_avatar(base: Url, user_id: &UserId) -> Result<(String, String), Error> {
+pub struct GetUserAvatarError(Error);
+
+impl<T: Into<Error>> From<T> for GetUserAvatarError {
+    fn from(err: T) -> Self {
+        Self(err.into())
+    }
+}
+
+impl From<MediaError> for GetUserAvatarError {
+    fn from(err: MediaError) -> Self {
+        Self(err.0)
+    }
+}
+
+pub fn get_user_avatar(
+    base: Url,
+    user_id: &UserId,
+) -> Result<(String, String), GetUserAvatarError> {
     let response = get_profile(base.clone(), user_id)
         .map_err::<Error, _>(Into::into)
         .and_then(|request| {
diff --git a/fractal-gtk/src/error.rs b/fractal-gtk/src/error.rs
index e0109494..15982fe9 100644
--- a/fractal-gtk/src/error.rs
+++ b/fractal-gtk/src/error.rs
@@ -1,4 +1,3 @@
-use fractal_api::identifiers::{EventId, RoomId};
 use std::io;
 use std::time::SystemTimeError;
 
@@ -30,10 +29,6 @@ pub enum Error {
     ReqwestError(fractal_api::reqwest::Error),
     NetworkError(fractal_api::reqwest::StatusCode),
     MatrixError(MatrixErrorCode, String),
-    SendMsgError(String),
-    SendMsgRedactionError(EventId),
-    TokenUsed,
-    Denied,
 }
 
 impl From<fractal_api::reqwest::Error> for Error {
@@ -51,53 +46,6 @@ impl From<StandardErrorResponse> for Error {
 derror!(fractal_api::url::ParseError, Error::BackendError);
 derror!(io::Error, Error::BackendError);
 derror!(glib::error::Error, Error::BackendError);
-derror!(regex::Error, Error::BackendError);
 derror!(fractal_api::identifiers::Error, Error::BackendError);
 derror!(SystemTimeError, Error::BackendError);
-
 derror!(serde_json::Error, Error::CacheError);
-
-#[derive(Debug)]
-pub enum BKError {
-    LoginError(Error),
-    SendTypingError(Error),
-    InviteError(Error),
-    ChangeLanguageError(Error),
-    NameError(Error),
-    AvatarError(Error),
-    MarkedAsReadError(Error),
-    UserSearchError(Error),
-    LogoutError(Error),
-    LeaveRoomError(Error),
-    DirectoryProtocolsError(Error),
-    RoomMembersError(Error),
-    AddedToFavError(Error),
-    GetThreePIDError(Error),
-    AddThreePIDError(Error),
-    SubmitPhoneTokenError(Error),
-    SetUserNameError(Error),
-    ChangePasswordError(Error),
-    AccountDestructionError(Error),
-    DeleteThreePIDError(Error),
-    GetTokenPhoneError(Error),
-    GetTokenEmailError(Error),
-    SetRoomNameError(Error),
-    SetRoomTopicError(Error),
-    SetUserAvatarError(Error),
-    SetRoomAvatarError(Error),
-    RoomMessagesToError(Error),
-    MediaError(Error),
-    SentMsgRedactionError(Error),
-    JoinRoomError(Error),
-    DirectorySearchError(Error),
-    NewRoomError(Error, RoomId),
-    RoomDetailError(Error),
-    RoomAvatarError(Error),
-    SentMsgError(Error),
-    AttachedFileError(Error),
-    RoomsError(Error),
-    UpdateRoomsError(Error),
-    RoomMessagesError(Error),
-    RoomElementError(Error),
-    SyncError(Error, u64),
-}
diff --git a/fractal-gtk/src/meson.build b/fractal-gtk/src/meson.build
index 3a60537b..cf955208 100644
--- a/fractal-gtk/src/meson.build
+++ b/fractal-gtk/src/meson.build
@@ -66,7 +66,6 @@ app_sources = files(
   'app/connect/new_room.rs',
   'app/connect/roomlist_search.rs',
   'app/connect/send.rs',
-  'app/backend_loop.rs',
   'app/mod.rs',
   'app/windowstate.rs',
   'appop/about.rs',
diff --git a/fractal-gtk/src/widgets/address.rs b/fractal-gtk/src/widgets/address.rs
index f8b739ff..883d229c 100644
--- a/fractal-gtk/src/widgets/address.rs
+++ b/fractal-gtk/src/widgets/address.rs
@@ -8,10 +8,9 @@ use rand::distributions::Alphanumeric;
 use rand::{thread_rng, Rng};
 use std::thread;
 
-use crate::app::dispatch_error;
 use crate::app::App;
 use crate::appop::AppOp;
-use crate::error::BKError;
+use crate::backend::HandleError;
 
 #[derive(Debug, Clone)]
 pub enum AddressType {
@@ -213,7 +212,7 @@ fn delete_address(medium: Medium, address: String, server_url: Url, access_token
                 APPOP!(get_three_pid);
             }
             Err(err) => {
-                dispatch_error(BKError::DeleteThreePIDError(err));
+                err.handle_error();
             }
         }
     });
@@ -236,7 +235,7 @@ fn add_address(
                     APPOP!(get_token_phone, (sid, secret))
                 }
                 Err(err) => {
-                    dispatch_error(BKError::GetTokenPhoneError(err));
+                    err.handle_error();
                 }
             }
         }
@@ -248,7 +247,7 @@ fn add_address(
                     APPOP!(get_token_email, (sid, secret));
                 }
                 Err(err) => {
-                    dispatch_error(BKError::GetTokenEmailError(err));
+                    err.handle_error();
                 }
             }
         }
diff --git a/fractal-gtk/src/widgets/image.rs b/fractal-gtk/src/widgets/image.rs
index 33af8f78..ffaafe12 100644
--- a/fractal-gtk/src/widgets/image.rs
+++ b/fractal-gtk/src/widgets/image.rs
@@ -1,5 +1,4 @@
-use crate::backend::media;
-use crate::backend::ThreadPool;
+use crate::backend::{media, ThreadPool};
 use fractal_api::url::Url;
 use gdk::prelude::GdkContextExt;
 use gdk_pixbuf::Pixbuf;
@@ -12,11 +11,10 @@ use gtk::DrawingArea;
 use log::error;
 use std::path::Path;
 use std::sync::mpsc::channel;
+use std::sync::mpsc::TryRecvError;
 use std::sync::mpsc::{Receiver, Sender};
 use std::sync::{Arc, Mutex};
 
-use std::sync::mpsc::TryRecvError;
-
 #[derive(Clone, Debug)]
 pub struct Image {
     pub path: String,
diff --git a/fractal-gtk/src/widgets/room.rs b/fractal-gtk/src/widgets/room.rs
index b64797c4..531ef6d6 100644
--- a/fractal-gtk/src/widgets/room.rs
+++ b/fractal-gtk/src/widgets/room.rs
@@ -6,13 +6,11 @@ use std::thread;
 
 use crate::types::Room;
 
-use crate::error::BKError;
-
 use crate::util::markup_text;
 
-use crate::app::dispatch_error;
 use crate::app::App;
 use crate::appop::AppOp;
+use crate::backend::HandleError;
 
 use crate::widgets;
 use crate::widgets::AvatarExt;
@@ -136,7 +134,7 @@ impl<'a> RoomBox<'a> {
                             APPOP!(set_join_to_room, (jtr));
                             APPOP!(reload_rooms);
                         }
-                        Err(err) => dispatch_error(BKError::JoinRoomError(err)),
+                        Err(err) => err.handle_error(),
                     },
                 );
             });
diff --git a/fractal-gtk/src/widgets/room_settings.rs b/fractal-gtk/src/widgets/room_settings.rs
index dd17fc11..195ab8d8 100644
--- a/fractal-gtk/src/widgets/room_settings.rs
+++ b/fractal-gtk/src/widgets/room_settings.rs
@@ -1,4 +1,4 @@
-use crate::backend::room;
+use crate::backend::{room, HandleError};
 use crate::clone;
 use fractal_api::identifiers::UserId;
 use fractal_api::r0::AccessToken;
@@ -13,9 +13,7 @@ use gtk::prelude::*;
 
 use crate::actions;
 use crate::actions::{ButtonState, StateExt};
-use crate::app::dispatch_error;
 use crate::app::App;
-use crate::error::BKError;
 use crate::types::Member;
 use crate::types::Room;
 use crate::util::markup_text;
@@ -434,7 +432,7 @@ impl RoomSettings {
                     APPOP!(set_room_avatar, (room, avatar));
                 }
                 Err(err) => {
-                    dispatch_error(BKError::RoomAvatarError(err));
+                    err.handle_error();
                 }
             },
         );
@@ -505,7 +503,7 @@ impl RoomSettings {
                     APPOP!(show_new_room_name);
                 }
                 Err(err) => {
-                    dispatch_error(BKError::SetRoomNameError(err));
+                    err.handle_error();
                 }
             },
         );
@@ -563,7 +561,7 @@ impl RoomSettings {
                     APPOP!(show_new_room_topic);
                 }
                 Err(err) => {
-                    dispatch_error(BKError::SetRoomTopicError(err));
+                    err.handle_error();
                 }
             },
         );


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