[fractal] API: Use UserId from ruma-identifiers for stronger validation



commit 923748082d54ad6cf829f10f29e622fc1f1ee3dd
Author: Alejandro Domínguez <adomu net-c com>
Date:   Fri Jan 17 15:54:27 2020 +0100

    API: Use UserId from ruma-identifiers for stronger validation
    
    The crate ruma-identifiers was updated to support
    historical IDs, so it can be safely used now

 fractal-gtk/src/actions/account_settings.rs        |   3 +-
 fractal-gtk/src/app/backend_loop.rs                |   6 +-
 fractal-gtk/src/appop/account.rs                   |  14 +-
 fractal-gtk/src/appop/invite.rs                    |   4 +-
 fractal-gtk/src/appop/login.rs                     |   3 +-
 fractal-gtk/src/appop/member.rs                    |  41 +++---
 fractal-gtk/src/appop/message.rs                   |   2 +-
 fractal-gtk/src/appop/mod.rs                       |   4 +-
 fractal-gtk/src/appop/room.rs                      |  15 +-
 fractal-gtk/src/appop/user.rs                      |  12 +-
 fractal-gtk/src/cache/mod.rs                       |  21 ++-
 fractal-gtk/src/cache/state.rs                     |   3 +-
 fractal-gtk/src/passwd.rs                          |  30 ++--
 fractal-gtk/src/uitypes.rs                         |   3 +-
 fractal-gtk/src/widgets/autocomplete.rs            |   2 +-
 fractal-gtk/src/widgets/avatar.rs                  |  18 +--
 fractal-gtk/src/widgets/member.rs                  |   8 +-
 fractal-gtk/src/widgets/members_list.rs            |  31 ++--
 fractal-gtk/src/widgets/message.rs                 |  20 +--
 fractal-gtk/src/widgets/room_settings.rs           |  38 ++---
 fractal-matrix-api/src/backend/media.rs            |   2 +-
 fractal-matrix-api/src/backend/mod.rs              |   8 +-
 fractal-matrix-api/src/backend/register.rs         |  11 +-
 fractal-matrix-api/src/backend/room.rs             | 130 +++++++++--------
 fractal-matrix-api/src/backend/sync.rs             |  98 +++++++------
 fractal-matrix-api/src/backend/types.rs            |  38 ++---
 fractal-matrix-api/src/backend/user.rs             |  31 ++--
 fractal-matrix-api/src/cache.rs                    |  31 ++--
 fractal-matrix-api/src/model/event.rs              |   4 +-
 fractal-matrix-api/src/model/member.rs             |  24 ++-
 fractal-matrix-api/src/model/message.rs            |  24 +--
 fractal-matrix-api/src/model/room.rs               | 161 ++++++++++++---------
 fractal-matrix-api/src/r0/account/login.rs         |   3 +-
 fractal-matrix-api/src/r0/account/register.rs      |   3 +-
 .../src/r0/membership/invite_user.rs               |   4 +-
 .../src/r0/profile/get_display_name.rs             |   3 +-
 fractal-matrix-api/src/r0/profile/get_profile.rs   |   3 +-
 .../src/r0/profile/set_avatar_url.rs               |   3 +-
 .../src/r0/profile/set_display_name.rs             |   3 +-
 fractal-matrix-api/src/r0/search/user.rs           |   3 +-
 .../src/r0/sync/get_joined_members.rs              |   4 +-
 fractal-matrix-api/src/r0/sync/sync_events.rs      |   6 +-
 fractal-matrix-api/src/r0/tag/create_tag.rs        |   4 +-
 fractal-matrix-api/src/r0/tag/delete_tag.rs        |   4 +-
 fractal-matrix-api/src/r0/typing.rs                |   4 +-
 fractal-matrix-api/src/util.rs                     |  28 ++--
 46 files changed, 476 insertions(+), 439 deletions(-)
---
diff --git a/fractal-gtk/src/actions/account_settings.rs b/fractal-gtk/src/actions/account_settings.rs
index e37f07cf..468cb679 100644
--- a/fractal-gtk/src/actions/account_settings.rs
+++ b/fractal-gtk/src/actions/account_settings.rs
@@ -1,4 +1,5 @@
 use crate::i18n::i18n;
+use fractal_api::identifiers::UserId;
 use fractal_api::r0::AccessToken;
 use fractal_api::url::Url;
 use gio::prelude::*;
