use gettextrs::gettext;
use gtk::{gio, glib, glib::clone, prelude::*, subclass::prelude::*};
use super::MembershipSubpageItem;
use crate::{
components::LoadingRow,
session::model::MemberList,
utils::{BoundObjectWeakRef, LoadingState},
};
mod imp {
use std::cell::{Cell, OnceCell, RefCell};
use super::*;
#[derive(Debug, Default, glib::Properties)]
#[properties(wrapper_type = super::ExtraLists)]
pub struct ExtraLists {
#[property(get, set = Self::set_members, construct_only)]
pub members: BoundObjectWeakRef<MemberList>,
pub state: RefCell<Option<LoadingRow>>,
#[property(get, construct_only)]
pub invited: OnceCell<MembershipSubpageItem>,
#[property(get, construct_only)]
pub banned: OnceCell<MembershipSubpageItem>,
pub invited_is_empty: Cell<bool>,
pub banned_is_empty: Cell<bool>,
}
#[glib::object_subclass]
impl ObjectSubclass for ExtraLists {
const NAME: &'static str = "ContentMembersExtraLists";
type Type = super::ExtraLists;
type Interfaces = (gio::ListModel,);
}
#[glib::derived_properties]
impl ObjectImpl for ExtraLists {
fn constructed(&self) {
self.parent_constructed();
let obj = self.obj();
let invited_members = obj.invited().model();
let banned_members = obj.banned().model();
invited_members.connect_items_changed(clone!(
#[weak]
obj,
move |_, _, _, _| {
obj.update_invited();
}
));
banned_members.connect_items_changed(clone!(
#[weak]
obj,
move |_, _, _, _| {
obj.update_banned();
}
));
self.invited_is_empty.set(invited_members.n_items() == 0);
self.banned_is_empty.set(banned_members.n_items() == 0);
}
}
impl ListModelImpl for ExtraLists {
fn item_type(&self) -> glib::Type {
glib::Object::static_type()
}
fn n_items(&self) -> u32 {
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 mut position = position;
if let Some(state_row) = &*self.state.borrow() {
if position == 0 {
return Some(state_row.clone().upcast());
}
position -= 1;
}
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
}
}
impl ExtraLists {
fn set_members(&self, members: MemberList) {
let obj = self.obj();
self.members.disconnect_signals();
let signal_handler_ids = vec![members.connect_state_notify(clone!(
#[weak]
obj,
move |members| {
obj.update_loading_state(members.state());
}
))];
obj.update_loading_state(members.state());
self.members.set(&members, signal_handler_ids);
obj.notify_members();
}
}
}
glib::wrapper! {
pub struct ExtraLists(ObjectSubclass<imp::ExtraLists>)
@implements gio::ListModel;
}
impl ExtraLists {
pub fn new(
members: &MemberList,
invited: &MembershipSubpageItem,
banned: &MembershipSubpageItem,
) -> Self {
glib::Object::builder()
.property("members", members)
.property("invited", invited)
.property("banned", banned)
.build()
}
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);
}
}
fn update_invited(&self) {
let imp = self.imp();
let was_empty = imp.invited_is_empty.get();
let is_empty = self.invited().model().n_items() == 0;
if was_empty == is_empty {
return;
}
imp.invited_is_empty.set(is_empty);
let position = imp.state.borrow().is_some() as u32;
let added = was_empty as u32;
let removed = (!was_empty) as u32;
self.items_changed(position, removed, added);
}
fn update_banned(&self) {
let imp = self.imp();
let was_empty = imp.banned_is_empty.get();
let is_empty = self.banned().model().n_items() == 0;
if was_empty == is_empty {
return;
}
imp.banned_is_empty.set(is_empty);
let position = imp.state.borrow().is_some() as u32 + (!imp.invited_is_empty.get()) as u32;
let added = was_empty as u32;
let removed = (!was_empty) as u32;
self.items_changed(position, removed, added);
}
}