[fractal] Backend: Implement and use sync response types and cleanup



commit 3194c13f1da150c454484e9c5690dcbd07082df0
Author: Alejandro Domínguez <adomu net-c com>
Date:   Mon Jan 21 22:43:38 2019 +0100

    Backend: Implement and use sync response types and cleanup
    
    We implemented the structs to which we parse the sync response except
    the events, which are a tough case for another commit. Some enhancements
    and cleanups were made to the code surrounding the sync management in
    order to be able to remove unnecessary error handling and/or queries.

 fractal-matrix-api/src/backend/sync.rs | 118 +++++-----
 fractal-matrix-api/src/model/mod.rs    |   1 +
 fractal-matrix-api/src/model/room.rs   | 228 +++++++++++++++++-
 fractal-matrix-api/src/model/sync.rs   | 125 ++++++++++
 fractal-matrix-api/src/types.rs        |   3 +
 fractal-matrix-api/src/util.rs         | 414 ++-------------------------------
 6 files changed, 434 insertions(+), 455 deletions(-)
---
diff --git a/fractal-matrix-api/src/backend/sync.rs b/fractal-matrix-api/src/backend/sync.rs
index f73163f6..34a87a3a 100644
--- a/fractal-matrix-api/src/backend/sync.rs
+++ b/fractal-matrix-api/src/backend/sync.rs
@@ -2,13 +2,13 @@ use crate::backend::types::BKResponse;
 use crate::backend::types::Backend;
 use crate::error::Error;
 use crate::globals;
+use crate::types::Event;
+use crate::types::Message;
 use crate::types::Room;
-use crate::util::get_rooms_from_json;
-use crate::util::get_rooms_notifies_from_json;
-use crate::util::get_rooms_timeline_from_json;
+use crate::types::SyncResponse;
+use crate::types::UnreadNotificationsCount;
 use crate::util::json_q;
 use crate::util::parse_m_direct;
-use crate::util::parse_sync_events;
 use log::error;
 use serde_json::json;
 use serde_json::Value as JsonValue;