@@ -21,7 +22,7 @@ pub fn new(
     backend: &Sender<BKCommand>,
     server_url: Url,
     access_token: AccessToken,
-    uid: String,
+    uid: UserId,
 ) -> gio::SimpleActionGroup {
     let actions = SimpleActionGroup::new();
     // TODO create two stats loading interaction and connect it to the avatar box
diff --git a/fractal-gtk/src/app/backend_loop.rs b/fractal-gtk/src/app/backend_loop.rs
index 8627597f..c86494ac 100644
--- a/fractal-gtk/src/app/backend_loop.rs
+++ b/fractal-gtk/src/app/backend_loop.rs
@@ -93,7 +93,7 @@ pub fn backend_loop(rx: Receiver<BKResponse>) {
                     let s = Some(since);
                     APPOP!(synced, (s));
                 }
-                BKResponse::Rooms(rooms, default) => {
+                BKResponse::Rooms(Ok((rooms, default))) => {
                     let clear_room_list = true;
                     APPOP!(set_rooms, (rooms, clear_room_list));
                     // Open the newly joined room
@@ -102,7 +102,7 @@ pub fn backend_loop(rx: Receiver<BKResponse>) {
                         APPOP!(set_active_room_by_id, (room_id));
                     }
                 }
-                BKResponse::UpdateRooms(rooms) => {
+                BKResponse::UpdateRooms(Ok(rooms)) => {
                     let clear_room_list = false;
                     APPOP!(set_rooms, (rooms, clear_room_list));
                 }
@@ -116,7 +116,7 @@ pub fn backend_loop(rx: Receiver<BKResponse>) {
                 BKResponse::RoomMembers(Ok((room, members))) => {
                     APPOP!(set_room_members, (room, members));
                 }
-                BKResponse::RoomMessages(msgs) => {
+                BKResponse::RoomMessages(Ok(msgs)) => {
                     APPOP!(show_room_messages, (msgs));
                 }
                 BKResponse::RoomMessagesTo(Ok((msgs, room, prev_batch))) => {
diff --git a/fractal-gtk/src/appop/account.rs b/fractal-gtk/src/appop/account.rs
index fafb3d5d..c5b102e8 100644
--- a/fractal-gtk/src/appop/account.rs
+++ b/fractal-gtk/src/appop/account.rs
@@ -289,7 +289,7 @@ impl AppOp {
 
         stack.set_visible_child_name("loading");
         self.get_three_pid();
-        uid.set_text(&login_data.uid);
+        uid.set_text(&login_data.uid.to_string());
         device_id.set_text(&self.device_id.clone().unwrap_or_default());
         homeserver.set_text(login_data.server_url.as_str());
         name.set_text(&login_data.username.unwrap_or_default());
@@ -502,7 +502,13 @@ impl AppOp {
         let w = widgets::Avatar::avatar_new(Some(100));
         avatar.add(&w);
 
-        let data = w.circle(login_data.uid.clone(), login_data.username, 100, None, None);
+        let data = w.circle(
+            login_data.uid.to_string(),
+            login_data.username,
+            100,
+            None,
+            None,
+        );
         download_to_cache(
             self.backend.clone(),
             login_data.server_url,
@@ -635,7 +641,7 @@ impl AppOp {
                     let _ = self.backend.send(BKCommand::ChangePassword(
                         login_data.server_url,
                         login_data.access_token,
-                        login_data.uid,
+                        login_data.uid.localpart().into(),
                         old.to_string(),
                         new.to_string(),
                     ));
@@ -743,7 +749,7 @@ impl AppOp {
                         let _ = backend.send(BKCommand::AccountDestruction(
                             login_data.server_url.clone(),
                             login_data.access_token.clone(),
-                            login_data.uid.clone(),
+                            login_data.uid.localpart().into(),
                             password.to_string(),
                         ));
                     }
diff --git a/fractal-gtk/src/appop/invite.rs b/fractal-gtk/src/appop/invite.rs
index d1e467a4..43ae39a3 100644
--- a/fractal-gtk/src/appop/invite.rs
+++ b/fractal-gtk/src/appop/invite.rs
@@ -1,6 +1,6 @@
 use crate::i18n::{i18n, i18n_k};
 
-use fractal_api::identifiers::RoomId;
+use fractal_api::identifiers::{RoomId, UserId};
 use gtk;
 use gtk::prelude::*;
 
@@ -80,7 +80,7 @@ impl AppOp {
         }
     }
 
-    pub fn rm_from_invite(&mut self, uid: String) {
+    pub fn rm_from_invite(&mut self, uid: UserId) {
         let idx = self.invite_list.iter().position(|x| x.0.uid == uid);
         if let Some(i) = idx {
             self.invite_list.remove(i);
diff --git a/fractal-gtk/src/appop/login.rs b/fractal-gtk/src/appop/login.rs
index 98394540..0050017d 100644
--- a/fractal-gtk/src/appop/login.rs
+++ b/fractal-gtk/src/appop/login.rs
@@ -1,5 +1,6 @@
 use log::error;
 
+use fractal_api::identifiers::UserId;
 use fractal_api::r0::AccessToken;
 
 use fractal_api::url::Url;
@@ -25,7 +26,7 @@ use super::LoginData;
 impl AppOp {
     pub fn bk_login(
         &mut self,
-        uid: String,
+        uid: UserId,
         access_token: AccessToken,
         device: Option<String>,
         server_url: Url,
diff --git a/fractal-gtk/src/appop/member.rs b/fractal-gtk/src/appop/member.rs
index c638337b..91104d67 100644
--- a/fractal-gtk/src/appop/member.rs
+++ b/fractal-gtk/src/appop/member.rs
@@ -1,9 +1,10 @@
 use fractal_api::clone;
-use fractal_api::identifiers::RoomId;
+use fractal_api::identifiers::{RoomId, UserId};
 use gtk;
 use gtk::prelude::*;
 
 use std::collections::HashMap;
+use std::convert::TryFrom;
 
 use crate::actions::AppState;
 use crate::appop::AppOp;
@@ -23,16 +24,11 @@ pub enum SearchType {
 
 impl AppOp {
     pub fn member_level(&self, member: &Member) -> i32 {
-        if let Some(r) = self
-            .active_room
+        self.active_room
             .as_ref()
-            .and_then(|a_room| self.rooms.get(a_room))
-        {
-            if let Some(level) = r.admins.get(&member.uid) {
-                return *level;
-            }
-        }
-        0
+            .and_then(|a_room| self.rooms.get(a_room)?.admins.get(&member.uid))
+            .copied()
+            .unwrap_or(0)
     }
 
     pub fn set_room_members(&mut self, room_id: RoomId, members: Vec<Member>) {
@@ -55,10 +51,10 @@ impl AppOp {
         // NOTE: maybe we should show this events in the message list to notify enters and leaves
         // to the user
 
-        let sender = ev.sender.clone();
+        let sender = ev.sender;
         match ev.content["membership"].as_str() {
             Some("leave") => {
-                if let Some(r) = self.rooms.get_mut(&ev.room.clone()) {
+                if let Some(r) = self.rooms.get_mut(&ev.room) {
                     r.members.remove(&sender);
                 }
             }
@@ -70,7 +66,7 @@ impl AppOp {
                     alias: Some(String::from(
                         ev.content["displayname"].as_str().unwrap_or_default(),
                     )),
-                    uid: sender.clone(),
+                    uid: sender,
                 };
                 if let Some(r) = self.rooms.get_mut(&ev.room.clone()) {
                     r.members.insert(m.uid.clone(), m.clone());
@@ -160,16 +156,17 @@ impl AppOp {
         }
         scroll.hide();
 
-        let t = term.unwrap_or_default();
-        let uid_in_term = t.contains("@") && t.contains(":");
+        let uid_term = term.and_then(|t| UserId::try_from(t.as_str()).ok());
         // Adding a new user if the user
-        if uid_in_term && !users.iter().find(|u| u.uid == t).is_some() {
-            let member = Member {
-                avatar: None,
-                alias: None,
-                uid: t,
-            };
-            users.insert(0, member);
+        if let Some(uid) = uid_term {
+            if let None = users.iter().find(|u| u.uid == uid) {
+                let member = Member {
+                    avatar: None,
+                    alias: None,
+                    uid,
+                };
+                users.insert(0, member);
+            }
         }
 
         for (i, u) in users.iter().enumerate() {
diff --git a/fractal-gtk/src/appop/message.rs b/fractal-gtk/src/appop/message.rs
index a56952a4..59a0671f 100644
--- a/fractal-gtk/src/appop/message.rs
+++ b/fractal-gtk/src/appop/message.rs
@@ -478,7 +478,7 @@ impl AppOp {
                     if let Some(user) = login_data.username {
                         highlights.push(user);
                     }
-                    highlights.push(login_data.uid.clone());
+                    highlights.push(login_data.uid.to_string());
                     highlights.push(String::from("message_menu"));
 
                     RowType::Mention
diff --git a/fractal-gtk/src/appop/mod.rs b/fractal-gtk/src/appop/mod.rs
index 6fab3629..aa3e8cfe 100644
--- a/fractal-gtk/src/appop/mod.rs
+++ b/fractal-gtk/src/appop/mod.rs
@@ -3,7 +3,7 @@ use std::collections::HashMap;
 use std::rc::Rc;
 use std::sync::mpsc::Sender;
 
-use fractal_api::identifiers::RoomId;
+use fractal_api::identifiers::{RoomId, UserId};
 use fractal_api::r0::AccessToken;
 
 use gtk;
@@ -49,7 +49,7 @@ use self::message::TmpMsg;
 #[derive(Clone, Debug)]
 pub struct LoginData {
     pub access_token: AccessToken,
-    pub uid: String,
+    pub uid: UserId,
     pub username: Option<String>,
     pub avatar: Option<String>,
     pub server_url: Url,
diff --git a/fractal-gtk/src/appop/room.rs b/fractal-gtk/src/appop/room.rs
index 3e146e54..eed2314f 100644
--- a/fractal-gtk/src/appop/room.rs
+++ b/fractal-gtk/src/appop/room.rs
@@ -173,14 +173,11 @@ impl AppOp {
                 .downcast::<gtk::Stack>()
                 .unwrap();
 
-            let user_power = match room.admins.get(&login_data.uid) {
-                Some(p) => *p,
-                None => room
-                    .power_levels
-                    .get("users_default")
-                    .map(|x| *x)
-                    .unwrap_or(-1),
-            };
+            let user_power = room
+                .admins
+                .get(&login_data.uid)
+                .copied()
+                .unwrap_or(room.default_power_level);
 
             // No room admin information, assuming normal
             if user_power >= 0 || room.admins.len() == 0 {
@@ -423,7 +420,7 @@ impl AppOp {
                     if *m != login_data.uid {
                         //FIXME: Find a better solution
                         // create a symlink from user avatar to room avatar (works only on unix)
-                        if let Ok(source) = cache_dir_path(None, m) {
+                        if let Ok(source) = cache_dir_path(None, &m.to_string()) {
                             if let Ok(dest) = cache_dir_path(None, &room_id.to_string()) {
                                 let _ = fs::symlink(source, dest);
                             }
diff --git a/fractal-gtk/src/appop/user.rs b/fractal-gtk/src/appop/user.rs
index 73398a6b..766bc332 100644
--- a/fractal-gtk/src/appop/user.rs
+++ b/fractal-gtk/src/appop/user.rs
@@ -53,7 +53,7 @@ impl AppOp {
                 .get_object::<gtk::Label>("user_info_uid")
                 .expect("Can't find user_info_avatar in ui file.");
 
-            uid.set_text(&login_data.uid);
+            uid.set_text(&login_data.uid.to_string());
             name.set_text(&login_data.username.clone().unwrap_or_default());
 
             /* remove all old avatar from the popover */
@@ -63,7 +63,7 @@ impl AppOp {
 
             let w = widgets::Avatar::avatar_new(Some(40));
             let data = w.circle(
-                login_data.uid.clone(),
+                login_data.uid.to_string(),
                 login_data.username.clone(),
                 40,
                 None,
@@ -86,7 +86,13 @@ impl AppOp {
         match login_data.avatar.clone() {
             Some(_) => {
                 let w = widgets::Avatar::avatar_new(Some(24));
-                let data = w.circle(login_data.uid.clone(), login_data.username, 24, None, None);
+                let data = w.circle(
+                    login_data.uid.to_string(),
+                    login_data.username,
+                    24,
+                    None,
+                    None,
+                );
                 download_to_cache(
                     self.backend.clone(),
                     login_data.server_url.clone(),
diff --git a/fractal-gtk/src/cache/mod.rs b/fractal-gtk/src/cache/mod.rs
index 1bbc22f0..1238dd3a 100644
--- a/fractal-gtk/src/cache/mod.rs
+++ b/fractal-gtk/src/cache/mod.rs
@@ -6,6 +6,7 @@ use serde::{Deserialize, Serialize};
 use crate::types::Room;
 use crate::types::RoomList;
 use failure::Error;
+use fractal_api::identifiers::UserId;
 use std::collections::HashMap;
 
 use crate::globals;
@@ -32,7 +33,7 @@ pub struct CacheData {
     pub since: Option<String>,
     pub rooms: RoomList,
     pub username: String,
-    pub uid: String,
+    pub uid: UserId,
     pub device_id: String,
 }
 
@@ -40,7 +41,7 @@ pub fn store(
     rooms: &RoomList,
     since: Option<String>,
     username: String,
-    uid: String,
+    uid: UserId,
     device_id: String,
 ) -> Result<(), Error> {
     // don't store all messages in the cache
@@ -100,15 +101,11 @@ pub fn load() -> Result<CacheData, Error> {
 pub fn download_to_cache(
     backend: Sender<BKCommand>,
     server_url: Url,
-    name: String,
+    uid: UserId,
     data: Rc<RefCell<AvatarData>>,
 ) {
     let (tx, rx) = channel::<(String, String)>();
-    let _ = backend.send(BKCommand::GetUserInfoAsync(
-        server_url,
-        name.clone(),
-        Some(tx),
-    ));
+    let _ = backend.send(BKCommand::GetUserInfoAsync(server_url, uid, Some(tx)));
 
     gtk::timeout_add(50, move || match rx.try_recv() {
         Err(TryRecvError::Empty) => gtk::Continue(true),
@@ -124,13 +121,13 @@ pub fn download_to_cache(
 pub fn download_to_cache_username(
     backend: Sender<BKCommand>,
     server_url: Url,
-    uid: &str,
+    uid: UserId,
     label: gtk::Label,
     avatar: Option<Rc<RefCell<AvatarData>>>,
 ) {
     let (tx, rx): (Sender<String>, Receiver<String>) = channel();
     backend
-        .send(BKCommand::GetUserNameAsync(server_url, uid.to_string(), tx))
+        .send(BKCommand::GetUserNameAsync(server_url, uid, tx))
         .unwrap();
     gtk::timeout_add(50, move || match rx.try_recv() {
         Err(TryRecvError::Empty) => gtk::Continue(true),
@@ -152,14 +149,14 @@ pub fn download_to_cache_username(
 pub fn download_to_cache_username_emote(
     backend: Sender<BKCommand>,
     server_url: Url,
-    uid: &str,
+    uid: UserId,
     text: &str,
     label: gtk::Label,
     avatar: Option<Rc<RefCell<AvatarData>>>,
 ) {
     let (tx, rx): (Sender<String>, Receiver<String>) = channel();
     backend
-        .send(BKCommand::GetUserNameAsync(server_url, uid.to_string(), tx))
+        .send(BKCommand::GetUserNameAsync(server_url, uid, tx))
         .unwrap();
     let text = text.to_string();
     gtk::timeout_add(50, move || match rx.try_recv() {
diff --git a/fractal-gtk/src/cache/state.rs b/fractal-gtk/src/cache/state.rs
index 2456c56c..61a7751e 100644
--- a/fractal-gtk/src/cache/state.rs
+++ b/fractal-gtk/src/cache/state.rs
@@ -13,6 +13,7 @@ use std::sync::{Arc, Mutex, MutexGuard};
 
 use crate::types::Message;
 use crate::types::Room;
+use fractal_api::identifiers::UserId;
 use fractal_api::util::cache_dir_path;
 
 // Models
@@ -24,7 +25,7 @@ use fractal_api::util::cache_dir_path;
 pub struct AppState {
     pub since: Option<String>,
     pub username: String,
-    pub uid: String,
+    pub uid: UserId,
     pub device_id: String,
 }
 
diff --git a/fractal-gtk/src/passwd.rs b/fractal-gtk/src/passwd.rs
index 0428e84b..eef37a29 100644
--- a/fractal-gtk/src/passwd.rs
+++ b/fractal-gtk/src/passwd.rs
@@ -1,4 +1,5 @@
 use fractal_api::derror;
+use fractal_api::identifiers::{Error as IdError, UserId};
 use fractal_api::r0::AccessToken;
 use fractal_api::url::ParseError;
 use fractal_api::url::Url;
@@ -8,6 +9,7 @@ use secret_service;
 pub enum Error {
     SecretServiceError,
     UrlParseError(ParseError),
+    IdParseError(IdError),
 }
 
 impl From<ParseError> for Error {
@@ -16,6 +18,12 @@ impl From<ParseError> for Error {
     }
 }
 
+impl From<IdError> for Error {
+    fn from(err: IdError) -> Error {
+        Error::IdParseError(err)
+    }
+}
+
 derror!(secret_service::SsError, Error::SecretServiceError);
 
 pub trait PasswordStorage {
@@ -37,19 +45,21 @@ pub trait PasswordStorage {
         ss_storage::get_pass()
     }
 
-    fn store_token(&self, uid: String, token: AccessToken) -> Result<(), Error> {
+    fn store_token(&self, uid: UserId, token: AccessToken) -> Result<(), Error> {
         ss_storage::store_token(uid, token)
     }
 
-    fn get_token(&self) -> Result<(Option<AccessToken>, String), Error> {
+    fn get_token(&self) -> Result<(Option<AccessToken>, UserId), Error> {
         ss_storage::get_token()
     }
 }
 
 mod ss_storage {
     use super::Error;
+    use fractal_api::identifiers::UserId;
     use fractal_api::r0::AccessToken;
     use fractal_api::url::Url;
+    use std::convert::TryFrom;
 
     use super::secret_service::EncryptionType;
     use super::secret_service::SecretService;
@@ -73,7 +83,7 @@ mod ss_storage {
         Ok(())
     }
 
-    pub fn store_token(uid: String, token: AccessToken) -> Result<(), Error> {
+    pub fn store_token(uid: UserId, token: AccessToken) -> Result<(), Error> {
         let ss = SecretService::new(EncryptionType::Dh)?;
         let collection = ss.get_default_collection()?;
         let key = "fractal-token";
@@ -84,17 +94,17 @@ mod ss_storage {
         // create new item
         collection.unlock()?;
         collection.create_item(
-            key,                          // label
-            vec![("uid", &uid)],          // properties
-            token.to_string().as_bytes(), // secret
-            true,                         // replace item with same attributes
-            "text/plain",                 // secret content type
+            key,                             // label
+            vec![("uid", &uid.to_string())], // properties
+            token.to_string().as_bytes(),    // secret
+            true,                            // replace item with same attributes
+            "text/plain",                    // secret content type
         )?;
 
         Ok(())
     }
 
-    pub fn get_token() -> Result<(Option<AccessToken>, String), Error> {
+    pub fn get_token() -> Result<(Option<AccessToken>, UserId), Error> {
         let ss = SecretService::new(EncryptionType::Dh)?;
         let collection = ss.get_default_collection()?;
         let allpass = collection.get_all_items()?;
@@ -120,7 +130,7 @@ mod ss_storage {
             .iter()
             .find(|&ref x| x.0 == "uid")
             .ok_or(Error::SecretServiceError)?;
-        let uid = attr.1.clone();
+        let uid = UserId::try_from(attr.1.as_str())?;
 
         Ok((token, uid))
     }
diff --git a/fractal-gtk/src/uitypes.rs b/fractal-gtk/src/uitypes.rs
index 68089a48..549463f6 100644
--- a/fractal-gtk/src/uitypes.rs
+++ b/fractal-gtk/src/uitypes.rs
@@ -2,6 +2,7 @@ use crate::types::Message;
 use crate::widgets;
 use chrono::prelude::DateTime;
 use chrono::prelude::Local;
+use fractal_api::identifiers::UserId;
 
 /* MessageContent contains all data needed to display one row
  * therefore it should contain only one Message body with one format
@@ -9,7 +10,7 @@ use chrono::prelude::Local;
 #[derive(Debug, Clone)]
 pub struct MessageContent {
     pub id: String,
-    pub sender: String,
+    pub sender: UserId,
     pub sender_name: Option<String>,
     pub mtype: RowType,
     pub body: String,
diff --git a/fractal-gtk/src/widgets/autocomplete.rs b/fractal-gtk/src/widgets/autocomplete.rs
index 0406f425..2afa0cac 100644
--- a/fractal-gtk/src/widgets/autocomplete.rs
+++ b/fractal-gtk/src/widgets/autocomplete.rs
@@ -499,7 +499,7 @@ impl Autocomplete {
                                 let mut count = 0;
                                 for (_, m) in r.members.iter() {
                                     let alias = &m.alias.clone().unwrap_or_default().to_lowercase();
-                                    let uid = &m.uid.clone().to_lowercase()[1..];
+                                    let uid = m.uid.localpart().to_lowercase();
                                     if alias.starts_with(&w) || uid.starts_with(&w) {
                                         list.push(m.clone());
                                         count = count + 1;
diff --git a/fractal-gtk/src/widgets/avatar.rs b/fractal-gtk/src/widgets/avatar.rs
index 4ab4d399..f188ec21 100644
--- a/fractal-gtk/src/widgets/avatar.rs
+++ b/fractal-gtk/src/widgets/avatar.rs
@@ -19,7 +19,7 @@ pub enum AvatarBadgeColor {
 pub type Avatar = gtk::Overlay;
 
 pub struct AvatarData {
-    uid: String,
+    id: String,
     username: Option<String>,
     size: i32,
     cache: Option<Pixbuf>,
@@ -31,13 +31,13 @@ impl AvatarData {
     pub fn redraw_fallback(&mut self, username: Option<String>) {
         self.username = username.clone();
         /* This function should never fail */
-        self.fallback = letter_avatar::generate::new(self.uid.clone(), username, self.size as f64)
+        self.fallback = letter_avatar::generate::new(self.id.clone(), username, self.size as f64)
             .expect("this function should never fail");
         self.widget.queue_draw();
     }
 
     pub fn redraw_pixbuf(&mut self) {
-        let path = cache_dir_path(None, &self.uid).unwrap_or_default();
+        let path = cache_dir_path(None, self.id.as_str()).unwrap_or_default();
         self.cache = load_pixbuf(&path, self.size);
         self.widget.queue_draw();
     }
@@ -49,7 +49,7 @@ pub trait AvatarExt {
     fn create_da(&self, size: Option<i32>) -> DrawingArea;
     fn circle(
         &self,
-        uid: String,
+        id: String,
         username: Option<String>,
         size: i32,
         badge: Option<AvatarBadgeColor>,
@@ -84,14 +84,14 @@ impl AvatarExt for gtk::Overlay {
         b
     }
     /// # Arguments
-    /// * `uid` - Matrix ID
+    /// * `id` - User or Room ID
     /// * `username` - Full name
     /// * `size` - Size of the avatar
     /// * `badge_color` - Badge color. None for no badge
     /// * `badge_size` - Badge size. None for size / 3
     fn circle(
         &self,
-        uid: String,
+        id: String,
         username: Option<String>,
         size: i32,
         badge_color: Option<AvatarBadgeColor>,
@@ -99,7 +99,7 @@ impl AvatarExt for gtk::Overlay {
     ) -> Rc<RefCell<AvatarData>> {
         self.clean();
         let da = self.create_da(Some(size));
-        let path = cache_dir_path(None, &uid).unwrap_or_default();
+        let path = cache_dir_path(None, id.as_str()).unwrap_or_default();
         let user_avatar = load_pixbuf(&path, size);
         let uname = username.clone();
         /* remove IRC postfix from the username */
@@ -109,7 +109,7 @@ impl AvatarExt for gtk::Overlay {
             None
         };
         /* This function should never fail */
-        let fallback = letter_avatar::generate::new(uid.clone(), username, size as f64)
+        let fallback = letter_avatar::generate::new(id.clone(), username, size as f64)
             .expect("this function should never fail");
 
         // Power level badge setup
@@ -130,7 +130,7 @@ impl AvatarExt for gtk::Overlay {
         }
 
         let data = AvatarData {
-            uid: uid.clone(),
+            id,
             username: uname,
             size: size,
             cache: user_avatar,
diff --git a/fractal-gtk/src/widgets/member.rs b/fractal-gtk/src/widgets/member.rs
index 0d219f38..9d291d6e 100644
--- a/fractal-gtk/src/widgets/member.rs
+++ b/fractal-gtk/src/widgets/member.rs
@@ -32,7 +32,7 @@ impl<'a> MemberBox<'a> {
         let w = gtk::Box::new(gtk::Orientation::Horizontal, 5);
         let v = gtk::Box::new(gtk::Orientation::Vertical, 0);
 
-        uid.set_text(&self.member.uid);
+        uid.set_text(&self.member.uid.to_string());
         uid.set_valign(gtk::Align::Start);
         uid.set_halign(gtk::Align::Start);
         uid.get_style_context().add_class("member-uid");
@@ -40,7 +40,7 @@ impl<'a> MemberBox<'a> {
         username.set_text(&self.member.get_alias());
         let mut alias = self.member.get_alias();
         alias.push_str("\n");
-        alias.push_str(&self.member.uid);
+        alias.push_str(&self.member.uid.to_string());
         username.set_tooltip_text(Some(&alias[..]));
         username.set_margin_end(5);
         username.set_ellipsize(pango::EllipsizeMode::End);
@@ -55,7 +55,7 @@ impl<'a> MemberBox<'a> {
             _ => None,
         };
         let data = avatar.circle(
-            self.member.uid.clone(),
+            self.member.uid.to_string(),
             Some(alias.clone()),
             globals::USERLIST_ICON_SIZE,
             badge,
@@ -95,7 +95,7 @@ impl<'a> MemberBox<'a> {
 
         let avatar = widgets::Avatar::avatar_new(Some(globals::PILL_ICON_SIZE));
         let data = avatar.circle(
-            self.member.uid.clone(),
+            self.member.uid.to_string(),
             Some(self.member.get_alias()),
             globals::PILL_ICON_SIZE,
             None,
diff --git a/fractal-gtk/src/widgets/members_list.rs b/fractal-gtk/src/widgets/members_list.rs
index 78bf8253..c4f351b5 100644
--- a/fractal-gtk/src/widgets/members_list.rs
+++ b/fractal-gtk/src/widgets/members_list.rs
@@ -1,4 +1,5 @@
 use fractal_api::clone;
+use fractal_api::identifiers::UserId;
 use std::cell::RefCell;
 use std::collections::hash_map::HashMap;
 use std::rc::Rc;
@@ -18,24 +19,21 @@ pub struct MembersList {
     search_entry: gtk::SearchEntry,
     error: gtk::Label,
     members: Vec<Member>,
-    admins: HashMap<String, i32>,
-    power_levels: HashMap<String, i32>,
+    admins: HashMap<UserId, i32>,
 }
 
 impl MembersList {
     pub fn new(
-        m: Vec<Member>,
-        admins: HashMap<String, i32>,
-        power_levels: HashMap<String, i32>,
-        entry: gtk::SearchEntry,
+        members: Vec<Member>,
+        admins: HashMap<UserId, i32>,
+        search_entry: gtk::SearchEntry,
     ) -> MembersList {
         MembersList {
             container: gtk::ListBox::new(),
             error: gtk::Label::new(None),
-            members: m,
-            search_entry: entry,
-            admins: admins,
-            power_levels: power_levels,
+            members,
+            search_entry,
+            admins,
         }
     }
 
@@ -61,7 +59,7 @@ impl MembersList {
 
     /* removes the content of the row with index i */
     #[allow(dead_code)]
-    pub fn update(&self, uid: String) -> Option<()> {
+    pub fn update(&self, uid: UserId) -> Option<()> {
         let mut index = None;
         for (i, member) in self.members.iter().enumerate() {
             if member.uid == uid {
@@ -147,7 +145,7 @@ fn load_row_content(member: Member, power_level: Option<i32>) -> gtk::Box {
     // Avatar
     let avatar = widgets::Avatar::avatar_new(Some(40));
     avatar.circle(
-        member.uid.clone(),
+        member.uid.to_string(),
         member.alias.clone(),
         40,
         badge_color,
@@ -183,7 +181,7 @@ fn load_row_content(member: Member, power_level: Option<i32>) -> gtk::Box {
     }
 
     // matrix ID + power level
-    let uid = gtk::Label::new(Some(member.uid.as_str()));
+    let uid = gtk::Label::new(Some(&member.uid.to_string()));
     uid.set_xalign(0.);
     uid.set_line_wrap(true);
     uid.set_line_wrap_mode(pango::WrapMode::Char);
@@ -209,14 +207,11 @@ fn load_row_content(member: Member, power_level: Option<i32>) -> gtk::Box {
 fn add_rows(
     container: gtk::ListBox,
     members: Vec<Member>,
-    admins: HashMap<String, i32>,
+    admins: HashMap<UserId, i32>,
 ) -> Option<usize> {
     /* Load just enough members to fill atleast the visible list */
     for member in members.iter() {
-        let admin = match admins.get(&member.uid) {
-            Some(pl) => Some(*pl),
-            None => None,
-        };
+        let admin = admins.get(&member.uid).copied();
         container.insert(&create_row(member.clone(), admin)?, -1);
     }
     None
diff --git a/fractal-gtk/src/widgets/message.rs b/fractal-gtk/src/widgets/message.rs
index b79f46b9..9df9b563 100644
--- a/fractal-gtk/src/widgets/message.rs
+++ b/fractal-gtk/src/widgets/message.rs
@@ -217,7 +217,7 @@ impl MessageBox {
         let avatar = widgets::Avatar::avatar_new(Some(globals::MSG_ICON_SIZE));
 
         let data = avatar.circle(
-            uid.clone(),
+            uid.to_string(),
             alias.clone(),
             globals::MSG_ICON_SIZE,
             None,
@@ -226,7 +226,7 @@ impl MessageBox {
         if let Some(name) = alias {
             self.username.set_text(&name);
         } else {
-            self.username.set_text(&uid);
+            self.username.set_text(&uid.to_string());
         }
 
         download_to_cache(
@@ -238,7 +238,7 @@ impl MessageBox {
         download_to_cache_username(
             self.backend.clone(),
             self.server_url.clone(),
-            &uid,
+            uid,
             self.username.clone(),
             Some(data.clone()),
         );
@@ -246,9 +246,7 @@ impl MessageBox {
         avatar
     }
 
-    fn build_room_msg_username(&self, sender: &str) -> gtk::Label {
-        let uname = String::from(sender);
-
+    fn build_room_msg_username(&self, uname: String) -> gtk::Label {
         self.username.set_text(&uname);
         self.username.set_justify(gtk::Justification::Left);
         self.username.set_halign(gtk::Align::Start);
@@ -581,7 +579,8 @@ impl MessageBox {
         // +----------+------+
         let info = gtk::Box::new(gtk::Orientation::Horizontal, 0);
 
-        let username = self.build_room_msg_username(&msg.sender);
+        let username =
+            self.build_room_msg_username(msg.sender_name.clone().unwrap_or(msg.sender.to_string()));
         let date = self.build_room_msg_date(&msg.date);
 
         self.username_event_box.add(&username);
@@ -595,10 +594,7 @@ impl MessageBox {
     fn build_room_msg_emote(&self, msg: &Message) -> gtk::Box {
         let bx = gtk::Box::new(gtk::Orientation::Horizontal, 0);
         /* Use MXID till we have a alias */
-        let sname = msg
-            .sender_name
-            .clone()
-            .unwrap_or(String::from(msg.sender.clone()));
+        let sname = msg.sender_name.clone().unwrap_or(msg.sender.to_string());
         let msg_label = gtk::Label::new(None);
         let body: &str = &msg.body;
         let markup = markup_text(body);
@@ -606,7 +602,7 @@ impl MessageBox {
         download_to_cache_username_emote(
             self.backend.clone(),
             self.server_url.clone(),
-            &sname,
+            msg.sender.clone(),
             &markup,
             msg_label.clone(),
             None,
diff --git a/fractal-gtk/src/widgets/room_settings.rs b/fractal-gtk/src/widgets/room_settings.rs
index 8f10f602..44c97ce4 100644
--- a/fractal-gtk/src/widgets/room_settings.rs
+++ b/fractal-gtk/src/widgets/room_settings.rs
@@ -1,4 +1,5 @@
 use fractal_api::clone;
+use fractal_api::identifiers::UserId;
 use fractal_api::r0::AccessToken;
 use std::cell::RefCell;
 use std::rc::Rc;
@@ -13,7 +14,6 @@ use gtk::prelude::*;
 use crate::actions;
 use crate::actions::{ButtonState, StateExt};
 use crate::backend::BKCommand;
-use crate::cache::download_to_cache;
 use crate::types::Member;
 use crate::util::markup_text;
 use crate::widgets;
@@ -25,7 +25,7 @@ use fractal_api::types::Room;
 pub struct RoomSettings {
     actions: gio::SimpleActionGroup,
     room: Room,
-    uid: String,
+    uid: UserId,
     builder: gtk::Builder,
     members_list: Option<MembersList>,
     backend: Sender<BKCommand>,
@@ -37,7 +37,7 @@ impl RoomSettings {
     pub fn new(
         window: &gtk::Window,
         backend: Sender<BKCommand>,
-        uid: String,
+        uid: UserId,
         room: Room,
         server_url: Url,
         access_token: AccessToken,
@@ -100,7 +100,7 @@ impl RoomSettings {
     }
 
     #[allow(dead_code)]
-    pub fn update_members_list(&self, uid: String) -> Option<()> {
+    pub fn update_members_list(&self, uid: UserId) -> Option<()> {
         self.members_list.clone()?.update(uid);
         None
     }
@@ -195,7 +195,7 @@ impl RoomSettings {
         let mut is_room = true;
         let mut is_group = false;
         let members: Vec<Member> = self.room.members.values().cloned().collect();
-        let power = *self.room.admins.get(&self.uid.clone()).unwrap_or(&0);
+        let power = self.room.admins.get(&self.uid).copied().unwrap_or(0);
 
         let edit = power >= 50 && !self.room.direct;
 
@@ -203,6 +203,8 @@ impl RoomSettings {
             is_room = false;
             is_group = false;
             self.get_direct_partner_uid(members.clone())
+                .as_ref()
+                .map(ToString::to_string)
         } else {
             /* we don't have private groups yet
             let description = Some(format!("Private Group · {} members", members.len()));
@@ -231,7 +233,7 @@ impl RoomSettings {
     }
 
     /* returns the uid of the fisrt member in the room, ignoring the current user */
-    fn get_direct_partner_uid(&self, members: Vec<Member>) -> Option<String> {
+    fn get_direct_partner_uid(&self, members: Vec<Member>) -> Option<UserId> {
         members
             .iter()
             .map(|m| m.uid.clone())
@@ -410,7 +412,7 @@ impl RoomSettings {
         None
     }
 
-    fn room_settings_show_avatar(&self, edit: bool) -> Option<()> {
+    fn room_settings_show_avatar(&self, edit: bool) {
         let container = self
             .builder
             .get_object::<gtk::Box>("room_settings_avatar_box")
@@ -426,20 +428,19 @@ impl RoomSettings {
             }
         }
 
+        let _ = self.backend.send(BKCommand::GetRoomAvatar(
+            self.server_url.clone(),
+            self.access_token.clone(),
+            self.room.id.clone(),
+        ));
         let image = widgets::Avatar::avatar_new(Some(100));
-        let data = image.circle(
+        let _data = image.circle(
             self.room.id.to_string(),
             self.room.name.clone(),
             100,
             None,
             None,
         );
-        download_to_cache(
-            self.backend.clone(),
-            self.server_url.clone(),
-            self.room.id.to_string(),
-            data,
-        );
 
         if edit {
             let overlay = self
@@ -469,8 +470,6 @@ impl RoomSettings {
             avatar_btn.hide();
             container.add(&image);
         }
-
-        return None;
     }
 
     pub fn update_room_name(&mut self) -> Option<()> {
@@ -649,12 +648,7 @@ impl RoomSettings {
             )
             .as_str(),
         );
-        let list = widgets::MembersList::new(
-            members.clone(),
-            self.room.admins.clone(),
-            self.room.power_levels.clone(),
-            entry,
-        );
+        let list = widgets::MembersList::new(members.clone(), self.room.admins.clone(), entry);
         let w = list.create()?;
         b.add(&w);
         self.members_list = Some(list);
diff --git a/fractal-matrix-api/src/backend/media.rs b/fractal-matrix-api/src/backend/media.rs
index 83d3ce3c..835aa531 100644
--- a/fractal-matrix-api/src/backend/media.rs
+++ b/fractal-matrix-api/src/backend/media.rs
@@ -117,7 +117,7 @@ fn get_room_media_list(
     }
 
     let evs = array.unwrap().iter().rev();
-    let media_list = Message::from_json_events_iter(room_id, evs);
+    let media_list = Message::from_json_events_iter(room_id, evs)?;
 
     Ok((media_list, prev_batch))
 }
diff --git a/fractal-matrix-api/src/backend/mod.rs b/fractal-matrix-api/src/backend/mod.rs
index 02aefcd9..0fc92df3 100644
--- a/fractal-matrix-api/src/backend/mod.rs
+++ b/fractal-matrix-api/src/backend/mod.rs
@@ -158,7 +158,7 @@ impl Backend {
             Ok(BKCommand::ChangePassword(
                 server,
                 access_token,
-                username,
+                user,
                 old_password,
                 new_password,
             )) => {
@@ -166,7 +166,7 @@ impl Backend {
                     let query = user::change_password(
                         server,
                         access_token,
-                        username,
+                        user,
                         old_password,
                         new_password,
                     );
@@ -174,9 +174,9 @@ impl Backend {
                         .expect_log("Connection closed");
                 });
             }
-            Ok(BKCommand::AccountDestruction(server, access_token, username, password)) => {
+            Ok(BKCommand::AccountDestruction(server, access_token, user, password)) => {
                 thread::spawn(move || {
-                    let query = user::account_destruction(server, access_token, username, password);
+                    let query = user::account_destruction(server, access_token, user, password);
                     tx.send(BKResponse::AccountDestruction(query))
                         .expect_log("Connection closed");
                 });
diff --git a/fractal-matrix-api/src/backend/register.rs b/fractal-matrix-api/src/backend/register.rs
index a03b343c..8d50ea1f 100644
--- a/fractal-matrix-api/src/backend/register.rs
+++ b/fractal-matrix-api/src/backend/register.rs
@@ -52,9 +52,9 @@ pub fn guest(bk: &Backend, server: Url, id_url: Url) {
                 let dev = response.device_id;
 
                 if let Some(tk) = response.access_token {
-                    tx.send(BKResponse::Token(uid, tk, dev, server, id_url))  // TODO: Use UserId and 
DeviceId
+                    tx.send(BKResponse::Token(uid, tk, dev, server, id_url))  // TODO: Use DeviceId
                         .expect_log("Connection closed");
-                    tx.send(BKResponse::Rooms(vec![], None))
+                    tx.send(BKResponse::Rooms(Ok((vec![], None))))
                         .expect_log("Connection closed");
                 } else {
                     tx.send(BKResponse::GuestLoginError(Error::BackendError))
@@ -104,11 +104,10 @@ pub fn login(bk: &Backend, user: String, password: String, server: Url, id_url:
 
         match query {
             Ok(response) => {
-                let uid = response.user_id.unwrap_or(user);
                 let dev = response.device_id;
 
-                if let (Some(tk), false) = (response.access_token, uid.is_empty()) {
-                    tx.send(BKResponse::Token(uid, tk, dev, server, id_url))  // TODO: Use UserId and 
DeviceId
+                if let (Some(tk), Some(uid)) = (response.access_token, response.user_id) {
+                    tx.send(BKResponse::Token(uid, tk, dev, server, id_url))  // TODO: Use DeviceId
                         .expect_log("Connection closed");
                 } else {
                     tx.send(BKResponse::LoginError(Error::BackendError))
@@ -164,7 +163,7 @@ pub fn register(bk: &Backend, user: String, password: String, server: Url, id_ur
                 let dev = response.device_id;
 
                 if let Some(tk) = response.access_token {
-                    tx.send(BKResponse::Token(uid, tk, dev, server, id_url))  // TODO: Use UserId
+                    tx.send(BKResponse::Token(uid, tk, dev, server, id_url))  // TODO: Use DeviceId
                         .expect_log("Connection closed");
                 }
             }
diff --git a/fractal-matrix-api/src/backend/room.rs b/fractal-matrix-api/src/backend/room.rs
index d031afe3..0c9004b0 100644
--- a/fractal-matrix-api/src/backend/room.rs
+++ b/fractal-matrix-api/src/backend/room.rs
@@ -1,8 +1,7 @@
 use log::error;
 use serde_json::json;
 
-use ruma_identifiers::RoomId;
-use ruma_identifiers::RoomIdOrAliasId;
+use ruma_identifiers::{Error as IdError, RoomId, RoomIdOrAliasId, UserId};
 use std::fs;
 use std::sync::mpsc::Sender;
 use url::Url;
@@ -220,9 +219,12 @@ pub fn get_room_messages(
         |r: JsonValue| {
             let array = r["chunk"].as_array();
             let evs = array.unwrap().iter().rev();
-            let list = Message::from_json_events_iter(&room_id, evs);
             let prev_batch = r["end"].as_str().map(String::from);
-            tx.send(BKResponse::RoomMessagesTo(Ok((list, room_id, prev_batch))))
+            let query = Message::from_json_events_iter(&room_id, evs)
+                .map(|list| (list, room_id, prev_batch))
+                .map_err(Into::into);
+
+            tx.send(BKResponse::RoomMessagesTo(query))
                 .expect_log("Connection closed");
         },
         |err| {
@@ -277,32 +279,38 @@ fn parse_context(
         |r: JsonValue| {
             let mut id: Option<String> = None;
 
-            let mut ms: Vec<Message> = vec![];
-            let array = r["events_before"].as_array();
-            for msg in array.unwrap().iter().rev() {
-                if id.is_none() {
-                    id = Some(msg["event_id"].as_str().unwrap_or_default().to_string());
+            let ms: Result<Vec<Message>, _> = r["events_before"]
+                .as_array()
+                .into_iter()
+                .flatten()
+                .rev()
+                .inspect(|msg| {
+                    if id.is_none() {
+                        id = Some(msg["event_id"].as_str().unwrap_or_default().to_string());
+                    }
+                })
+                .filter(|msg| Message::supported_event(&&msg))
+                .map(|msg| Message::parse_room_message(&room_id, msg))
+                .collect();
+
+            match ms {
+                Ok(msgs) if msgs.is_empty() && id.is_some() => {
+                    // there's no messages so we'll try with a bigger context
+                    if let Err(err) =
+                        parse_context(tx.clone(), tk, baseu, room_id, &id.unwrap(), limit * 2)
+                    {
+                        tx.send(BKResponse::RoomMessagesTo(Err(err)))
+                            .expect_log("Connection closed");
+                    }
                 }
-
-                if !Message::supported_event(&&msg) {
-                    continue;
+                Ok(msgs) => {
+                    tx.send(BKResponse::RoomMessagesTo(Ok((msgs, room_id, None))))
+                        .expect_log("Connection closed");
                 }
-
-                let m = Message::parse_room_message(&room_id, msg);
-                ms.push(m);
-            }
-
-            if ms.is_empty() && id.is_some() {
-                // there's no messages so we'll try with a bigger context
-                if let Err(err) =
-                    parse_context(tx.clone(), tk, baseu, room_id, &id.unwrap(), limit * 2)
-                {
-                    tx.send(BKResponse::RoomMessagesTo(Err(err)))
+                Err(err) => {
+                    tx.send(BKResponse::RoomMessagesTo(Err(err.into())))
                         .expect_log("Connection closed");
                 }
-            } else {
-                tx.send(BKResponse::RoomMessagesTo(Ok((ms, room_id, None))))
-                    .expect_log("Connection closed");
             }
         },
         |err| {
@@ -386,13 +394,13 @@ pub fn send_msg(
 pub fn send_typing(
     base: Url,
     access_token: AccessToken,
-    userid: String,
+    user_id: UserId,
     room_id: RoomId,
 ) -> Result<(), Error> {
     let params = TypingNotificationParameters { access_token };
     let body = TypingNotificationBody::Typing(Duration::from_secs(4));
 
-    send_typing_notification(base, &room_id, &userid, &params, &body)
+    send_typing_notification(base, &room_id, &user_id, &params, &body)
         .map_err(Into::into)
         .and_then(|request| {
             HTTP_CLIENT
@@ -775,34 +783,36 @@ pub fn new_room(
     Ok(())
 }
 
-pub fn update_direct_chats(url: Url, data: Arc<Mutex<BackendData>>, user: String, room_id: RoomId) {
+fn update_direct_chats(url: Url, data: Arc<Mutex<BackendData>>, user_id: UserId, room_id: RoomId) {
     get!(
         url.clone(),
         |r: JsonValue| {
-            let mut directs: HashMap<String, Vec<String>> = HashMap::new();
-            let direct_obj = r.as_object().unwrap();
-
-            direct_obj.iter().for_each(|(userid, rooms)| {
-                let roomlist: Vec<String> = rooms
-                    .as_array()
-                    .unwrap()
-                    .iter()
-                    .map(|x| x.as_str().unwrap().to_string())
-                    .collect();
-                directs.insert(userid.clone(), roomlist);
-            });
-
-            if directs.contains_key(&user) {
-                if let Some(v) = directs.get_mut(&user) {
-                    v.push(room_id.to_string())
-                };
-            } else {
-                directs.insert(user, vec![room_id.to_string()]);
-            }
-            data.lock().unwrap().m_direct = directs.clone();
+            let directs: Result<HashMap<UserId, Vec<RoomId>>, IdError> = r
+                .as_object()
+                .into_iter()
+                .flatten()
+                .map(|(uid, rooms)| {
+                    let roomlist = rooms
+                        .as_array()
+                        .unwrap()
+                        .iter()
+                        .map(|x| RoomId::try_from(x.as_str().unwrap_or_default()))
+                        .collect::<Result<Vec<RoomId>, IdError>>()?;
+                    Ok((UserId::try_from(uid.as_str())?, roomlist))
+                })
+                .collect();
+
+            if let Ok(mut directs) = directs {
+                if let Some(v) = directs.get_mut(&user_id) {
+                    v.push(room_id);
+                } else {
+                    directs.insert(user_id, vec![room_id]);
+                }
+                data.lock().unwrap().m_direct = directs.clone();
 
-            let attrs = json!(directs.clone());
-            put!(url, &attrs, |_| {}, |err| error!("{:?}", err));
+                let attrs = json!(directs);
+                put!(url, &attrs, |_| {}, |err| error!("{:?}", err));
+            }
         },
         |err| {
             error!("Can't set m.direct: {:?}", err);
@@ -814,7 +824,7 @@ pub fn direct_chat(
     bk: &Backend,
     base: Url,
     access_token: AccessToken,
-    userid: String,
+    user_id: UserId,
     user: Member,
     internal_id: RoomId,
 ) -> Result<(), Error> {
@@ -836,7 +846,7 @@ pub fn direct_chat(
     let direct_url = bk.url(
         base,
         &access_token,
-        &format!("user/{}/account_data/m.direct", userid),
+        &format!("user/{}/account_data/m.direct", user_id),
         vec![],
     )?;
 
@@ -877,17 +887,17 @@ pub fn direct_chat(
 pub fn add_to_fav(
     base: Url,
     access_token: AccessToken,
-    userid: String,
+    user_id: UserId,
     room_id: RoomId,
     tofav: bool,
 ) -> Result<(RoomId, bool), Error> {
     let request_res = if tofav {
         let params = CreateTagParameters { access_token };
         let body = CreateTagBody { order: Some(0.5) };
-        create_tag(base, &userid, &room_id, "m.favourite", &params, &body)
+        create_tag(base, &user_id, &room_id, "m.favourite", &params, &body)
     } else {
         let params = DeleteTagParameters { access_token };
-        delete_tag(base, &userid, &room_id, "m.favourite", &params)
+        delete_tag(base, &user_id, &room_id, "m.favourite", &params)
     };
 
     request_res
@@ -905,7 +915,7 @@ pub fn invite(
     base: Url,
     access_token: AccessToken,
     room_id: RoomId,
-    user_id: String,
+    user_id: UserId,
 ) -> Result<(), Error> {
     let params = InviteUserParameters { access_token };
     let body = InviteUserBody { user_id };
@@ -925,7 +935,7 @@ pub fn set_language(
     bk: &Backend,
     access_token: AccessToken,
     server: Url,
-    userid: String,
+    user_id: UserId,
     room_id: RoomId,
     input_language: String,
 ) -> Result<(), Error> {
@@ -934,7 +944,7 @@ pub fn set_language(
         &access_token,
         &format!(
             "user/{}/rooms/{}/account_data/org.gnome.fractal.language",
-            userid, room_id,
+            user_id, room_id,
         ),
         vec![],
     )?;
diff --git a/fractal-matrix-api/src/backend/sync.rs b/fractal-matrix-api/src/backend/sync.rs
index 99625106..27120bd3 100644
--- a/fractal-matrix-api/src/backend/sync.rs
+++ b/fractal-matrix-api/src/backend/sync.rs
@@ -23,8 +23,10 @@ use crate::util::ResultExpectLog;
 
 use log::error;
 use reqwest::Client;
+use ruma_identifiers::UserId;
 use serde_json::value::from_value;
 use std::{
+    convert::TryFrom,
     thread,
     time::{self, Duration},
 };
@@ -34,7 +36,7 @@ pub fn sync(
     bk: &Backend,
     base: Url,
     access_token: AccessToken,
-    userid: String,
+    user_id: UserId,
     since: Option<String>,
     initial: bool,
 ) {
@@ -111,18 +113,22 @@ pub fn sync(
                     let join = &response.rooms.join;
 
                     // New rooms
-                    let rs = Room::from_sync_response(&response, &userid, &base);
+                    let rs = Room::from_sync_response(&response, user_id.clone(), &base)
+                        .map_err(Into::into);
                     tx.send(BKResponse::UpdateRooms(rs))
                         .expect_log("Connection closed");
 
                     // Message events
                     let msgs = join
                         .iter()
-                        .flat_map(|(k, room)| {
+                        .try_fold(Vec::new(), |mut acum, (k, room)| {
                             let events = room.timeline.events.iter();
-                            Message::from_json_events_iter(&k, events).into_iter()
+                            Message::from_json_events_iter(&k, events).map(|msgs| {
+                                acum.extend(msgs);
+                                acum
+                            })
                         })
-                        .collect();
+                        .map_err(Into::into);
                     tx.send(BKResponse::RoomMessages(msgs))
                         .expect_log("Connection closed");
 
@@ -141,34 +147,34 @@ pub fn sync(
                         .iter()
                         .map(|(k, room)| {
                             let ephemerals = &room.ephemeral.events;
-                            let mut typing_room: Room =
-                                Room::new(k.clone(), RoomMembership::Joined(RoomTag::None));
-                            let mut typing: Vec<Member> = Vec::new();
-                            for event in ephemerals.iter() {
-                                if let Some(typing_users) = event
-                                    .get("content")
-                                    .and_then(|x| x.get("user_ids"))
-                                    .and_then(|x| x.as_array())
-                                {
-                                    for user in typing_users {
-                                        let user: String = from_value(user.to_owned()).unwrap();
-                                        // ignoring the user typing notifications
-                                        if user == userid {
-                                            continue;
-                                        }
-                                        typing.push(Member {
-                                            uid: user,
-                                            alias: None,
-                                            avatar: None,
-                                        });
+                            let typing: Vec<Member> = ephemerals.iter()
+                                .flat_map(|event| {
+                                    event
+                                        .get("content")
+                                        .and_then(|x| x.get("user_ids"))
+                                        .and_then(|x| x.as_array())
+                                        .unwrap_or(&vec![])
+                                        .to_owned()
+                                })
+                                .filter_map(|user| from_value(user).ok())
+                                // ignoring the user typing notifications
+                                .filter(|user| *user != user_id)
+                                .map(|uid| {
+                                    Member {
+                                        uid,
+                                        alias: None,
+                                        avatar: None,
                                     }
-                                }
+                                })
+                                .collect();
+
+                            Room {
+                                typing_users: typing,
+                                ..Room::new(k.clone(), RoomMembership::Joined(RoomTag::None))
                             }
-                            typing_room.typing_users = typing;
-                            typing_room
                         })
                         .collect();
-                    tx.send(BKResponse::UpdateRooms(rooms))
+                    tx.send(BKResponse::UpdateRooms(Ok(rooms)))
                         .expect_log("Connection closed");
 
                     // Other events
@@ -180,10 +186,13 @@ pub fn sync(
                                 .filter(|x| x["type"] != "m.room.message")
                                 .map(move |ev| Event {
                                     room: k.clone(),
-                                    sender: ev["sender"]
-                                        .as_str()
-                                        .map(Into::into)
-                                        .unwrap_or_default(),
+                                    sender: UserId::try_from(
+                                        ev["sender"].as_str().unwrap_or_default(),
+                                    )
+                                    .unwrap(),
+                                    // TODO: Correct error management is too hard here,
+                                    //       needs refactoring, but this workaround
+                                    //       is enough
                                     content: ev["content"].clone(),
                                     redacts: ev["redacts"]
                                         .as_str()
@@ -234,16 +243,21 @@ pub fn sync(
                             }
                         });
                 } else {
-                    data.lock().unwrap().m_direct = parse_m_direct(&response.account_data.events);
+                    if let Ok(m_direct) = parse_m_direct(&response.account_data.events) {
+                        data.lock().unwrap().m_direct = m_direct;
+                    }
 
-                    let rooms = Room::from_sync_response(&response, &userid, &base);
-                    let def = data
-                        .lock()
-                        .unwrap()
-                        .join_to_room
-                        .as_ref()
-                        .and_then(|jtr| rooms.iter().find(|x| x.id == *jtr).cloned());
-                    tx.send(BKResponse::Rooms(rooms, def))
+                    let rooms_def =
+                        Room::from_sync_response(&response, user_id, &base)
+                            .map(|rooms| {
+                                let def =
+                                    data.lock().unwrap().join_to_room.as_ref().and_then(|jtr| {
+                                        rooms.iter().find(|x| x.id == *jtr).cloned()
+                                    });
+                                (rooms, def)
+                            })
+                            .map_err(Into::into);
+                    tx.send(BKResponse::Rooms(rooms_def))
                         .expect_log("Connection closed");
                 }
 
diff --git a/fractal-matrix-api/src/backend/types.rs b/fractal-matrix-api/src/backend/types.rs
index a66cfe45..9297b708 100644
--- a/fractal-matrix-api/src/backend/types.rs
+++ b/fractal-matrix-api/src/backend/types.rs
@@ -1,4 +1,4 @@
-use ruma_identifiers::RoomId;
+use ruma_identifiers::{RoomId, UserId};
 use std::collections::HashMap;
 use std::sync::mpsc::Sender;
 use std::sync::{Arc, Condvar, Mutex};
@@ -25,8 +25,8 @@ pub enum BKCommand {
     Register(String, String, Url, Url),
     #[allow(dead_code)]
     Guest(Url, Url),
-    GetUsername(Url, String),
-    SetUserName(Url, AccessToken, String, String),
+    GetUsername(Url, UserId),
+    SetUserName(Url, AccessToken, UserId, String),
     GetThreePID(Url, AccessToken),
     GetTokenEmail(Url, AccessToken, Url, String, String),
     GetTokenPhone(Url, AccessToken, Url, String, String),
@@ -35,9 +35,9 @@ pub enum BKCommand {
     DeleteThreePID(Url, AccessToken, Medium, String),
     ChangePassword(Url, AccessToken, String, String, String),
     AccountDestruction(Url, AccessToken, String, String),
-    GetAvatar(Url, String),
-    SetUserAvatar(Url, AccessToken, String, String),
-    Sync(Url, AccessToken, String, Option<String>, bool),
+    GetAvatar(Url, UserId),
+    SetUserAvatar(Url, AccessToken, UserId, String),
+    Sync(Url, AccessToken, UserId, Option<String>, bool),
     GetRoomMembers(Url, AccessToken, RoomId),
     GetRoomMessages(Url, AccessToken, RoomId, String),
     GetRoomMessagesFromMsg(Url, AccessToken, RoomId, Message),
@@ -56,11 +56,11 @@ pub enum BKCommand {
     GetFileAsync(Url, Sender<String>),
     GetAvatarAsync(Url, Option<Member>, Sender<String>),
     GetMedia(Url, String),
-    GetUserInfoAsync(Url, String, Option<Sender<(String, String)>>),
-    GetUserNameAsync(Url, String, Sender<String>),
+    GetUserInfoAsync(Url, UserId, Option<Sender<(String, String)>>),
+    GetUserNameAsync(Url, UserId, Sender<String>),
     SendMsg(Url, AccessToken, Message),
     SendMsgRedaction(Url, AccessToken, Message),
-    SendTyping(Url, AccessToken, String, RoomId),
+    SendTyping(Url, AccessToken, UserId, RoomId),
     SetRoom(Url, AccessToken, RoomId),
     ShutDown,
     DirectoryProtocols(Url, AccessToken),
@@ -73,20 +73,20 @@ pub enum BKCommand {
     SetRoomAvatar(Url, AccessToken, RoomId, String),
     AttachFile(Url, AccessToken, Message),
     NewRoom(Url, AccessToken, String, RoomType, RoomId),
-    DirectChat(Url, AccessToken, String, Member, RoomId),
-    AddToFav(Url, AccessToken, String, RoomId, bool),
+    DirectChat(Url, AccessToken, UserId, Member, RoomId),
+    AddToFav(Url, AccessToken, UserId, RoomId, bool),
     AcceptInv(Url, AccessToken, RoomId),
     RejectInv(Url, AccessToken, RoomId),
     UserSearch(Url, AccessToken, String),
-    Invite(Url, AccessToken, RoomId, String),
-    ChangeLanguage(AccessToken, Url, String, RoomId, String),
+    Invite(Url, AccessToken, RoomId, UserId),
+    ChangeLanguage(AccessToken, Url, UserId, RoomId, String),
     SendBKResponse(BKResponse),
 }
 
 #[derive(Debug)]
 pub enum BKResponse {
     ShutDown,
-    Token(String, AccessToken, Option<String>, Url, Url),
+    Token(UserId, AccessToken, Option<String>, Url, Url),
     Logout(Result<(), Error>),
     Name(Result<Option<String>, Error>),
     SetUserName(Result<String, Error>),
@@ -101,13 +101,13 @@ pub enum BKResponse {
     Avatar(Result<String, Error>),
     SetUserAvatar(Result<String, Error>),
     Sync(Result<String, Error>),
-    Rooms(Vec<Room>, Option<Room>),
-    UpdateRooms(Vec<Room>),
+    Rooms(Result<(Vec<Room>, Option<Room>), Error>),
+    UpdateRooms(Result<Vec<Room>, Error>),
     RoomDetail(Result<(RoomId, String, String), Error>),
     RoomAvatar(Result<(RoomId, Option<Url>), Error>),
     NewRoomAvatar(RoomId),
     RoomMemberEvent(Event),
-    RoomMessages(Vec<Message>),
+    RoomMessages(Result<Vec<Message>, Error>),
     RoomMessagesInit(Vec<Message>),
     RoomMessagesTo(Result<(Vec<Message>, RoomId, Option<String>), Error>),
     RoomMembers(Result<(RoomId, Vec<Member>), Error>),
@@ -151,7 +151,7 @@ pub enum RoomType {
 pub struct BackendData {
     pub rooms_since: String,
     pub join_to_room: Option<RoomId>,
-    pub m_direct: HashMap<String, Vec<String>>,
+    pub m_direct: HashMap<UserId, Vec<RoomId>>,
 }
 
 #[derive(Clone)]
@@ -161,7 +161,7 @@ pub struct Backend {
     pub internal_tx: Option<Sender<BKCommand>>,
 
     // user info cache, uid -> (name, avatar)
-    pub user_info_cache: CacheMap<Arc<Mutex<(String, String)>>>,
+    pub user_info_cache: CacheMap<UserId, Arc<Mutex<(String, String)>>>,
     // semaphore to limit the number of threads downloading images
     pub limit_threads: Arc<(Mutex<u8>, Condvar)>,
 }
diff --git a/fractal-matrix-api/src/backend/user.rs b/fractal-matrix-api/src/backend/user.rs
index a76953da..4450169c 100644
--- a/fractal-matrix-api/src/backend/user.rs
+++ b/fractal-matrix-api/src/backend/user.rs
@@ -1,3 +1,4 @@
+use ruma_identifiers::UserId;
 use std::fs;
 use url::Url;
 
@@ -5,7 +6,6 @@ use crate::backend::types::Backend;
 use crate::error::Error;
 use crate::util::cache_dir_path;
 use crate::util::dw_media;
-use crate::util::encode_uid;
 use crate::util::get_user_avatar;
 use crate::util::semaphore;
 use crate::util::ContentType;
@@ -66,8 +66,8 @@ use crate::r0::Medium;
 use crate::r0::ThreePIDCredentials;
 use crate::types::Member;
 
-pub fn get_username(base: Url, uid: String) -> Result<Option<String>, Error> {
-    get_display_name(base, &encode_uid(&uid))
+pub fn get_username(base: Url, uid: UserId) -> Result<Option<String>, Error> {
+    get_display_name(base, &uid)
         .map_err(Into::into)
         .and_then(|request| {
             HTTP_CLIENT
@@ -81,8 +81,8 @@ pub fn get_username(base: Url, uid: String) -> Result<Option<String>, Error> {
 
 // FIXME: This function manages errors *really* wrong and isn't more async
 // than the normal function. It should be removed.
-pub fn get_username_async(base: Url, uid: String) -> String {
-    get_display_name(base, &encode_uid(&uid))
+pub fn get_username_async(base: Url, uid: UserId) -> String {
+    get_display_name(base, &uid)
         .map_err::<Error, _>(Into::into)
         .and_then(|request| {
             HTTP_CLIENT
@@ -93,13 +93,13 @@ pub fn get_username_async(base: Url, uid: String) -> String {
         })
         .ok()
         .and_then(|response| response.displayname)
-        .unwrap_or(uid)
+        .unwrap_or(uid.to_string())
 }
 
 pub fn set_username(
     base: Url,
     access_token: AccessToken,
-    uid: String,
+    uid: UserId,
     username: String,
 ) -> Result<String, Error> {
     let params = SetDisplayNameParameters { access_token };
@@ -107,7 +107,7 @@ pub fn set_username(
         displayname: Some(username.clone()),
     };
 
-    set_display_name(base, &params, &body, &encode_uid(&uid))
+    set_display_name(base, &params, &body, &uid)
         .map_err(Into::into)
         .and_then(|request| {
             HTTP_CLIENT
@@ -339,7 +339,7 @@ pub fn account_destruction(
         .and(Ok(()))
 }
 
-pub fn get_avatar(base: Url, userid: String) -> Result<String, Error> {
+pub fn get_avatar(base: Url, userid: UserId) -> Result<String, Error> {
     get_user_avatar(&base, &userid).map(|(_, fname)| fname)
 }
 
@@ -360,7 +360,7 @@ pub fn get_avatar_async(bk: &Backend, base: Url, member: Option<Member>, tx: Sen
 pub fn set_user_avatar(
     base: Url,
     access_token: AccessToken,
-    id: String,
+    uid: UserId,
     avatar: String,
 ) -> Result<String, Error> {
     let params_upload = CreateContentParameters {
@@ -386,7 +386,7 @@ pub fn set_user_avatar(
                 avatar_url: Some(upload_response.content_uri),
             };
 
-            set_avatar_url(base, &params_avatar, &body, &encode_uid(&id))
+            set_avatar_url(base, &params_avatar, &body, &uid)
                 .map_err(Into::into)
                 .and_then(|request| {
                     HTTP_CLIENT
@@ -401,12 +401,11 @@ pub fn set_user_avatar(
 pub fn get_user_info_async(
     bk: &mut Backend,
     baseu: Url,
-    uid: String,
+    uid: UserId,
     tx: Option<Sender<(String, String)>>,
 ) {
-    if let Some(info) = bk.user_info_cache.get(&uid) {
+    if let Some(info) = bk.user_info_cache.get(&uid).cloned() {
         if let Some(tx) = tx.clone() {
-            let info = info.clone();
             thread::spawn(move || {
                 let i = info.lock().unwrap().clone();
                 tx.send(i).expect_log("Connection closed");
@@ -455,12 +454,12 @@ pub fn search(
         .map(|response| response.results.into_iter().map(Into::into).collect())
 }
 
-fn get_user_avatar_img(baseu: &Url, userid: &str, avatar: &str) -> Result<String, Error> {
+fn get_user_avatar_img(baseu: &Url, userid: &UserId, avatar: &str) -> Result<String, Error> {
     if avatar.is_empty() {
         return Ok(String::new());
     }
 
-    let dest = cache_dir_path(None, &userid)?;
+    let dest = cache_dir_path(None, &userid.to_string())?;
     dw_media(
         baseu,
         &avatar,
diff --git a/fractal-matrix-api/src/cache.rs b/fractal-matrix-api/src/cache.rs
index 3d39000e..1615c039 100644
--- a/fractal-matrix-api/src/cache.rs
+++ b/fractal-matrix-api/src/cache.rs
@@ -1,25 +1,27 @@
 use std::collections::HashMap;
+use std::hash::Hash;
 use std::time::Instant;
 
-pub struct CacheMap<T: Clone> {
-    map: HashMap<String, (Instant, T)>,
+#[derive(Clone)]
+pub struct CacheMap<K: Clone, V: Clone> {
+    map: HashMap<K, (Instant, V)>,
     timeout: u64,
 }
 
-impl<T: Clone> CacheMap<T> {
-    pub fn new() -> CacheMap<T> {
+impl<K: Clone + Eq + Hash, V: Clone> CacheMap<K, V> {
+    pub fn new() -> Self {
         CacheMap {
             map: HashMap::new(),
             timeout: 10,
         }
     }
 
-    pub fn timeout(mut self, timeout: u64) -> CacheMap<T> {
+    pub fn timeout(mut self, timeout: u64) -> Self {
         self.timeout = timeout;
         self
     }
 
-    pub fn get(&self, k: &String) -> Option<&T> {
+    pub fn get(&self, k: &K) -> Option<&V> {
         match self.map.get(k) {
             Some(t) => {
                 if t.0.elapsed().as_secs() >= self.timeout {
@@ -31,23 +33,8 @@ impl<T: Clone> CacheMap<T> {
         }
     }
 
-    pub fn insert(&mut self, k: String, v: T) {
+    pub fn insert(&mut self, k: K, v: V) {
         let now = Instant::now();
         self.map.insert(k, (now, v));
     }
 }
-
-impl<T: Clone> Clone for CacheMap<T> {
-    fn clone(&self) -> CacheMap<T> {
-        let mut map: CacheMap<T> = CacheMap {
-            map: HashMap::new(),
-            timeout: self.timeout,
-        };
-
-        for (k, v) in self.map.iter() {
-            map.map.insert(k.clone(), (v.0.clone(), v.1.clone()));
-        }
-
-        map
-    }
-}
diff --git a/fractal-matrix-api/src/model/event.rs b/fractal-matrix-api/src/model/event.rs
index 83bdbf58..787f9be3 100644
--- a/fractal-matrix-api/src/model/event.rs
+++ b/fractal-matrix-api/src/model/event.rs
@@ -1,9 +1,9 @@
-use ruma_identifiers::RoomId;
+use ruma_identifiers::{RoomId, UserId};
 use serde_json::Value as JsonValue;
 
 #[derive(Debug, Clone)]
 pub struct Event {
-    pub sender: String,
+    pub sender: UserId,
     pub stype: String,
     pub room: RoomId,
     pub id: String,
diff --git a/fractal-matrix-api/src/model/member.rs b/fractal-matrix-api/src/model/member.rs
index 160beae8..6d9913ab 100644
--- a/fractal-matrix-api/src/model/member.rs
+++ b/fractal-matrix-api/src/model/member.rs
@@ -1,5 +1,6 @@
 use crate::r0::search::user::User;
 use crate::r0::sync::get_joined_members::RoomMember;
+use ruma_identifiers::UserId;
 use serde::{Deserialize, Serialize};
 use std::collections::HashMap;
 use url::Url;
@@ -7,10 +8,7 @@ use url::Url;
 // TODO: Make this non-(de)serializable
 #[derive(Debug, Clone, Serialize, Deserialize)]
 pub struct Member {
-    // The mxid is either inside the json object, or outside of it.
-    // Since we don't know, we always have to populate it manually
-    #[serde(default)]
-    pub uid: String,
+    pub uid: UserId,
     #[serde(rename = "display_name")]
     pub alias: Option<String>,
     #[serde(rename = "avatar_url")]
@@ -24,7 +22,7 @@ impl Member {
                 return alias.clone();
             }
         }
-        self.uid.clone()
+        self.uid.to_string()
     }
 }
 
@@ -44,19 +42,15 @@ impl From<User> for Member {
     }
 }
 
-impl From<(String, RoomMember)> for Member {
-    fn from(uid_roommember: (String, RoomMember)) -> Self {
+impl From<(UserId, RoomMember)> for Member {
+    fn from((uid, roommember): (UserId, RoomMember)) -> Self {
         Member {
-            uid: uid_roommember.0,
-            alias: uid_roommember.1.display_name,
-            avatar: uid_roommember
-                .1
-                .avatar_url
-                .as_ref()
-                .map(ToString::to_string),
+            uid,
+            alias: roommember.display_name,
+            avatar: roommember.avatar_url.as_ref().map(ToString::to_string),
         }
     }
 }
 
 // hashmap userid -> Member
-pub type MemberList = HashMap<String, Member>;
+pub type MemberList = HashMap<UserId, Member>;
diff --git a/fractal-matrix-api/src/model/message.rs b/fractal-matrix-api/src/model/message.rs
index 884d5dbe..353cb139 100644
--- a/fractal-matrix-api/src/model/message.rs
+++ b/fractal-matrix-api/src/model/message.rs
@@ -1,16 +1,17 @@
 use chrono::prelude::*;
 use chrono::DateTime;
 use chrono::TimeZone;
-use ruma_identifiers::RoomId;
+use ruma_identifiers::{Error as IdError, RoomId, UserId};
 use serde::{Deserialize, Serialize};
 use serde_json::Value as JsonValue;
 use std::cmp::Ordering;
 use std::collections::HashMap;
+use std::convert::TryInto;
 
 //FIXME make properties privat
 #[derive(Debug, Clone, Serialize, Deserialize)]
 pub struct Message {
-    pub sender: String,
+    pub sender: UserId,
     pub mtype: String,
     pub body: String,
     pub date: DateTime<Local>,
@@ -21,7 +22,7 @@ pub struct Message {
     pub formatted_body: Option<String>,
     pub format: Option<String>,
     pub source: Option<String>,
-    pub receipt: HashMap<String, i64>, // This `HashMap` associates the user ID with a timestamp
+    pub receipt: HashMap<UserId, i64>, // This `HashMap` associates the user ID with a timestamp
     pub redacted: bool,
     // The event ID of the message this is in reply to.
     pub in_reply_to: Option<String>,
@@ -48,7 +49,7 @@ impl PartialOrd for Message {
 }
 
 impl Message {
-    pub fn new(room: RoomId, sender: String, body: String, mtype: String) -> Self {
+    pub fn new(room: RoomId, sender: UserId, body: String, mtype: String) -> Self {
         let date = Local::now();
         Message {
             id: get_txn_id(&room, &body, &date.to_string()),
@@ -96,8 +97,8 @@ impl Message {
     ///
     /// * `roomid` - The message room id
     /// * `msg` - The message event as Json
-    pub fn parse_room_message(room_id: &RoomId, msg: &JsonValue) -> Message {
-        let sender = msg["sender"].as_str().unwrap_or_default();
+    pub fn parse_room_message(room_id: &RoomId, msg: &JsonValue) -> Result<Message, IdError> {
+        let sender: UserId = msg["sender"].as_str().unwrap_or_default().try_into()?;
 
         let timestamp = msg["origin_server_ts"].as_i64().unwrap_or_default() / 1000;
         let server_timestamp: DateTime<Local> = Local.timestamp(timestamp, 0);
@@ -108,7 +109,7 @@ impl Message {
         let redacted = msg["unsigned"].get("redacted_because") != None;
 
         let mut message = Message {
-            sender: sender.to_string(),
+            sender,
             date: server_timestamp,
             room: room_id.clone(),
             id: id.to_string(),
@@ -132,7 +133,7 @@ impl Message {
             _ => {}
         };
 
-        message
+        Ok(message)
     }
 
     fn parse_m_room_message(&mut self, c: &JsonValue) {
@@ -190,7 +191,10 @@ impl Message {
     ///
     /// * `roomid` - The messages room id
     /// * `events` - An iterator to the json events
-    pub fn from_json_events_iter<'a, I>(room_id: &RoomId, events: I) -> Vec<Message>
+    pub fn from_json_events_iter<'a, I>(
+        room_id: &RoomId,
+        events: I,
+    ) -> Result<Vec<Message>, IdError>
     where
         I: Iterator<Item = &'a JsonValue>,
     {
@@ -200,7 +204,7 @@ impl Message {
             .collect()
     }
 
-    pub fn set_receipt(&mut self, receipt: HashMap<String, i64>) {
+    pub fn set_receipt(&mut self, receipt: HashMap<UserId, i64>) {
         self.receipt = receipt;
     }
 }
diff --git a/fractal-matrix-api/src/model/room.rs b/fractal-matrix-api/src/model/room.rs
index e250c4e3..e49ee5d0 100644
--- a/fractal-matrix-api/src/model/room.rs
+++ b/fractal-matrix-api/src/model/room.rs
@@ -8,9 +8,10 @@ use crate::r0::sync::sync_events::Response as SyncResponse;
 use crate::util::get_user_avatar;
 use crate::util::parse_m_direct;
 use log::{debug, info};
-use ruma_identifiers::RoomId;
+use ruma_identifiers::{Error as IdError, RoomId, UserId};
 use serde::{Deserialize, Serialize};
 use std::collections::{HashMap, HashSet};
+use std::convert::{TryFrom, TryInto};
 use url::Url;
 
 #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
@@ -99,8 +100,8 @@ pub struct Room {
 
     /// Hashmap with the room users power levels
     /// the key will be the userid and the value will be the level
-    pub admins: HashMap<String, i32>,
-    pub power_levels: HashMap<String, i32>,
+    pub admins: HashMap<UserId, i32>,
+    pub default_power_level: i32,
 }
 
 impl Room {
@@ -124,13 +125,17 @@ impl Room {
             typing_users: Default::default(),
             language: Default::default(),
             admins: Default::default(),
-            power_levels: Default::default(),
+            default_power_level: -1,
         }
     }
 
-    pub fn from_sync_response(response: &SyncResponse, userid: &str, baseu: &Url) -> Vec<Self> {
+    pub fn from_sync_response(
+        response: &SyncResponse,
+        user_id: UserId,
+        baseu: &Url,
+    ) -> Result<Vec<Self>, IdError> {
         // getting the list of direct rooms
-        let direct: HashSet<String> = parse_m_direct(&response.account_data.events)
+        let direct: HashSet<RoomId> = parse_m_direct(&response.account_data.events)?
             .values()
             .flatten()
             .cloned()
@@ -154,17 +159,17 @@ impl Room {
                 .map(|lang| lang.to_string());
 
             let mut r = Self {
-                name: calculate_room_name(stevents, userid),
+                name: calculate_room_name(stevents, &user_id),
                 avatar: evc(stevents, "m.room.avatar", "url"),
                 alias: evc(stevents, "m.room.canonical_alias", "alias"),
                 topic: evc(stevents, "m.room.topic", "topic"),
-                direct: direct.contains(&k.to_string()),
+                direct: direct.contains(&k),
                 notifications: room.unread_notifications.notification_count,
                 highlight: room.unread_notifications.highlight_count,
                 prev_batch: timeline.prev_batch.clone(),
-                messages: Message::from_json_events_iter(&k, timeline.events.iter()),
-                admins: get_admins(stevents),
-                power_levels: get_power_levels(stevents),
+                messages: Message::from_json_events_iter(&k, timeline.events.iter())?,
+                admins: get_admins(stevents)?,
+                default_power_level: get_default_power_level(stevents),
                 members: stevents
                     .iter()
                     .filter(|x| x["type"] == "m.room.member")
@@ -187,25 +192,26 @@ impl Room {
                 .into_iter()
                 .find(|x| x["type"] == "m.fully_read")
                 .and_then(|fread| fread["content"]["event_id"].as_str())
+                .map(ToOwned::to_owned)
             {
-                r.add_receipt_from_fully_read(userid, ev);
+                r.add_receipt_from_fully_read(user_id.clone(), ev);
             }
 
-            r
+            Ok(r)
         });
 
         let left_rooms = response.rooms.leave.iter().map(|(k, room)| {
-            if let Some(last_event) = room.timeline.events.last() {
-                let leave_id = &last_event["sender"];
-                if leave_id != userid {
+            let r = if let Some(last_event) = room.timeline.events.last() {
+                let leave_id = UserId::try_from(last_event["sender"].as_str().unwrap_or_default())?;
+                if leave_id != user_id {
                     let kick_reason = &last_event["content"]["reason"];
                     if let Some((kicker_alias, kicker_avatar)) =
-                        get_user_avatar(baseu, leave_id.as_str().unwrap_or_default()).ok()
+                        get_user_avatar(baseu, &leave_id).ok()
                     {
                         let kicker = Member {
                             alias: Some(kicker_alias),
                             avatar: Some(kicker_avatar),
-                            uid: String::from(leave_id.as_str().unwrap_or_default()),
+                            uid: leave_id,
                         };
                         let reason = Reason::Kicked(
                             String::from(kick_reason.as_str().unwrap_or_default()),
@@ -220,37 +226,51 @@ impl Room {
                 }
             } else {
                 Self::new(k.clone(), RoomMembership::Left(Reason::None))
-            }
-        });
+            };
 
-        let invited_rooms = response.rooms.invite.iter().filter_map(|(k, room)| {
-            let stevents = &room.invite_state.events;
-            if let Some((alias, avatar)) = stevents
-                .iter()
-                .find(|x| x["content"]["membership"] == "invite" && x["state_key"] == userid)
-                .and_then(|ev| {
-                    get_user_avatar(baseu, ev["sender"].as_str().unwrap_or_default()).ok()
-                })
-            {
-                let inv_sender = Member {
-                    alias: Some(alias),
-                    avatar: Some(avatar),
-                    uid: String::from(userid),
-                };
-
-                Some(Self {
-                    name: calculate_room_name(stevents, userid),
-                    avatar: evc(stevents, "m.room.avatar", "url"),
-                    alias: evc(stevents, "m.room.canonical_alias", "alias"),
-                    topic: evc(stevents, "m.room.topic", "topic"),
-                    direct: direct.contains(&k.to_string()),
-                    ..Self::new(k.clone(), RoomMembership::Invited(inv_sender))
-                })
-            } else {
-                None
-            }
+            Ok(r)
         });
 
+        let invited_rooms = response
+            .rooms
+            .invite
+            .iter()
+            .map(|(k, room)| {
+                let stevents = &room.invite_state.events;
+                let alias_avatar: Result<Option<(String, String)>, IdError> = stevents
+                    .iter()
+                    .find(|x| {
+                        x["content"]["membership"] == "invite"
+                            && x["state_key"] == user_id.to_string().as_str()
+                    })
+                    .map_or(Ok(None), |ev| {
+                        Ok(get_user_avatar(
+                            baseu,
+                            &UserId::try_from(ev["sender"].as_str().unwrap_or_default())?,
+                        )
+                        .ok())
+                    });
+                if let Some((alias, avatar)) = alias_avatar? {
+                    let inv_sender = Member {
+                        alias: Some(alias),
+                        avatar: Some(avatar),
+                        uid: user_id.clone(),
+                    };
+
+                    Ok(Some(Self {
+                        name: calculate_room_name(stevents, &user_id),
+                        avatar: evc(stevents, "m.room.avatar", "url"),
+                        alias: evc(stevents, "m.room.canonical_alias", "alias"),
+                        topic: evc(stevents, "m.room.topic", "topic"),
+                        direct: direct.contains(&k),
+                        ..Self::new(k.clone(), RoomMembership::Invited(inv_sender))
+                    }))
+                } else {
+                    Ok(None)
+                }
+            })
+            .filter_map(Result::transpose);
+
         joined_rooms
             .chain(left_rooms)
             .chain(invited_rooms)
@@ -258,7 +278,7 @@ impl Room {
     }
 
     pub fn add_receipt_from_json(&mut self, mut events: Vec<&JsonValue>) {
-        let receipts: HashMap<String, HashMap<String, i64>> = events
+        let receipts: HashMap<String, HashMap<UserId, i64>> = events
             .pop()
             .and_then(|ev| ev["content"].as_object())
             .into_iter()
@@ -273,29 +293,28 @@ impl Room {
                         if ts == 0 {
                             info!("Possibly malformed timestamp, working around synapse bug 4898");
                         };
-                        (uid.to_string(), ts)
+                        Ok((UserId::try_from(uid.as_str())?, ts))
                     })
-                    .collect();
+                    .collect::<Result<HashMap<UserId, i64>, IdError>>()
+                    .ok()?;
 
                 Some((mid.to_string(), receipts))
             })
             .collect();
 
-        if !receipts.is_empty() {
-            for msg in self.messages.iter_mut() {
-                if let Some(r) = receipts.get(&msg.id) {
-                    msg.set_receipt(r.clone());
-                }
+        for msg in self.messages.iter_mut() {
+            if let Some(r) = receipts.get(&msg.id) {
+                msg.set_receipt(r.clone());
             }
         }
     }
 
-    pub fn add_receipt_from_fully_read(&mut self, uid: &str, evid: &str) {
+    pub fn add_receipt_from_fully_read(&mut self, uid: UserId, evid: String) {
         let _ = self
             .messages
             .iter_mut()
-            .filter(|msg| msg.id == evid.to_string())
-            .map(|msg| msg.receipt.insert(uid.to_string(), 0));
+            .filter(|msg| msg.id == evid)
+            .map(|msg| msg.receipt.insert(uid.clone(), 0));
     }
 }
 
@@ -330,27 +349,32 @@ fn evc(events: &Vec<JsonValue>, t: &str, field: &str) -> Option<String> {
         .map(Into::into)
 }
 
-fn get_admins(stevents: &Vec<JsonValue>) -> HashMap<String, i32> {
+fn get_admins(stevents: &Vec<JsonValue>) -> Result<HashMap<UserId, i32>, IdError> {
     stevents
         .iter()
         .filter(|x| x["type"] == "m.room.power_levels")
         .filter_map(|ev| ev["content"]["users"].as_object())
         .flatten()
-        .map(|(k, v)| (k.clone(), v.as_i64().map(|v| v as i32).unwrap_or_default()))
+        .map(|(k, v)| {
+            Ok((
+                UserId::try_from(k.as_str())?,
+                v.as_i64().map(|v| v as i32).unwrap_or_default(),
+            ))
+        })
         .collect()
 }
 
-fn get_power_levels(stevents: &Vec<JsonValue>) -> HashMap<String, i32> {
+fn get_default_power_level(stevents: &Vec<JsonValue>) -> i32 {
     stevents
         .iter()
         .filter(|x| x["type"] == "m.room.power_levels")
-        .filter_map(|ev| ev["content"].as_object())
-        .flatten()
-        .map(|(k, v)| (k.clone(), v.as_i64().map(|v| v as i32).unwrap_or_default()))
-        .collect()
+        .filter_map(|ev| ev["content"]["users_default"].as_i64())
+        .last()
+        .unwrap_or(-1) as i32
 }
 
-fn calculate_room_name(events: &Vec<JsonValue>, userid: &str) -> Option<String> {
+fn calculate_room_name(events: &Vec<JsonValue>, user_id: &UserId) -> Option<String> {
+    let userid = user_id.to_string();
     // looking for "m.room.name" event
     if let Some(name) = events
         .iter()
@@ -377,8 +401,9 @@ fn calculate_room_name(events: &Vec<JsonValue>, userid: &str) -> Option<String>
         .iter()
         .filter(|x| {
             (x["type"] == "m.room.member"
-                && ((x["content"]["membership"] == "join" && x["sender"] != userid)
-                    || (x["content"]["membership"] == "invite" && x["state_key"] != userid)))
+                && ((x["content"]["membership"] == "join" && x["sender"] != userid.as_str())
+                    || (x["content"]["membership"] == "invite"
+                        && x["state_key"] != userid.as_str())))
         })
         .take(3)
         .map(|m| {
@@ -401,7 +426,7 @@ fn parse_room_member(msg: &JsonValue) -> Option<Member> {
     let _ = c["membership"].as_str().filter(|&m| m == "join")?;
 
     Some(Member {
-        uid: msg["sender"].as_str().map(Into::into)?,
+        uid: msg["sender"].as_str().unwrap_or_default().try_into().ok()?,
         alias: c["displayname"].as_str().map(Into::into),
         avatar: c["avatar_url"].as_str().map(Into::into),
     })
diff --git a/fractal-matrix-api/src/r0/account/login.rs b/fractal-matrix-api/src/r0/account/login.rs
index 28f1f617..14f3bfcc 100644
--- a/fractal-matrix-api/src/r0/account/login.rs
+++ b/fractal-matrix-api/src/r0/account/login.rs
@@ -4,6 +4,7 @@ use reqwest::Client;
 use reqwest::Error;
 use reqwest::Request;
 use ruma_identifiers::DeviceId;
+use ruma_identifiers::UserId;
 use serde::{Deserialize, Serialize};
 use url::Url;
 
@@ -31,7 +32,7 @@ pub enum Auth {
 #[derive(Clone, Debug, Deserialize)]
 pub struct Response {
     pub access_token: Option<AccessToken>,
-    pub user_id: Option<String>,
+    pub user_id: Option<UserId>,
     pub device_id: Option<DeviceId>,
 }
 
diff --git a/fractal-matrix-api/src/r0/account/register.rs b/fractal-matrix-api/src/r0/account/register.rs
index 5c01a7f1..c072f24c 100644
--- a/fractal-matrix-api/src/r0/account/register.rs
+++ b/fractal-matrix-api/src/r0/account/register.rs
@@ -4,6 +4,7 @@ use reqwest::Client;
 use reqwest::Error;
 use reqwest::Request;
 use ruma_identifiers::DeviceId;
+use ruma_identifiers::UserId;
 use serde::{Deserialize, Serialize};
 use std::ops::Not;
 use url::Url;
@@ -54,7 +55,7 @@ pub struct Body {
 
 #[derive(Clone, Debug, Deserialize)]
 pub struct Response {
-    pub user_id: String,
+    pub user_id: UserId,
     pub access_token: Option<AccessToken>,
     pub device_id: Option<DeviceId>,
 }
diff --git a/fractal-matrix-api/src/r0/membership/invite_user.rs 
b/fractal-matrix-api/src/r0/membership/invite_user.rs
index 7bb8c07e..1873928a 100644
--- a/fractal-matrix-api/src/r0/membership/invite_user.rs
+++ b/fractal-matrix-api/src/r0/membership/invite_user.rs
@@ -2,7 +2,7 @@ use crate::r0::AccessToken;
 use reqwest::Client;
 use reqwest::Error;
 use reqwest::Request;
-use ruma_identifiers::RoomId;
+use ruma_identifiers::{RoomId, UserId};
 use serde::Serialize;
 use url::Url;
 
@@ -13,7 +13,7 @@ pub struct Parameters {
 
 #[derive(Clone, Debug, Serialize)]
 pub struct Body {
-    pub user_id: String,
+    pub user_id: UserId,
 }
 
 pub fn request(
diff --git a/fractal-matrix-api/src/r0/profile/get_display_name.rs 
b/fractal-matrix-api/src/r0/profile/get_display_name.rs
index a871b55c..0c8f3f19 100644
--- a/fractal-matrix-api/src/r0/profile/get_display_name.rs
+++ b/fractal-matrix-api/src/r0/profile/get_display_name.rs
@@ -1,6 +1,7 @@
 use reqwest::Client;
 use reqwest::Error;
 use reqwest::Request;
+use ruma_identifiers::UserId;
 use serde::Deserialize;
 use url::Url;
 
@@ -9,7 +10,7 @@ pub struct Response {
     pub displayname: Option<String>,
 }
 
-pub fn request(base: Url, user_id: &str) -> Result<Request, Error> {
+pub fn request(base: Url, user_id: &UserId) -> Result<Request, Error> {
     let url = base
         .join(&format!(
             "/_matrix/client/r0/profile/{}/displayname",
diff --git a/fractal-matrix-api/src/r0/profile/get_profile.rs 
b/fractal-matrix-api/src/r0/profile/get_profile.rs
index 7af43940..6713a1f1 100644
--- a/fractal-matrix-api/src/r0/profile/get_profile.rs
+++ b/fractal-matrix-api/src/r0/profile/get_profile.rs
@@ -2,6 +2,7 @@ use crate::serde::option_url;
 use reqwest::Client;
 use reqwest::Error;
 use reqwest::Request;
+use ruma_identifiers::UserId;
 use serde::Deserialize;
 use url::Url;
 
@@ -13,7 +14,7 @@ pub struct Response {
     pub displayname: Option<String>,
 }
 
-pub fn request(base: Url, user_id: &str) -> Result<Request, Error> {
+pub fn request(base: Url, user_id: &UserId) -> Result<Request, Error> {
     let url = base
         .join(&format!("/_matrix/client/r0/profile/{}", user_id))
         .expect("Malformed URL in get_profile_avatar");
diff --git a/fractal-matrix-api/src/r0/profile/set_avatar_url.rs 
b/fractal-matrix-api/src/r0/profile/set_avatar_url.rs
index 715235e1..dd6bb497 100644
--- a/fractal-matrix-api/src/r0/profile/set_avatar_url.rs
+++ b/fractal-matrix-api/src/r0/profile/set_avatar_url.rs
@@ -3,6 +3,7 @@ use crate::serde::option_url;
 use reqwest::Client;
 use reqwest::Error;
 use reqwest::Request;
+use ruma_identifiers::UserId;
 use serde::Serialize;
 use url::Url;
 
@@ -21,7 +22,7 @@ pub fn request(
     base: Url,
     params: &Parameters,
     body: &Body,
-    user_id: &str,
+    user_id: &UserId,
 ) -> Result<Request, Error> {
     let url = base
         .join(&format!(
diff --git a/fractal-matrix-api/src/r0/profile/set_display_name.rs 
b/fractal-matrix-api/src/r0/profile/set_display_name.rs
index a399042e..f3b54b05 100644
--- a/fractal-matrix-api/src/r0/profile/set_display_name.rs
+++ b/fractal-matrix-api/src/r0/profile/set_display_name.rs
@@ -2,6 +2,7 @@ use crate::r0::AccessToken;
 use reqwest::Client;
 use reqwest::Error;
 use reqwest::Request;
+use ruma_identifiers::UserId;
 use serde::Serialize;
 use url::Url;
 
@@ -20,7 +21,7 @@ pub fn request(
     base: Url,
     params: &Parameters,
     body: &Body,
-    user_id: &str,
+    user_id: &UserId,
 ) -> Result<Request, Error> {
     let url = base
         .join(&format!(
diff --git a/fractal-matrix-api/src/r0/search/user.rs b/fractal-matrix-api/src/r0/search/user.rs
index 2fc55367..af5191c8 100644
--- a/fractal-matrix-api/src/r0/search/user.rs
+++ b/fractal-matrix-api/src/r0/search/user.rs
@@ -3,6 +3,7 @@ use crate::serde::option_url;
 use reqwest::Client;
 use reqwest::Error;
 use reqwest::Request;
+use ruma_identifiers::UserId;
 use serde::{Deserialize, Serialize};
 use url::Url;
 
@@ -35,7 +36,7 @@ pub struct Response {
 
 #[derive(Clone, Debug, Deserialize)]
 pub struct User {
-    pub user_id: String,
+    pub user_id: UserId,
     #[serde(default)]
     pub display_name: Option<String>,
     #[serde(with = "option_url")]
diff --git a/fractal-matrix-api/src/r0/sync/get_joined_members.rs 
b/fractal-matrix-api/src/r0/sync/get_joined_members.rs
index e8776c39..483aa388 100644
--- a/fractal-matrix-api/src/r0/sync/get_joined_members.rs
+++ b/fractal-matrix-api/src/r0/sync/get_joined_members.rs
@@ -3,7 +3,7 @@ use crate::serde::option_url;
 use reqwest::Client;
 use reqwest::Error;
 use reqwest::Request;
-use ruma_identifiers::RoomId;
+use ruma_identifiers::{RoomId, UserId};
 use serde::{Deserialize, Serialize};
 use std::collections::HashMap;
 use url::Url;
@@ -16,7 +16,7 @@ pub struct Parameters {
 #[derive(Clone, Debug, Deserialize)]
 pub struct Response {
     #[serde(default)]
-    pub joined: HashMap<String, RoomMember>,
+    pub joined: HashMap<UserId, RoomMember>,
 }
 
 #[derive(Clone, Debug, Deserialize)]
diff --git a/fractal-matrix-api/src/r0/sync/sync_events.rs b/fractal-matrix-api/src/r0/sync/sync_events.rs
index 5aaf9bc2..87be786f 100644
--- a/fractal-matrix-api/src/r0/sync/sync_events.rs
+++ b/fractal-matrix-api/src/r0/sync/sync_events.rs
@@ -3,7 +3,7 @@ use crate::r0::AccessToken;
 use reqwest::Client;
 use reqwest::Error;
 use reqwest::Request;
-use ruma_identifiers::RoomId;
+use ruma_identifiers::{RoomId, UserId};
 use serde::ser::SerializeMap;
 use serde::{Deserialize, Serialize, Serializer};
 use serde_json::Value as JsonValue;
@@ -217,9 +217,9 @@ pub struct ToDevice {
 #[derive(Clone, Debug, Deserialize)]
 pub struct DeviceLists {
     #[serde(default)]
-    pub changed: Vec<String>,
+    pub changed: Vec<UserId>,
     #[serde(default)]
-    pub left: Vec<String>,
+    pub left: Vec<UserId>,
 }
 
 pub fn request(base: Url, params: &Parameters) -> Result<Request, Error> {
diff --git a/fractal-matrix-api/src/r0/tag/create_tag.rs b/fractal-matrix-api/src/r0/tag/create_tag.rs
index e143d604..968b573e 100644
--- a/fractal-matrix-api/src/r0/tag/create_tag.rs
+++ b/fractal-matrix-api/src/r0/tag/create_tag.rs
@@ -2,7 +2,7 @@ use crate::r0::AccessToken;
 use reqwest::Client;
 use reqwest::Error;
 use reqwest::Request;
-use ruma_identifiers::RoomId;
+use ruma_identifiers::{RoomId, UserId};
 use serde::Serialize;
 use url::Url;
 
@@ -20,7 +20,7 @@ pub struct Body {
 
 pub fn request(
     base: Url,
-    user_id: &str,
+    user_id: &UserId,
     room_id: &RoomId,
     tag: &str,
     params: &Parameters,
diff --git a/fractal-matrix-api/src/r0/tag/delete_tag.rs b/fractal-matrix-api/src/r0/tag/delete_tag.rs
index 6c116725..0a45efa1 100644
--- a/fractal-matrix-api/src/r0/tag/delete_tag.rs
+++ b/fractal-matrix-api/src/r0/tag/delete_tag.rs
@@ -2,7 +2,7 @@ use crate::r0::AccessToken;
 use reqwest::Client;
 use reqwest::Error;
 use reqwest::Request;
-use ruma_identifiers::RoomId;
+use ruma_identifiers::{RoomId, UserId};
 use serde::Serialize;
 use url::Url;
 
@@ -13,7 +13,7 @@ pub struct Parameters {
 
 pub fn request(
     base: Url,
-    user_id: &str,
+    user_id: &UserId,
     room_id: &RoomId,
     tag: &str,
     params: &Parameters,
diff --git a/fractal-matrix-api/src/r0/typing.rs b/fractal-matrix-api/src/r0/typing.rs
index 473a023d..bf538e53 100644
--- a/fractal-matrix-api/src/r0/typing.rs
+++ b/fractal-matrix-api/src/r0/typing.rs
@@ -2,7 +2,7 @@ use crate::r0::AccessToken;
 use reqwest::Client;
 use reqwest::Error;
 use reqwest::Request;
-use ruma_identifiers::RoomId;
+use ruma_identifiers::{RoomId, UserId};
 use serde::ser::SerializeMap;
 use serde::Serialize;
 use serde::Serializer;
@@ -44,7 +44,7 @@ impl Serialize for Body {
 pub fn request(
     base: Url,
     room_id: &RoomId,
-    user_id: &str,
+    user_id: &UserId,
     params: &Parameters,
     body: &Body,
 ) -> Result<Request, Error> {
diff --git a/fractal-matrix-api/src/util.rs b/fractal-matrix-api/src/util.rs
index 202bda53..9a81e7d5 100644
--- a/fractal-matrix-api/src/util.rs
+++ b/fractal-matrix-api/src/util.rs
@@ -5,12 +5,12 @@ use serde_json::json;
 use serde_json::Value as JsonValue;
 
 use directories::ProjectDirs;
-use ruma_identifiers::RoomId;
+use ruma_identifiers::{Error as IdError, RoomId, UserId};
 use std::collections::HashMap;
+use std::convert::TryFrom;
 use std::io::Read;
 use std::path::Path;
 use std::path::PathBuf;
-use url::percent_encoding::{utf8_percent_encode, USERINFO_ENCODE_SET};
 use url::Url;
 
 use std::fs::{create_dir_all, write};
@@ -171,7 +171,7 @@ impl ContentType {
     }
 }
 
-pub fn parse_m_direct(events: &Vec<JsonValue>) -> HashMap<String, Vec<String>> {
+pub fn parse_m_direct(events: &Vec<JsonValue>) -> Result<HashMap<UserId, Vec<RoomId>>, IdError> {
     events
         .iter()
         .find(|x| x["type"] == "m.direct")
@@ -179,14 +179,14 @@ pub fn parse_m_direct(events: &Vec<JsonValue>) -> HashMap<String, Vec<String>> {
         .cloned()
         .unwrap_or_default()
         .iter()
-        .map(|(k, v)| {
-            let value = v
+        .map(|(uid, rid)| {
+            let value = rid
                 .as_array()
                 .unwrap_or(&vec![])
                 .iter()
-                .map(|rid| rid.as_str().map(Into::into).unwrap_or_default())
-                .collect();
-            (k.clone(), value)
+                .map(|rid| RoomId::try_from(rid.as_str().unwrap_or_default()))
+                .collect::<Result<Vec<RoomId>, IdError>>()?;
+            Ok((UserId::try_from(uid.as_str())?, value))
         })
         .collect()
 }
@@ -308,8 +308,8 @@ pub fn json_q(method: &str, url: Url, attrs: &JsonValue) -> Result<JsonValue, Er
     }
 }
 
-pub fn get_user_avatar(base: &Url, userid: &str) -> Result<(String, String), Error> {
-    let response = get_profile(base.clone(), &encode_uid(userid))
+pub fn get_user_avatar(base: &Url, user_id: &UserId) -> Result<(String, String), Error> {
+    let response = get_profile(base.clone(), user_id)
         .map_err::<Error, _>(Into::into)
         .and_then(|request| {
             HTTP_CLIENT
@@ -322,12 +322,12 @@ pub fn get_user_avatar(base: &Url, userid: &str) -> Result<(String, String), Err
     let name = response
         .displayname
         .filter(|n| !n.is_empty())
-        .unwrap_or(userid.to_string());
+        .unwrap_or(user_id.to_string());
 
     let img = response
         .avatar_url
         .map(|url| {
-            let dest = cache_dir_path(None, userid)?;
+            let dest = cache_dir_path(None, &user_id.to_string())?;
             dw_media(
                 base,
                 url.as_str(),
@@ -382,10 +382,6 @@ pub fn cache_dir_path(dir: Option<&str>, name: &str) -> Result<String, Error> {
         .ok_or(Error::CacheError)
 }
 
-pub fn encode_uid(userid: &str) -> String {
-    utf8_percent_encode(userid, USERINFO_ENCODE_SET).collect::<String>()
-}
-
 pub trait ResultExpectLog {
     fn expect_log(&self, log: &str);
 }


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