fractal/session/view/content/room_details/permissions/
privileged_members.rs1use std::collections::BTreeMap;
2
3use adw::subclass::prelude::*;
4use gtk::{gio, glib, glib::clone, prelude::*};
5use indexmap::IndexMap;
6use ruma::{Int, OwnedUserId};
7
8use super::MemberPowerLevel;
9use crate::{
10 session::model::{Permissions, PowerLevel, User},
11 utils::BoundObjectWeakRef,
12};
13
14mod imp {
15 use std::cell::{Cell, RefCell};
16
17 use super::*;
18
19 #[derive(Debug, Default, glib::Properties)]
20 #[properties(wrapper_type = super::PrivilegedMembers)]
21 pub struct PrivilegedMembers {
22 pub(super) list: RefCell<IndexMap<OwnedUserId, MemberPowerLevel>>,
24 #[property(get, set = Self::set_permissions, construct_only)]
26 permissions: BoundObjectWeakRef<Permissions>,
27 #[property(get)]
29 changed: Cell<bool>,
30 }
31
32 #[glib::object_subclass]
33 impl ObjectSubclass for PrivilegedMembers {
34 const NAME: &'static str = "RoomDetailsPermissionsPrivilegedMembers";
35 type Type = super::PrivilegedMembers;
36 type Interfaces = (gio::ListModel,);
37 }
38
39 #[glib::derived_properties]
40 impl ObjectImpl for PrivilegedMembers {}
41
42 impl ListModelImpl for PrivilegedMembers {
43 fn item_type(&self) -> glib::Type {
44 MemberPowerLevel::static_type()
45 }
46
47 fn n_items(&self) -> u32 {
48 self.list.borrow().len() as u32
49 }
50
51 fn item(&self, position: u32) -> Option<glib::Object> {
52 self.list
53 .borrow()
54 .get_index(position as usize)
55 .map(|(_, member)| member.clone().upcast())
56 }
57 }
58
59 impl PrivilegedMembers {
60 fn set_permissions(&self, permissions: &Permissions) {
62 let changed_handler = permissions.connect_changed(clone!(
63 #[weak(rename_to = imp)]
64 self,
65 move |_| {
66 imp.update();
67 }
68 ));
69 self.permissions.set(permissions, vec![changed_handler]);
70
71 self.update();
72 }
73
74 fn update(&self) {
76 let Some(permissions) = self.permissions.obj() else {
77 return;
78 };
79 let Some(room) = permissions.room() else {
80 return;
81 };
82 let Some(session) = room.session() else {
83 return;
84 };
85
86 let members = room.get_or_create_members();
87 let mut users = permissions.power_levels().users;
88
89 let mut removed_users = Vec::new();
90 {
91 for user_id in self.list.borrow().keys() {
92 if !users.contains_key(user_id) {
93 removed_users.push(user_id.clone());
94 continue;
95 }
96
97 users.remove(user_id);
99 }
100 }
101
102 for user_id in removed_users {
103 self.remove_member(&user_id);
104 }
105
106 let mut new_handlers = Vec::with_capacity(users.len());
108 let new_members = users.into_keys().map(|user_id| {
109 let user = members
110 .get(&user_id)
111 .and_upcast::<User>()
112 .unwrap_or_else(|| {
113 session.remote_cache().user(user_id.clone()).upcast()
115 });
116 let member = MemberPowerLevel::new(&user, &permissions);
117
118 let handler = member.connect_power_level_notify(clone!(
119 #[weak(rename_to = imp)]
120 self,
121 move |_| {
122 imp.update_changed();
123 }
124 ));
125 new_handlers.push(handler);
126
127 (user_id, member)
128 });
129
130 self.add_members(new_members);
131 }
132
133 fn remove_member(&self, user_id: &OwnedUserId) {
135 let Some((pos, ..)) = self.list.borrow_mut().shift_remove_full(user_id) else {
136 return;
137 };
138
139 self.obj().items_changed(pos as u32, 1, 0);
140 }
141
142 pub(super) fn add_members(
144 &self,
145 members: impl ExactSizeIterator<Item = (OwnedUserId, MemberPowerLevel)>,
146 ) {
147 let pos = self.n_items();
148 let added = members.len() as u32;
149
150 self.list.borrow_mut().extend(members);
151
152 self.update_changed();
153 self.obj().items_changed(pos, 0, added);
154 }
155
156 fn update_changed(&self) {
158 let changed = self.compute_changed();
159
160 if self.changed.get() == changed {
161 return;
162 }
163
164 self.changed.set(changed);
165 self.obj().notify_changed();
166 }
167
168 fn compute_changed(&self) -> bool {
170 let Some(permissions) = self.permissions.obj() else {
171 return false;
172 };
173
174 let users = permissions.power_levels().users;
175 let list = self.list.borrow();
176
177 if users.len() != list.len() {
178 return true;
179 }
180
181 for (user_id, member) in list.iter() {
182 let Some(pl) = users.get(user_id) else {
183 return true;
185 };
186
187 if member.power_level() != PowerLevel::from(*pl) {
188 return true;
189 }
190 }
191
192 false
193 }
194 }
195}
196
197glib::wrapper! {
198 pub struct PrivilegedMembers(ObjectSubclass<imp::PrivilegedMembers>)
200 @implements gio::ListModel;
201}
202
203impl PrivilegedMembers {
204 pub fn new(permissions: &Permissions) -> Self {
206 glib::Object::builder()
207 .property("permissions", permissions)
208 .build()
209 }
210
211 pub(crate) fn add_members(
213 &self,
214 members: impl ExactSizeIterator<Item = (OwnedUserId, MemberPowerLevel)>,
215 ) {
216 let imp = self.imp();
217
218 let mut new_members = Vec::with_capacity(members.len());
219
220 {
221 let list = imp.list.borrow();
222 for (user_id, new_member) in members {
223 if let Some(member) = list.get(&user_id) {
224 member.set_power_level(new_member.power_level());
225 } else {
226 new_members.push((user_id, new_member));
227 }
228 }
229 }
230
231 if !new_members.is_empty() {
232 self.imp().add_members(new_members.into_iter());
233 }
234 }
235
236 pub(crate) fn collect(&self) -> BTreeMap<OwnedUserId, Int> {
238 self.imp()
239 .list
240 .borrow()
241 .values()
242 .filter_map(MemberPowerLevel::to_parts)
243 .collect()
244 }
245}