Added notifications using libnotify
This commit is contained in:
parent
d6fed8c2a2
commit
591d65a54f
5 changed files with 112 additions and 34 deletions
10
Cargo.toml
10
Cargo.toml
|
@ -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
6
TODO
|
@ -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
|
||||
|
||||
|
|
94
src/app.rs
94
src/app.rs
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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()?;
|
||||
|
||||
|
|
10
src/util.rs
10
src/util.rs
|
@ -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]))?))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue