fractal/session/view/content/room_details/permissions/
permissions_subpage.rs

1use adw::{prelude::*, subclass::prelude::*};
2use gettextrs::gettext;
3use gtk::{CompositeTemplate, glib, glib::clone};
4use ruma::{
5    Int,
6    events::{
7        StateEventType, TimelineEventType,
8        room::power_levels::{PowerLevelAction, RoomPowerLevels},
9    },
10};
11
12use super::{PermissionsAddMembersSubpage, PermissionsMembersSubpage, PrivilegedMembers};
13use crate::{
14    components::{ButtonCountRow, LoadingButton, PowerLevelSelectionRow},
15    session::model::{Permissions, PowerLevel},
16    toast,
17    utils::BoundObjectWeakRef,
18};
19
20mod imp {
21    use std::cell::{Cell, OnceCell};
22
23    use glib::subclass::InitializingObject;
24
25    use super::*;
26
27    #[derive(Debug, Default, CompositeTemplate, glib::Properties)]
28    #[template(
29        resource = "/org/gnome/Fractal/ui/session/view/content/room_details/permissions/permissions_subpage.ui"
30    )]
31    #[properties(wrapper_type = super::PermissionsSubpage)]
32    pub struct PermissionsSubpage {
33        #[template_child]
34        save_button: TemplateChild<LoadingButton>,
35        #[template_child]
36        messages_row: TemplateChild<PowerLevelSelectionRow>,
37        #[template_child]
38        redact_own_row: TemplateChild<PowerLevelSelectionRow>,
39        #[template_child]
40        redact_others_row: TemplateChild<PowerLevelSelectionRow>,
41        #[template_child]
42        notify_room_row: TemplateChild<PowerLevelSelectionRow>,
43        #[template_child]
44        state_row: TemplateChild<PowerLevelSelectionRow>,
45        #[template_child]
46        name_row: TemplateChild<PowerLevelSelectionRow>,
47        #[template_child]
48        topic_row: TemplateChild<PowerLevelSelectionRow>,
49        #[template_child]
50        avatar_row: TemplateChild<PowerLevelSelectionRow>,
51        #[template_child]
52        aliases_row: TemplateChild<PowerLevelSelectionRow>,
53        #[template_child]
54        history_visibility_row: TemplateChild<PowerLevelSelectionRow>,
55        #[template_child]
56        encryption_row: TemplateChild<PowerLevelSelectionRow>,
57        #[template_child]
58        power_levels_row: TemplateChild<PowerLevelSelectionRow>,
59        #[template_child]
60        server_acl_row: TemplateChild<PowerLevelSelectionRow>,
61        #[template_child]
62        upgrade_row: TemplateChild<PowerLevelSelectionRow>,
63        #[template_child]
64        invite_row: TemplateChild<PowerLevelSelectionRow>,
65        #[template_child]
66        kick_row: TemplateChild<PowerLevelSelectionRow>,
67        #[template_child]
68        ban_row: TemplateChild<PowerLevelSelectionRow>,
69        #[template_child]
70        members_default_spin_row: TemplateChild<adw::SpinRow>,
71        #[template_child]
72        members_default_adjustment: TemplateChild<gtk::Adjustment>,
73        #[template_child]
74        members_default_text_row: TemplateChild<adw::ActionRow>,
75        #[template_child]
76        members_default_label: TemplateChild<gtk::Label>,
77        #[template_child]
78        members_privileged_button: TemplateChild<ButtonCountRow>,
79        /// The subpage to view and edit members with custom power levels.
80        #[template_child]
81        members_subpage: TemplateChild<PermissionsMembersSubpage>,
82        /// The subpage to add members with custom power levels.
83        #[template_child]
84        add_members_subpage: TemplateChild<PermissionsAddMembersSubpage>,
85        /// The permissions to watch.
86        #[property(get, set = Self::set_permissions, construct_only)]
87        permissions: BoundObjectWeakRef<Permissions>,
88        /// Whether our own user can change the power levels in this room.
89        #[property(get)]
90        editable: Cell<bool>,
91        /// Whether the permissions were changed by the user.
92        #[property(get)]
93        changed: Cell<bool>,
94        /// The list of members with custom power levels.
95        #[property(get)]
96        privileged_members: OnceCell<PrivilegedMembers>,
97        /// Whether an update is in progress.
98        ///
99        /// Avoids to call `Self::update_changed()` too often when several rows
100        /// might be changed at once.
101        update_in_progress: Cell<bool>,
102    }
103
104    #[glib::object_subclass]
105    impl ObjectSubclass for PermissionsSubpage {
106        const NAME: &'static str = "RoomDetailsPermissionsSubpage";
107        type Type = super::PermissionsSubpage;
108        type ParentType = adw::NavigationPage;
109
110        fn class_init(klass: &mut Self::Class) {
111            Self::bind_template(klass);
112            Self::bind_template_callbacks(klass);
113        }
114
115        fn instance_init(obj: &InitializingObject<Self>) {
116            obj.init_template();
117        }
118    }
119
120    #[glib::derived_properties]
121    impl ObjectImpl for PermissionsSubpage {}
122
123    impl WidgetImpl for PermissionsSubpage {}
124    impl NavigationPageImpl for PermissionsSubpage {}
125
126    #[gtk::template_callbacks]
127    impl PermissionsSubpage {
128        /// Set the permissions to watch.
129        fn set_permissions(&self, permissions: &Permissions) {
130            let changed_handler = permissions.connect_changed(clone!(
131                #[weak(rename_to = imp)]
132                self,
133                move |_| {
134                    imp.update();
135                }
136            ));
137
138            self.permissions.set(permissions, vec![changed_handler]);
139
140            let privileged_members = PrivilegedMembers::new(permissions);
141            self.privileged_members
142                .set(privileged_members.clone())
143                .unwrap();
144
145            privileged_members.connect_changed_notify(clone!(
146                #[weak(rename_to = imp)]
147                self,
148                move |_| {
149                    imp.update_changed();
150                }
151            ));
152
153            self.members_subpage
154                .set_list(Some(privileged_members.clone()));
155
156            self.add_members_subpage.set_permissions(Some(permissions));
157            self.add_members_subpage
158                .set_privileged_members(Some(privileged_members));
159
160            self.update();
161        }
162
163        /// The list of members with custom power levels.
164        fn privileged_members(&self) -> &PrivilegedMembers {
165            self.privileged_members
166                .get()
167                .expect("privileged members should be initialized")
168        }
169
170        /// Update all the permissions.
171        fn update(&self) {
172            let Some(permissions) = self.permissions.obj() else {
173                return;
174            };
175
176            self.update_in_progress.set(true);
177
178            let can_change = permissions
179                .is_allowed_to(PowerLevelAction::SendState(StateEventType::RoomPowerLevels));
180            self.set_editable(can_change);
181
182            self.update_room_actions();
183            self.update_member_actions();
184            self.update_members_power_levels();
185
186            self.save_button.set_is_loading(false);
187
188            self.update_in_progress.set(false);
189            self.update_changed();
190        }
191
192        /// Set whether our own user can change the power levels in this room.
193        fn set_editable(&self, editable: bool) {
194            if self.editable.get() == editable {
195                return;
196            }
197
198            self.editable.set(editable);
199            self.obj().notify_editable();
200        }
201
202        /// Update whether the permissions were changed by the user.
203        fn update_changed(&self) {
204            if self.update_in_progress.get() {
205                // Do not update, it will be called when all updates are done.
206                return;
207            }
208
209            let changed = self.compute_changed();
210
211            if self.changed.get() == changed {
212                return;
213            }
214
215            self.changed.set(changed);
216            self.obj().notify_changed();
217        }
218
219        /// Compute whether the user changed the permissions.
220        #[allow(clippy::too_many_lines)]
221        fn compute_changed(&self) -> bool {
222            let Some(privileged_members) = self.privileged_members.get() else {
223                return false;
224            };
225
226            if privileged_members.changed() {
227                return true;
228            }
229
230            let Some(permissions) = self.permissions.obj() else {
231                return false;
232            };
233            let power_levels = permissions.power_levels();
234
235            let events_default = PowerLevel::from(power_levels.events_default);
236            if self.messages_row.selected_power_level() != events_default {
237                return true;
238            }
239
240            let redact_own = event_power_level(
241                &power_levels,
242                &TimelineEventType::RoomRedaction,
243                events_default,
244            );
245            if self.redact_own_row.selected_power_level() != redact_own {
246                return true;
247            }
248
249            let redact_others = redact_own.max(power_levels.redact.into());
250            if self.redact_others_row.selected_power_level() != redact_others {
251                return true;
252            }
253
254            let notify_room = PowerLevel::from(power_levels.notifications.room);
255            if self.notify_room_row.selected_power_level() != notify_room {
256                return true;
257            }
258
259            let state_default = PowerLevel::from(power_levels.state_default);
260            if self.state_row.selected_power_level() != state_default {
261                return true;
262            }
263
264            let name =
265                event_power_level(&power_levels, &TimelineEventType::RoomName, state_default);
266            if self.name_row.selected_power_level() != name {
267                return true;
268            }
269
270            let topic =
271                event_power_level(&power_levels, &TimelineEventType::RoomTopic, state_default);
272            if self.topic_row.selected_power_level() != topic {
273                return true;
274            }
275
276            let avatar =
277                event_power_level(&power_levels, &TimelineEventType::RoomAvatar, state_default);
278            if self.avatar_row.selected_power_level() != avatar {
279                return true;
280            }
281
282            let aliases = event_power_level(
283                &power_levels,
284                &TimelineEventType::RoomCanonicalAlias,
285                state_default,
286            );
287            if self.aliases_row.selected_power_level() != aliases {
288                return true;
289            }
290
291            let history_visibility = event_power_level(
292                &power_levels,
293                &TimelineEventType::RoomHistoryVisibility,
294                state_default,
295            );
296            if self.history_visibility_row.selected_power_level() != history_visibility {
297                return true;
298            }
299
300            let encryption = event_power_level(
301                &power_levels,
302                &TimelineEventType::RoomEncryption,
303                state_default,
304            );
305            if self.encryption_row.selected_power_level() != encryption {
306                return true;
307            }
308
309            let pl = event_power_level(
310                &power_levels,
311                &TimelineEventType::RoomPowerLevels,
312                state_default,
313            );
314            if self.power_levels_row.selected_power_level() != pl {
315                return true;
316            }
317
318            let server_acl = event_power_level(
319                &power_levels,
320                &TimelineEventType::RoomServerAcl,
321                state_default,
322            );
323            if self.server_acl_row.selected_power_level() != server_acl {
324                return true;
325            }
326
327            let upgrade = event_power_level(
328                &power_levels,
329                &TimelineEventType::RoomTombstone,
330                state_default,
331            );
332            if self.upgrade_row.selected_power_level() != upgrade {
333                return true;
334            }
335
336            let invite = PowerLevel::from(power_levels.invite);
337            if self.invite_row.selected_power_level() != invite {
338                return true;
339            }
340
341            let kick = PowerLevel::from(power_levels.kick);
342            if self.kick_row.selected_power_level() != kick {
343                return true;
344            }
345
346            let ban = PowerLevel::from(power_levels.ban);
347            if self.ban_row.selected_power_level() != ban {
348                return true;
349            }
350
351            let default_pl = PowerLevel::from(power_levels.users_default);
352            self.members_default_adjustment.value() as PowerLevel != default_pl
353        }
354
355        /// Update the room actions section.
356        fn update_room_actions(&self) {
357            let Some(permissions) = self.permissions.obj() else {
358                return;
359            };
360
361            let editable = self.editable.get();
362            let power_levels = permissions.power_levels();
363            let own_pl = permissions.own_power_level();
364
365            let events_default = PowerLevel::from(power_levels.events_default);
366            self.messages_row.set_selected_power_level(events_default);
367            self.messages_row
368                .set_read_only(!editable || own_pl < events_default);
369
370            let redact_own = event_power_level(
371                &power_levels,
372                &TimelineEventType::RoomRedaction,
373                events_default,
374            );
375            self.redact_own_row.set_selected_power_level(redact_own);
376            self.redact_own_row
377                .set_read_only(!editable || own_pl < redact_own);
378
379            let redact_others = redact_own.max(power_levels.redact.into());
380            self.redact_others_row
381                .set_selected_power_level(redact_others);
382            self.redact_others_row
383                .set_read_only(!editable || own_pl < redact_others);
384
385            let notify_room = PowerLevel::from(power_levels.notifications.room);
386            self.notify_room_row.set_selected_power_level(notify_room);
387            self.notify_room_row
388                .set_read_only(!editable || own_pl < notify_room);
389
390            let state_default = PowerLevel::from(power_levels.state_default);
391            self.state_row.set_selected_power_level(state_default);
392            self.state_row
393                .set_read_only(!editable || own_pl < state_default);
394
395            self.update_state_rows();
396        }
397
398        /// Update the rows about state events, except the default one.
399        fn update_state_rows(&self) {
400            let Some(permissions) = self.permissions.obj() else {
401                return;
402            };
403
404            let editable = self.editable.get();
405            let power_levels = permissions.power_levels();
406            let own_pl = permissions.own_power_level();
407            let state_default = self.state_row.selected_power_level();
408
409            let name =
410                event_power_level(&power_levels, &TimelineEventType::RoomName, state_default);
411            self.name_row.set_selected_power_level(name);
412            self.name_row.set_read_only(!editable || own_pl < name);
413
414            let topic =
415                event_power_level(&power_levels, &TimelineEventType::RoomTopic, state_default);
416            self.topic_row.set_selected_power_level(topic);
417            self.topic_row.set_read_only(!editable || own_pl < topic);
418
419            let avatar =
420                event_power_level(&power_levels, &TimelineEventType::RoomAvatar, state_default);
421            self.avatar_row.set_selected_power_level(avatar);
422            self.avatar_row.set_read_only(!editable || own_pl < avatar);
423
424            let aliases = event_power_level(
425                &power_levels,
426                &TimelineEventType::RoomCanonicalAlias,
427                state_default,
428            );
429            self.aliases_row.set_selected_power_level(aliases);
430            self.aliases_row
431                .set_read_only(!editable || own_pl < aliases);
432
433            let history_visibility = event_power_level(
434                &power_levels,
435                &TimelineEventType::RoomHistoryVisibility,
436                state_default,
437            );
438            self.history_visibility_row
439                .set_selected_power_level(history_visibility);
440            self.history_visibility_row
441                .set_read_only(!editable || own_pl < history_visibility);
442
443            let encryption = event_power_level(
444                &power_levels,
445                &TimelineEventType::RoomEncryption,
446                state_default,
447            );
448            self.encryption_row.set_selected_power_level(encryption);
449            self.encryption_row
450                .set_read_only(!editable || own_pl < encryption);
451
452            let pl = event_power_level(
453                &power_levels,
454                &TimelineEventType::RoomPowerLevels,
455                state_default,
456            );
457            self.power_levels_row.set_selected_power_level(pl);
458            self.power_levels_row
459                .set_read_only(!editable || own_pl < pl);
460
461            let server_acl = event_power_level(
462                &power_levels,
463                &TimelineEventType::RoomServerAcl,
464                state_default,
465            );
466            self.server_acl_row.set_selected_power_level(server_acl);
467            self.server_acl_row
468                .set_read_only(!editable || own_pl < server_acl);
469
470            let upgrade = event_power_level(
471                &power_levels,
472                &TimelineEventType::RoomTombstone,
473                state_default,
474            );
475            self.upgrade_row.set_selected_power_level(upgrade);
476            self.upgrade_row
477                .set_read_only(!editable || own_pl < upgrade);
478        }
479
480        /// Update the member actions section.
481        fn update_member_actions(&self) {
482            let Some(permissions) = self.permissions.obj() else {
483                return;
484            };
485
486            let editable = self.editable.get();
487            let power_levels = permissions.power_levels();
488            let own_pl = permissions.own_power_level();
489
490            let invite = PowerLevel::from(power_levels.invite);
491            self.invite_row.set_selected_power_level(invite);
492            self.invite_row.set_read_only(!editable || own_pl < invite);
493
494            let kick = PowerLevel::from(power_levels.kick);
495            self.kick_row.set_selected_power_level(kick);
496            self.kick_row.set_read_only(!editable || own_pl < kick);
497
498            let ban = PowerLevel::from(power_levels.ban);
499            self.ban_row.set_selected_power_level(ban);
500            self.ban_row.set_read_only(!editable || own_pl < ban);
501        }
502
503        /// Update the member roles section.
504        fn update_members_power_levels(&self) {
505            let Some(permissions) = self.permissions.obj() else {
506                return;
507            };
508            let power_levels = permissions.power_levels();
509
510            let default_pl = PowerLevel::from(power_levels.users_default);
511            self.members_default_adjustment.set_value(default_pl as f64);
512            self.members_default_label
513                .set_label(&default_pl.to_string());
514
515            // We cannot change any required power level to something higher than ours.
516            let own_pl = permissions.own_power_level();
517            let max = default_pl.max(own_pl);
518            self.members_default_adjustment.set_upper(max as f64);
519
520            let editable = self.editable.get();
521            let can_change_default = editable && own_pl >= default_pl;
522            self.members_default_spin_row
523                .set_visible(can_change_default);
524            self.members_default_text_row
525                .set_visible(!can_change_default);
526
527            self.members_privileged_button
528                .set_count(power_levels.users.len().to_string());
529        }
530
531        /// Go back to the previous page in the room details.
532        ///
533        /// If there are changes in the page, ask the user to confirm.
534        #[template_callback]
535        async fn go_back(&self) {
536            let obj = self.obj();
537            let mut reset_after = false;
538
539            if self.changed.get() {
540                let title = gettext("Save Changes?");
541                let description = gettext(
542                    "This page contains unsaved changes. Changes which are not saved will be lost.",
543                );
544                let dialog = adw::AlertDialog::builder()
545                    .title(title)
546                    .body(description)
547                    .default_response("cancel")
548                    .build();
549
550                dialog.add_responses(&[
551                    ("cancel", &gettext("Cancel")),
552                    ("discard", &gettext("Discard")),
553                    ("save", &gettext("Save")),
554                ]);
555                dialog.set_response_appearance("discard", adw::ResponseAppearance::Destructive);
556                dialog.set_response_appearance("save", adw::ResponseAppearance::Suggested);
557
558                match dialog.choose_future(&*obj).await.as_str() {
559                    "discard" => {
560                        reset_after = true;
561                    }
562                    "save" => {
563                        self.save().await;
564                    }
565                    _ => {
566                        return;
567                    }
568                }
569            }
570
571            let _ = obj.activate_action("navigation.pop", None);
572
573            if reset_after {
574                self.update();
575            }
576        }
577
578        /// Save the changes of this page.
579        #[template_callback]
580        async fn save(&self) {
581            if !self.compute_changed() {
582                return;
583            }
584
585            let Some(permissions) = self.permissions.obj() else {
586                return;
587            };
588
589            self.save_button.set_is_loading(true);
590
591            let Some(power_levels) = self.collect_power_levels() else {
592                return;
593            };
594
595            if permissions.set_power_levels(power_levels).await.is_err() {
596                toast!(self.obj(), gettext("Could not save permissions"));
597                self.save_button.set_is_loading(false);
598            }
599        }
600
601        /// Collect the current power levels.
602        ///
603        /// Returns `None` if the permissions could not be upgraded.
604        fn collect_power_levels(&self) -> Option<RoomPowerLevels> {
605            let permissions = self.permissions.obj()?;
606
607            let mut power_levels = permissions.power_levels();
608
609            let events_default = self.messages_row.selected_power_level();
610            power_levels.events_default = Int::new_saturating(events_default);
611
612            let mut redact_own = self.redact_own_row.selected_power_level();
613            let redact_others = self.redact_others_row.selected_power_level();
614
615            // redact_own cannot be higher than redact_others because redact_others depends
616            // also on redact_own.
617            redact_own = redact_own.min(redact_others);
618            set_event_power_level(
619                &mut power_levels,
620                TimelineEventType::RoomRedaction,
621                redact_own,
622                events_default,
623            );
624
625            power_levels.redact = Int::new_saturating(redact_others);
626
627            let notify_room = self.notify_room_row.selected_power_level();
628            power_levels.notifications.room = Int::new_saturating(notify_room);
629
630            let state_default = self.state_row.selected_power_level();
631            power_levels.state_default = Int::new_saturating(state_default);
632
633            let name = self.name_row.selected_power_level();
634            set_event_power_level(
635                &mut power_levels,
636                TimelineEventType::RoomName,
637                name,
638                state_default,
639            );
640
641            let topic = self.topic_row.selected_power_level();
642            set_event_power_level(
643                &mut power_levels,
644                TimelineEventType::RoomTopic,
645                topic,
646                state_default,
647            );
648
649            let avatar = self.avatar_row.selected_power_level();
650            set_event_power_level(
651                &mut power_levels,
652                TimelineEventType::RoomAvatar,
653                avatar,
654                state_default,
655            );
656
657            let aliases = self.aliases_row.selected_power_level();
658            set_event_power_level(
659                &mut power_levels,
660                TimelineEventType::RoomCanonicalAlias,
661                aliases,
662                state_default,
663            );
664
665            let history_visibility = self.history_visibility_row.selected_power_level();
666            set_event_power_level(
667                &mut power_levels,
668                TimelineEventType::RoomHistoryVisibility,
669                history_visibility,
670                state_default,
671            );
672
673            let encryption = self.encryption_row.selected_power_level();
674            set_event_power_level(
675                &mut power_levels,
676                TimelineEventType::RoomEncryption,
677                encryption,
678                state_default,
679            );
680
681            let pl = self.power_levels_row.selected_power_level();
682            set_event_power_level(
683                &mut power_levels,
684                TimelineEventType::RoomPowerLevels,
685                pl,
686                state_default,
687            );
688
689            let server_acl = self.server_acl_row.selected_power_level();
690            set_event_power_level(
691                &mut power_levels,
692                TimelineEventType::RoomServerAcl,
693                server_acl,
694                state_default,
695            );
696
697            let upgrade = self.upgrade_row.selected_power_level();
698            set_event_power_level(
699                &mut power_levels,
700                TimelineEventType::RoomTombstone,
701                upgrade,
702                state_default,
703            );
704
705            let invite = self.invite_row.selected_power_level();
706            power_levels.invite = Int::new_saturating(invite);
707
708            let kick = self.kick_row.selected_power_level();
709            power_levels.kick = Int::new_saturating(kick);
710
711            let ban = self.ban_row.selected_power_level();
712            power_levels.ban = Int::new_saturating(ban);
713
714            let default_pl = self.members_default_adjustment.value() as PowerLevel;
715            power_levels.users_default = Int::new_saturating(default_pl);
716
717            let privileged_members = self.privileged_members();
718            power_levels.users = privileged_members.collect();
719
720            Some(power_levels)
721        }
722
723        /// Handle when a value in the page has changed.
724        #[template_callback]
725        fn value_changed(&self) {
726            if self.update_in_progress.get() {
727                // No need to run checks.
728                return;
729            }
730
731            self.update_changed();
732        }
733
734        /// Handle when the redact_own row has changed.
735        #[template_callback]
736        fn redact_own_changed(&self) {
737            if self.update_in_progress.get() {
738                // No need to run checks.
739                return;
740            }
741
742            let redact_own = self.redact_own_row.selected_power_level();
743            let redact_others = self.redact_others_row.selected_power_level();
744
745            // redact_own cannot be higher than redact_others because redact_others depends
746            // also on redact_own.
747            if redact_others < redact_own {
748                self.update_in_progress.set(true);
749
750                self.redact_others_row.set_selected_power_level(redact_own);
751
752                self.update_in_progress.set(false);
753            }
754
755            self.update_changed();
756        }
757
758        /// Handle when the redact_others row has changed.
759        #[template_callback]
760        fn redact_others_changed(&self) {
761            if self.update_in_progress.get() {
762                // No need to run checks.
763                return;
764            }
765
766            let redact_own = self.redact_own_row.selected_power_level();
767            let redact_others = self.redact_others_row.selected_power_level();
768
769            // redact_own cannot be higher than redact_others because redact_others depends
770            // also on redact_own.
771            if redact_others < redact_own {
772                self.update_in_progress.set(true);
773
774                self.redact_own_row.set_selected_power_level(redact_others);
775
776                self.update_in_progress.set(false);
777            }
778
779            self.update_changed();
780        }
781
782        /// Handle when the state default has changed.
783        #[template_callback]
784        fn state_default_changed(&self) {
785            if self.update_in_progress.get() {
786                // No need to run checks.
787                return;
788            }
789
790            self.update_in_progress.set(true);
791
792            self.update_state_rows();
793
794            self.update_in_progress.set(false);
795            self.update_changed();
796        }
797    }
798}
799
800glib::wrapper! {
801    /// Subpage to view and change the permissions of a room.
802    pub struct PermissionsSubpage(ObjectSubclass<imp::PermissionsSubpage>)
803        @extends gtk::Widget, gtk::Window, adw::NavigationPage, @implements gtk::Accessible;
804}
805
806impl PermissionsSubpage {
807    pub fn new(permissions: &Permissions) -> Self {
808        glib::Object::builder()
809            .property("permissions", permissions)
810            .build()
811    }
812}
813
814/// Set the power level for the given event type in the given power levels.
815fn set_event_power_level(
816    power_levels: &mut RoomPowerLevels,
817    event_type: TimelineEventType,
818    value: PowerLevel,
819    default: PowerLevel,
820) {
821    if value == default {
822        power_levels.events.remove(&event_type);
823    } else {
824        power_levels
825            .events
826            .insert(event_type, Int::new_saturating(value));
827    }
828}
829
830/// Get the necessary power level for the given event type in the given power
831/// levels.
832fn event_power_level(
833    power_levels: &RoomPowerLevels,
834    event_type: &TimelineEventType,
835    default: i64,
836) -> i64 {
837    power_levels
838        .events
839        .get(event_type)
840        .copied()
841        .map_or(default, Into::into)
842}