Commit 20c9ebd4 authored by Julian Sparber's avatar Julian Sparber

roomsettings: move room settings panel to a custom widget

parent 692045fe
......@@ -49,7 +49,6 @@ fractal-gtk/src/app/connect/media_viewer.rs
fractal-gtk/src/app/connect/mod.rs
fractal-gtk/src/app/connect/more_members.rs
fractal-gtk/src/app/connect/new_room.rs
fractal-gtk/src/app/connect/room_config.rs
fractal-gtk/src/app/connect/roomlist_search.rs
fractal-gtk/src/app/connect/scroll.rs
fractal-gtk/src/app/connect/search.rs
......
......@@ -17,7 +17,6 @@ mod markdown;
mod media_viewer;
mod more_members;
mod new_room;
mod room_config;
mod roomlist_search;
mod scroll;
mod search;
......@@ -88,7 +87,6 @@ impl App {
self.connect_spellcheck();
self.connect_directory();
self.connect_room_config();
self.connect_leave_room_dialog();
self.connect_new_room_dialog();
self.connect_join_room_dialog();
......
extern crate gtk;
use self::gtk::prelude::*;
use app::App;
impl App {
pub fn connect_room_config(&self) {
let op = &self.op;
let back = self.ui.builder
.get_object::<gtk::Button>("room_settings_back_button")
.expect("Can't find room_settings_back_button in ui file.");
let name_btn = self.ui.builder
.get_object::<gtk::Button>("room_settings_room_name_button")
.expect("Can't find room_settings_room_name_button in ui file.");
let name_entry = self.ui.builder
.get_object::<gtk::Entry>("room_settings_room_name_entry")
.expect("Can't find room_settings_room_name_entry in ui file.");
let topic_btn = self.ui.builder
.get_object::<gtk::Button>("room_settings_room_topic_button")
.expect("Can't find room_settings_room_topic_button in ui file.");
let topic_entry = self.ui.builder
.get_object::<gtk::Entry>("room_settings_room_topic_entry")
.expect("Can't find room_settings_room_topic_entry in ui file.");
/* Headerbar */
back.connect_clicked(clone!(op => move |_| {
op.lock().unwrap().close_room_settings();
}));
let button = name_btn.clone();
name_entry.connect_property_text_notify(clone!(op => move |w| {
let lock = op.try_lock();
if let Ok(guard) = lock {
let result = guard.validate_room_name(w.get_text());
if result.is_some() {
button.show();
return;
}
}
button.hide();
}));
let button = topic_btn.clone();
topic_entry.connect_property_text_notify(clone!(op => move |w| {
let lock = op.try_lock();
if let Ok(guard) = lock {
let result = guard.validate_room_topic(w.get_text());
if result.is_some() {
button.show();
return;
}
}
button.hide();
}));
let button = name_btn.clone();
name_entry.connect_activate(move |_w| {
let _ = button.emit("clicked", &[]);
});
name_btn.connect_clicked(clone!(op => move |_| {
op.lock().unwrap().update_room_name();
}));
let button = topic_btn.clone();
topic_entry.connect_activate(move |_w| {
let _ = button.emit("clicked", &[]);
});
topic_btn.connect_clicked(clone!(op => move |_| {
op.lock().unwrap().update_room_topic();
}));
/* Connect avatar button */
let avatar_btn = self.ui.builder
.get_object::<gtk::Button>("room_settings_avatar_button")
.expect("Can't find room_settings_avatar_button in ui file.");
let builder = &self.ui.builder;
avatar_btn.connect_clicked(clone!(op, builder => move |_| {
let window = builder
.get_object::<gtk::Window>("main_window")
.expect("Can't find main_window in ui file.");
let file_chooser = gtk::FileChooserNative::new("Pick a new room avatar", Some(&window), gtk::FileChooserAction::Open, Some("Select"), None);
/* http://gtk-rs.org/docs/gtk/struct.FileChooser.html */
let result = gtk::NativeDialog::run(&file_chooser.clone().upcast::<gtk::NativeDialog>());
if gtk::ResponseType::from(result) == gtk::ResponseType::Accept {
if let Some(file) = file_chooser.get_filename() {
if let Some(path) = file.to_str() {
op.lock().unwrap().update_room_avatar(String::from(path));
}
}
}
}));
}
}
......@@ -114,16 +114,6 @@ impl App {
stack.add_named(&child, "account-settings");
stack_header.add_named(&child_header, "account-settings");
/* Add room settings view to the main stack */
let child = ui.builder
.get_object::<gtk::Box>("room_settings_box")
.expect("Can't find room_settings_box in ui file.");
let child_header = ui.builder
.get_object::<gtk::Box>("room_settings_headerbar")
.expect("Can't find room_settings_headerbar in ui file.");
stack.add_named(&child, "room-settings");
stack_header.add_named(&child_header, "room-settings");
/* Add media viewer to the main stack */
let child = ui.builder
.get_object::<gtk::Box>("media_viewer_box")
......
......@@ -77,6 +77,7 @@ pub struct AppOp {
pub autoscroll: bool,
pub active_room: Option<String>,
pub rooms: RoomList,
pub room_settings: Option<widgets::RoomSettings>,
pub roomlist: widgets::RoomList,
pub load_more_spn: gtk::Spinner,
pub more_members_btn: gtk::Button,
......@@ -121,6 +122,7 @@ impl AppOp {
autoscroll: true,
active_room: None,
rooms: HashMap::new(),
room_settings: None,
username: None,
uid: None,
avatar: None,
......
......@@ -14,251 +14,51 @@ use std::sync::mpsc::TryRecvError;
use std::sync::mpsc::{Receiver, Sender};
use widgets;
use widgets::members_list::MembersList;
use widgets::room_settings::RoomSettings;
use widgets::AvatarExt;
impl AppOp {
pub fn show_room_settings(&mut self) {
//check for type we have to show
self.init_room_settings();
self.set_state(AppState::RoomSettings);
}
pub fn close_room_settings(&mut self) {
let scroll = self.ui
.builder
.get_object::<gtk::ScrolledWindow>("room_settings_scroll")
.expect("Can't find room_settings_scroll in ui file.");
let b = self.ui
.builder
.get_object::<gtk::Frame>("room_settings_members_list")
.expect("Can't find room_settings_members_list in ui file.");
for w in b.get_children().iter() {
b.remove(w);
}
if let Some(adj) = scroll.get_vadjustment() {
adj.set_value(0f64);
}
self.set_state(AppState::Chat);
}
fn init_room_settings(&mut self) -> Option<()> {
let room = self.rooms.get(&self.active_room.clone()?)?;
let avatar = room.avatar.clone();
let name = room.name.clone();
let topic = room.topic.clone();
let mut is_room = true;
let mut is_group = false;
let members: Vec<Member> = room.members.values().cloned().collect();
let power = *room.power_levels.get(&self.uid.clone()?).unwrap_or(&0);
let edit = power >= 50 && !room.direct;
let description = if room.direct {
is_room = false;
is_group = false;
self.get_direct_partner_uid(members.clone())
} else {
/* we don't have private groups yet
let description = Some(format!("Private Group · {} members", members.len()));
*/
//Some(format!("Public Room · {} members", members.len()))
Some(format!("Room · {} members", members.len()))
};
self.room_settings_show_avatar(avatar, edit);
self.room_settings_show_room_name(name, edit);
self.room_settings_show_room_topic(topic, is_room, edit);
self.room_settings_show_room_type(description);
self.room_settings_show_members(members);
/* admin parts */
self.room_settings_show_group_room(is_room || is_group);
self.room_settings_show_admin_groupe(is_group && edit);
self.room_settings_show_admin_room(is_room && edit);
self.room_settings_hide_not_implemented_widgets();
None
self.create_room_settings();
}
/* returns the uid of the fisrt member in the room, ignoring the current user */
fn get_direct_partner_uid(&self, members: Vec<Member>) -> Option<String> {
let mut uid = None;
for member in members {
if member.uid != self.uid.clone()? {
uid = Some(member.uid);
break;
}
}
uid
}
pub fn create_room_settings(&mut self) -> Option<()> {
let stack = self.ui.builder
.get_object::<gtk::Stack>("main_content_stack")
.expect("Can't find main_content_stack in ui file.");
let stack_header = self.ui.builder
.get_object::<gtk::Stack>("headerbar_stack")
.expect("Can't find headerbar_stack in ui file.");
pub fn room_settings_show_room_name(&self, text: Option<String>, edit: bool) -> Option<()> {
let label = self.ui
.builder
.get_object::<gtk::Label>("room_settings_room_name")
.expect("Can't find room_settings_room_name in ui file.");
let b = self.ui
.builder
.get_object::<gtk::Box>("room_settings_room_name_box")
.expect("Can't find room_settings_room_topic_entry in ui file.");
let entry = self.ui
.builder
.get_object::<gtk::Entry>("room_settings_room_name_entry")
.expect("Can't find room_settings_room_name_entry in ui file.");
let button = self.ui
.builder
.get_object::<gtk::Button>("room_settings_room_name_button")
.expect("Can't find room_settings_room_name_button in ui file.");
{
let room = self.rooms.get(&self.active_room.clone()?)?;
let mut panel = widgets::RoomSettings::new(self.backend.clone(), self.uid.clone(), room.clone());
let (body, header) = panel.create()?;
if edit {
if let Some(text) = text {
entry.set_text(&text);
} else {
entry.set_text("");
}
label.hide();
entry.set_editable(true);
self.reset_action_button(button);
b.show();
} else {
if let Some(text) = text {
label.set_text(&text);
} else {
label.set_text("Noname");
/* remove old panel */
if let Some(widget) = stack.get_child_by_name("room-settings") {
stack.remove(&widget);
}
b.hide();
label.show();
}
None
}
pub fn reset_action_button(&self, button: gtk::Button) {
let image = gtk::Image::new_from_icon_name("emblem-ok-symbolic", 1);
button.set_image(&image);
button.set_sensitive(true);
}
pub fn room_settings_show_room_topic(
&self,
text: Option<String>,
is_room: bool,
edit: bool,
) -> Option<()> {
let label = self.ui
.builder
.get_object::<gtk::Label>("room_settings_room_topic")
.expect("Can't find room_settings_room_topic in ui file.");
let b = self.ui
.builder
.get_object::<gtk::Box>("room_settings_room_topic_box")
.expect("Can't find room_settings_room_topic_entry in ui file.");
let entry = self.ui
.builder
.get_object::<gtk::Entry>("room_settings_room_topic_entry")
.expect("Can't find room_settings_room_topic_entry in ui file.");
let button = self.ui
.builder
.get_object::<gtk::Button>("room_settings_room_topic_button")
.expect("Can't find room_settings_room_topic_button in ui file.");
if is_room {
if edit {
if let Some(text) = text {
entry.set_text(&text);
} else {
entry.set_text("");
}
label.hide();
entry.set_editable(true);
self.reset_action_button(button);
b.show();
} else {
b.hide();
if let Some(text) = text {
label.set_text(&text);
label.show();
} else {
label.hide();
}
if let Some(widget) = stack_header.get_child_by_name("room-settings") {
stack_header.remove(&widget);
}
} else {
b.hide();
label.hide();
}
None
}
pub fn room_settings_show_group_room(&self, show: bool) -> Option<()> {
let notify = self.ui
.builder
.get_object::<gtk::Frame>("room_settings_notification_sounds")
.expect("Can't find room_settings_notification_sounds in ui file.");
let invite = self.ui
.builder
.get_object::<gtk::Button>("room_settings_invite")
.expect("Can't find room_settings_invite in ui file.");
if show {
notify.show();
invite.show();
} else {
notify.hide();
invite.hide();
}
None
}
stack.add_named(&body, "room-settings");
stack_header.add_named(&header, "room-settings");
pub fn room_settings_show_admin_groupe(&self, show: bool) -> Option<()> {
let history = self.ui
.builder
.get_object::<gtk::Frame>("room_settings_history_visibility")
.expect("Can't find room_settings_history_visibility in ui file.");
/* Headerbar */
panel.get_back_button()?.connect_clicked(clone!(stack_header, stack => move |_| {
/* we should use self.set_state(AppState::Chat);
* we could maybe create an internal command */
stack.set_visible_child_name("chat");
stack_header.set_visible_child_name("normal");
}));
if show {
history.show();
} else {
history.hide();
self.room_settings = Some(panel);
}
None
}
pub fn room_settings_show_admin_room(&self, show: bool) -> Option<()> {
let room = self.ui
.builder
.get_object::<gtk::Frame>("room_settings_room_visibility")
.expect("Can't find room_settings_room_visibility in ui file.");
let join = self.ui
.builder
.get_object::<gtk::Frame>("room_settings_join")
.expect("Can't find room_settings_join in ui file.");
if show {
room.show();
join.show();
} else {
room.hide();
join.hide();
}
None
}
pub fn room_settings_show_room_type(&self, text: Option<String>) -> Option<()> {
let label = self.ui
.builder
.get_object::<gtk::Label>("room_settings_room_description")
.expect("Can't find room_settings_room_name in ui file.");
self.set_state(AppState::RoomSettings);
if let Some(text) = text {
label.set_text(&text);
label.show();
} else {
label.hide();
}
None
}
......@@ -313,205 +113,42 @@ impl AppOp {
return None;
}
pub fn update_room_avatar(&mut self, file: String) -> Option<()> {
let avatar_spinner = self.ui
.builder
.get_object::<gtk::Spinner>("room_settings_avatar_spinner")
.expect("Can't find room_settings_avatar_spinner in ui file.");
let avatar_btn = self.ui
.builder
.get_object::<gtk::Button>("room_settings_avatar_button")
.expect("Can't find room_settings_avatar_button in ui file.");
let room = self.rooms.get(&self.active_room.clone()?)?;
let command = BKCommand::SetRoomAvatar(room.id.clone(), file.clone());
self.backend.send(command).unwrap();
self.room_settings_show_avatar(Some(file), true);
avatar_btn.set_sensitive(false);
avatar_spinner.show();
None
pub fn close_room_settings(&mut self) {
self.set_state(AppState::Chat);
}
pub fn update_room_name(&mut self) -> Option<()> {
let entry = self.ui
.builder
.get_object::<gtk::Entry>("room_settings_room_name_entry")
.expect("Can't find room_settings_name_entry in ui file.");
let button = self.ui
.builder
.get_object::<gtk::Button>("room_settings_room_name_button")
.expect("Can't find room_settings_name_button in ui file.");
let new_name = entry.get_text()?;
let room = self.rooms.get(&self.active_room.clone()?)?;
let spinner = gtk::Spinner::new();
spinner.start();
button.set_image(&spinner);
button.set_sensitive(false);
entry.set_editable(false);
let command = BKCommand::SetRoomName(room.id.clone(), new_name.clone());
self.backend.send(command).unwrap();
pub fn show_new_room_avatar(&self) -> Option<()> {
let panel = self.room_settings.clone()?;
panel.show_new_room_avatar();
None
}
pub fn validate_room_name(&self, new_name: Option<String>) -> Option<String> {
let room = self.rooms.get(&self.active_room.clone()?)?;
let old_name = room.name.clone()?;
let new_name = new_name?;
if new_name != "" && new_name != old_name {
return Some(new_name);
}
pub fn show_new_room_name(&self) -> Option<()> {
let panel = self.room_settings.clone()?;
panel.show_new_room_name();
None
}
pub fn validate_room_topic(&self, new_name: Option<String>) -> Option<String> {
let room = self.rooms.get(&self.active_room.clone()?)?;
let old_name = room.topic.clone()?;
let new_name = new_name?;
if new_name != "" && new_name != old_name {
return Some(new_name);
}
pub fn show_new_room_topic(&self) -> Option<()> {
let panel = self.room_settings.clone()?;
panel.show_new_room_topic();
None
}
pub fn update_room_topic(&mut self) -> Option<()> {
let name = self.ui
.builder
.get_object::<gtk::Entry>("room_settings_room_topic_entry")
.expect("Can't find room_settings_topic in ui file.");
let button = self.ui
.builder
.get_object::<gtk::Button>("room_settings_room_topic_button")
.expect("Can't find room_settings_topic_button in ui file.");
let topic = name.get_text()?;
let room = self.rooms.get(&self.active_room.clone()?)?;
let spinner = gtk::Spinner::new();
spinner.start();
button.set_image(&spinner);
button.set_sensitive(false);
name.set_editable(false);
let command = BKCommand::SetRoomTopic(room.id.clone(), topic.clone());
self.backend.send(command).unwrap();
pub fn update_members_list(&self, uid: String) -> Option<()> {
self.room_settings.clone()?.update_members_list(uid);
None
}
pub fn show_new_room_avatar(&self) {
let avatar_spinner = self.ui
.builder
.get_object::<gtk::Spinner>("room_settings_avatar_spinner")
.expect("Can't find room_settings_avatar_spinner in ui file.");
let avatar_btn = self.ui
.builder
.get_object::<gtk::Button>("room_settings_avatar_button")
.expect("Can't find room_settings_avatar_button in ui file.");
/* We could update the avatar for this room,
* but we are waiting for the new avatar event */
avatar_spinner.hide();
avatar_btn.set_sensitive(true);
}
pub fn show_new_room_name(&self) {
let entry = self.ui
.builder
.get_object::<gtk::Entry>("room_settings_room_name_entry")
.expect("Can't find room_settings_room_name_entry in ui file.");
let button = self.ui
.builder
.get_object::<gtk::Button>("room_settings_room_name_button")
.expect("Can't find room_settings_name_button in ui file.");
button.hide();
entry.set_editable(true);
self.reset_action_button(button);
}
pub fn show_new_room_topic(&self) {
let entry = self.ui
.builder
.get_object::<gtk::Entry>("room_settings_room_topic_entry")
.expect("Can't find room_settings_room_topic_entry in ui file.");
let button = self.ui
.builder
.get_object::<gtk::Button>("room_settings_room_topic_button")
.expect("Can't find room_settings_topic_button in ui file.");
button.hide();
entry.set_editable(true);
self.reset_action_button(button);
}
fn room_settings_hide_not_implemented_widgets(&self) -> Option<()> {
let notification = self.ui
.builder
.get_object::<gtk::Frame>("room_settings_notification_sounds")
.expect("Can't find room_settings_notification_sounds in ui file.");
let media = self.ui
.builder
.get_object::<gtk::Frame>("room_settings_media")
.expect("Can't find room_settings_media in ui file.");
let switch = self.ui
.builder
.get_object::<gtk::Frame>("room_settings_notification_switch")
.expect("Can't find room_settings_notification_switch in ui file.");
let history = self.ui
.builder
.get_object::<gtk::Frame>("room_settings_history_visibility")
.expect("Can't find room_settings_history_visibility in ui file.");
let join = self.ui
.builder
.get_object::<gtk::Frame>("room_settings_join")
.expect("Can't find room_settings_join in ui file.");
let room = self.ui
.builder
.get_object::<gtk::Frame>("room_settings_room_visibility")
.expect("Can't find room_settings_room_visibility in ui file.");
notification.hide();
media.hide();
switch.hide();
history.hide();
room.hide();
join.hide();
None
}
fn room_settings_show_members(&self, members: Vec<Member>) -> Option<()> {
let entry = self.ui
.builder
.get_object::<gtk::SearchEntry>("room_settings_members_search")
.expect("Can't find room_settings_members_search in ui file.");
let b = self.ui
.builder
.get_object::<gtk::Frame>("room_settings_members_list")
.expect("Can't find room_settings_members_list in ui file.");
let label = self.ui
.builder
.get_object::<gtk::Label>("room_settings_member_list_title")
.expect("Can't find room_settings_member_list_title in ui file.");
for w in b.get_children().iter() {
b.remove(w);
}
label.set_text(&format!("{} members", members.len()));
let list = widgets::MembersList::new(members.clone(), entry);
let w = list.create()?;
/* ask for all avatars */
pub fn request_members_list(&self) -> Option<()> {
let room = self.rooms.get(&self.active_room.clone()?)?;
let members: Vec<Member> = room.members.values().cloned().collect();
for (i, member) in members.iter().enumerate() {
account_settings_get_member_info(
self.backend.clone(),
member.uid.clone(),
i,
list.clone(),
);
member.uid.clone());
}
b.add(&w);
None
}
}
......@@ -520,9 +157,7 @@ impl AppOp {
pub fn account_settings_get_member_info(
backend: Sender<BKCommand>,
sender: String,
index: usize,
list: MembersList,
) {
) {
let (tx, rx): (Sender<(String, String)>, Receiver<(String, String)>) = channel();
backend
.send(BKCommand::GetUserInfoAsync(sender.clone(), Some(tx)))
......@@ -532,8 +167,8 @@ pub fn account_settings_get_member_info(
Err(TryRecvError::Disconnected) => gtk::Continue(false),
Ok((_name, _avatar)) => {
/* update UI */
/*println!("Update user {} with {}", sender, avatar); */
list.update(index);
//view.update_members_list(index);
//APPOP!(update_members_list, (sender));
gtk::Continue(false)
}
});
......
......@@ -56,8 +56,6 @@ impl UI {
.expect("Can't load ui file: password_dialog.ui");
builder.add_from_resource("/org/gnome/Fractal/ui/account_settings.ui")
.expect("Can't load ui file: account_settings.ui");
builder.add_from_resource("/org/gnome/Fractal/ui/room_settings.ui")
.expect("Can't load ui file: room_settings.ui");
builder.add_from_resource("/org/gnome/Fractal/ui/media_viewer.ui")
.expect("Can't load ui file: media_viewer.ui");
......
......@@ -47,8 +47,15 @@ impl MembersList {
}
/* removes the content of the row with index i */
pub fn update(&self, i: usize) -> Option<()> {
let widget = self.container.get_row_at_index(i as i32)?;
pub fn update(&self, uid: String) -> Option<()> {
let mut index = None;
for (i, member) in self.members.iter().enumerate() {
if member.uid == uid {
index = Some(i);
break;
}
}
let widget = self.container.get_row_at_index(index? as i32)?;