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

1use adw::subclass::prelude::*;
2use gtk::{glib, glib::clone, prelude::*};
3use ruma::{Int, OwnedUserId, events::room::power_levels::PowerLevelUserAction};
4
5use crate::{
6    prelude::*,
7    session::model::{MemberRole, POWER_LEVEL_MAX, POWER_LEVEL_MIN, Permissions, PowerLevel, User},
8    utils::BoundObjectWeakRef,
9};
10
11mod imp {
12    use std::cell::{Cell, OnceCell};
13
14    use super::*;
15
16    #[derive(Debug, Default, glib::Properties)]
17    #[properties(wrapper_type = super::MemberPowerLevel)]
18    pub struct MemberPowerLevel {
19        /// The permissions to watch.
20        #[property(get, set = Self::set_permissions, construct_only)]
21        permissions: BoundObjectWeakRef<Permissions>,
22        /// The room member or remote user.
23        #[property(get, construct_only)]
24        user: OnceCell<User>,
25        /// The wanted power level of the member.
26        ///
27        /// Initially, it should be the same as the member's, but can change
28        /// independently.
29        #[property(get, set = Self::set_power_level, explicit_notify,  minimum = POWER_LEVEL_MIN, maximum = POWER_LEVEL_MAX)]
30        power_level: Cell<PowerLevel>,
31        /// The wanted role of the member.
32        #[property(get, builder(MemberRole::default()))]
33        role: Cell<MemberRole>,
34        /// Whether this member's power level can be edited.
35        #[property(get)]
36        editable: Cell<bool>,
37    }
38
39    #[glib::object_subclass]
40    impl ObjectSubclass for MemberPowerLevel {
41        const NAME: &'static str = "RoomDetailsPermissionsMemberPowerLevel";
42        type Type = super::MemberPowerLevel;
43    }
44
45    #[glib::derived_properties]
46    impl ObjectImpl for MemberPowerLevel {
47        fn constructed(&self) {
48            self.parent_constructed();
49
50            self.update_power_level();
51            self.update_role();
52            self.update_editable();
53        }
54    }
55
56    impl MemberPowerLevel {
57        /// Set the room member.
58        fn set_permissions(&self, permissions: &Permissions) {
59            let changed_handler = permissions.connect_changed(clone!(
60                #[weak(rename_to = imp)]
61                self,
62                move |_| {
63                    imp.update_power_level();
64                    imp.update_role();
65                    imp.update_editable();
66                }
67            ));
68            self.permissions.set(permissions, vec![changed_handler]);
69        }
70
71        /// Update the wanted power level of the member.
72        fn update_power_level(&self) {
73            let Some(user) = self.user.get() else {
74                return;
75            };
76            let Some(permissions) = self.permissions.obj() else {
77                return;
78            };
79
80            self.set_power_level(permissions.user_power_level(user.user_id()));
81        }
82
83        /// Set the wanted power level of the member.
84        fn set_power_level(&self, power_level: PowerLevel) {
85            if self.power_level.get() == power_level {
86                return;
87            }
88
89            self.power_level.set(power_level);
90            self.update_role();
91            self.obj().notify_power_level();
92        }
93
94        /// Update the wanted role of the member.
95        fn update_role(&self) {
96            let Some(permissions) = self.permissions.obj() else {
97                return;
98            };
99
100            let role = permissions.role(self.power_level.get());
101
102            if self.role.get() == role {
103                return;
104            }
105
106            self.role.set(role);
107            self.obj().notify_role();
108        }
109
110        /// Update whether this member's power level can be edited.
111        fn update_editable(&self) {
112            let Some(user) = self.user.get() else {
113                return;
114            };
115            let Some(permissions) = self.permissions.obj() else {
116                return;
117            };
118
119            let editable =
120                permissions.can_do_to_user(user.user_id(), PowerLevelUserAction::ChangePowerLevel);
121
122            if self.editable.get() == editable {
123                return;
124            }
125
126            self.editable.set(editable);
127            self.obj().notify_editable();
128        }
129    }
130}
131
132glib::wrapper! {
133    /// A room member with a cached wanted power level.
134    pub struct MemberPowerLevel(ObjectSubclass<imp::MemberPowerLevel>);
135}
136
137impl MemberPowerLevel {
138    /// Constructs a new `MemberPowerLevel` with the given user and permissions.
139    pub fn new(user: &impl IsA<User>, permissions: &Permissions) -> Self {
140        glib::Object::builder()
141            .property("user", user)
142            .property("permissions", permissions)
143            .build()
144    }
145
146    /// Get the parts of this member, to use in the power levels event.
147    ///
148    /// Returns `None` if the permissions could not be upgraded, or if the power
149    /// level is the users default.
150    pub(crate) fn to_parts(&self) -> Option<(OwnedUserId, Int)> {
151        let permissions = self.permissions()?;
152
153        let users_default = permissions.default_power_level();
154        let pl = self.power_level();
155
156        if pl == users_default {
157            return None;
158        }
159
160        Some((self.user().user_id().clone(), Int::new_saturating(pl)))
161    }
162
163    /// The string to use to search for this member.
164    pub(crate) fn search_string(&self) -> String {
165        let user = self.user();
166        format!(
167            "{} {} {} {}",
168            user.display_name(),
169            user.user_id(),
170            self.role(),
171            self.power_level(),
172        )
173    }
174}