parent
b9bbc20ade
commit
4af74a30b6
6 changed files with 204 additions and 90 deletions
|
@ -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,
|
||||
|
|
|
@ -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 -->
|
||||
|
|
|
@ -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
|
||||
|
|
66
fractal-gtk/src/widgets/member.rs
Normal file
66
fractal-gtk/src/widgets/member.rs
Normal 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)
|
||||
}
|
||||
});
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in a new issue