Member list with avatar and alias

See #18
This commit is contained in:
Daniel García Moreno 2017-12-09 17:50:37 +01:00
parent b9bbc20ade
commit 4af74a30b6
6 changed files with 204 additions and 90 deletions

View file

@ -760,6 +760,11 @@ pub fn parse_room_member(msg: &JsonValue) -> Option<Member> {
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<Member> {
Some(s) => Some(strn!(s))
};
if c["membership"].as_str().is_none() {
return None;
}
Some(Member {
uid: strn!(sender),
alias: displayname,

View file

@ -19,6 +19,66 @@
<mime-type>image/gif</mime-type>
</mime-types>
</object>
<object class="GtkPopoverMenu" id="members_popover">
<property name="can_focus">False</property>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_left">6</property>
<property name="margin_right">6</property>
<property name="margin_top">6</property>
<property name="margin_bottom">6</property>
<property name="orientation">vertical</property>
<property name="spacing">6</property>
<child>
<object class="GtkSearchEntry" id="members_search">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="primary_icon_name">edit-find-symbolic</property>
<property name="primary_icon_activatable">False</property>
<property name="primary_icon_sensitive">False</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkScrolledWindow">
<property name="width_request">200</property>
<property name="height_request">250</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="hscrollbar_policy">never</property>
<property name="shadow_type">in</property>
<child>
<object class="GtkViewport">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkListBox" id="member_list">
<property name="visible">True</property>
<property name="can_focus">False</property>
</object>
</child>
</object>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="submenu">main</property>
<property name="position">1</property>
</packing>
</child>
</object>
<object class="GtkListStore" id="members_store">
<columns>
<!-- column-name alias -->
@ -37,70 +97,6 @@
<property name="popup_single_match">False</property>
<property name="inline_selection">True</property>
</object>
<object class="GtkPopoverMenu" id="members_popover">
<property name="can_focus">False</property>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_left">6</property>
<property name="margin_right">6</property>
<property name="margin_top">6</property>
<property name="margin_bottom">6</property>
<property name="orientation">vertical</property>
<property name="spacing">6</property>
<child>
<object class="GtkScrolledWindow">
<property name="width_request">200</property>
<property name="height_request">250</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="shadow_type">in</property>
<child>
<object class="GtkViewport">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkTreeView" id="members_treeview">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="model">members_store</property>
<property name="headers_visible">False</property>
<property name="enable_grid_lines">vertical</property>
<child internal-child="selection">
<object class="GtkTreeSelection"/>
</child>
<child>
<object class="GtkTreeViewColumn">
<property name="title" translatable="yes">name</property>
<child>
<object class="GtkCellRendererText">
<property name="ellipsize">end</property>
</object>
<attributes>
<attribute name="text">0</attribute>
</attributes>
</child>
</object>
</child>
</object>
</child>
</object>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
</object>
<packing>
<property name="submenu">main</property>
<property name="position">1</property>
</packing>
</child>
</object>
<object class="GtkListStore" id="new_room_combo">
<columns>
<!-- column-name name -->

View file

@ -601,9 +601,19 @@ impl AppOp {
.get_object::<gtk::ListStore>("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::<gtk::Label>("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<Member>) {
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

View file

@ -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<BKCommand>, img: gtk::Image, m: Option<Member>, size: i32) {
let (tx, rx): (Sender<String>, Receiver<String>) = 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)
}
});
}

View file

@ -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<BKCommand>, img: gtk::Image, m: Option<Member>) {
let (tx, rx): (Sender<String>, Receiver<String>) = 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)
}
});
}

View file

@ -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;