@@ -67,31 +67,53 @@ pub fn sync(bk: &Backend, new_since: Option<String>, initial: bool) -> Result<()
         &url,
         &attrs,
         |r: JsonValue| {
-            let next_batch: String = r["next_batch"].as_str().map(Into::into).unwrap_or_default();
-            if let Some(since) = since {
-                // New rooms
-                match get_rooms_from_json(&r, &userid, &baseu) {
-                    Ok(rs) => tx.send(BKResponse::NewRooms(rs)).unwrap(),
-                    Err(err) => tx.send(BKResponse::SyncError(err)).unwrap(),
-                };
-
-                // Message events
-                match get_rooms_timeline_from_json(&baseu, &r, &tk, &since) {
-                    Ok(msgs) => tx.send(BKResponse::RoomMessages(msgs)).unwrap(),
-                    Err(err) => tx.send(BKResponse::RoomMessagesError(err)).unwrap(),
-                };
-                // Room notifications
-                if let Ok(notifies) = get_rooms_notifies_from_json(&r) {
-                    for (r, n, h) in notifies {
-                        tx.send(BKResponse::RoomNotifications(r.clone(), n, h))
+            if let Ok(response) = serde_json::from_value::<SyncResponse>(r) {
+                if since.is_some() {
+                    let join = &response.rooms.join;
+
+                    // New rooms
+                    let rs = Room::from_sync_response(&response, &userid, &baseu);
+                    tx.send(BKResponse::NewRooms(rs)).unwrap();
+
+                    // Message events
+                    let msgs = join
+                        .iter()
+                        .flat_map(|(k, room)| {
+                            let events = room.timeline.events.iter();
+                            Message::from_json_events_iter(&k, events).into_iter()
+                        })
+                        .collect();
+                    tx.send(BKResponse::RoomMessages(msgs)).unwrap();
+
+                    // Room notifications
+                    for (k, room) in join.iter() {
+                        let UnreadNotificationsCount {
+                            highlight_count: h,
+                            notification_count: n,
+                        } = room.unread_notifications;
+                        tx.send(BKResponse::RoomNotifications(k.clone(), n, h))
                             .unwrap();
                     }
-                };
-                // Other events
-                match parse_sync_events(&r) {
-                    Err(err) => tx.send(BKResponse::SyncError(err)).unwrap(),
-                    Ok(events) => {
-                        for ev in events {
+
+                    // Other events
+                    join.iter()
+                        .flat_map(|(k, room)| {
+                            room.timeline
+                                .events
+                                .iter()
+                                .filter(|x| x["type"] != "m.room.message")
+                                .map(move |ev| Event {
+                                    room: k.clone(),
+                                    sender: ev["sender"]
+                                        .as_str()
+                                        .map(Into::into)
+                                        .unwrap_or_default(),
+                                    content: ev["content"].clone(),
+                                    stype: ev["type"].as_str().map(Into::into).unwrap_or_default(),
+                                    id: ev["id"].as_str().map(Into::into).unwrap_or_default(),
+                                })
+                        })
+                        .for_each(|ev| {
                             match ev.stype.as_ref() {
                                 "m.room.name" => {
                                     let name = ev.content["name"]
@@ -121,35 +143,25 @@ pub fn sync(bk: &Backend, new_since: Option<String>, initial: bool) -> Result<()
                                     error!("EVENT NOT MANAGED: {:?}", ev);
                                 }
                             }
-                        }
-                    }
-                };
-            } else {
-                data.lock().unwrap().m_direct = parse_m_direct(&r);
-
-                let rooms = match get_rooms_from_json(&r, &userid, &baseu) {
-                    Ok(rs) => rs,
-                    Err(err) => {
-                        tx.send(BKResponse::SyncError(err)).unwrap();
-                        Default::default()
-                    }
-                };
-
-                let mut def: Option<Room> = None;
-                let jtr = data.lock().unwrap().join_to_room.clone();
-                if !jtr.is_empty() {
-                    if let Some(r) = rooms.iter().find(|x| x.id == jtr) {
-                        def = Some(r.clone());
-                    }
+                        });
+                } else {
+                    data.lock().unwrap().m_direct = parse_m_direct(&response.account_data.events);
+
+                    let rooms = Room::from_sync_response(&response, &userid, &baseu);
+                    let jtr = data.lock().unwrap().join_to_room.clone();
+                    let def = if !jtr.is_empty() {
+                        rooms.iter().find(|x| x.id == jtr).cloned()
+                    } else {
+                        None
+                    };
+                    tx.send(BKResponse::Rooms(rooms, def)).unwrap();
                 }
-                tx.send(BKResponse::Rooms(rooms, def)).unwrap();
-            }
 
-            tx.send(BKResponse::Sync(next_batch.clone())).unwrap();
-            data.lock().unwrap().since = if !next_batch.is_empty() {
-                Some(next_batch)
+                let next_batch = response.next_batch;
+                tx.send(BKResponse::Sync(next_batch.clone())).unwrap();
+                data.lock().unwrap().since = Some(next_batch).filter(|s| !s.is_empty());
             } else {
-                None
+                tx.send(BKResponse::SyncError(Error::BackendError)).unwrap();
             }
         },
         |err| {
diff --git a/fractal-matrix-api/src/model/mod.rs b/fractal-matrix-api/src/model/mod.rs
index 07faeeb8..956385fd 100644
--- a/fractal-matrix-api/src/model/mod.rs
+++ b/fractal-matrix-api/src/model/mod.rs
@@ -5,4 +5,5 @@ pub mod protocol;
 pub mod register;
 pub mod room;
 pub mod stickers;
+pub mod sync;
 pub mod userinfo;
diff --git a/fractal-matrix-api/src/model/room.rs b/fractal-matrix-api/src/model/room.rs
index 3bdcf6e3..c57c15b8 100644
--- a/fractal-matrix-api/src/model/room.rs
+++ b/fractal-matrix-api/src/model/room.rs
@@ -3,8 +3,12 @@ use serde_json::Value as JsonValue;
 use crate::model::member::Member;
 use crate::model::member::MemberList;
 use crate::model::message::Message;
+use crate::types::SyncResponse;
+use crate::util::get_user_avatar;
+use crate::util::parse_m_direct;
 use serde::{Deserialize, Serialize};
-use std::collections::HashMap;
+use std::collections::{HashMap, HashSet};
+use url::Url;
 
 #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
 pub enum RoomMembership {
@@ -99,6 +103,114 @@ impl Room {
         }
     }
 
+    pub fn from_sync_response(r: &SyncResponse, userid: &str, baseu: &Url) -> Vec<Self> {
+        let join = r.rooms.join.clone();
+        let leave = r.rooms.leave.clone();
+        let invite = r.rooms.invite.clone();
+
+        // getting the list of direct rooms
+        let direct: HashSet<String> = parse_m_direct(&r.account_data.events)
+            .values()
+            .flatten()
+            .cloned()
+            .collect();
+
+        let mut rooms: Vec<Self> = vec![];
+        for (k, room) in join.iter() {
+            let stevents = &room.state.events;
+            let timeline = &room.timeline;
+            let ephemeral = &room.ephemeral;
+            let dataevs = &room.account_data.events;
+            let name = calculate_room_name(stevents, userid);
+            let mut room_tag = RoomTag::None;
+            for tag in dataevs.iter().filter(|x| x["type"] == "m.tag") {
+                if tag["content"]["tags"]["m.favourite"].as_object().is_some() {
+                    room_tag = RoomTag::Favourite;
+                }
+            }
+            let mut r = Self::new(k.clone(), RoomMembership::Joined(room_tag));
+
+            r.name = name;
+            r.avatar = Some(evc(stevents, "m.room.avatar", "url"));
+            r.alias = Some(evc(stevents, "m.room.canonical_alias", "alias"));
+            r.topic = Some(evc(stevents, "m.room.topic", "topic"));
+            r.direct = direct.contains(k);
+            r.notifications = room.unread_notifications.notification_count;
+            r.highlight = room.unread_notifications.highlight_count;
+
+            r.prev_batch = timeline.prev_batch.clone();
+
+            let ms = Message::from_json_events_iter(&k, timeline.events.iter());
+            r.messages.extend(ms);
+
+            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(fread) = dataevs.into_iter().find(|x| x["type"] == "m.fully_read") {
+                if let Some(ev) = fread["content"]["event_id"].as_str() {
+                    r.add_receipt_from_fully_read(userid, ev);
+                }
+            }
+
+            let mevents = stevents.iter().filter(|x| x["type"] == "m.room.member");
+
+            for ev in mevents {
+                let member = parse_room_member(ev);
+                if let Some(m) = member {
+                    r.members.insert(m.uid.clone(), m.clone());
+                }
+            }
+
+            // power levels info
+            r.power_levels = get_admins(stevents);
+
+            rooms.push(r);
+        }
+
+        // left rooms
+        for k in leave.keys() {
+            let r = Self::new(k.clone(), RoomMembership::Left);
+            rooms.push(r);
+        }
+
+        // invitations
+        for (k, room) in invite.iter() {
+            let stevents = &room.invite_state.events;
+            let name = calculate_room_name(stevents, userid);
+
+            if let Some(ev) = stevents
+                .iter()
+                .find(|x| x["membership"] == "invite" && x["state_key"] == userid)
+            {
+                if let Ok((alias, avatar)) =
+                    get_user_avatar(baseu, ev["sender"].as_str().unwrap_or_default())
+                {
+                    let inv_sender = Member {
+                        alias: Some(alias),
+                        avatar: Some(avatar),
+                        uid: String::from(userid),
+                    };
+                    let mut r = Self::new(k.clone(), RoomMembership::Invited(inv_sender));
+                    r.name = name;
+
+                    r.avatar = Some(evc(stevents, "m.room.avatar", "url"));
+                    r.alias = Some(evc(stevents, "m.room.canonical_alias", "alias"));
+                    r.topic = Some(evc(stevents, "m.room.topic", "topic"));
+                    r.direct = direct.contains(k);
+
+                    rooms.push(r);
+                }
+            }
+        }
+
+        rooms
+    }
+
     pub fn add_receipt_from_json(&mut self, mut events: Vec<&JsonValue>) {
         let receipts = events
             .pop()
@@ -215,3 +327,117 @@ pub struct PublicRoomsChunk {
     pub topic: Option<String>,
     pub world_readable: bool,
 }
+
+fn evc(events: &Vec<JsonValue>, t: &str, field: &str) -> String {
+    events
+        .iter()
+        .find(|x| x["type"] == t)
+        .and_then(|js| js["content"][field].as_str())
+        .map(Into::into)
+        .unwrap_or_default()
+}
+
+fn get_admins(stevents: &Vec<JsonValue>) -> HashMap<String, i32> {
+    let mut admins = HashMap::new();
+
+    let plevents = stevents
+        .iter()
+        .filter(|x| x["type"] == "m.room.power_levels");
+
+    for ev in plevents {
+        if let Some(users) = ev["content"]["users"].as_object() {
+            for u in users.keys() {
+                let level = users[u].as_i64().unwrap_or_default();
+                admins.insert(u.to_string(), level as i32);
+            }
+        }
+    }
+
+    admins
+}
+
+fn calculate_room_name(events: &Vec<JsonValue>, userid: &str) -> Option<String> {
+    // looking for "m.room.name" event
+    if let Some(name) = events.iter().find(|x| x["type"] == "m.room.name") {
+        if let Some(name) = name["content"]["name"].as_str() {
+            if !name.to_string().is_empty() {
+                return Some(name.to_string());
+            }
+        }
+    }
+
+    // looking for "m.room.canonical_alias" event
+    if let Some(name) = events
+        .iter()
+        .find(|x| x["type"] == "m.room.canonical_alias")
+    {
+        if let Some(name) = name["content"]["alias"].as_str() {
+            return Some(name.to_string());
+        }
+    }
+
+    // we look for members that aren't me
+    let filter = |x: &&JsonValue| {
+        (x["type"] == "m.room.member"
+            && ((x["content"]["membership"] == "join" && x["sender"] != userid)
+                || (x["content"]["membership"] == "invite" && x["state_key"] != userid)))
+    };
+    let c = events.iter().filter(&filter);
+    let members = events.iter().filter(&filter);
+    let mut members2 = events.iter().filter(&filter);
+
+    if c.count() == 0 {
+        // we don't have information to calculate the name
+        return None;
+    }
+
+    let m1 = match members2.next() {
+        Some(m) => {
+            let sender = m["sender"].as_str().unwrap_or("NONAMED");
+            m["content"]["displayname"].as_str().unwrap_or(sender)
+        }
+        None => "",
+    };
+    let m2 = match members2.next() {
+        Some(m) => {
+            let sender = m["sender"].as_str().unwrap_or("NONAMED");
+            m["content"]["displayname"].as_str().unwrap_or(sender)
+        }
+        None => "",
+    };
+
+    let name = match members.count() {
+        0 => String::from("EMPTY ROOM"),
+        1 => String::from(m1),
+        2 => format!("{} and {}", m1, m2),
+        _ => format!("{} and Others", m1),
+    };
+
+    Some(name)
+}
+
+fn parse_room_member(msg: &JsonValue) -> Option<Member> {
+    let sender = msg["sender"].as_str().unwrap_or_default();
+
+    let c = &msg["content"];
+
+    let membership = c["membership"].as_str();
+    if membership.is_none() || membership.unwrap() != "join" {
+        return None;
+    }
+
+    let displayname = match c["displayname"].as_str() {
+        None => None,
+        Some(s) => Some(String::from(s)),
+    };
+    let avatar_url = match c["avatar_url"].as_str() {
+        None => None,
+        Some(s) => Some(String::from(s)),
+    };
+
+    Some(Member {
+        uid: String::from(sender),
+        alias: displayname,
+        avatar: avatar_url,
+    })
+}
diff --git a/fractal-matrix-api/src/model/sync.rs b/fractal-matrix-api/src/model/sync.rs
new file mode 100644
index 00000000..1b740ead
--- /dev/null
+++ b/fractal-matrix-api/src/model/sync.rs
@@ -0,0 +1,125 @@
+use serde::Deserialize;
+use serde_json::Value as JsonValue;
+use std::collections::HashMap;
+
+#[derive(Clone, Debug, Deserialize)]
+pub struct SyncResponse {
+    pub next_batch: String,
+    #[serde(default)]
+    pub rooms: Rooms,
+    pub presence: Option<Presence>,
+    #[serde(default)]
+    pub account_data: AccountData,
+    pub to_device: Option<ToDevice>,
+    pub device_lists: Option<DeviceLists>,
+    #[serde(default)]
+    pub device_one_time_keys_count: HashMap<String, u64>,
+}
+
+#[derive(Clone, Debug, Default, Deserialize)]
+pub struct Rooms {
+    #[serde(default)]
+    pub leave: HashMap<String, LeftRoom>,
+    #[serde(default)]
+    pub join: HashMap<String, JoinedRoom>,
+    #[serde(default)]
+    pub invite: HashMap<String, InvitedRoom>,
+}
+
+#[derive(Clone, Debug, Deserialize)]
+pub struct JoinedRoom {
+    #[serde(default)]
+    pub unread_notifications: UnreadNotificationsCount,
+    #[serde(default)]
+    pub timeline: Timeline,
+    #[serde(default)]
+    pub state: State,
+    #[serde(default)]
+    pub account_data: AccountData,
+    #[serde(default)]
+    pub ephemeral: Ephemeral,
+}
+
+#[derive(Clone, Debug, Default, Deserialize)]
+pub struct Ephemeral {
+    // TODO: Implement Event
+    #[serde(default)]
+    pub events: Vec<JsonValue>,
+}
+
+#[derive(Clone, Debug, Default, Deserialize)]
+pub struct UnreadNotificationsCount {
+    #[serde(default)]
+    pub highlight_count: i32,
+    #[serde(default)]
+    pub notification_count: i32,
+}
+
+#[derive(Clone, Debug, Deserialize)]
+pub struct InvitedRoom {
+    #[serde(default)]
+    pub invite_state: InviteState,
+}
+
+#[derive(Clone, Debug, Default, Deserialize)]
+pub struct InviteState {
+    // TODO: Implement StrippedState
+    #[serde(default)]
+    pub events: Vec<JsonValue>,
+}
+
+#[derive(Clone, Debug, Deserialize)]
+pub struct LeftRoom {
+    #[serde(default)]
+    pub timeline: Timeline,
+    #[serde(default)]
+    pub state: State,
+    #[serde(default)]
+    pub account_data: AccountData,
+}
+
+#[derive(Clone, Debug, Default, Deserialize)]
+pub struct State {
+    // TODO: Implement StateEvent
+    #[serde(default)]
+    pub events: Vec<JsonValue>,
+}
+
+#[derive(Clone, Debug, Default, Deserialize)]
+pub struct Timeline {
+    #[serde(default)]
+    pub limited: bool,
+    pub prev_batch: Option<String>,
+    // TODO: Implement RoomEvent
+    #[serde(default)]
+    pub events: Vec<JsonValue>,
+}
+
+#[derive(Clone, Debug, Deserialize)]
+pub struct Presence {
+    // TODO: Implement Event
+    #[serde(default)]
+    pub events: Vec<JsonValue>,
+}
+
+#[derive(Clone, Debug, Default, Deserialize)]
+pub struct AccountData {
+    // TODO: Implement Event
+    #[serde(default)]
+    pub events: Vec<JsonValue>,
+}
+
+#[derive(Clone, Debug, Deserialize)]
+pub struct ToDevice {
+    // TODO: Implement Event
+    #[serde(default)]
+    pub events: Vec<JsonValue>,
+}
+
+#[derive(Clone, Debug, Deserialize)]
+pub struct DeviceLists {
+    #[serde(default)]
+    pub changed: Vec<String>,
+    #[serde(default)]
+    pub left: Vec<String>,
+}
diff --git a/fractal-matrix-api/src/types.rs b/fractal-matrix-api/src/types.rs
index 3d2e4c23..d841d436 100644
--- a/fractal-matrix-api/src/types.rs
+++ b/fractal-matrix-api/src/types.rs
@@ -14,4 +14,7 @@ pub use crate::model::room::RoomTag;
 pub use crate::model::room::ThirdPartyNetworks;
 pub use crate::model::stickers::Sticker;
 pub use crate::model::stickers::StickerGroup;
+pub use crate::model::sync::JoinedRoom;
+pub use crate::model::sync::SyncResponse;
+pub use crate::model::sync::UnreadNotificationsCount;
 pub use crate::model::userinfo::UserInfo;
diff --git a/fractal-matrix-api/src/util.rs b/fractal-matrix-api/src/util.rs
index 9e457161..e91376d9 100644
--- a/fractal-matrix-api/src/util.rs
+++ b/fractal-matrix-api/src/util.rs
@@ -14,17 +14,13 @@ use std::fs::create_dir_all;
 use std::fs::File;
 use std::io::prelude::*;
 
-use std::collections::HashSet;
 use std::sync::{Arc, Condvar, Mutex};
 use std::thread;
 
 use std::time::Duration as StdDuration;
 
 use crate::error::Error;
-use crate::types::Event;
-use crate::types::Member;
 use crate::types::Message;
-use crate::types::{Room, RoomMembership, RoomTag};
 
 use reqwest::header::CONTENT_TYPE;
 
@@ -140,277 +136,24 @@ macro_rules! query {
     };
 }
 
-pub fn evc(events: &JsonValue, t: &str, field: &str) -> String {
+pub fn parse_m_direct(events: &Vec<JsonValue>) -> HashMap<String, Vec<String>> {
     events
-        .as_array()
-        .and_then(|arr| arr.iter().find(|x| x["type"] == t))
-        .and_then(|js| js["content"][field].as_str())
-        .map(Into::into)
-        .unwrap_or_default()
-}
-
-pub fn parse_m_direct(r: &JsonValue) -> HashMap<String, Vec<String>> {
-    let mut direct = HashMap::new();
-
-    &r["account_data"]["events"]
-        .as_array()
-        .unwrap_or(&vec![])
         .iter()
         .find(|x| x["type"] == "m.direct")
         .and_then(|js| js["content"].as_object())
-        .map(|js| {
-            for (k, v) in js.iter() {
-                let value = v
-                    .as_array()
-                    .unwrap_or(&vec![])
-                    .iter()
-                    .map(|rid| rid.as_str().unwrap_or_default().to_string())
-                    .collect::<Vec<String>>();
-                direct.insert(k.clone(), value);
-            }
-        });
-
-    direct
-}
-
-pub fn get_rooms_from_json(r: &JsonValue, userid: &str, baseu: &Url) -> Result<Vec<Room>, Error> {
-    let rooms = &r["rooms"];
-
-    let join = rooms["join"].as_object().ok_or(Error::BackendError)?;
-    let leave = rooms["leave"].as_object().ok_or(Error::BackendError)?;
-    let invite = rooms["invite"].as_object().ok_or(Error::BackendError)?;
-
-    // getting the list of direct rooms
-    let mut direct: HashSet<String> = HashSet::new();
-    for v in parse_m_direct(r).values() {
-        for rid in v {
-            direct.insert(rid.clone());
-        }
-    }
-
-    let mut rooms: Vec<Room> = vec![];
-    for k in join.keys() {
-        let room = join.get(k).ok_or(Error::BackendError)?;
-        let stevents = &room["state"]["events"];
-        let timeline = &room["timeline"];
-        let ephemeral = &room["ephemeral"];
-        let dataevs = &room["account_data"]["events"];
-        let name = calculate_room_name(stevents, userid)?;
-        let mut room_tag = RoomTag::None;
-        if let Some(ev) = dataevs.as_array() {
-            for tag in ev.iter().filter(|x| x["type"] == "m.tag") {
-                if tag["content"]["tags"]["m.favourite"].as_object().is_some() {
-                    room_tag = RoomTag::Favourite;
-                }
-            }
-        }
-
-        let mut r = Room::new(k.clone(), RoomMembership::Joined(room_tag));
-        r.name = name;
-
-        r.avatar = Some(evc(stevents, "m.room.avatar", "url"));
-        r.alias = Some(evc(stevents, "m.room.canonical_alias", "alias"));
-        r.topic = Some(evc(stevents, "m.room.topic", "topic"));
-        r.direct = direct.contains(k);
-        r.notifications = room["unread_notifications"]["notification_count"]
-            .as_i64()
-            .unwrap_or_default() as i32;
-        r.highlight = room["unread_notifications"]["highlight_count"]
-            .as_i64()
-            .unwrap_or_default() as i32;
-
-        r.prev_batch = timeline["prev_batch"].as_str().map(String::from);
-
-        if let Some(evs) = timeline["events"].as_array() {
-            let ms = Message::from_json_events_iter(&k, evs.iter());
-            r.messages.extend(ms);
-        }
-
-        if let Some(evs) = ephemeral["events"].as_array() {
-            r.add_receipt_from_json(
-                evs.into_iter()
-                    .filter(|ev| ev["type"] == "m.receipt")
-                    .collect::<Vec<&JsonValue>>(),
-            );
-        }
-        // Adding fully read to the receipts events
-        if let Some(evs) = dataevs.as_array() {
-            if let Some(fread) = evs.into_iter().find(|x| x["type"] == "m.fully_read") {
-                if let Some(ev) = fread["content"]["event_id"].as_str() {
-                    r.add_receipt_from_fully_read(userid, ev);
-                }
-            }
-        }
-
-        let mevents = stevents
-            .as_array()
-            .unwrap()
-            .iter()
-            .filter(|x| x["type"] == "m.room.member");
-
-        for ev in mevents {
-            let member = parse_room_member(ev);
-            if let Some(m) = member {
-                r.members.insert(m.uid.clone(), m.clone());
-            }
-        }
-
-        // power levels info
-        r.power_levels = get_admins(stevents);
-
-        rooms.push(r);
-    }
-
-    // left rooms
-    for k in leave.keys() {
-        let r = Room::new(k.clone(), RoomMembership::Left);
-        rooms.push(r);
-    }
-
-    // invitations
-    for k in invite.keys() {
-        let room = invite.get(k).ok_or(Error::BackendError)?;
-        let stevents = &room["invite_state"]["events"];
-        let name = calculate_room_name(stevents, userid)?;
-        if let Some(arr) = stevents.as_array() {
-            if let Some(ev) = arr
-                .iter()
-                .find(|x| x["membership"] == "invite" && x["state_key"] == userid)
-            {
-                if let Ok((alias, avatar)) =
-                    get_user_avatar(baseu, ev["sender"].as_str().unwrap_or_default())
-                {
-                    let inv_sender = Member {
-                        alias: Some(alias),
-                        avatar: Some(avatar),
-                        uid: String::from(userid),
-                    };
-                    let mut r = Room::new(k.clone(), RoomMembership::Invited(inv_sender));
-                    r.name = name;
-
-                    r.avatar = Some(evc(stevents, "m.room.avatar", "url"));
-                    r.alias = Some(evc(stevents, "m.room.canonical_alias", "alias"));
-                    r.topic = Some(evc(stevents, "m.room.topic", "topic"));
-                    r.direct = direct.contains(k);
-
-                    rooms.push(r);
-                }
-            }
-        }
-    }
-
-    Ok(rooms)
-}
-
-pub fn get_admins(stevents: &JsonValue) -> HashMap<String, i32> {
-    let mut admins = HashMap::new();
-
-    let plevents = stevents
-        .as_array()
-        .unwrap()
+        .cloned()
+        .unwrap_or_default()
         .iter()
-        .filter(|x| x["type"] == "m.room.power_levels");
-
-    for ev in plevents {
-        if let Some(users) = ev["content"]["users"].as_object() {
-            for u in users.keys() {
-                let level = users[u].as_i64().unwrap_or_default();
-                admins.insert(u.to_string(), level as i32);
-            }
-        }
-    }
-
-    admins
-}
-
-pub fn get_rooms_timeline_from_json(
-    baseu: &Url,
-    r: &JsonValue,
-    tk: &str,
-    prev_batch: &str,
-) -> Result<Vec<Message>, Error> {
-    let rooms = &r["rooms"];
-    let join = rooms["join"].as_object().ok_or(Error::BackendError)?;
-
-    let mut msgs: Vec<Message> = vec![];
-    for k in join.keys() {
-        let room = join.get(k).ok_or(Error::BackendError)?;
-
-        if let (Some(true), Some(pb)) = (
-            room["timeline"]["limited"].as_bool(),
-            room["timeline"]["prev_batch"].as_str(),
-        ) {
-            let pbs = pb.to_string();
-            let fill_the_gap =
-                fill_room_gap(baseu, String::from(tk), k.clone(), &prev_batch, &pbs)?;
-            for m in fill_the_gap {
-                msgs.push(m);
-            }
-        }
-
-        let timeline = room["timeline"]["events"].as_array();
-        if timeline.is_none() {
-            continue;
-        }
-
-        let events = timeline.unwrap().iter();
-        let ms = Message::from_json_events_iter(&k, events);
-        msgs.extend(ms);
-    }
-
-    Ok(msgs)
-}
-
-pub fn get_rooms_notifies_from_json(r: &JsonValue) -> Result<Vec<(String, i32, i32)>, Error> {
-    let rooms = &r["rooms"];
-    let join = rooms["join"].as_object().ok_or(Error::BackendError)?;
-
-    let mut out: Vec<(String, i32, i32)> = vec![];
-    for k in join.keys() {
-        let room = join.get(k).ok_or(Error::BackendError)?;
-        let n = room["unread_notifications"]["notification_count"]
-            .as_i64()
-            .unwrap_or_default() as i32;
-        let h = room["unread_notifications"]["highlight_count"]
-            .as_i64()
-            .unwrap_or_default() as i32;
-
-        out.push((k.clone(), n, h));
-    }
-
-    Ok(out)
-}
-
-pub fn parse_sync_events(r: &JsonValue) -> Result<Vec<Event>, Error> {
-    let rooms = &r["rooms"];
-    let join = rooms["join"].as_object().ok_or(Error::BackendError)?;
-
-    let mut evs: Vec<Event> = vec![];
-    for k in join.keys() {
-        let room = join.get(k).ok_or(Error::BackendError)?;
-        let timeline = room["timeline"]["events"].as_array();
-        if timeline.is_none() {
-            return Ok(evs);
-        }
-
-        let events = timeline
-            .unwrap()
-            .iter()
-            .filter(|x| x["type"] != "m.room.message");
-
-        for ev in events {
-            //info!("ev: {:#?}", ev);
-            evs.push(Event {
-                room: k.clone(),
-                sender: String::from(ev["sender"].as_str().unwrap_or_default()),
-                content: ev["content"].clone(),
-                stype: String::from(ev["type"].as_str().unwrap_or_default()),
-                id: String::from(ev["id"].as_str().unwrap_or_default()),
-            });
-        }
-    }
-
-    Ok(evs)
+        .map(|(k, v)| {
+            let value = v
+                .as_array()
+                .unwrap_or(&vec![])
+                .iter()
+                .map(|rid| rid.as_str().map(Into::into).unwrap_or_default())
+                .collect();
+            (k.clone(), value)
+        })
+        .collect()
 }
 
 pub fn get_prev_batch_from(
@@ -718,111 +461,6 @@ pub fn get_room_avatar(base: &Url, tk: &str, userid: &str, roomid: &str) -> Resu
     Ok(fname)
 }
 
-pub fn calculate_room_name(roomst: &JsonValue, userid: &str) -> Result<Option<String>, Error> {
-    // looking for "m.room.name" event
-    let events = roomst.as_array().ok_or(Error::BackendError)?;
-    if let Some(name) = events.iter().find(|x| x["type"] == "m.room.name") {
-        if let Some(name) = name["content"]["name"].as_str() {
-            if !name.to_string().is_empty() {
-                return Ok(Some(name.to_string()));
-            }
-        }
-    }
-
-    // looking for "m.room.canonical_alias" event
-    if let Some(name) = events
-        .iter()
-        .find(|x| x["type"] == "m.room.canonical_alias")
-    {
-        if let Some(name) = name["content"]["alias"].as_str() {
-            return Ok(Some(name.to_string()));
-        }
-    }
-
-    // we look for members that aren't me
-    let filter = |x: &&JsonValue| {
-        (x["type"] == "m.room.member"
-            && ((x["content"]["membership"] == "join" && x["sender"] != userid)
-                || (x["content"]["membership"] == "invite" && x["state_key"] != userid)))
-    };
-    let c = events.iter().filter(&filter);
-    let members = events.iter().filter(&filter);
-    let mut members2 = events.iter().filter(&filter);
-
-    if c.count() == 0 {
-        // we don't have information to calculate the name
-        return Ok(None);
-    }
-
-    let m1 = match members2.next() {
-        Some(m) => {
-            let sender = m["sender"].as_str().unwrap_or("NONAMED");
-            m["content"]["displayname"].as_str().unwrap_or(sender)
-        }
-        None => "",
-    };
-    let m2 = match members2.next() {
-        Some(m) => {
-            let sender = m["sender"].as_str().unwrap_or("NONAMED");
-            m["content"]["displayname"].as_str().unwrap_or(sender)
-        }
-        None => "",
-    };
-
-    let name = match members.count() {
-        0 => String::from("EMPTY ROOM"),
-        1 => String::from(m1),
-        2 => format!("{} and {}", m1, m2),
-        _ => format!("{} and Others", m1),
-    };
-
-    Ok(Some(name))
-}
-
-/// Recursive function that tries to get all messages in a room from a batch id to a batch id,
-/// following the response pagination
-pub fn fill_room_gap(
-    baseu: &Url,
-    tk: String,
-    roomid: String,
-    from: &str,
-    to: &str,
-) -> Result<Vec<Message>, Error> {
-    let mut ms: Vec<Message> = vec![];
-    let nend;
-
-    let params = &[
-        ("dir", String::from("f")),
-        ("limit", format!("{}", globals::PAGE_LIMIT)),
-        ("access_token", tk.clone()),
-        ("from", String::from(from)),
-        ("to", String::from(to)),
-    ];
-
-    let path = format!("rooms/{}/messages", roomid);
-    let url = client_url(baseu, &path, params)?;
-
-    let r = json_q("get", &url, &json!(null), globals::TIMEOUT)?;
-    nend = String::from(r["end"].as_str().unwrap_or_default());
-
-    let array = r["chunk"].as_array();
-    if array.is_none() || array.unwrap().is_empty() {
-        return Ok(ms);
-    }
-
-    let evs = array.unwrap().iter();
-    let mevents = Message::from_json_events_iter(&roomid, evs);
-    ms.extend(mevents);
-
-    // loading more until no more messages
-    let more = fill_room_gap(baseu, tk, roomid, &nend, to)?;
-    for m in more.iter() {
-        ms.insert(0, m.clone());
-    }
-
-    Ok(ms)
-}
-
 pub fn build_url(base: &Url, path: &str, params: &[(&str, String)]) -> Result<Url, Error> {
     let mut url = base.join(path)?;
 
@@ -882,32 +520,6 @@ pub fn get_user_avatar_img(baseu: &Url, userid: &str, avatar: &str) -> Result<St
     thumb(baseu, &avatar, Some(&dest))
 }
 
-pub fn parse_room_member(msg: &JsonValue) -> Option<Member> {
-    let sender = msg["sender"].as_str().unwrap_or_default();
-
-    let c = &msg["content"];
-
-    let membership = c["membership"].as_str();
-    if membership.is_none() || membership.unwrap() != "join" {
-        return None;
-    }
-
-    let displayname = match c["displayname"].as_str() {
-        None => None,
-        Some(s) => Some(String::from(s)),
-    };
-    let avatar_url = match c["avatar_url"].as_str() {
-        None => None,
-        Some(s) => Some(String::from(s)),
-    };
-
-    Some(Member {
-        uid: String::from(sender),
-        alias: displayname,
-        avatar: avatar_url,
-    })
-}
-
 pub fn encode_uid(userid: &str) -> String {
     utf8_percent_encode(userid, USERINFO_ENCODE_SET).collect::<String>()
 }


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