diff --git a/res/main_window.glade b/res/main_window.glade
index dc18840..7c5f281 100644
--- a/res/main_window.glade
+++ b/res/main_window.glade
@@ -24,6 +24,8 @@
+
+
+
+
+
diff --git a/src/app.rs b/src/app.rs
index b813ab3..69955c9 100644
--- a/src/app.rs
+++ b/src/app.rs
@@ -298,31 +298,37 @@ impl AppOp {
self.backend.send(BKCommand::Sync).unwrap();
}
- pub fn set_rooms(&mut self, rooms: HashMap, def: Option<(String, String)>) {
+ pub fn set_rooms(&mut self, rooms: Vec, def: Option) {
let store: gtk::TreeStore = self.gtk_builder.get_object("rooms_tree_store")
.expect("Couldn't find rooms_tree_store in ui file.");
- let mut array: Vec<(String, String)> = vec![];
- for (id, name) in rooms {
- array.push((name, id));
+ let mut array: Vec = vec![];
+
+ for r in rooms {
+ array.push(r);
}
- array.sort_by(|x, y| x.0.to_lowercase().cmp(&y.0.to_lowercase()));
+ array.sort_by(|x, y| x.name.to_lowercase().cmp(&y.name.to_lowercase()));
- let mut default: Option<(String, String)> = def;
+ let mut default: Option = def;
for v in array {
if default.is_none() {
- default = Some((v.0.clone(), v.1.clone()));
+ default = Some(v.clone());
}
+ let ns = match v.notifications {
+ 0 => String::new(),
+ i => format!("{}", i),
+ };
+
store.insert_with_values(None, None,
- &[0, 1],
- &[&v.0, &v.1]);
+ &[0, 1, 2],
+ &[&v.name, &v.id, &ns]);
}
if let Some(def) = default {
- self.set_active_room(def.1, def.0);
+ self.set_active_room(def.id, def.name);
} else {
self.room_panel(RoomPanel::NoRoom);
}
@@ -434,9 +440,38 @@ impl AppOp {
MsgPos::Bottom => messages.add(&msg),
MsgPos::Top => messages.insert(&msg, 1),
};
-
} else {
- // TODO: update the unread messages count in room list
+ self.update_room_notifications(&msg.room, |n| n + 1);
+ }
+ }
+
+ pub fn update_room_notifications(&self, roomid: &str, f: fn(i32) -> i32) {
+ let store: gtk::TreeStore = self.gtk_builder.get_object("rooms_tree_store")
+ .expect("Couldn't find rooms_tree_store in ui file.");
+
+ if let Some(iter) = store.get_iter_first() {
+ loop {
+ let v1 = store.get_value(&iter, 1);
+ let id: &str = v1.get().unwrap();
+ let v2 = store.get_value(&iter, 2);
+ let ns: &str = v2.get().unwrap();
+ let res: Result = ns.parse();
+ let n: i32 = f(res.unwrap_or(0));
+ let formatted = match n {
+ 0 => String::from(""),
+ i => format!("{}", i),
+ };
+ if id == roomid {
+ store.set_value(&iter, 2, >k::Value::from(&formatted));
+ }
+ if !store.iter_next(&iter) { break; }
+ }
+ }
+ }
+
+ pub fn mark_as_read(&self, msgs: Vec) {
+ if let Some(msg) = msgs.iter().filter(|x| x.room == self.active_room).last() {
+ self.backend.send(BKCommand::MarkAsRead(msg.room.clone(), msg.id.clone())).unwrap();
}
}
@@ -641,6 +676,7 @@ impl App {
if !msgs.is_empty() {
theop.lock().unwrap().scroll_down();
+ theop.lock().unwrap().mark_as_read(msgs);
}
theop.lock().unwrap().room_panel(RoomPanel::Room);
@@ -671,6 +707,9 @@ impl App {
Ok(BKResponse::JoinRoom) => {
theop.lock().unwrap().reload_rooms();
},
+ Ok(BKResponse::MarkedAsRead(r, _)) => {
+ theop.lock().unwrap().update_room_notifications(&r, |_| 0);
+ },
// errors
Ok(err) => {
println!("Query error: {:?}", err);
diff --git a/src/backend.rs b/src/backend.rs
index aebce22..55fa62a 100644
--- a/src/backend.rs
+++ b/src/backend.rs
@@ -11,7 +11,6 @@ use self::serde_json::Value as JsonValue;
use std::sync::{Arc, Mutex};
use std::thread;
-use std::collections::HashMap;
use self::url::Url;
use std::sync::mpsc::{Sender, Receiver};
use std::sync::mpsc::channel;
@@ -62,6 +61,7 @@ pub enum BKCommand {
DirectoryProtocols,
DirectorySearch(String, String, bool),
JoinRoom(String),
+ MarkAsRead(String, String),
}
#[derive(Debug)]
@@ -70,7 +70,7 @@ pub enum BKResponse {
Name(String),
Avatar(String),
Sync,
- Rooms(HashMap, Option<(String, String)>),
+ Rooms(Vec, Option),
RoomDetail(String, String),
RoomAvatar(String),
RoomMessages(Vec),
@@ -80,6 +80,7 @@ pub enum BKResponse {
DirectoryProtocols(Vec),
DirectorySearch(Vec),
JoinRoom,
+ MarkedAsRead(String, String),
//errors
UserNameError(Error),
@@ -96,6 +97,7 @@ pub enum BKResponse {
CommandError(Error),
DirectoryError(Error),
JoinRoomError(Error),
+ MarkAsReadError(Error),
}
@@ -194,6 +196,10 @@ impl Backend {
let r = self.join_room(roomid);
bkerror!(r, tx, BKResponse::JoinRoomError);
},
+ Ok(BKCommand::MarkAsRead(roomid, evid)) => {
+ let r = self.mark_as_read(roomid, evid);
+ bkerror!(r, tx, BKResponse::MarkAsReadError);
+ },
Ok(BKCommand::ShutDown) => {
return false;
},
@@ -390,11 +396,11 @@ impl Backend {
if since.is_empty() {
let rooms = get_rooms_from_json(r, &userid).unwrap();
- let mut def: Option<(String, String)> = None;
+ let mut def: Option = None;
let jtr = data.lock().unwrap().join_to_room.clone();
if !jtr.is_empty() {
- if let Some(name) = rooms.iter().find(|x| *(x.0) == jtr) {
- def = Some((name.1.clone(), name.0.clone()));
+ if let Some(r) = rooms.iter().find(|x| x.id == jtr) {
+ def = Some(r.clone());
}
}
@@ -487,7 +493,6 @@ impl Backend {
}
url = url.join(¶ms)?;
-
let tx = self.tx.clone();
let data = self.data.clone();
get!(&url,
@@ -688,16 +693,15 @@ impl Backend {
let mut rooms: Vec = vec![];
for room in r["chunk"].as_array().unwrap() {
let alias = String::from(room["canonical_alias"].as_str().unwrap_or(""));
- let r = Room {
- alias: alias,
- id: String::from(room["room_id"].as_str().unwrap_or("")),
- avatar: String::from(room["avatar_url"].as_str().unwrap_or("")),
- name: String::from(room["name"].as_str().unwrap_or("")),
- topic: String::from(room["topic"].as_str().unwrap_or("")),
- members: room["num_joined_members"].as_i64().unwrap_or(0) as i32,
- world_readable: room["world_readable"].as_bool().unwrap_or(false),
- guest_can_join: room["guest_can_join"].as_bool().unwrap_or(false),
- };
+ let id = String::from(room["room_id"].as_str().unwrap_or(""));
+ let name = String::from(room["name"].as_str().unwrap_or(""));
+ let mut r = Room::new(id, name);
+ r.alias = alias;
+ r.avatar = String::from(room["avatar_url"].as_str().unwrap_or(""));
+ r.topic = String::from(room["topic"].as_str().unwrap_or(""));
+ r.members = room["num_joined_members"].as_i64().unwrap_or(0) as i32;
+ r.world_readable = room["world_readable"].as_bool().unwrap_or(false);
+ r.guest_can_join = room["guest_can_join"].as_bool().unwrap_or(false);
rooms.push(r);
}
@@ -727,4 +731,22 @@ impl Backend {
Ok(())
}
+
+ pub fn mark_as_read(&self, roomid: String, eventid: String) -> Result<(), Error> {
+ let baseu = self.get_base_url()?;
+ let tk = self.data.lock().unwrap().access_token.clone();
+ let mut url = baseu.join("/_matrix/client/r0/rooms/")?;
+ url = url.join(&format!("{}/receipt/m.read/{}", roomid, eventid))?;
+ url = url.join(&format!("?access_token={}", tk))?;
+
+ let tx = self.tx.clone();
+ let r = roomid.clone();
+ let e = eventid.clone();
+ post!(&url,
+ move |_: JsonValue| { tx.send(BKResponse::MarkedAsRead(r, e)).unwrap(); },
+ |err| { tx.send(BKResponse::MarkAsReadError(err)).unwrap(); }
+ );
+
+ Ok(())
+ }
}
diff --git a/src/types.rs b/src/types.rs
index d372619..aaaa0c3 100644
--- a/src/types.rs
+++ b/src/types.rs
@@ -11,6 +11,7 @@ pub struct Message {
pub room: String,
pub thumb: String,
pub url: String,
+ pub id: String,
}
#[derive(Debug)]
@@ -40,9 +41,42 @@ pub struct Room {
pub id: String,
pub avatar: String,
pub name: String,
- pub guest_can_join: bool,
pub topic: String,
- pub members: i32,
- pub world_readable: bool,
pub alias: String,
+ pub guest_can_join: bool,
+ pub world_readable: bool,
+ pub members: i32,
+ pub notifications: i32,
+}
+
+impl Room {
+ pub fn new(id: String, name: String) -> Room {
+ Room {
+ id: id,
+ name: name,
+ avatar: String::new(),
+ topic: String::new(),
+ alias: String::new(),
+ guest_can_join: true,
+ world_readable: true,
+ members: 0,
+ notifications: 0,
+ }
+ }
+}
+
+impl Clone for Room {
+ fn clone(&self) -> Room {
+ Room {
+ id: self.id.clone(),
+ name: self.name.clone(),
+ avatar: self.avatar.clone(),
+ topic: self.topic.clone(),
+ alias: self.alias.clone(),
+ guest_can_join: self.guest_can_join,
+ world_readable: self.world_readable,
+ members: self.members,
+ notifications: self.notifications,
+ }
+ }
}
diff --git a/src/util.rs b/src/util.rs
index 96419f2..1698655 100644
--- a/src/util.rs
+++ b/src/util.rs
@@ -11,7 +11,6 @@ use self::regex::Regex;
use self::serde_json::Value as JsonValue;
-use std::collections::HashMap;
use self::url::Url;
use std::io::Read;
use std::path::Path;
@@ -27,6 +26,7 @@ use self::time::Duration;
use error::Error;
use types::Message;
+use types::Room;
// from https://stackoverflow.com/a/43992218/1592377
@@ -134,7 +134,18 @@ macro_rules! thumb {
};
}
-pub fn get_rooms_from_json(r: JsonValue, userid: &str) -> Result, Error> {
+pub fn evc(events: &JsonValue, t: &str, field: &str) -> String {
+ if let Some(arr) = events.as_array() {
+ return match arr.iter().find(|x| x["type"] == t) {
+ Some(js) => String::from(js["content"][field].as_str().unwrap_or("")),
+ None => String::new(),
+ };
+ }
+
+ String::new()
+}
+
+pub fn get_rooms_from_json(r: JsonValue, userid: &str) -> Result, Error> {
let rooms = &r["rooms"];
// TODO: do something with invite and leave
//let invite = rooms["invite"].as_object().ok_or(Error::BackendError)?;
@@ -142,14 +153,22 @@ pub fn get_rooms_from_json(r: JsonValue, userid: &str) -> Result = HashMap::new();
+ let mut rooms: Vec = vec![];
for k in join.keys() {
let room = join.get(k).ok_or(Error::BackendError)?;
- let name = calculate_room_name(&room["state"]["events"], userid)?;
- rooms_map.insert(k.clone(), name);
+ let stevents = &room["state"]["events"];
+ let name = calculate_room_name(stevents, userid)?;
+ let mut r = Room::new(k.clone(), name);
+
+ r.avatar = evc(stevents, "m.room.avatar", "url");
+ r.alias = evc(stevents, "m.room.canonical_alias", "alias");
+ r.topic = evc(stevents, "m.room.topic", "topic");
+ r.notifications = room["unread_notifications"]["notification_count"]
+ .as_i64().unwrap_or(0) as i32;
+ rooms.push(r);
}
- Ok(rooms_map)
+ Ok(rooms)
}
pub fn get_rooms_timeline_from_json(baseu: &Url, r: JsonValue) -> Result, Error> {
@@ -417,6 +436,7 @@ pub fn calculate_room_name(roomst: &JsonValue, userid: &str) -> Result Message {
let sender = msg["sender"].as_str().unwrap_or("");
let age = msg["age"].as_i64().unwrap_or(0);
+ let id = msg["id"].as_str().unwrap_or("");
let c = &msg["content"];
let mtype = c["msgtype"].as_str().unwrap_or("");
@@ -442,6 +462,7 @@ pub fn parse_room_message(baseu: &Url, roomid: String, msg: &JsonValue) -> Messa
room: roomid.clone(),
url: url,
thumb: thumb,
+ id: String::from(id),
}
}