diff --git a/fractal-api/src/util.rs b/fractal-api/src/util.rs index fff0cddd..291a74e7 100644 --- a/fractal-api/src/util.rs +++ b/fractal-api/src/util.rs @@ -760,6 +760,11 @@ pub fn parse_room_member(msg: &JsonValue) -> Option { 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(strn!(s)) @@ -769,10 +774,6 @@ pub fn parse_room_member(msg: &JsonValue) -> Option { Some(s) => Some(strn!(s)) }; - if c["membership"].as_str().is_none() { - return None; - } - Some(Member { uid: strn!(sender), alias: displayname, diff --git a/fractal-gtk/res/main_window.glade b/fractal-gtk/res/main_window.glade index 0d9503e5..ebaa83ef 100644 --- a/fractal-gtk/res/main_window.glade +++ b/fractal-gtk/res/main_window.glade @@ -19,6 +19,66 @@ image/gif + + False + + + True + False + 6 + 6 + 6 + 6 + vertical + 6 + + + True + True + edit-find-symbolic + False + False + + + False + True + 0 + + + + + 200 + 250 + True + True + never + in + + + True + False + + + True + False + + + + + + + False + True + 1 + + + + + main + 1 + + + @@ -37,70 +97,6 @@ False True - - False - - - True - False - 6 - 6 - 6 - 6 - vertical - 6 - - - 200 - 250 - True - True - in - - - True - False - - - True - True - members_store - False - vertical - - - - - - name - - - end - - - 0 - - - - - - - - - - - False - True - 0 - - - - - main - 1 - - - diff --git a/fractal-gtk/src/app.rs b/fractal-gtk/src/app.rs index c73eb383..b0d5205d 100644 --- a/fractal-gtk/src/app.rs +++ b/fractal-gtk/src/app.rs @@ -601,9 +601,19 @@ impl AppOp { .get_object::("members_store") .expect("Can't find members_store in ui file."); members.clear(); + + let mlist: gtk::ListBox = self.gtk_builder + .get_object("member_list") + .expect("Couldn't find member_list in ui file."); + for ch in mlist.get_children() { + mlist.remove(&ch); + } + for (_, m) in room.members.iter() { self.add_room_member(m.clone()); } + self.reset_member_search(); + self.show_all_members(); let name_label = self.gtk_builder .get_object::("room_name") @@ -1344,6 +1354,7 @@ impl AppOp { } } } + self.show_all_members(); } Some("join") => { let m = Member { @@ -1355,6 +1366,7 @@ impl AppOp { r.members.insert(m.uid.clone(), m.clone()); } self.add_room_member(m); + self.show_all_members(); } Some(_) => { // ignoring other memberships @@ -1500,6 +1512,50 @@ impl AppOp { self.disconnect(); self.gtk_app.quit(); } + + pub fn reset_member_search(&self) { + let inp: gtk::SearchEntry = self.gtk_builder + .get_object("members_search") + .expect("Couldn't find members_searcn in ui file."); + inp.set_text(""); + } + + pub fn show_members(&self, members: Vec) { + let mlist: gtk::ListBox = self.gtk_builder + .get_object("member_list") + .expect("Couldn't find member_list in ui file."); + for ch in mlist.get_children() { + mlist.remove(&ch); + } + + for m in members { + let mb = widgets::MemberBox::new(&m, &self); + mlist.add(&mb.widget()); + } + } + + pub fn show_all_members(&self) { + let inp: gtk::SearchEntry = self.gtk_builder + .get_object("members_search") + .expect("Couldn't find members_searcn in ui file."); + let text = inp.get_text(); + if let Some(r) = self.rooms.get(&self.active_room.clone().unwrap_or_default()) { + let members = match text { + // all members if no search text + None => r.members.values().cloned().collect(), + Some(t) => { + // members with the text in the alias + r.members.values().filter(move |x| { + match x.alias { + None => false, + Some(ref a) => a.to_lowercase().contains(&t.to_lowercase()) + } + }).cloned().collect() + } + }; + self.show_members(members); + } + } } /// State for the main thread. @@ -1602,6 +1658,8 @@ impl App { self.connect_new_room_dialog(); self.connect_search(); + + self.connect_member_search(); } fn create_actions(&self) { @@ -1897,6 +1955,17 @@ impl App { btn.connect_clicked(move |_| op.lock().unwrap().search(input.get_text())); } + fn connect_member_search(&self) { + let input: gtk::SearchEntry = self.gtk_builder + .get_object("members_search") + .expect("Couldn't find members_searcn in ui file."); + + let op = self.op.clone(); + input.connect_search_changed(move |_| { + op.lock().unwrap().show_all_members(); + }); + } + fn connect_login_button(&self) { // Login click let btn: gtk::Button = self.gtk_builder diff --git a/fractal-gtk/src/widgets/member.rs b/fractal-gtk/src/widgets/member.rs new file mode 100644 index 00000000..73273202 --- /dev/null +++ b/fractal-gtk/src/widgets/member.rs @@ -0,0 +1,66 @@ +extern crate gtk; +extern crate gdk_pixbuf; + +use self::gdk_pixbuf::Pixbuf; +use self::gtk::prelude::*; + +use types::Member; + +use backend::BKCommand; + +use std::sync::mpsc::channel; +use std::sync::mpsc::{Sender, Receiver}; + +use app::AppOp; + +// Room Search item +pub struct MemberBox<'a> { + member: &'a Member, + op: &'a AppOp, +} + +impl<'a> MemberBox<'a> { + pub fn new(member: &'a Member, op: &'a AppOp) -> MemberBox<'a> { + MemberBox { + member: member, + op: op, + } + } + + pub fn widget(&self) -> gtk::Box { + let backend = self.op.backend.clone(); + let username = gtk::Label::new(""); + let w = gtk::Box::new(gtk::Orientation::Horizontal, 5); + + username.set_text(&self.member.get_alias().unwrap_or_default()); + + let avatar = gtk::Image::new_from_icon_name("avatar-default-symbolic", 3); + get_member_avatar(backend.clone(), avatar.clone(), Some(self.member.clone()), 30); + avatar.set_alignment(0.5, 0.); + + w.pack_start(&avatar, false, false, 5); + w.pack_start(&username, false, false, 5); + + w.show_all(); + w + } +} + +pub fn get_member_avatar(backend: Sender, img: gtk::Image, m: Option, size: i32) { + let (tx, rx): (Sender, Receiver) = channel(); + backend.send(BKCommand::GetAvatarAsync(m.clone(), tx)).unwrap(); + gtk::timeout_add(50, move || match rx.try_recv() { + Err(_) => gtk::Continue(true), + Ok(avatar) => { + if let Ok(pixbuf) = Pixbuf::new_from_file_at_scale(&avatar, size, size, false) { + img.set_from_pixbuf(&pixbuf); + } else { + // trying again if fail + img.set_from_icon_name("avatar-default-symbolic", 5); + get_member_avatar(backend.clone(), img.clone(), m.clone(), size); + } + + gtk::Continue(false) + } + }); +} diff --git a/fractal-gtk/src/widgets/message.rs b/fractal-gtk/src/widgets/message.rs index f7f0bb8b..ec14ef14 100644 --- a/fractal-gtk/src/widgets/message.rs +++ b/fractal-gtk/src/widgets/message.rs @@ -17,11 +17,10 @@ use backend::BKCommand; use fractal_api as api; use util; -use std::sync::mpsc::channel; -use std::sync::mpsc::{Sender, Receiver}; use std::path::Path; use app::AppOp; +use widgets::member::get_member_avatar; // Room Message item pub struct MessageBox<'a> { @@ -128,7 +127,7 @@ impl<'a> MessageBox<'a> { avatar = gtk::Image::new_from_icon_name("avatar-default-symbolic", 5); } - get_message_avatar(backend.clone(), avatar.clone(), m.cloned()); + get_member_avatar(backend.clone(), avatar.clone(), m.cloned(), 40); avatar.set_alignment(0.5, 0.); avatar @@ -247,22 +246,3 @@ impl<'a> MessageBox<'a> { info } } - -fn get_message_avatar(backend: Sender, img: gtk::Image, m: Option) { - let (tx, rx): (Sender, Receiver) = channel(); - backend.send(BKCommand::GetAvatarAsync(m.clone(), tx)).unwrap(); - gtk::timeout_add(50, move || match rx.try_recv() { - Err(_) => gtk::Continue(true), - Ok(avatar) => { - if let Ok(pixbuf) = Pixbuf::new_from_file_at_scale(&avatar, 40, 40, false) { - img.set_from_pixbuf(&pixbuf); - } else { - // trying again if fail - img.set_from_icon_name("avatar-default-symbolic", 5); - get_message_avatar(backend.clone(), img.clone(), m.clone()); - } - - gtk::Continue(false) - } - }); -} diff --git a/fractal-gtk/src/widgets/mod.rs b/fractal-gtk/src/widgets/mod.rs index 97c2f0f7..b5e2667b 100644 --- a/fractal-gtk/src/widgets/mod.rs +++ b/fractal-gtk/src/widgets/mod.rs @@ -1,5 +1,7 @@ mod message; mod room; +mod member; pub use self::message::MessageBox; pub use self::room::RoomBox; +pub use self::member::MemberBox;