Added notifications using libnotify

This commit is contained in:
Daniel García Moreno 2017-08-30 13:09:16 +02:00
parent d6fed8c2a2
commit 591d65a54f
5 changed files with 112 additions and 34 deletions

View file

@ -6,6 +6,7 @@ version = "0.1.0"
chrono = "0.4.0"
gdk-pixbuf = "0.2.0"
gio = "0.2.0"
glib = "0.3.1"
regex = "0.2.2"
reqwest = "0.7.3"
secret-service = "0.4.0"
@ -16,10 +17,13 @@ time = "0.1.38"
url = "1.5.1"
xdg = "2.1.0"
[dependencies.cairo-rs]
features = ["png"]
version = "0.2.0"
[dependencies.gtk]
features = ["v3_22"]
version = "0.2.0"
[dependencies.cairo-rs]
version = "0.2.0"
features = ["png"]
[dependencies.libnotify]
git = "https://github.com/danigm/rust-libnotify"

6
TODO
View file

@ -1,4 +1,5 @@
Fixs:
* Fix room list after join
* Ignore launched threads when changing room...
* Sort rooms by last message or fav?
@ -16,6 +17,11 @@ Functionality:
* Send media messages (images / videos)
* Store last read message to show differently
Events to manage:
* New room events
* Invite event
* Join events
Other stuff:
* Set the app name correctly to show in the shell topbar

View file

