[fractal] Sync through matrix-sdk



commit 8968b9d12e49644216aa9a1940f488070647a6aa
Author: Alejandro Domínguez <adomu net-c com>
Date:   Tue Sep 29 00:11:52 2020 +0200

    Sync through matrix-sdk

 fractal-gtk/Cargo.toml                        |   2 +-
 fractal-gtk/src/appop/invite.rs               |  13 +-
 fractal-gtk/src/appop/member.rs               |  30 +-
 fractal-gtk/src/appop/notifications.rs        |   2 +-
 fractal-gtk/src/appop/room.rs                 |  22 +-
 fractal-gtk/src/appop/sync.rs                 |  44 +-
 fractal-gtk/src/backend/media.rs              |  30 +-
 fractal-gtk/src/backend/room.rs               |  17 +-
 fractal-gtk/src/backend/sync.rs               | 313 +++++--------
 fractal-gtk/src/error.rs                      |  34 +-
 fractal-gtk/src/meson.build                   |   1 -
 fractal-gtk/src/model/event.rs                |  18 -
 fractal-gtk/src/model/message.rs              | 440 +++++++++++++------
 fractal-gtk/src/model/mod.rs                  |   1 -
 fractal-gtk/src/model/room.rs                 | 604 +++++++++++++++-----------
 fractal-gtk/src/passwd.rs                     |   9 +-
 fractal-gtk/src/widgets/media_viewer.rs       |   2 +-
 fractal-gtk/src/widgets/members_list.rs       |  10 +-
 fractal-gtk/src/widgets/roomlist.rs           |   4 +-
 fractal-gtk/src/widgets/roomrow.rs            |   2 +-
 fractal-matrix-api/src/meson.build            |   3 -
 fractal-matrix-api/src/r0.rs                  |   2 -
 fractal-matrix-api/src/r0/filter.rs           | 104 -----
 fractal-matrix-api/src/r0/sync.rs             |   1 -
 fractal-matrix-api/src/r0/sync/sync_events.rs | 226 ----------
 25 files changed, 871 insertions(+), 1063 deletions(-)
---
diff --git a/fractal-gtk/Cargo.toml b/fractal-gtk/Cargo.toml
index 713ae9be..b1fb3d8d 100644
--- a/fractal-gtk/Cargo.toml
+++ b/fractal-gtk/Cargo.toml
@@ -81,4 +81,4 @@ features = ["serde_untagged"]
 
 [dependencies.tokio]
 version = "0.2.22"
