|
|
|
|
@ -1,9 +1,15 @@
|
|
|
|
|
use gettextrs::gettext; |
|
|
|
|
use gtk::{gio, glib, glib::clone, prelude::*, subclass::prelude::*}; |
|
|
|
|
|
|
|
|
|
use super::MembershipSubpageItem; |
|
|
|
|
use crate::{ |
|
|
|
|
components::{LoadingRow, LoadingState}, |
|
|
|
|
session::model::MemberList, |
|
|
|
|
utils::BoundObjectWeakRef, |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
mod imp { |
|
|
|
|
use std::cell::Cell; |
|
|
|
|
use std::cell::{Cell, RefCell}; |
|
|
|
|
|
|
|
|
|
use once_cell::{sync::Lazy, unsync::OnceCell}; |
|
|
|
|
|
|
|
|
|
@ -11,6 +17,8 @@ mod imp {
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Default)] |
|
|
|
|
pub struct ExtraLists { |
|
|
|
|
pub members: BoundObjectWeakRef<MemberList>, |
|
|
|
|
pub state: RefCell<Option<LoadingRow>>, |
|
|
|
|
pub invited: OnceCell<MembershipSubpageItem>, |
|
|
|
|
pub banned: OnceCell<MembershipSubpageItem>, |
|
|
|
|
pub invited_is_empty: Cell<bool>, |
|
|
|
|
@ -28,6 +36,9 @@ mod imp {
|
|
|
|
|
fn properties() -> &'static [glib::ParamSpec] { |
|
|
|
|
static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| { |
|
|
|
|
vec![ |
|
|
|
|
glib::ParamSpecObject::builder::<MemberList>("members") |
|
|
|
|
.construct_only() |
|
|
|
|
.build(), |
|
|
|
|
glib::ParamSpecObject::builder::<MembershipSubpageItem>("invited") |
|
|
|
|
.construct_only() |
|
|
|
|
.build(), |
|
|
|
|
@ -44,6 +55,7 @@ mod imp {
|
|
|
|
|
let obj = self.obj(); |
|
|
|
|
|
|
|
|
|
match pspec.name() { |
|
|
|
|
"members" => obj.set_members(value.get::<Option<MemberList>>().unwrap().as_ref()), |
|
|
|
|
"invited" => obj.set_invited(value.get().unwrap()), |
|
|
|
|
"banned" => obj.set_banned(value.get().unwrap()), |
|
|
|
|
_ => unimplemented!(), |
|
|
|
|
@ -54,6 +66,7 @@ mod imp {
|
|
|
|
|
let obj = self.obj(); |
|
|
|
|
|
|
|
|
|
match pspec.name() { |
|
|
|
|
"members" => obj.members().to_value(), |
|
|
|
|
"invited" => obj.invited().to_value(), |
|
|
|
|
"banned" => obj.banned().to_value(), |
|
|
|
|
_ => unimplemented!(), |
|
|
|
|
@ -78,6 +91,10 @@ mod imp {
|
|
|
|
|
self.invited_is_empty.set(invited_members.n_items() == 0); |
|
|
|
|
self.banned_is_empty.set(banned_members.n_items() == 0); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
fn dispose(&self) { |
|
|
|
|
self.members.disconnect_signals(); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
impl ListModelImpl for ExtraLists { |
|
|
|
|
@ -86,30 +103,32 @@ mod imp {
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
fn n_items(&self) -> u32 { |
|
|
|
|
let mut len = 0; |
|
|
|
|
|
|
|
|
|
if !self.invited_is_empty.get() { |
|
|
|
|
len += 1; |
|
|
|
|
} |
|
|
|
|
if !self.banned_is_empty.get() { |
|
|
|
|
len += 1; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
len |
|
|
|
|
self.state.borrow().is_some() as u32 |
|
|
|
|
+ (!self.invited_is_empty.get()) as u32 |
|
|
|
|
+ (!self.banned_is_empty.get()) as u32 |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
fn item(&self, position: u32) -> Option<glib::Object> { |
|
|
|
|
let has_invited = !self.invited_is_empty.get(); |
|
|
|
|
let has_banned = !self.banned_is_empty.get(); |
|
|
|
|
let mut position = position; |
|
|
|
|
|
|
|
|
|
if let Some(state_row) = &*self.state.borrow() { |
|
|
|
|
if position == 0 { |
|
|
|
|
return Some(state_row.clone().upcast()); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if position == 0 && has_invited { |
|
|
|
|
let invited = self.invited.get().unwrap(); |
|
|
|
|
return Some(invited.clone().upcast()); |
|
|
|
|
position -= 1; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if has_banned && ((position == 0 && !has_invited) || (position == 1 && has_invited)) { |
|
|
|
|
let banned = self.banned.get().unwrap(); |
|
|
|
|
return Some(banned.clone().upcast()); |
|
|
|
|
if !self.invited_is_empty.get() { |
|
|
|
|
if position == 0 { |
|
|
|
|
return self.invited.get().cloned().and_upcast(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
position -= 1; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if !self.banned_is_empty.get() && position == 0 { |
|
|
|
|
return self.banned.get().cloned().and_upcast(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
None |
|
|
|
|
@ -123,13 +142,74 @@ glib::wrapper! {
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
impl ExtraLists { |
|
|
|
|
pub fn new(invited: &MembershipSubpageItem, banned: &MembershipSubpageItem) -> Self { |
|
|
|
|
pub fn new( |
|
|
|
|
members: &MemberList, |
|
|
|
|
invited: &MembershipSubpageItem, |
|
|
|
|
banned: &MembershipSubpageItem, |
|
|
|
|
) -> Self { |
|
|
|
|
glib::Object::builder() |
|
|
|
|
.property("members", members) |
|
|
|
|
.property("invited", invited) |
|
|
|
|
.property("banned", banned) |
|
|
|
|
.build() |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
pub fn members(&self) -> Option<MemberList> { |
|
|
|
|
self.imp().members.obj() |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
fn set_members(&self, members: Option<&MemberList>) { |
|
|
|
|
let Some(members) = members else { |
|
|
|
|
// Ignore if there is no list.
|
|
|
|
|
return; |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
let imp = self.imp(); |
|
|
|
|
imp.members.disconnect_signals(); |
|
|
|
|
|
|
|
|
|
let signal_handler_ids = vec![members.connect_notify_local( |
|
|
|
|
Some("state"), |
|
|
|
|
clone!(@weak self as obj => move |members, _| { |
|
|
|
|
obj.update_loading_state(members.state()); |
|
|
|
|
}), |
|
|
|
|
)]; |
|
|
|
|
self.update_loading_state(members.state()); |
|
|
|
|
|
|
|
|
|
imp.members.set(members, signal_handler_ids); |
|
|
|
|
self.notify("members"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// Update this list for the given loading state.
|
|
|
|
|
fn update_loading_state(&self, state: LoadingState) { |
|
|
|
|
let imp = self.imp(); |
|
|
|
|
|
|
|
|
|
if state == LoadingState::Ready { |
|
|
|
|
if imp.state.take().is_some() { |
|
|
|
|
self.items_changed(0, 1, 0); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
let mut added = false; |
|
|
|
|
{ |
|
|
|
|
let mut state_row_borrow = imp.state.borrow_mut(); |
|
|
|
|
let state_row = state_row_borrow.get_or_insert_with(|| { |
|
|
|
|
added = true; |
|
|
|
|
LoadingRow::new() |
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
let error = (state == LoadingState::Error) |
|
|
|
|
.then(|| gettext("Could not load the full list of room members")); |
|
|
|
|
|
|
|
|
|
state_row.set_error(error.as_deref()); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if added { |
|
|
|
|
self.items_changed(0, 0, 1); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// The subpage item for invited members.
|
|
|
|
|
pub fn invited(&self) -> &MembershipSubpageItem { |
|
|
|
|
self.imp().invited.get().unwrap() |
|
|
|
|
@ -162,12 +242,13 @@ impl ExtraLists {
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
imp.invited_is_empty.set(is_empty); |
|
|
|
|
let position = imp.state.borrow().is_some() as u32; |
|
|
|
|
|
|
|
|
|
let added = if was_empty { 1 } else { 0 }; |
|
|
|
|
// If it is not added, it is removed.
|
|
|
|
|
let removed = 1 - added; |
|
|
|
|
let added = was_empty as u32; |
|
|
|
|
let removed = (!was_empty) as u32; |
|
|
|
|
|
|
|
|
|
self.items_changed(0, removed, added); |
|
|
|
|
self.items_changed(position, removed, added); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
fn update_banned(&self) { |
|
|
|
|
@ -183,11 +264,11 @@ impl ExtraLists {
|
|
|
|
|
|
|
|
|
|
imp.banned_is_empty.set(is_empty); |
|
|
|
|
|
|
|
|
|
let position = if imp.invited_is_empty.get() { 0 } else { 1 }; |
|
|
|
|
let position = imp.state.borrow().is_some() as u32 + (!imp.invited_is_empty.get()) as u32; |
|
|
|
|
|
|
|
|
|
let added = if was_empty { 1 } else { 0 }; |
|
|
|
|
// If it is not added, it is removed.
|
|
|
|
|
let removed = 1 - added; |
|
|
|
|
let added = was_empty as u32; |
|
|
|
|
let removed = (!was_empty) as u32; |
|
|
|
|
|
|
|
|
|
self.items_changed(position, removed, added); |
|
|
|
|
} |
|
|
|
|
|