@ -1,8 +1,10 @@
extern crate glib;
extern crate gtk;
extern crate gio;
extern crate gdk_pixbuf;
extern crate secret_service;
extern crate libnotify;
use self::secret_service::SecretService;
use self::secret_service::EncryptionType;
@ -45,6 +47,7 @@ pub struct AppOp {
pub backend: Sender<backend::BKCommand>,
pub active_room: String,
pub members: HashMap<String, Member>,
pub rooms: HashMap<String, Room>,
pub load_more_btn: gtk::Button,
}
@ -124,7 +127,6 @@ impl AppOp {
let ser = server_url.clone();
self.backend.send(BKCommand::Register(uname, pass, ser)).unwrap();
self.hide_popup();
self.clear_room_list();
}
pub fn connect(&self, username: String, password: String, server: Option<String>) {
@ -145,7 +147,6 @@ impl AppOp {
let ser = server_url.clone();
self.backend.send(BKCommand::Login(uname, pass, ser)).unwrap();
self.hide_popup();
self.clear_room_list();
}
pub fn connect_guest(&self, server: Option<String>) {
@ -157,7 +158,6 @@ impl AppOp {
self.show_user_loading();
self.backend.send(BKCommand::Guest(server_url)).unwrap();
self.hide_popup();
self.clear_room_list();
}
pub fn get_username(&self) {
@ -295,7 +295,11 @@ impl AppOp {
}
pub fn sync(&self) {
self.backend.send(BKCommand::Sync).unwrap();
let tx = self.backend.clone();
gtk::timeout_add(1000, move || {
tx.send(BKCommand::Sync).unwrap();
gtk::Continue(false)
});
}
pub fn set_rooms(&mut self, rooms: Vec<Room>, def: Option<Room>) {
@ -304,7 +308,11 @@ impl AppOp {
let mut array: Vec<Room> = vec![];
self.rooms.clear();
store.clear();
for r in rooms {
self.rooms.insert(r.id.clone(), r.clone());
array.push(r);
}
@ -340,17 +348,10 @@ impl AppOp {
.expect("Can't find main_content_stack in ui file.")
.set_visible_child_name("Chat");
self.clear_room_list();
self.room_panel(RoomPanel::Loading);
self.backend.send(BKCommand::SyncForced).unwrap();
}
pub fn clear_room_list(&self) {
let store: gtk::TreeStore = self.gtk_builder.get_object("rooms_tree_store")
.expect("Couldn't find rooms_tree_store in ui file.");
store.clear();
}
pub fn set_active_room(&mut self, room: String, name: String) {
self.active_room = room;
@ -604,6 +605,50 @@ impl AppOp {
btn.set_label("Search");
btn.set_sensitive(true);
}
pub fn notify(&self, msg: &Message) {
let roomname = match self.rooms.get(&msg.room) {
Some(r) => r.name.clone(),
None => msg.room.clone(),
};
let mut body = msg.body.clone();
body.truncate(80);
let (tx, rx): (Sender<(String, String)>, Receiver<(String, String)>) = channel();
self.backend.send(BKCommand::GetUserInfoAsync(msg.sender.clone(), tx)).unwrap();
gtk::timeout_add(50, move || {
match rx.try_recv() {
Err(_) => gtk::Continue(true),
Ok((name, avatar)) => {
let summary = format!("@{} / {}", name, roomname);
let n = libnotify::Notification::new(&summary,
Some(&body[..]),
Some(&avatar[..]));
n.show().unwrap();
gtk::Continue(false)
}
}
});
}
pub fn show_room_messages(&self, msgs: Vec<Message>, init: bool) {
for msg in msgs.iter() {
self.add_room_message(msg, MsgPos::Bottom);
if !init {
self.notify(msg);
}
}
if !msgs.is_empty() {
self.scroll_down();
self.mark_as_read(msgs);
}
if init {
self.room_panel(RoomPanel::Room);
}
}
}
/// State for the main thread.
@ -639,11 +684,12 @@ impl App {
backend: apptx,
active_room: String::from(""),
members: HashMap::new(),
rooms: HashMap::new(),
}
));
let theop = op.clone();
gtk::timeout_add(50, move || {
gtk::timeout_add(500, move || {
let recv = rx.try_recv();
match recv {
Ok(BKResponse::Token(uid, _)) => {
@ -673,16 +719,10 @@ impl App {
theop.lock().unwrap().set_room_avatar(avatar);
},
Ok(BKResponse::RoomMessages(msgs)) => {
for msg in msgs.iter() {
theop.lock().unwrap().add_room_message(msg, MsgPos::Bottom);
}
if !msgs.is_empty() {
theop.lock().unwrap().scroll_down();
theop.lock().unwrap().mark_as_read(msgs);
}
theop.lock().unwrap().room_panel(RoomPanel::Room);
theop.lock().unwrap().show_room_messages(msgs, false);
},
Ok(BKResponse::RoomMessagesInit(msgs)) => {
theop.lock().unwrap().show_room_messages(msgs, true);
},
Ok(BKResponse::RoomMessagesTo(msgs)) => {
for msg in msgs.iter().rev() {
@ -941,6 +981,14 @@ impl App {
pub fn run(self) {
self.op.lock().unwrap().init();
if let Err(err) = libnotify::init("guillotine") {
println!("Error: can't init notifications: {}", err);
};
glib::set_application_name("guillotine");
glib::set_prgname(Some("guillotine"));
gtk::main();
libnotify::uninit();
}
}

View file

@ -55,6 +55,7 @@ pub enum BKCommand {
GetRoomMessagesTo(String),
GetAvatarAsync(String, Sender<String>),
GetThumbAsync(String, Sender<String>),
GetUserInfoAsync(String, Sender<(String, String)>),
SendMsg(String, String),
SetRoom(String),
ShutDown,
@ -74,6 +75,7 @@ pub enum BKResponse {
RoomDetail(String, String),
RoomAvatar(String),
RoomMessages(Vec<Message>),
RoomMessagesInit(Vec<Message>),
RoomMessagesTo(Vec<Message>),
RoomMembers(Vec<Member>),
SendMsg,
@ -162,6 +164,10 @@ impl Backend {
let r = self.get_avatar_async(&sender, ctx);
bkerror!(r, tx, BKResponse::CommandError);
},
Ok(BKCommand::GetUserInfoAsync(sender, ctx)) => {
let r = self.get_user_info_async(&sender, ctx);
bkerror!(r, tx, BKResponse::CommandError);
},
Ok(BKCommand::GetThumbAsync(media, ctx)) => {
let r = self.get_thumb_async(media, ctx);
bkerror!(r, tx, BKResponse::CommandError);
@ -347,7 +353,7 @@ impl Backend {
let tx = self.tx.clone();
thread::spawn(move || {
match get_user_avatar(&baseu, &userid) {
Ok(fname) => {
Ok((_, fname)) => {
tx.send(BKResponse::Avatar(fname)).unwrap();
},
Err(err) => {
@ -511,7 +517,7 @@ impl Backend {
ms.push(m);
}
match to {
false => tx.send(BKResponse::RoomMessages(ms)).unwrap(),
false => tx.send(BKResponse::RoomMessagesInit(ms)).unwrap(),
true => tx.send(BKResponse::RoomMessagesTo(ms)).unwrap(),
};
},
@ -569,7 +575,7 @@ impl Backend {
let u = String::from(uid);
thread::spawn(move || {
match get_user_avatar(&baseu, &u) {
Ok(fname) => { tx.send(fname).unwrap(); },
Ok((_, fname)) => { tx.send(fname).unwrap(); },
Err(_) => { tx.send(String::from("")).unwrap(); }
};
});
@ -577,6 +583,20 @@ impl Backend {
Ok(())
}
pub fn get_user_info_async(&self, uid: &str, tx: Sender<(String, String)>) -> Result<(), Error> {
let baseu = self.get_base_url()?;
let u = String::from(uid);
thread::spawn(move || {
match get_user_avatar(&baseu, &u) {
Ok(info) => { tx.send(info).unwrap(); },
Err(_) => { tx.send((String::new(), String::new())).unwrap(); }
};
});
Ok(())
}
pub fn get_thumb_async(&self, media: String, tx: Sender<String>) -> Result<(), Error> {
let baseu = self.get_base_url()?;

View file

@ -276,22 +276,22 @@ pub fn json_q(method: &str, url: &Url, attrs: &JsonValue) -> Result<JsonValue, E
}
}
pub fn get_user_avatar(baseu: &Url, userid: &str) -> Result<String, Error> {
pub fn get_user_avatar(baseu: &Url, userid: &str) -> Result<(String, String), Error> {
let url = baseu.join("/_matrix/client/r0/profile/")?.join(userid)?;
let attrs = json!(null);
match json_q("get", &url, &attrs) {
Ok(js) => {
let name = String::from(js["displayname"].as_str().unwrap_or("@"));
match js["avatar_url"].as_str() {
Some(url) => Ok(thumb!(baseu, &url)?),
Some(url) => Ok((name.clone(), thumb!(baseu, &url)?)),
None => {
let name = js["displayname"].as_str().unwrap_or("@");
Ok(draw_identicon(userid, String::from(name))?)
Ok((name.clone(), draw_identicon(userid, name)?))
},
}
},
Err(_) => {
Ok(draw_identicon(userid, String::from(&userid[1..2]))?)
Ok((String::from(userid), draw_identicon(userid, String::from(&userid[1..2]))?))
}
}
}