-features = ["rt-threaded"]
+features = ["rt-threaded", "time"]
diff --git a/fractal-gtk/src/appop/invite.rs b/fractal-gtk/src/appop/invite.rs
index 5563bea5..40b9bdfa 100644
--- a/fractal-gtk/src/appop/invite.rs
+++ b/fractal-gtk/src/appop/invite.rs
@@ -257,19 +257,18 @@ impl AppOp {
         let empty = String::new();
         let room_name = room_name.unwrap_or(&empty);
         let title = i18n_k("Join {room_name}?", &[("room_name", &room_name)]);
-        let secondary;
-        if let Some(ref sender) = sender {
+        let secondary = if let Some(ref sender) = sender {
             let sender_name = sender.get_alias();
-            secondary = i18n_k(
+            i18n_k(
                 "You’ve been invited to join <b>{room_name}</b> room by <b>{sender_name}</b>",
                 &[("room_name", &room_name), ("sender_name", &sender_name)],
-            );
+            )
         } else {
-            secondary = i18n_k(
+            i18n_k(
                 "You’ve been invited to join <b>{room_name}</b>",
                 &[("room_name", &room_name)],
-            );
-        }
+            )
+        };
 
         dialog.set_property_text(Some(title.as_str()));
         dialog.set_property_secondary_use_markup(true);
diff --git a/fractal-gtk/src/appop/member.rs b/fractal-gtk/src/appop/member.rs
index b7e85676..094be415 100644
--- a/fractal-gtk/src/appop/member.rs
+++ b/fractal-gtk/src/appop/member.rs
@@ -1,6 +1,10 @@
 use crate::backend::{user, HandleError};
 use either::Either;
 use fractal_api::{
+    events::{
+        room::member::{MemberEventContent, MembershipState},
+        StateEvent,
+    },
     identifiers::{RoomId, UserId},
     url::Url,
 };
@@ -16,7 +20,7 @@ use crate::appop::AppOp;
 use crate::widgets;
 use crate::App;
 
-use crate::model::{event::Event, member::Member};
+use crate::model::member::Member;
 
 #[derive(Debug, Clone)]
 pub enum SearchType {
@@ -25,7 +29,7 @@ pub enum SearchType {
 }
 
 impl AppOp {
-    pub fn member_level(&self, member: &Member) -> i32 {
+    pub fn member_level(&self, member: &Member) -> i64 {
         self.active_room
             .as_ref()
             .and_then(|a_room| self.rooms.get(a_room)?.admins.get(&member.uid))
@@ -49,28 +53,28 @@ impl AppOp {
         }
     }
 
-    pub fn room_member_event(&mut self, ev: Event) {
+    pub fn room_member_event(&mut self, ev: StateEvent<MemberEventContent>) {
         // NOTE: maybe we should show this events in the message list to notify enters and leaves
         // to the user
 
         let sender = ev.sender;
-        match ev.content["membership"].as_str() {
-            Some("leave") => {
-                if let Some(r) = self.rooms.get_mut(&ev.room) {
+        match ev.content.membership {
+            MembershipState::Leave => {
+                if let Some(r) = self.rooms.get_mut(&ev.room_id) {
                     r.members.remove(&sender);
                 }
             }
-            Some("join") => {
+            MembershipState::Join => {
                 let m = Member {
-                    avatar: ev.content["avatar_url"]
-                        .as_str()
-                        .map(Url::parse)
-                        .and_then(Result::ok)
+                    avatar: ev
+                        .content
+                        .avatar_url
+                        .and_then(|u| Url::parse(&u).ok())
                         .map(Either::Left),
-                    alias: ev.content["displayname"].as_str().map(String::from),
+                    alias: ev.content.displayname,
                     uid: sender,
                 };
-                if let Some(r) = self.rooms.get_mut(&ev.room.clone()) {
+                if let Some(r) = self.rooms.get_mut(&ev.room_id) {
                     r.members.insert(m.uid.clone(), m);
                 }
             }
diff --git a/fractal-gtk/src/appop/notifications.rs b/fractal-gtk/src/appop/notifications.rs
index d39ce3e9..85f63c24 100644
--- a/fractal-gtk/src/appop/notifications.rs
+++ b/fractal-gtk/src/appop/notifications.rs
@@ -8,7 +8,7 @@ impl AppOp {
         self.update_title();
     }
 
-    pub fn set_room_notifications(&mut self, room_id: RoomId, n: i32, h: i32) {
+    pub fn set_room_notifications(&mut self, room_id: RoomId, n: u64, h: u64) {
         if let Some(r) = self.rooms.get_mut(&room_id) {
             r.notifications = n;
             r.highlight = h;
diff --git a/fractal-gtk/src/appop/room.rs b/fractal-gtk/src/appop/room.rs
index 72907110..021b77ed 100644
--- a/fractal-gtk/src/appop/room.rs
+++ b/fractal-gtk/src/appop/room.rs
@@ -53,10 +53,11 @@ impl AppOp {
         for room in rooms {
             // removing left rooms
             if let RoomMembership::Left(kicked) = room.membership.clone() {
-                if let Reason::Kicked(reason, kicker) = kicked {
+                if let Reason::Kicked(reason, kicker_uid) = kicked {
                     if let Some(r) = self.rooms.get(&room.id) {
                         let room_name = r.name.clone().unwrap_or_default();
-                        self.kicked_room(room_name, reason, kicker.alias.unwrap_or_default());
+                        let kicker = r.members.get(&kicker_uid);
+                        self.kicked_room(room_name, reason, kicker);
                     }
                 }
                 if self.active_room.as_ref().map_or(false, |x| x == &room.id) {
@@ -182,8 +183,9 @@ impl AppOp {
             if let Some(language) = room.language.clone() {
                 self.set_language(language);
             }
-            if let RoomMembership::Invited(ref sender) = room.membership {
-                self.show_inv_dialog(Some(sender), room.name.as_ref());
+            if let RoomMembership::Invited(ref sender_uid) = room.membership {
+                let sender = room.members.get(sender_uid);
+                self.show_inv_dialog(sender, room.name.as_ref());
                 self.invitation_roomid = Some(room.id.clone());
                 return;
             }
@@ -359,7 +361,7 @@ impl AppOp {
         dialog.present();
     }
 
-    pub fn kicked_room(&self, room_name: String, reason: String, kicker: String) {
+    pub fn kicked_room(&self, room_name: String, reason: String, kicker: Option<&Member>) {
         let parent: gtk::Window = self
             .ui
             .builder
@@ -367,7 +369,15 @@ impl AppOp {
             .expect("Can't find main_window in ui file.");
         let viewer = widgets::KickedDialog::new();
         viewer.set_parent_window(&parent);
-        viewer.show(&room_name, &reason, &kicker);
+        let kicker_str = kicker
+            .map(|k| {
+                k.alias
+                    .as_ref()
+                    .map(String::as_str)
+                    .unwrap_or(k.uid.as_str())
+            })
+            .unwrap_or_default();
+        viewer.show(&room_name, &reason, kicker_str);
     }
 
     pub fn create_new_room(&mut self) {
diff --git a/fractal-gtk/src/appop/sync.rs b/fractal-gtk/src/appop/sync.rs
index e9a63e31..0400bc28 100644
--- a/fractal-gtk/src/appop/sync.rs
+++ b/fractal-gtk/src/appop/sync.rs
@@ -1,9 +1,6 @@
-use log::info;
-use std::thread;
-
 use crate::util::i18n::i18n;
 
-use crate::app::App;
+use crate::app::{App, RUNTIME};
 use crate::appop::AppOp;
 use crate::backend::{
     sync::{self, RoomElement, SyncRet},
@@ -19,7 +16,7 @@ impl AppOp {
         }
     }
 
-    pub fn sync(&mut self, initial: bool, number_tries: u64) {
+    pub fn sync(&mut self, initial: bool, number_tries: u32) {
         if let (Some(login_data), false) = (self.login_data.clone(), self.syncing) {
             self.syncing = true;
             // for the initial sync we set the since to None to avoid long syncing
@@ -28,16 +25,18 @@ impl AppOp {
             // https://matrix.org/docs/spec/client_server/latest.html#syncing
             let join_to_room = self.join_to_room.clone();
             let since = self.since.clone().filter(|_| !initial);
-            thread::spawn(move || {
-                match sync::sync(
-                    login_data.session_client.clone(),
-                    login_data.access_token,
+            RUNTIME.spawn(async move {
+                let query = sync::sync(
+                    login_data.session_client,
                     login_data.uid,
                     join_to_room,
                     since,
                     initial,
                     number_tries,
-                ) {
+                )
+                .await;
+
+                match query {
                     Ok(SyncRet::NoSince { rooms, next_batch }) => {
                         match rooms {
                             Ok((rooms, default)) => {
@@ -56,13 +55,11 @@ impl AppOp {
                             }
                         };
 
-                        info!("SYNC");
                         let s = Some(next_batch);
                         APPOP!(synced, (s));
                     }
                     Ok(SyncRet::WithSince {
                         update_rooms,
-                        room_messages,
                         room_notifications,
                         update_rooms_2,
                         other,
@@ -71,15 +68,9 @@ impl AppOp {
                         match update_rooms {
                             Ok(rooms) => {
                                 let clear_room_list = false;
+                                let msgs: Vec<_> =
+                                    rooms.iter().flat_map(|r| &r.messages).cloned().collect();
                                 APPOP!(set_rooms, (rooms, clear_room_list));
-                            }
-                            Err(err) => {
-                                err.handle_error();
-                            }
-                        }
-
-                        match room_messages {
-                            Ok(msgs) => {
                                 APPOP!(show_room_messages, (msgs));
                             }
                             Err(err) => {
@@ -99,8 +90,14 @@ impl AppOp {
 
                         for (room_id, unread_notifications) in room_notifications {
                             let r = room_id;
-                            let n = unread_notifications.notification_count;
-                            let h = unread_notifications.highlight_count;
+                            let n: u64 = unread_notifications
+                                .notification_count
+                                .map(Into::into)
+                                .unwrap_or_default();
+                            let h: u64 = unread_notifications
+                                .highlight_count
+                                .map(Into::into)
+                                .unwrap_or_default();
                             APPOP!(set_room_notifications, (r, n, h));
                         }
 
@@ -133,7 +130,6 @@ impl AppOp {
                             }
                         }
 
-                        info!("SYNC");
                         let s = Some(next_batch);
                         APPOP!(synced, (s));
                     }
@@ -152,7 +148,7 @@ impl AppOp {
         self.initial_sync(false);
     }
 
-    pub fn sync_error(&mut self, number_tries: u64) {
+    pub fn sync_error(&mut self, number_tries: u32) {
         self.syncing = false;
         self.sync(false, number_tries);
     }
diff --git a/fractal-gtk/src/backend/media.rs b/fractal-gtk/src/backend/media.rs
index 3176624d..b8db5019 100644
--- a/fractal-gtk/src/backend/media.rs
+++ b/fractal-gtk/src/backend/media.rs
@@ -1,8 +1,9 @@
 use super::MediaError;
 use crate::globals;
-use fractal_api::identifiers::{Error as IdError, EventId, RoomId};
+use fractal_api::identifiers::{EventId, RoomId};
 use fractal_api::url::Url;
 use fractal_api::{Client as MatrixClient, Error as MatrixError};
+use std::convert::TryInto;
 use std::path::PathBuf;
 
 use crate::model::message::Message;
@@ -48,14 +49,11 @@ pub async fn get_media_list(
         .ok()
 }
 
-enum GetRoomMediaListError {
-    Matrix(MatrixError),
-    EventsDeserialization(IdError),
-}
+struct GetRoomMediaListError(MatrixError);
 
-impl From<MatrixError> for GetRoomMediaListError {
-    fn from(err: MatrixError) -> Self {
-        Self::Matrix(err)
+impl<T: Into<MatrixError>> From<T> for GetRoomMediaListError {
+    fn from(err: T) -> Self {
+        Self(err.into())
     }
 }
 
@@ -79,14 +77,18 @@ async fn get_room_media_list(
     let response = session_client.room_messages(request).await?;
 
     let prev_batch = response.end.unwrap_or_default();
-    // Deserialization to JsonValue should not fail
-    let evs = response
+
+    let media_list = response
         .chunk
-        .iter()
+        .into_iter()
         .rev()
-        .map(|ev| serde_json::to_value(ev.json().get()).unwrap());
-    let media_list = Message::from_json_events(room_id, evs)
-        .map_err(GetRoomMediaListError::EventsDeserialization)?;
+        .filter_map(|ev| {
+            ev.deserialize()
+                .map(TryInto::try_into)
+                .map(Result::ok)
+                .transpose()
+        })
+        .collect::<Result<_, _>>()?;
 
     Ok((media_list, prev_batch))
 }
diff --git a/fractal-gtk/src/backend/room.rs b/fractal-gtk/src/backend/room.rs
index a9423054..a2064d47 100644
--- a/fractal-gtk/src/backend/room.rs
+++ b/fractal-gtk/src/backend/room.rs
@@ -3,7 +3,7 @@ use serde_json::json;
 
 use fractal_api::{
     api::error::ErrorKind as RumaErrorKind,
-    identifiers::{Error as IdError, EventId, RoomId, RoomIdOrAliasId, UserId},
+    identifiers::{EventId, RoomId, RoomIdOrAliasId, UserId},
     url::{ParseError as UrlError, Url},
     Client as MatrixClient, Error as MatrixError, FromHttpResponseError as RumaResponseError,
     ServerError,
@@ -12,7 +12,7 @@ use serde::Serialize;
 use std::io::Error as IoError;
 use std::path::Path;
 
-use std::convert::TryFrom;
+use std::convert::{TryFrom, TryInto};
 use std::time::Duration;
 
 use crate::globals;
@@ -228,7 +228,6 @@ pub async fn get_room_members(
 pub enum RoomMessagesToError {
     MessageNotSent,
     Matrix(MatrixError),
-    EventsDeserialization(IdError),
 }
 
 impl<T: Into<MatrixError>> From<T> for RoomMessagesToError {
@@ -260,13 +259,17 @@ pub async fn get_room_messages(
     let response = session_client.room_messages(request).await?;
 
     let prev_batch = response.end;
-    let evs = response
+    let list: Vec<Message> = response
         .chunk
         .into_iter()
         .rev()
-        .map(|ev| serde_json::to_value(ev.json().get()).unwrap());
-    let list = Message::from_json_events(&room_id, evs)
-        .map_err(RoomMessagesToError::EventsDeserialization)?;
+        .filter_map(|ev| {
+            ev.deserialize()
+                .map(TryInto::try_into)
+                .map(Result::ok)
+                .transpose()
+        })
+        .collect::<Result<_, _>>()?;
 
     Ok((list, room_id, prev_batch))
 }
diff --git a/fractal-gtk/src/backend/sync.rs b/fractal-gtk/src/backend/sync.rs
index 3aef92ba..f46f214c 100644
--- a/fractal-gtk/src/backend/sync.rs
+++ b/fractal-gtk/src/backend/sync.rs
@@ -1,36 +1,31 @@
-use crate::client::ProxySettings;
-use crate::error::{Error, StandardErrorResponse};
 use crate::globals;
 use crate::model::{
-    event::Event,
     member::Member,
-    message::Message,
     room::{Room, RoomMembership, RoomTag},
 };
-use fractal_api::r0::filter::EventFilter;
-use fractal_api::r0::filter::Filter;
-use fractal_api::r0::filter::RoomEventFilter;
-use fractal_api::r0::filter::RoomFilter;
-use fractal_api::r0::sync::sync_events::request as sync_events;
-use fractal_api::r0::sync::sync_events::IncludeState;
-use fractal_api::r0::sync::sync_events::Parameters as SyncParameters;
-use fractal_api::r0::sync::sync_events::Response as SyncResponse;
-use fractal_api::r0::sync::sync_events::UnreadNotificationsCount;
-use fractal_api::r0::AccessToken;
+use fractal_api::api::r0::filter::Filter as EventFilter;
+use fractal_api::api::r0::filter::FilterDefinition;
+use fractal_api::api::r0::filter::LazyLoadOptions;
+use fractal_api::api::r0::filter::RoomEventFilter;
+use fractal_api::api::r0::filter::RoomFilter;
+use fractal_api::api::r0::sync::sync_events::Filter;
+use fractal_api::api::r0::sync::sync_events::UnreadNotificationsCount;
+use fractal_api::assign;
+use fractal_api::events::room::member::MemberEventContent;
+use fractal_api::events::AnyEphemeralRoomEventContent;
+use fractal_api::events::AnySyncMessageEvent;
+use fractal_api::events::AnySyncRoomEvent;
+use fractal_api::events::AnySyncStateEvent;
+use fractal_api::events::StateEvent;
+use fractal_api::SyncSettings;
 
 use fractal_api::identifiers::{EventId, RoomId, UserId};
-use fractal_api::reqwest::blocking::{Client, Response};
 use fractal_api::Client as MatrixClient;
+use fractal_api::Error as MatrixError;
 use log::error;
-use serde::de::DeserializeOwned;
-use serde_json::value::from_value;
-use std::{
-    collections::HashMap,
-    convert::{TryFrom, TryInto},
-    thread, time,
-};
+use std::{collections::HashMap, time::Duration};
 
-use super::{remove_matrix_access_token_if_present, HandleError};
+use super::{get_ruma_client_error, remove_matrix_access_token_if_present, HandleError};
 use crate::app::App;
 use crate::APPOP;
 
@@ -38,12 +33,12 @@ pub enum RoomElement {
     Name(RoomId, String),
     Topic(RoomId, String),
     NewAvatar(RoomId),
-    MemberEvent(Event),
+    MemberEvent(StateEvent<MemberEventContent>),
     RemoveMessage(RoomId, EventId),
 }
 
 #[derive(Debug)]
-pub struct SyncError(Error, u64);
+pub struct SyncError(MatrixError, u32);
 
 impl HandleError for SyncError {
     fn handle_error(&self) {
@@ -58,9 +53,9 @@ impl HandleError for SyncError {
 }
 
 #[derive(Debug)]
-pub struct RoomsError(Error);
+pub struct RoomsError(MatrixError);
 
-impl<T: Into<Error>> From<T> for RoomsError {
+impl<T: Into<MatrixError>> From<T> for RoomsError {
     fn from(err: T) -> Self {
         Self(err.into())
     }
@@ -69,9 +64,9 @@ impl<T: Into<Error>> From<T> for RoomsError {
 impl HandleError for RoomsError {}
 
 #[derive(Debug)]
-pub struct UpdateRoomsError(Error);
+pub struct UpdateRoomsError(MatrixError);
 
-impl<T: Into<Error>> From<T> for UpdateRoomsError {
+impl<T: Into<MatrixError>> From<T> for UpdateRoomsError {
     fn from(err: T) -> Self {
         Self(err.into())
     }
@@ -80,20 +75,9 @@ impl<T: Into<Error>> From<T> for UpdateRoomsError {
 impl HandleError for UpdateRoomsError {}
 
 #[derive(Debug)]
-pub struct RoomMessagesError(Error);
-
-impl<T: Into<Error>> From<T> for RoomMessagesError {
-    fn from(err: T) -> Self {
-        Self(err.into())
-    }
-}
-
-impl HandleError for RoomMessagesError {}
-
-#[derive(Debug)]
-pub struct RoomElementError(Error);
+pub struct RoomElementError(MatrixError);
 
-impl<T: Into<Error>> From<T> for RoomElementError {
+impl<T: Into<MatrixError>> From<T> for RoomElementError {
     fn from(err: T) -> Self {
         Self(err.into())
     }
@@ -108,7 +92,6 @@ pub enum SyncRet {
     },
     WithSince {
         update_rooms: Result<Vec<Room>, UpdateRoomsError>,
-        room_messages: Result<Vec<Message>, RoomMessagesError>,
         room_notifications: HashMap<RoomId, UnreadNotificationsCount>,
         update_rooms_2: Result<Vec<Room>, UpdateRoomsError>,
         other: Result<Vec<RoomElement>, RoomElementError>,
@@ -116,83 +99,56 @@ pub enum SyncRet {
     },
 }
 
-pub fn sync(
+pub async fn sync(
     session_client: MatrixClient,
-    access_token: AccessToken,
     user_id: UserId,
     join_to_room: Option<RoomId>,
     since: Option<String>,
     initial: bool,
-    number_tries: u64,
+    number_tries: u32,
 ) -> Result<SyncRet, SyncError> {
-    let base = session_client.homeserver().clone();
-
-    let (timeout, filter) = if !initial {
-        (time::Duration::from_secs(30), Default::default())
+    let timeline_not_types = [String::from("m.call.*")];
+    let timeline_types = [String::from("m.room.message"), String::from("m.sticker")];
+    let state_types = [String::from("m.room.*")];
+    let sync_settings = if !initial {
+        SyncSettings::new().timeout(Duration::from_secs(30))
     } else {
-        let filter = Filter {
-            room: Some(RoomFilter {
-                state: Some(RoomEventFilter {
-                    lazy_load_members: true,
-                    types: Some(vec!["m.room.*"]),
-                    ..Default::default()
+        // Don't filter event fields, it breaks deserialization.
+        // Clearly the Matrix API is very static-typing-unfriendly right now.
+        let filter = assign!(FilterDefinition::empty(), {
+            presence: assign!(EventFilter::empty(), {
+                types: Some(&[]),
+            }),
+            room: assign!(RoomFilter::empty(), {
+                timeline: assign!(RoomEventFilter::empty(), {
+                    not_types: &timeline_not_types,
+                    limit: Some(globals::PAGE_LIMIT.into()),
+                    types: Some(&timeline_types),
                 }),
-                timeline: Some(RoomEventFilter {
-                    types: Some(vec!["m.room.message", "m.sticker"]),
-                    not_types: vec!["m.call.*"],
-                    limit: Some(globals::PAGE_LIMIT as i32),
-                    ..Default::default()
+                ephemeral: assign!(RoomEventFilter::empty(), {
+                    types: Some(&[]),
                 }),
-                ephemeral: Some(RoomEventFilter {
-                    types: Some(vec![]),
-                    ..Default::default()
+                state: assign!(RoomEventFilter::empty(), {
+                    types: Some(&state_types),
+                    lazy_load_options: LazyLoadOptions::Enabled {
+                        include_redundant_members: false,
+                    },
                 }),
-                ..Default::default()
             }),
-            presence: Some(EventFilter {
-                types: Some(vec![]),
-                ..Default::default()
-            }),
-            event_fields: Some(vec![
-                "type",
-                "content",
-                "sender",
-                "origin_server_ts",
-                "event_id",
-                "unsigned",
-            ]),
-            ..Default::default()
-        };
+        });
 
-        (Default::default(), filter)
+        SyncSettings::new().filter(Filter::FilterDefinition(filter))
     };
 
-    let params = SyncParameters {
-        access_token: access_token.clone(),
-        filter,
-        include_state: IncludeState::Changed {
-            since: since.clone().unwrap_or_default(),
-            timeout,
-        },
-        set_presence: Default::default(),
+    let sync_settings = match since.clone() {
+        Some(sync_token) => sync_settings.token(sync_token),
+        None => sync_settings,
     };
 
-    let client_builder_timeout = Client::builder().timeout(Some(globals::TIMEOUT + timeout));
-
-    let query = ProxySettings::current().and_then(|proxy_settings| {
-        let client = proxy_settings
-            .apply_to_blocking_client_builder(client_builder_timeout)
-            .build()?;
-        let request = sync_events(base.clone(), &params)?;
-        let response = client.execute(request)?;
-
-        matrix_response::<SyncResponse>(response)
-    });
-
-    match query {
+    match session_client.sync_once(sync_settings).await {
         Ok(response) => {
             if since.is_none() {
-                let rooms = Room::from_sync_response(session_client, &response, user_id)
+                let rooms = Room::from_sync_response(&response, user_id)
                     .map(|rooms| {
                         let def = join_to_room
                             .and_then(|jtr| rooms.iter().find(|x| x.id == jtr).cloned());
@@ -208,20 +164,7 @@ pub fn sync(
 
                 // New rooms
                 let update_rooms =
-                    Room::from_sync_response(session_client, &response, user_id.clone())
-                        .map_err(Into::into);
-
-                // Message events
-                let room_messages = join
-                    .iter()
-                    .try_fold(Vec::new(), |mut acum, (k, room)| {
-                        let events = room.timeline.events.clone();
-                        Message::from_json_events(&k, events).map(|msgs| {
-                            acum.extend(msgs);
-                            acum
-                        })
-                    })
-                    .map_err(Into::into);
+                    Room::from_sync_response(&response, user_id.clone()).map_err(Into::into);
 
                 // Room notifications
                 let room_notifications = join
@@ -230,96 +173,76 @@ pub fn sync(
                     .collect();
 
                 // Typing notifications
-                let update_rooms_2 = Ok(join
+                let update_rooms_2 = join
                     .iter()
                     .map(|(k, room)| {
-                        let ephemerals = &room.ephemeral.events;
-                        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()
+                        let typing: Vec<Member> = room.ephemeral.events
+                            .iter()
+                            .map(|ev| ev.deserialize())
+                            .collect::<Result<Vec<_>, _>>()?
+                            .into_iter()
+                            .filter_map(|event| match event.content() {
+                                AnyEphemeralRoomEventContent::Typing(content) => {
+                                    Some(content.user_ids)
+                                }
+                                _ => None,
                             })
-                            .filter_map(|user| from_value(user).ok())
+                            .flatten()
                             // ignoring the user typing notifications
                             .filter(|user| *user != user_id)
-                            .map(|uid| {
-                                Member {
-                                    uid,
-                                    alias: None,
-                                    avatar: None,
-                                }
+                            .map(|uid| Member {
+                                uid,
+                                alias: None,
+                                avatar: None,
                             })
                             .collect();
 
-                        Room {
+                        Ok(Room {
                             typing_users: typing,
                             ..Room::new(k.clone(), RoomMembership::Joined(RoomTag::None))
-                        }
+                        })
                     })
-                    .collect());
+                    .collect();
 
                 // Other events
                 let other = join
                     .iter()
-                    .flat_map(|(k, room)| {
+                    .flat_map(|(room_id, room)| {
+                        let room_id = room_id.clone();
                         room.timeline
                             .events
                             .iter()
-                            .filter(|x| x["type"] != "m.room.message")
-                            .map(move |ev| {
-                                Ok(Event {
-                                    room: k.clone(),
-                                    sender: UserId::try_from(
-                                        ev["sender"].as_str().unwrap_or_default(),
-                                    )?,
-                                    content: ev["content"].clone(),
-                                    redacts: ev["redacts"]
-                                        .as_str()
-                                        .map(|r| r.try_into())
-                                        .transpose()?,
-                                    stype: ev["type"].as_str().map(Into::into).unwrap_or_default(),
-                                    id: ev["id"].as_str().map(Into::into).unwrap_or_default(),
-                                })
-                            })
+                            .map(move |ev| Ok((room_id.clone(), ev.deserialize()?)))
                     })
-                    .filter_map(|ev| {
-                        let ev = match ev {
-                            Ok(ev) => ev,
+                    .filter_map(|rid_ev| {
+                        let (room_id, event) = match rid_ev {
+                            Ok(roomid_event) => roomid_event,
                             Err(err) => return Some(Err(err)),
                         };
 
-                        match ev.stype.as_ref() {
-                            "m.room.name" => {
-                                let name = ev.content["name"]
-                                    .as_str()
-                                    .map(Into::into)
-                                    .unwrap_or_default();
-                                Some(Ok(RoomElement::Name(ev.room.clone(), name)))
+                        match event {
+                            AnySyncRoomEvent::State(AnySyncStateEvent::RoomName(ev)) => {
+                                let name = ev.content.name().map(Into::into).unwrap_or_default();
+                                Some(Ok(RoomElement::Name(room_id, name)))
+                            }
+                            AnySyncRoomEvent::State(AnySyncStateEvent::RoomTopic(ev)) => {
+                                Some(Ok(RoomElement::Topic(room_id, ev.content.topic)))
+                            }
+                            AnySyncRoomEvent::State(AnySyncStateEvent::RoomAvatar(_)) => {
+                                Some(Ok(RoomElement::NewAvatar(room_id)))
+                            }
+                            AnySyncRoomEvent::State(AnySyncStateEvent::RoomMember(ev)) => {
+                                Some(Ok(RoomElement::MemberEvent(ev.into_full_event(room_id))))
                             }
-                            "m.room.topic" => {
-                                let t = ev.content["topic"]
-                                    .as_str()
-                                    .map(Into::into)
-                                    .unwrap_or_default();
-                                Some(Ok(RoomElement::Topic(ev.room.clone(), t)))
+                            AnySyncRoomEvent::Message(AnySyncMessageEvent::RoomRedaction(ev)) => {
+                                Some(Ok(RoomElement::RemoveMessage(room_id, ev.redacts)))
                             }
-                            "m.room.avatar" => Some(Ok(RoomElement::NewAvatar(ev.room.clone()))),
-                            "m.room.member" => Some(Ok(RoomElement::MemberEvent(ev))),
-                            "m.room.redaction" => Some(Ok(RoomElement::RemoveMessage(
-                                ev.room.clone(),
-                                ev.redacts.expect(
-                                    "Events of type m.room.redaction should have a 'redacts' field",
-                                ),
-                            ))),
-                            "m.sticker" => {
+                            AnySyncRoomEvent::Message(AnySyncMessageEvent::RoomMessage(_)) => None,
+                            AnySyncRoomEvent::Message(AnySyncMessageEvent::Sticker(_)) => {
                                 // This event is managed in the room list
                                 None
                             }
-                            _ => {
+                            ev => {
                                 error!("EVENT NOT MANAGED: {:?}", ev);
                                 None
                             }
@@ -331,7 +254,6 @@ pub fn sync(
 
                 Ok(SyncRet::WithSince {
                     update_rooms,
-                    room_messages,
                     room_notifications,
                     update_rooms_2,
                     other,
@@ -342,36 +264,19 @@ pub fn sync(
         Err(err) => {
             // we wait if there's an error to avoid 100% CPU
             // we wait even longer, if it's a 429 (Too Many Requests) error
-            let waiting_time = match err {
-                Error::NetworkError(status) if status.as_u16() == 429 => {
-                    10 * 2_u64.pow(
-                        number_tries
-                            .try_into()
-                            .expect("The number of sync tries couldn't be transformed into a u32."),
-                    )
+            let waiting_time = Duration::from_secs(match get_ruma_client_error(&err) {
+                Some(ruma_err) if ruma_err.status_code.as_u16() == 429 => {
+                    10 * 2_u64.pow(number_tries)
                 }
                 _ => 10,
-            };
+            });
             error!(
-                "Sync Error, waiting {:?} seconds to respond for the next sync",
-                waiting_time
+                "Sync Error, waiting {} seconds to respond for the next sync",
+                waiting_time.as_secs()
             );
-            thread::sleep(time::Duration::from_secs(waiting_time));
+            tokio::time::delay_for(waiting_time).await;
 
             Err(SyncError(err, number_tries))
         }
     }
 }
-
-/// Returns the deserialized response to the given request. Handles Matrix errors.
-fn matrix_response<T: DeserializeOwned>(response: Response) -> Result<T, Error> {
-    if !response.status().is_success() {
-        let status = response.status();
-        return match response.json::<StandardErrorResponse>() {
-            Ok(error_response) => Err(Error::from(error_response)),
-            Err(_) => Err(Error::NetworkError(status)),
-        };
-    }
-
-    response.json::<T>().map_err(Into::into)
-}
diff --git a/fractal-gtk/src/error.rs b/fractal-gtk/src/error.rs
index e9034489..43fcbb64 100644
--- a/fractal-gtk/src/error.rs
+++ b/fractal-gtk/src/error.rs
@@ -1,30 +1,7 @@
-use serde::Deserialize;
-
-#[derive(Clone, Debug, Deserialize)]
-pub struct StandardErrorResponse {
-    pub errcode: String,
-    pub error: String,
-}
-
-type MatrixErrorCode = String;
-
-#[macro_export]
-macro_rules! derror {
-    ($from: path, $to: path) => {
-        impl From<$from> for Error {
-            fn from(_: $from) -> Error {
-                $to
-            }
-        }
-    };
-}
-
 #[derive(Debug)]
 pub enum Error {
-    BackendError,
+    GlibError(glib::Error),
     ReqwestError(fractal_api::reqwest::Error),
-    NetworkError(fractal_api::reqwest::StatusCode),
-    MatrixError(MatrixErrorCode, String),
 }
 
 impl From<fractal_api::reqwest::Error> for Error {
@@ -33,11 +10,8 @@ impl From<fractal_api::reqwest::Error> for Error {
     }
 }
 
-impl From<StandardErrorResponse> for Error {
-    fn from(resp: StandardErrorResponse) -> Error {
-        Error::MatrixError(resp.errcode, resp.error)
+impl From<glib::Error> for Error {
+    fn from(err: glib::Error) -> Error {
+        Error::GlibError(err)
     }
 }
-
-derror!(glib::error::Error, Error::BackendError);
-derror!(fractal_api::identifiers::Error, Error::BackendError);
diff --git a/fractal-gtk/src/meson.build b/fractal-gtk/src/meson.build
index e86770af..92fc9f02 100644
--- a/fractal-gtk/src/meson.build
+++ b/fractal-gtk/src/meson.build
@@ -81,7 +81,6 @@ app_sources = files(
   'backend/user.rs',
   'cache/mod.rs',
   'cache/state.rs',
-  'model/event.rs',
   'model/fileinfo.rs',
   'model/member.rs',
   'model/message.rs',
diff --git a/fractal-gtk/src/model/message.rs b/fractal-gtk/src/model/message.rs
index 783a934e..3aeee80d 100644
--- a/fractal-gtk/src/model/message.rs
+++ b/fractal-gtk/src/model/message.rs
@@ -1,15 +1,20 @@
 use chrono::prelude::*;
 use chrono::DateTime;
-use chrono::TimeZone;
 use fractal_api::{
-    identifiers::{Error as IdError, EventId, RoomId, UserId},
+    events::{
+        room::message::{MessageEventContent, RedactedMessageEventContent, Relation},
+        sticker::{RedactedStickerEventContent, StickerEventContent},
+        AnyMessageEvent, AnyRedactedMessageEvent, AnyRedactedSyncMessageEvent, AnyRoomEvent,
+        AnySyncMessageEvent, AnySyncRoomEvent, EventContent, MessageEvent, RedactedMessageEvent,
+    },
+    identifiers::{EventId, RoomId, UserId},
     url::Url,
 };
 use serde::{Deserialize, Serialize};
 use serde_json::Value as JsonValue;
 use std::cmp::Ordering;
 use std::collections::HashMap;
-use std::convert::TryInto;
+use std::convert::TryFrom;
 use std::path::PathBuf;
 
 //FIXME make properties private
@@ -61,87 +66,183 @@ impl PartialOrd for Message {
     }
 }
 
-impl Message {
-    /// List all supported types. By default a message map a m.room.message event, but there's
-    /// other events that we want to show in the message history so we map other event types to our
-    /// Message struct, like stickers
-    const SUPPORTED_EVENTS: [&'static str; 2] = ["m.room.message", "m.sticker"];
+impl From<MessageEvent<MessageEventContent>> for Message {
+    fn from(msg: MessageEvent<MessageEventContent>) -> Self {
+        let source = serde_json::to_string_pretty(&msg).ok();
 
-    pub fn new(
-        room: RoomId,
-        sender: UserId,
-        body: String,
-        mtype: String,
-        id: Option<EventId>,
-    ) -> Self {
-        let date = Local::now();
-        Message {
-            id,
-            sender,
-            mtype,
-            body,
-            date,
-            room,
-            thumb: None,
-            local_path_thumb: None,
+        let initial_message = Self {
+            sender: msg.sender,
+            date: msg.origin_server_ts.into(),
+            room: msg.room_id,
+            // It is mandatory for a message event to have
+            // an event_id field
+            id: Some(msg.event_id),
+            mtype: String::new(),
+            body: String::new(),
             url: None,
             local_path: None,
+            thumb: None,
+            local_path_thumb: None,
             formatted_body: None,
             format: None,
-            source: None,
+            source,
             receipt: HashMap::new(),
             redacted: false,
             in_reply_to: None,
             replace: None,
             extra_content: None,
-        }
-    }
-
-    /// Generates an unique transaction id for this message
-    /// The txn_id is generated using the md5sum of a concatenation of the message room id, the
-    /// message body and the date.
-    ///
-    /// 
https://matrix.org/docs/spec/client_server/r0.3.0.html#put-matrix-client-r0-rooms-roomid-send-eventtype-txnid
-    // TODO: Return matrix_sdk::uuid::Uuid
-    pub fn get_txn_id(&self) -> String {
-        let msg_str = format!("{}{}{}", self.room, self.body, self.date);
-        let digest = md5::compute(msg_str.as_bytes());
-        format!("{:x}", digest)
-    }
+        };
 
-    /// Helper function to use in iterator filter of a matrix.org json response to filter supported
-    /// events
-    fn supported_event(ev: &JsonValue) -> bool {
-        let type_ = ev["type"].as_str().unwrap_or_default();
+        match msg.content {
+            MessageEventContent::Audio(content) => Self {
+                mtype: String::from("m.audio"),
+                body: content.body,
+                url: content.url.and_then(|u| Url::parse(&u).ok()),
+                ..initial_message
+            },
+            MessageEventContent::File(content) => {
+                let url = content.url.and_then(|u| Url::parse(&u).ok());
+                Self {
+                    mtype: String::from("m.file"),
+                    body: content.body,
+                    url: url.clone(),
+                    thumb: content
+                        .info
+                        .and_then(|c_info| Url::parse(&c_info.thumbnail_url?).ok())
+                        .or(url),
+                    ..initial_message
+                }
+            }
+            MessageEventContent::Image(content) => {
+                let url = content.url.and_then(|u| Url::parse(&u).ok());
+                Self {
+                    mtype: String::from("m.image"),
+                    body: content.body,
+                    url: url.clone(),
+                    thumb: content
+                        .info
+                        .and_then(|c_info| Url::parse(&c_info.thumbnail_url?).ok())
+                        .or(url),
+                    ..initial_message
+                }
+            }
+            MessageEventContent::Video(content) => {
+                let url = content.url.and_then(|u| Url::parse(&u).ok());
+                Self {
+                    mtype: String::from("m.video"),
+                    body: content.body,
+                    url: url.clone(),
+                    thumb: content
+                        .info
+                        .and_then(|c_info| Url::parse(&c_info.thumbnail_url?).ok())
+                        .or(url),
+                    ..initial_message
+                }
+            }
+            MessageEventContent::Text(content) => {
+                let (in_reply_to, replace) =
+                    content.relates_to.map_or(Default::default(), |r| match r {
+                        Relation::Replacement(rep) => (None, Some(rep.event_id)),
+                        Relation::Reply { in_reply_to } => (Some(in_reply_to.event_id), None),
+                        _ => (None, None),
+                    });
+                let (body, formatted, in_reply_to) = content.new_content.map_or(
+                    (content.body, content.formatted, in_reply_to),
+                    |nc| {
+                        let in_reply_to = nc.relates_to.and_then(|r| match r {
+                            Relation::Reply { in_reply_to } => Some(in_reply_to.event_id),
+                            _ => None,
+                        });
 
-        Self::SUPPORTED_EVENTS.contains(&type_)
-    }
+                        (nc.body, nc.formatted, in_reply_to)
+                    },
+                );
+                let (formatted_body, format) = formatted.map_or(Default::default(), |f| {
+                    (Some(f.body), Some(f.format.as_str().into()))
+                });
 
-    /// Parses a matrix.org event and return a Message object
-    ///
-    /// # Arguments
-    ///
-    /// * `roomid` - The message room id
-    /// * `msg` - The message event as Json
-    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()?;
+                Self {
+                    mtype: String::from("m.text"),
+                    body,
+                    formatted_body,
+                    format,
+                    in_reply_to,
+                    replace,
+                    ..initial_message
+                }
+            }
+            MessageEventContent::Emote(content) => {
+                let (formatted_body, format): (Option<String>, Option<String>) =
+                    content.formatted.map_or((None, None), |f| {
+                        (Some(f.body), Some(f.format.as_str().into()))
+                    });
+                Self {
+                    mtype: String::from("m.emote"),
+                    body: content.body,
+                    formatted_body,
+                    format,
+                    ..initial_message
+                }
+            }
+            MessageEventContent::Location(content) => Self {
+                mtype: String::from("m.location"),
+                body: content.body,
+                ..initial_message
+            },
+            MessageEventContent::Notice(content) => {
+                let (in_reply_to, replace) =
+                    content.relates_to.map_or(Default::default(), |r| match r {
+                        Relation::Replacement(rep) => (None, Some(rep.event_id)),
+                        Relation::Reply { in_reply_to } => (Some(in_reply_to.event_id), None),
+                        _ => (None, None),
+                    });
+                let (body, formatted, in_reply_to) = content.new_content.map_or(
+                    (content.body, content.formatted, in_reply_to),
+                    |nc| {
+                        let in_reply_to = nc.relates_to.and_then(|r| match r {
+                            Relation::Reply { in_reply_to } => Some(in_reply_to.event_id),
+                            _ => None,
+                        });
 
-        let timestamp = msg["origin_server_ts"].as_i64().unwrap_or_default() / 1000;
-        let server_timestamp: DateTime<Local> = Local.timestamp(timestamp, 0);
+                        (nc.body, nc.formatted, in_reply_to)
+                    },
+                );
+                let (formatted_body, format) = formatted.map_or(Default::default(), |f| {
+                    (Some(f.body), Some(f.format.as_str().into()))
+                });
 
-        let id: EventId = msg["event_id"].as_str().unwrap_or_default().try_into()?;
-        let type_ = msg["type"].as_str().unwrap_or_default();
+                Self {
+                    mtype: String::from("m.notice"),
+                    body,
+                    formatted_body,
+                    format,
+                    in_reply_to,
+                    replace,
+                    ..initial_message
+                }
+            }
+            MessageEventContent::ServerNotice(content) => Self {
+                mtype: String::from("m.server_notice"),
+                body: content.body,
+                ..initial_message
+            },
+            _ => initial_message,
+        }
+    }
+}
 
-        let redacted = msg["unsigned"].get("redacted_because") != None;
+impl From<RedactedMessageEvent<RedactedMessageEventContent>> for Message {
+    fn from(msg: RedactedMessageEvent<RedactedMessageEventContent>) -> Self {
+        let source = serde_json::to_string_pretty(&msg).ok();
 
-        let mut message = Message {
-            sender,
-            date: server_timestamp,
-            room: room_id.clone(),
+        Self {
+            sender: msg.sender,
+            date: msg.origin_server_ts.into(),
+            room: msg.room_id,
             // It is mandatory for a message event to have
             // an event_id field
-            id: Some(id),
-            mtype: type_.to_string(),
+            id: Some(msg.event_id),
+            mtype: String::from(msg.content.event_type()),
             body: String::new(),
             url: None,
             local_path: None,
@@ -149,90 +250,167 @@ impl Message {
             local_path_thumb: None,
             formatted_body: None,
             format: None,
-            source: serde_json::to_string_pretty(&msg).ok(),
+            source,
             receipt: HashMap::new(),
-            redacted,
+            redacted: true,
             in_reply_to: None,
             replace: None,
             extra_content: None,
-        };
+        }
+    }
+}
 
-        let c = &msg["content"];
-        match type_ {
-            "m.room.message" => message.parse_m_room_message(c),
-            "m.sticker" => message.parse_m_sticker(c),
-            _ => {}
-        };
+impl From<MessageEvent<StickerEventContent>> for Message {
+    fn from(msg: MessageEvent<StickerEventContent>) -> Self {
+        let source = serde_json::to_string_pretty(&msg).ok();
+        let url = Url::parse(&msg.content.url).ok();
 
-        Ok(message)
+        Self {
+            sender: msg.sender,
+            date: msg.origin_server_ts.into(),
+            room: msg.room_id,
+            // It is mandatory for a message event to have
+            // an event_id field
+            id: Some(msg.event_id),
+            mtype: String::from(msg.content.event_type()),
+            body: msg.content.body,
+            url: url.clone(),
+            local_path: None,
+            thumb: msg
+                .content
+                .info
+                .thumbnail_url
+                .and_then(|thumb| Url::parse(&thumb).ok())
+                .or(url),
+            local_path_thumb: None,
+            formatted_body: None,
+            format: None,
+            source,
+            receipt: HashMap::new(),
+            redacted: false,
+            in_reply_to: None,
+            replace: None,
+            extra_content: None,
+        }
     }
+}
 
-    fn parse_m_room_message(&mut self, mut c: &JsonValue) {
-        let rel_type = c["m.relates_to"]["rel_type"]
-            .as_str()
-            .map(String::from)
-            .unwrap_or_default();
-
-        if rel_type == "m.replace" {
-            self.replace = c["m.relates_to"]["event_id"]
-                .as_str()
-                .and_then(|evid| evid.try_into().ok());
-            c = &c["m.new_content"];
+impl From<RedactedMessageEvent<RedactedStickerEventContent>> for Message {
+    fn from(msg: RedactedMessageEvent<RedactedStickerEventContent>) -> Self {
+        let source = serde_json::to_string_pretty(&msg).ok();
+
+        Self {
+            sender: msg.sender,
+            date: msg.origin_server_ts.into(),
+            room: msg.room_id,
+            // It is mandatory for a message event to have
+            // an event_id field
+            id: Some(msg.event_id),
+            mtype: String::from(msg.content.event_type()),
+            body: String::new(),
+            url: None,
+            local_path: None,
+            thumb: None,
+            local_path_thumb: None,
+            formatted_body: None,
+            format: None,
+            source,
+            receipt: HashMap::new(),
+            redacted: true,
+            in_reply_to: None,
+            replace: None,
+            extra_content: None,
         }
+    }
+}
+
+impl TryFrom<AnyRoomEvent> for Message {
+    type Error = ();
 
-        let mtype = c["msgtype"].as_str().map(String::from).unwrap_or_default();
-        let body = c["body"].as_str().map(String::from).unwrap_or_default();
-        let formatted_body = c["formatted_body"].as_str().map(String::from);
-        let format = c["format"].as_str().map(String::from);
-
-        match mtype.as_str() {
-            "m.image" | "m.file" | "m.video" | "m.audio" => {
-                self.url = c["url"].as_str().map(Url::parse).and_then(Result::ok);
-                self.thumb = c["info"]["thumbnail_url"]
-                    .as_str()
-                    .map(Url::parse)
-                    .and_then(Result::ok)
-                    .or_else(|| Some(self.url.clone()?));
+    fn try_from(event: AnyRoomEvent) -> Result<Self, Self::Error> {
+        match event {
+            AnyRoomEvent::Message(AnyMessageEvent::RoomMessage(room_messages_event)) => {
+                Ok(Self::from(room_messages_event))
             }
-            "m.text" => {
-                // Only m.text messages can be replies for backward compatibility
-                // https://matrix.org/docs/spec/client_server/r0.4.0.html#rich-replies
-                self.in_reply_to = c["m.relates_to"]["m.in_reply_to"]["event_id"]
-                    .as_str()
-                    .and_then(|evid| evid.try_into().ok());
+            AnyRoomEvent::Message(AnyMessageEvent::Sticker(sticker_event)) => {
+                Ok(Self::from(sticker_event))
             }
-            _ => {}
-        };
+            AnyRoomEvent::RedactedMessage(AnyRedactedMessageEvent::RoomMessage(
+                redacted_room_messages_event,
+            )) => Ok(Self::from(redacted_room_messages_event)),
+            AnyRoomEvent::RedactedMessage(AnyRedactedMessageEvent::Sticker(
+                redacted_sticker_event,
+            )) => Ok(Self::from(redacted_sticker_event)),
+            _ => Err(()),
+        }
+    }
+}
 
-        self.mtype = mtype;
-        self.body = body;
-        self.formatted_body = formatted_body;
-        self.format = format;
+impl TryFrom<(RoomId, AnySyncRoomEvent)> for Message {
+    type Error = ();
+
+    fn try_from((room_id, event): (RoomId, AnySyncRoomEvent)) -> Result<Self, Self::Error> {
+        match event {
+            AnySyncRoomEvent::Message(AnySyncMessageEvent::RoomMessage(room_messages_event)) => {
+                Ok(Self::from(room_messages_event.into_full_event(room_id)))
+            }
+            AnySyncRoomEvent::Message(AnySyncMessageEvent::Sticker(sticker_event)) => {
+                Ok(Self::from(sticker_event.into_full_event(room_id)))
+            }
+            AnySyncRoomEvent::RedactedMessage(AnyRedactedSyncMessageEvent::RoomMessage(
+                redacted_room_messages_event,
+            )) => Ok(Self::from(
+                redacted_room_messages_event.into_full_event(room_id),
+            )),
+            AnySyncRoomEvent::RedactedMessage(AnyRedactedSyncMessageEvent::Sticker(
+                redacted_sticker_event,
+            )) => Ok(Self::from(redacted_sticker_event.into_full_event(room_id))),
+            _ => Err(()),
+        }
     }
+}
 
-    fn parse_m_sticker(&mut self, c: &JsonValue) {
-        self.url = c["url"].as_str().map(Url::parse).and_then(Result::ok);
-        self.thumb = c["info"]["thumbnail_url"]
-            .as_str()
-            .map(Url::parse)
-            .and_then(Result::ok)
-            .or_else(|| Some(self.url.clone()?));
-        self.body = c["body"].as_str().map(String::from).unwrap_or_default();
+impl Message {
+    pub fn new(
+        room: RoomId,
+        sender: UserId,
+        body: String,
+        mtype: String,
+        id: Option<EventId>,
+    ) -> Self {
+        let date = Local::now();
+        Message {
+            id,
+            sender,
+            mtype,
+            body,
+            date,
+            room,
+            thumb: None,
+            local_path_thumb: None,
+            url: None,
+            local_path: None,
+            formatted_body: None,
+            format: None,
+            source: None,
+            receipt: HashMap::new(),
+            redacted: false,
+            in_reply_to: None,
+            replace: None,
+            extra_content: None,
+        }
     }
 
-    /// Create a vec of Message from a json event list
+    /// Generates an unique transaction id for this message
+    /// The txn_id is generated using the md5sum of a concatenation of the message room id, the
+    /// message body and the date.
     ///
-    /// * `roomid` - The messages room id
-    /// * `events` - An iterator to the json events
-    pub fn from_json_events<I>(room_id: &RoomId, events: I) -> Result<Vec<Message>, IdError>
-    where
-        I: IntoIterator<Item = JsonValue>,
-    {
-        events
-            .into_iter()
-            .filter(Message::supported_event)
-            .map(|msg| Message::parse_room_message(&room_id, &msg))
-            .collect()
+    /// 
https://matrix.org/docs/spec/client_server/r0.3.0.html#put-matrix-client-r0-rooms-roomid-send-eventtype-txnid
+    // TODO: Return matrix_sdk::uuid::Uuid
+    pub fn get_txn_id(&self) -> String {
+        let msg_str = format!("{}{}{}", self.room, self.body, self.date);
+        let digest = md5::compute(msg_str.as_bytes());
+        format!("{:x}", digest)
     }
 
     pub fn set_receipt(&mut self, receipt: HashMap<UserId, i64>) {
diff --git a/fractal-gtk/src/model/mod.rs b/fractal-gtk/src/model/mod.rs
index 0569a8c0..6d9eedcb 100644
--- a/fractal-gtk/src/model/mod.rs
+++ b/fractal-gtk/src/model/mod.rs
@@ -1,4 +1,3 @@
-pub mod event;
 pub mod fileinfo;
 pub mod member;
 pub mod message;
diff --git a/fractal-gtk/src/model/room.rs b/fractal-gtk/src/model/room.rs
index e11952be..a9600ccc 100644
--- a/fractal-gtk/src/model/room.rs
+++ b/fractal-gtk/src/model/room.rs
@@ -1,21 +1,23 @@
-use serde_json::Value as JsonValue;
-
-use crate::app::RUNTIME;
-use crate::backend::user::get_user_avatar;
 use crate::model::member::Member;
 use crate::model::member::MemberList;
 use crate::model::message::Message;
+use chrono::DateTime;
+use chrono::Utc;
 use either::Either;
+use fractal_api::api::r0::sync::sync_events::Response as SyncResponse;
 use fractal_api::directory::PublicRoomsChunk;
-use fractal_api::identifiers::{Error as IdError, EventId, RoomAliasId, RoomId, UserId};
-use fractal_api::r0::sync::sync_events::Response as SyncResponse;
+use fractal_api::events::{
+    room::member::{MemberEventContent, MembershipState},
+    AnyBasicEvent, AnyStrippedStateEvent, AnySyncEphemeralRoomEvent, AnySyncStateEvent,
+    SyncStateEvent,
+};
+use fractal_api::identifiers::{EventId, RoomAliasId, RoomId, UserId};
 use fractal_api::url::{ParseError as UrlError, Url};
-use fractal_api::Client as MatrixClient;
+use fractal_api::Error as MatrixError;
 use log::{debug, info};
 use serde::{Deserialize, Serialize};
 use std::collections::{HashMap, HashSet};
 use std::convert::{TryFrom, TryInto};
-use std::path::PathBuf;
 
 #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
 pub enum RoomMembership {
@@ -23,7 +25,7 @@ pub enum RoomMembership {
     None,
     Joined(RoomTag),
     // An invite is send by some other user
-    Invited(Member),
+    Invited(UserId),
     Left(Reason),
 }
 
@@ -72,7 +74,7 @@ impl Default for RoomMembership {
 #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
 pub enum Reason {
     None,
-    Kicked(String, Member),
+    Kicked(String, UserId),
 }
 
 #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
@@ -94,8 +96,8 @@ pub struct Room {
     pub world_readable: bool,
     pub n_members: u64,
     pub members: MemberList,
-    pub notifications: i32,
-    pub highlight: i32,
+    pub notifications: u64,
+    pub highlight: u64,
     pub messages: Vec<Message>,
     pub membership: RoomMembership,
     pub direct: bool,
@@ -105,8 +107,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<UserId, i32>,
-    pub default_power_level: i32,
+    pub admins: HashMap<UserId, i64>,
+    pub default_power_level: i64,
 }
 
 impl Room {
@@ -135,106 +137,261 @@ impl Room {
     }
 
     pub fn from_sync_response(
-        session_client: MatrixClient,
         response: &SyncResponse,
         user_id: UserId,
-    ) -> Result<Vec<Self>, IdError> {
+    ) -> Result<Vec<Self>, MatrixError> {
         // getting the list of direct rooms
-        let direct: HashSet<RoomId> = parse_m_direct(&response.account_data.events)
-            .values()
+        let direct: HashSet<RoomId> = response.account_data.events
+            .iter()
+            // Synapse sometimes sends an object with the key "[object Object]"
+            // instead of a user ID, so we have to skip those invalid objects
+            // in the array in order to avoid discarding everything
+            .filter_map(|ev| ev.deserialize().ok())
+            .find_map(|event| match event {
+                AnyBasicEvent::Direct(ev) => Some(ev.content.0),
+                _ => None,
+            })
+            .into_iter()
             .flatten()
-            .cloned()
+            .flat_map(|(_, rids)| rids)
             .collect();
 
+        /*
+        response.rooms.join.iter().for_each(|(_, room)| {
+            room.state.events.iter().for_each(|ev| log::info!("stevents: {}", ev.json().get()));
+        });
+        */
+
         let joined_rooms = response.rooms.join.iter().map(|(k, room)| {
-            let stevents = &room.state.events;
-            let timeline = &room.timeline;
-            let ephemeral = &room.ephemeral;
-            let dataevs = &room.account_data.events;
+            let stevents = room
+                .state
+                .events
+                .iter()
+                .map(|ev| ev.deserialize())
+                .collect::<Result<Vec<_>, _>>()?;
+            let dataevs = room
+                .account_data
+                .events
+                .iter()
+                .map(|ev| ev.deserialize())
+                .collect::<Result<Vec<_>, _>>()?;
             let room_tag = dataevs
                 .iter()
-                .filter(|x| x["type"] == "m.tag")
-                .find_map(|tag| tag["content"]["tags"]["m.favourite"].as_object())
+                .find_map(|event| match event {
+                    AnyBasicEvent::Tag(ev) => ev.content.tags.get("m.favourite"),
+                    _ => None,
+                })
                 .and(Some(RoomTag::Favourite))
                 .unwrap_or(RoomTag::None);
-            let room_lang = dataevs
+
+            let members: MemberList = stevents
                 .iter()
-                .filter(|x| x["type"] == "org.gnome.fractal.language")
-                .find_map(|entry| entry["content"]["input_language"].as_str())
-                .map(|lang| lang.to_string());
+                .filter_map(|event| match event {
+                    AnySyncStateEvent::RoomMember(ev) => parse_room_member(ev),
+                    _ => None,
+                })
+                .map(|m| (m.uid.clone(), m))
+                .collect();
 
             let mut r = Self {
-                name: calculate_room_name(stevents, &user_id),
-                avatar: evc(stevents, "m.room.avatar", "url").and_then(|url| Url::parse(&url).ok()),
-                alias: evc(stevents, "m.room.canonical_alias", "alias")
-                    .and_then(|alias| RoomAliasId::try_from(alias).ok()),
-                topic: evc(stevents, "m.room.topic", "topic"),
+                name: stevents
+                    .iter()
+                    .filter_map(|event| match event {
+                        AnySyncStateEvent::RoomName(ev) => {
+                            ev.content.name().filter(|name| !name.is_empty()).map(Err)
+                        }
+                        AnySyncStateEvent::RoomCanonicalAlias(ev) => ev
+                            .content
+                            .alias
+                            .as_ref()
+                            .map(|r_alias| Ok(r_alias.as_str())),
+                        _ => None,
+                    })
+                    .try_fold(None, |_, alias_name| alias_name.map(Some))
+                    .unwrap_or_else(Some)
+                    .map(Into::into)
+                    .or_else(|| {
+                        let members: Vec<_> = members
+                            .values()
+                            .map(|m| m.alias.as_deref().unwrap_or_else(|| m.uid.as_str()))
+                            .filter(|&uid| uid == user_id.as_str())
+                            .collect();
+                        room_name_from_members(&members)
+                    }),
+                avatar: stevents
+                    .iter()
+                    .find_map(|event| match event {
+                        AnySyncStateEvent::RoomAvatar(ev) => Some(ev.content.url.as_deref()),
+                        _ => None,
+                    })
+                    .flatten()
+                    .map(Url::parse)
+                    .and_then(Result::ok),
+                alias: stevents
+                    .iter()
+                    .find_map(|event| match event {
+                        AnySyncStateEvent::RoomCanonicalAlias(ev) => Some(ev.content.alias.clone()),
+                        _ => None,
+                    })
+                    .flatten(),
+                topic: stevents.iter().find_map(|event| match event {
+                    AnySyncStateEvent::RoomTopic(ev) => Some(ev.content.topic.clone()),
+                    _ => None,
+                }),
                 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(&k, timeline.events.clone())?,
-                admins: get_admins(stevents)?,
-                default_power_level: get_default_power_level(stevents),
-                members: stevents
+                notifications: room
+                    .unread_notifications
+                    .notification_count
+                    .map(Into::into)
+                    .unwrap_or_default(),
+                highlight: room
+                    .unread_notifications
+                    .highlight_count
+                    .map(Into::into)
+                    .unwrap_or_default(),
+                prev_batch: room.timeline.prev_batch.clone(),
+                messages: room
+                    .timeline
+                    .events
+                    .iter()
+                    .map(|ev| Ok((k.clone(), ev.deserialize()?)))
+                    .filter_map(|k_ev| k_ev.map(TryInto::try_into).map(Result::ok).transpose())
+                    .collect::<Result<Vec<_>, MatrixError>>()?,
+                admins: stevents
                     .iter()
-                    .filter(|x| x["type"] == "m.room.member")
-                    .filter_map(parse_room_member)
-                    .map(|m| (m.uid.clone(), m))
+                    .filter_map(|event| match event {
+                        AnySyncStateEvent::RoomPowerLevels(ev) => Some(ev.content.users.clone()),
+                        _ => None,
+                    })
+                    .flatten()
+                    .map(|(uid, level)| (uid, level.into()))
                     .collect(),
-                language: room_lang,
+                default_power_level: stevents
+                    .iter()
+                    .filter_map(|event| match event {
+                        AnySyncStateEvent::RoomPowerLevels(ev) => {
+                            Some(ev.content.users_default.clone().into())
+                        }
+                        _ => None,
+                    })
+                    .last()
+                    .unwrap_or(-1),
+                members,
+                language: dataevs
+                    .iter()
+                    .find_map(|event| match event {
+                        AnyBasicEvent::Custom(ev)
+                            if ev.content.event_type == "org.gnome.fractal.language" =>
+                        {
+                            ev.content.json["input_language"].as_str()
+                        }
+                        _ => None,
+                    })
+                    .map(String::from),
                 ..Self::new(k.clone(), RoomMembership::Joined(room_tag))
             };
 
-            r.add_receipt_from_json(
-                ephemeral
-                    .events
-                    .iter()
-                    .filter(|ev| ev["type"] == "m.receipt")
-                    .collect(),
-            );
-            // Adding fully read to the receipts events
-            if let Some(ev) = dataevs
+            let receipts: HashMap<EventId, HashMap<UserId, i64>> = room
+                .ephemeral
+                .events
+                .iter()
+                .find_map(|event| match event.deserialize() {
+                    Ok(AnySyncEphemeralRoomEvent::Receipt(ev)) => Some(Ok(ev.content.0)),
+                    Ok(_) => None,
+                    Err(err) => Some(Err(err)),
+                })
+                .transpose()?
+                .into_iter()
+                .flatten()
+                .map(|(event_id, receipts)| {
+                    let receipts = receipts
+                        .read
+                        .into_iter()
+                        .flatten()
+                        .map(|(uid, receipt)| {
+                            let ts = receipt
+                                .ts
+                                .map(DateTime::<Utc>::from)
+                                .map(|time| time.timestamp())
+                                .unwrap_or_default();
+                            (uid, ts)
+                        })
+                        .inspect(|(_, ts)| {
+                            debug!("Value of timestamp 'ts': {:?}", ts);
+                            if *ts == 0 {
+                                info!(
+                                    "Possibly malformed timestamp, working around synapse bug 4898"
+                                );
+                            };
+                        })
+                        .collect();
+
+                    (event_id, receipts)
+                })
+                .collect();
+
+            r.messages
+                .iter_mut()
+                .filter_map(|msg| {
+                    let receipt = msg
+                        .id
+                        .as_ref()
+                        .and_then(|evid| receipts.get(evid))
+                        .cloned()?;
+                    Some((msg, receipt))
+                })
+                .for_each(|(msg, receipt)| msg.set_receipt(receipt));
+
+            if let Some(event_id) = room
+                .ephemeral
+                .events
                 .iter()
-                .find(|x| x["type"] == "m.fully_read")
-                .and_then(|fread| fread["content"]["event_id"].as_str()?.try_into().ok())
+                .find_map(|event| match event.deserialize() {
+                    Ok(AnySyncEphemeralRoomEvent::FullyRead(ev)) => Some(Ok(ev.content.event_id)),
+                    Ok(_) => None,
+                    Err(err) => Some(Err(err)),
+                })
+                .transpose()?
             {
-                r.add_receipt_from_fully_read(user_id.clone(), ev);
+                let event_id = Some(event_id);
+
+                r.messages
+                    .iter_mut()
+                    .filter(|msg| msg.id == event_id)
+                    .for_each(|msg| {
+                        msg.receipt.insert(user_id.clone(), 0);
+                    });
             }
 
             Ok(r)
         });
 
         let left_rooms = response.rooms.leave.iter().map(|(k, room)| {
-            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 {
+            // TODO: The spec doesn't explain where to get the reason
+            //       for the kicking from, so matrix-sdk doesn't support
+            //       that.
+            if let Some(last_event) = room
+                .timeline
+                .events
+                .last()
+                .map(|ev| serde_json::to_value(ev.json()))
+                .transpose()?
+            {
+                if let Some(kicker) =
+                    UserId::try_from(last_event["sender"].as_str().unwrap_or_default())
+                        .ok()
+                        .filter(|leave_uid| *leave_uid != user_id)
+                {
                     let kick_reason = &last_event["content"]["reason"];
-                    if let Ok((kicker_alias, kicker_avatar)) = RUNTIME
-                        .handle()
-                        .block_on(get_user_avatar(session_client.clone(), &leave_id))
-                    {
-                        let kicker = Member {
-                            alias: Some(kicker_alias),
-                            avatar: Some(Either::Right(kicker_avatar)),
-                            uid: leave_id,
-                        };
-                        let reason = Reason::Kicked(
-                            String::from(kick_reason.as_str().unwrap_or_default()),
-                            kicker,
-                        );
-                        Self::new(k.clone(), RoomMembership::Left(reason))
-                    } else {
-                        Self::new(k.clone(), RoomMembership::Left(Reason::None))
-                    }
-                } else {
-                    Self::new(k.clone(), RoomMembership::Left(Reason::None))
+                    let reason = Reason::Kicked(
+                        String::from(kick_reason.as_str().unwrap_or_default()),
+                        kicker,
+                    );
+                    return Ok(Self::new(k.clone(), RoomMembership::Left(reason)));
                 }
-            } else {
-                Self::new(k.clone(), RoomMembership::Left(Reason::None))
             };
 
-            Ok(r)
+            Ok(Self::new(k.clone(), RoomMembership::Left(Reason::None)))
         });
 
         let invited_rooms = response
@@ -242,34 +399,75 @@ impl Room {
             .invite
             .iter()
             .map(|(k, room)| {
-                let stevents = &room.invite_state.events;
-                let alias_avatar: Result<Option<(String, PathBuf)>, IdError> = stevents
+                let stevents = room
+                    .invite_state
+                    .events
                     .iter()
-                    .find(|x| {
-                        x["content"]["membership"] == "invite"
-                            && x["state_key"] == user_id.to_string().as_str()
+                    .map(|ev| ev.deserialize())
+                    .collect::<Result<Vec<_>, _>>()?;
+                let inv_sender = stevents
+                    .iter()
+                    .find_map(|event| match event {
+                        AnyStrippedStateEvent::RoomMember(ev)
+                            if ev.content.membership == MembershipState::Invite
+                                && ev.state_key == user_id =>
+                        {
+                            Some(ev)
+                        }
+                        _ => None,
                     })
-                    .map_or(Ok(None), |ev| {
-                        let user_id = UserId::try_from(ev["sender"].as_str().unwrap_or_default())?;
-                        let avatar = RUNTIME
-                            .handle()
-                            .block_on(get_user_avatar(session_client.clone(), &user_id));
-                        Ok(avatar.ok())
-                    });
-                if let Some((alias, avatar)) = alias_avatar? {
-                    let inv_sender = Member {
-                        alias: Some(alias),
-                        avatar: Some(Either::Right(avatar)),
-                        uid: user_id.clone(),
-                    };
-
+                    .map(|ev| ev.sender.clone());
+                if let Some(inv_sender) = inv_sender {
                     Ok(Some(Self {
-                        name: calculate_room_name(stevents, &user_id),
-                        avatar: evc(stevents, "m.room.avatar", "url")
-                            .and_then(|url| Url::parse(&url).ok()),
-                        alias: evc(stevents, "m.room.canonical_alias", "alias")
-                            .and_then(|alias| RoomAliasId::try_from(alias).ok()),
-                        topic: evc(stevents, "m.room.topic", "topic"),
+                        name: stevents
+                            .iter()
+                            .filter_map(|event| match event {
+                                AnyStrippedStateEvent::RoomName(ev) => {
+                                    ev.content.name().filter(|name| !name.is_empty()).map(Err)
+                                }
+                                AnyStrippedStateEvent::RoomCanonicalAlias(ev) => ev
+                                    .content
+                                    .alias
+                                    .as_ref()
+                                    .map(|r_alias| Ok(r_alias.as_str())),
+                                _ => None,
+                            })
+                            .try_fold(None, |_, alias_name| alias_name.map(Some))
+                            .unwrap_or_else(Some)
+                            .map(Into::into)
+                            .or_else(|| {
+                                let members: Vec<_> = stevents
+                                    .iter()
+                                    .filter_map(|event| member_from_stripped_event(event, &user_id))
+                                    .take(3)
+                                    .map(Into::into)
+                                    .collect();
+                                room_name_from_members(&members)
+                            }),
+                        avatar: stevents
+                            .iter()
+                            .find_map(|event| match event {
+                                AnyStrippedStateEvent::RoomAvatar(ev) => {
+                                    Some(ev.content.url.as_deref())
+                                }
+                                _ => None,
+                            })
+                            .flatten()
+                            .map(Url::parse)
+                            .and_then(Result::ok),
+                        alias: stevents
+                            .iter()
+                            .find_map(|event| match event {
+                                AnyStrippedStateEvent::RoomCanonicalAlias(ev) => {
+                                    Some(ev.content.alias.clone())
+                                }
+                                _ => None,
+                            })
+                            .flatten(),
+                        topic: stevents.iter().find_map(|event| match event {
+                            AnyStrippedStateEvent::RoomTopic(ev) => Some(ev.content.topic.clone()),
+                            _ => None,
+                        }),
                         direct: direct.contains(&k),
                         ..Self::new(k.clone(), RoomMembership::Invited(inv_sender))
                     }))
@@ -284,49 +482,6 @@ impl Room {
             .chain(invited_rooms)
             .collect()
     }
-
-    pub fn add_receipt_from_json(&mut self, mut events: Vec<&JsonValue>) {
-        let receipts: HashMap<EventId, HashMap<UserId, i64>> = events
-            .pop()
-            .and_then(|ev| ev["content"].as_object())
-            .into_iter()
-            .flatten()
-            .filter_map(|(mid, obj)| {
-                let event_id = mid.as_str().try_into().ok()?;
-                let receipts = obj["m.read"]
-                    .as_object()?
-                    .iter()
-                    .map(|(uid, ts)| {
-                        debug!("Value of timestamp 'ts': {}", ts);
-                        let ts = ts["ts"].as_i64().unwrap_or(0);
-                        if ts == 0 {
-                            info!("Possibly malformed timestamp, working around synapse bug 4898");
-                        };
-                        Ok((UserId::try_from(uid.as_str())?, ts))
-                    })
-                    .collect::<Result<HashMap<UserId, i64>, IdError>>()
-                    .ok()?;
-
-                Some((event_id, receipts))
-            })
-            .collect();
-
-        for msg in self.messages.iter_mut() {
-            if let Some(r) = msg.id.as_ref().and_then(|evid| receipts.get(evid)) {
-                msg.set_receipt(r.clone());
-            }
-        }
-    }
-
-    pub fn add_receipt_from_fully_read(&mut self, uid: UserId, event_id: EventId) {
-        let event_id = Some(event_id);
-
-        let _ = self
-            .messages
-            .iter_mut()
-            .filter(|msg| msg.id == event_id)
-            .map(|msg| msg.receipt.insert(uid.clone(), 0));
-    }
 }
 
 impl TryFrom<PublicRoomsChunk> for Room {
@@ -358,121 +513,56 @@ impl PartialEq for Room {
 
 pub type RoomList = HashMap<RoomId, Room>;
 
-fn evc(events: &[JsonValue], t: &str, field: &str) -> Option<String> {
-    events
-        .iter()
-        .find(|x| x["type"] == t)
-        .and_then(|js| js["content"][field].as_str())
-        .map(Into::into)
-}
-
-fn get_admins(stevents: &[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)| {
-            Ok((
-                UserId::try_from(k.as_str())?,
-                v.as_i64().map(|v| v as i32).unwrap_or_default(),
-            ))
-        })
-        .collect()
-}
-
-fn get_default_power_level(stevents: &[JsonValue]) -> i32 {
-    stevents
-        .iter()
-        .filter(|x| x["type"] == "m.room.power_levels")
-        .filter_map(|ev| ev["content"]["users_default"].as_i64())
-        .last()
-        .unwrap_or(-1) as i32
-}
-
-fn calculate_room_name(events: &[JsonValue], user_id: &UserId) -> Option<String> {
-    let userid = user_id.to_string();
-    // looking for "m.room.name" event
-    if let Some(name) = events
-        .iter()
-        .find(|x| x["type"] == "m.room.name")
-        .and_then(|name| name["content"]["name"].as_str())
-        .filter(|name| !name.is_empty())
-        .map(Into::into)
-    {
-        return Some(name);
-    }
-
-    // looking for "m.room.canonical_alias" event
-    if let Some(name) = events
-        .iter()
-        .find(|x| x["type"] == "m.room.canonical_alias")
-        .and_then(|name| name["content"]["alias"].as_str())
-        .map(Into::into)
-    {
-        return Some(name);
+fn member_from_stripped_event<'a>(
+    event: &'a AnyStrippedStateEvent,
+    user_id: &UserId,
+) -> Option<&'a str> {
+    match event {
+        AnyStrippedStateEvent::RoomMember(ev) => match ev.content.membership {
+            MembershipState::Join if ev.sender.as_str() != user_id.as_str() => Some(
+                ev.content
+                    .displayname
+                    .as_ref()
+                    .map(String::as_str)
+                    .unwrap_or_else(|| ev.sender.as_str()),
+            ),
+            MembershipState::Invite if ev.state_key.as_str() != user_id.as_str() => Some(
+                ev.content
+                    .displayname
+                    .as_ref()
+                    .map(String::as_str)
+                    .unwrap_or_else(|| ev.state_key.as_str()),
+            ),
+            _ => None,
+        },
+        _ => None,
     }
+}
 
-    // we look for members that aren't me
-    let members: Vec<&str> = events
-        .iter()
-        .filter(|x| {
-            x["type"] == "m.room.member"
-                && ((x["content"]["membership"] == "join" && x["sender"] != userid.as_str())
-                    || (x["content"]["membership"] == "invite"
-                        && x["state_key"] != userid.as_str()))
-        })
-        .take(3)
-        .map(|m| {
-            let sender = m["sender"].as_str().unwrap_or("NONAMED");
-            m["content"]["displayname"].as_str().unwrap_or(sender)
-        })
-        .collect();
-
+fn room_name_from_members(members: &[&str]) -> Option<String> {
     match members.len() {
-        // we don't have information to calculate the name
         0 => None,
-        1 => Some(members[0].to_string()),
+        1 => Some(members[0].to_owned()),
         2 => Some(format!("{} and {}", members[0], members[1])),
         _ => Some(format!("{} and Others", members[0])),
     }
 }
 
-fn parse_room_member(msg: &JsonValue) -> Option<Member> {
-    let c = &msg["content"];
-    let _ = c["membership"].as_str().filter(|&m| m == "join")?;
-
-    Some(Member {
-        uid: msg["sender"].as_str().unwrap_or_default().try_into().ok()?,
-        alias: c["displayname"].as_str().map(String::from),
-        avatar: c["avatar_url"]
-            .as_str()
-            .map(Url::parse)
-            .and_then(Result::ok)
-            .map(Either::Left),
-    })
-}
-
-fn parse_m_direct(events: &[JsonValue]) -> HashMap<UserId, Vec<RoomId>> {
-    events
-        .iter()
-        .find(|x| x["type"] == "m.direct")
-        .and_then(|js| js["content"].as_object())
-        .cloned()
-        .unwrap_or_default()
-        .iter()
-        // Synapse sometimes sends an object with the key "[object Object]"
-        // instead of a user ID, so we have to skip those invalid objects
-        // in the array in order to avoid discarding everything
-        .filter_map(|(uid, rid)| {
-            let value = rid
-                .as_array()
-                .unwrap_or(&vec![])
-                .iter()
-                .map(|rid| RoomId::try_from(rid.as_str().unwrap_or_default()))
-                .collect::<Result<Vec<RoomId>, IdError>>()
-                .ok()?;
-            Some((UserId::try_from(uid.as_str()).ok()?, value))
+fn parse_room_member(msg: &SyncStateEvent<MemberEventContent>) -> Option<Member> {
+    if msg.content.membership == MembershipState::Join {
+        Some(Member {
+            uid: msg.sender.clone(),
+            alias: msg.content.displayname.clone(),
+            avatar: msg
+                .content
+                .avatar_url
+                .as_ref()
+                .map(String::as_str)
+                .map(Url::parse)
+                .and_then(Result::ok)
+                .map(Either::Left),
         })
-        .collect()
+    } else {
+        None
+    }
 }
diff --git a/fractal-gtk/src/passwd.rs b/fractal-gtk/src/passwd.rs
index 44b6a44c..741f36ba 100644
--- a/fractal-gtk/src/passwd.rs
+++ b/fractal-gtk/src/passwd.rs
@@ -1,4 +1,3 @@
-use crate::derror;
 use fractal_api::identifiers::{Error as IdError, UserId};
 use fractal_api::r0::AccessToken;
 use fractal_api::url::ParseError;
@@ -11,6 +10,12 @@ pub enum Error {
     IdParseError(IdError),
 }
 
+impl From<secret_service::SsError> for Error {
+    fn from(_: secret_service::SsError) -> Error {
+        Error::SecretServiceError
+    }
+}
+
 impl From<ParseError> for Error {
     fn from(err: ParseError) -> Error {
         Error::UrlParseError(err)
@@ -23,8 +28,6 @@ impl From<IdError> for Error {
     }
 }
 
-derror!(secret_service::SsError, Error::SecretServiceError);
-
 pub trait PasswordStorage {
     fn delete_pass(&self, key: &str) -> Result<(), Error> {
         ss_storage::delete_pass(key)
diff --git a/fractal-gtk/src/widgets/media_viewer.rs b/fractal-gtk/src/widgets/media_viewer.rs
index 6e22a74e..57f8e635 100644
--- a/fractal-gtk/src/widgets/media_viewer.rs
+++ b/fractal-gtk/src/widgets/media_viewer.rs
@@ -129,7 +129,7 @@ struct Data {
     server_url: Url,
     access_token: AccessToken,
     uid: UserId,
-    admins: HashMap<UserId, i32>,
+    admins: HashMap<UserId, i64>,
 
     widget: Widget,
     media_list: Vec<Message>,
diff --git a/fractal-gtk/src/widgets/members_list.rs b/fractal-gtk/src/widgets/members_list.rs
index 69f2f729..f1a44ac1 100644
--- a/fractal-gtk/src/widgets/members_list.rs
+++ b/fractal-gtk/src/widgets/members_list.rs
@@ -18,13 +18,13 @@ pub struct MembersList {
     search_entry: gtk::SearchEntry,
     error: gtk::Label,
     members: Vec<Member>,
-    admins: HashMap<UserId, i32>,
+    admins: HashMap<UserId, i64>,
 }
 
 impl MembersList {
     pub fn new(
         members: Vec<Member>,
-        admins: HashMap<UserId, i32>,
+        admins: HashMap<UserId, i64>,
         search_entry: gtk::SearchEntry,
     ) -> MembersList {
         MembersList {
@@ -96,7 +96,7 @@ impl MembersList {
     }
 }
 
-fn create_row(member: Member, power_level: Option<i32>) -> Option<gtk::ListBoxRow> {
+fn create_row(member: Member, power_level: Option<i64>) -> Option<gtk::ListBoxRow> {
     let row = gtk::ListBoxRow::new();
     row.connect_draw(clone!(@strong member => move |w, _| {
         if w.get_child().is_none() {
@@ -111,7 +111,7 @@ fn create_row(member: Member, power_level: Option<i32>) -> Option<gtk::ListBoxRo
 }
 
 /* creating the row is quite slow, therefore we have a small delay when scrolling the members list */
-fn load_row_content(member: Member, power_level: Option<i32>) -> gtk::Box {
+fn load_row_content(member: Member, power_level: Option<i64>) -> gtk::Box {
     let b = gtk::Box::new(gtk::Orientation::Horizontal, 12);
 
     // Power level badge colour
@@ -188,7 +188,7 @@ fn load_row_content(member: Member, power_level: Option<i32>) -> gtk::Box {
 fn add_rows(
     container: gtk::ListBox,
     members: Vec<Member>,
-    admins: HashMap<UserId, i32>,
+    admins: HashMap<UserId, i64>,
 ) -> Option<usize> {
     /* Load just enough members to fill atleast the visible list */
     for member in members.iter() {
diff --git a/fractal-gtk/src/widgets/roomlist.rs b/fractal-gtk/src/widgets/roomlist.rs
index f8509cb0..91a9f0e4 100644
--- a/fractal-gtk/src/widgets/roomlist.rs
+++ b/fractal-gtk/src/widgets/roomlist.rs
@@ -190,7 +190,7 @@ impl RoomListGroup {
             .count()
     }
 
-    pub fn set_room_notifications(&mut self, room_id: RoomId, n: i32, h: i32) {
+    pub fn set_room_notifications(&mut self, room_id: RoomId, n: u64, h: u64) {
         if let Some(ref mut r) = self.rooms.get_mut(&room_id) {
             r.set_notifications(n, h);
         }
@@ -645,7 +645,7 @@ impl RoomList {
             + self.rooms.get().rooms_with_notifications()
     }
 
-    pub fn set_room_notifications(&mut self, room_id: RoomId, n: i32, h: i32) {
+    pub fn set_room_notifications(&mut self, room_id: RoomId, n: u64, h: u64) {
         run_in_group!(self, &room_id, set_room_notifications, room_id, n, h);
     }
 
diff --git a/fractal-gtk/src/widgets/roomrow.rs b/fractal-gtk/src/widgets/roomrow.rs
index e2fa4fe0..5628b858 100644
--- a/fractal-gtk/src/widgets/roomrow.rs
+++ b/fractal-gtk/src/widgets/roomrow.rs
@@ -76,7 +76,7 @@ impl RoomRow {
         rr
     }
 
-    pub fn set_notifications(&mut self, n: i32, h: i32) {
+    pub fn set_notifications(&mut self, n: u64, h: u64) {
         self.room.notifications = n;
         self.room.highlight = h;
         self.notifications.set_text(&format!("{}", n));
diff --git a/fractal-matrix-api/src/meson.build b/fractal-matrix-api/src/meson.build
index 27307166..2c252dd4 100644
--- a/fractal-matrix-api/src/meson.build
+++ b/fractal-matrix-api/src/meson.build
@@ -9,12 +9,9 @@ api_sources = files(
   'r0/contact/create.rs',
   'r0/contact/delete.rs',
   'r0/server/domain_info.rs',
-  'r0/sync/sync_events.rs',
   'r0/account.rs',
   'r0/contact.rs',
-  'r0/filter.rs',
   'r0/server.rs',
-  'r0/sync.rs',
   'identity.rs',
   'lib.rs',
   'r0.rs',
diff --git a/fractal-matrix-api/src/r0.rs b/fractal-matrix-api/src/r0.rs
index fc097102..f90c93a5 100644
--- a/fractal-matrix-api/src/r0.rs
+++ b/fractal-matrix-api/src/r0.rs
@@ -1,8 +1,6 @@
 pub mod account;
 pub mod contact;
-pub mod filter;
 pub mod server;
-pub mod sync;
 
 use serde::{Deserialize, Serialize, Serializer};
 use std::convert::TryFrom;



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