Skip to main content

matrix_sdk/room/
member.rs

1use std::ops::Deref;
2
3use ruma::{
4    events::room::{MediaSource, power_levels::UserPowerLevel},
5    int,
6};
7
8use crate::{
9    BaseRoomMember, Client, Result,
10    media::{MediaFormat, MediaRequestParameters},
11};
12
13/// The high-level `RoomMember` representation
14#[derive(Debug, Clone)]
15pub struct RoomMember {
16    inner: BaseRoomMember,
17    pub(crate) client: Client,
18}
19
20impl Deref for RoomMember {
21    type Target = BaseRoomMember;
22
23    fn deref(&self) -> &Self::Target {
24        &self.inner
25    }
26}
27
28impl RoomMember {
29    pub(crate) fn new(client: Client, member: BaseRoomMember) -> Self {
30        Self { inner: member, client }
31    }
32
33    /// Gets the avatar of this member, if set.
34    ///
35    /// Returns the avatar. If a thumbnail is requested no guarantee on the size
36    /// of the image is given.
37    ///
38    /// # Arguments
39    ///
40    /// - `format` - The desired format of the avatar.
41    ///
42    /// # Examples
43    ///
44    /// ```no_run
45    /// use matrix_sdk::{
46    ///     Client, RoomMemberships, media::MediaFormat, room::RoomMember,
47    ///     ruma::room_id,
48    /// };
49    /// # use url::Url;
50    /// # let homeserver = Url::parse("http://example.com").unwrap();
51    /// # async {
52    /// # let user = "example";
53    /// let client = Client::new(homeserver).await.unwrap();
54    /// client.matrix_auth().login_username(user, "password").send().await.unwrap();
55    /// let room_id = room_id!("!roomid:example.com");
56    /// let room = client.get_room(&room_id).unwrap();
57    /// let members = room.members(RoomMemberships::empty()).await.unwrap();
58    /// let member = members.first().unwrap();
59    /// if let Some(avatar) = member.avatar(MediaFormat::File).await.unwrap() {
60    ///     std::fs::write("avatar.png", avatar);
61    /// }
62    /// # };
63    /// ```
64    pub async fn avatar(&self, format: MediaFormat) -> Result<Option<Vec<u8>>> {
65        let Some(url) = self.avatar_url() else { return Ok(None) };
66        let request = MediaRequestParameters { source: MediaSource::Plain(url.to_owned()), format };
67        Ok(Some(self.client.media().get_media_content(&request, true).await?))
68    }
69
70    /// Adds the room member to the current account data's ignore list which
71    /// will ignore the user across all rooms.
72    pub async fn ignore(&self) -> Result<()> {
73        self.client.account().ignore_user(self.inner.user_id()).await
74    }
75
76    /// Removes the room member from the current account data's ignore list
77    /// which will unignore the user across all rooms.
78    pub async fn unignore(&self) -> Result<()> {
79        self.client.account().unignore_user(self.inner.user_id()).await
80    }
81
82    /// Returns true if the member of the room is the user of the account
83    pub fn is_account_user(&self) -> bool {
84        match self.client.user_id() {
85            Some(id) => id == self.inner.user_id(),
86            None => false,
87        }
88    }
89
90    /// Get the suggested role of this member based on their power level.
91    pub fn suggested_role_for_power_level(&self) -> RoomMemberRole {
92        RoomMemberRole::suggested_role_for_power_level(self.power_level())
93    }
94}
95
96/// The role of a member in a room.
97#[derive(Clone, Debug, PartialEq)]
98#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
99pub enum RoomMemberRole {
100    /// The member is a creator.
101    ///
102    /// A creator has an infinite power level and cannot be demoted, so this
103    /// role is immutable. A room can have several creators.
104    ///
105    /// It is available in room versions where
106    /// `explicitly_privilege_room_creators` in [`AuthorizationRules`] is set to
107    /// `true`.
108    ///
109    /// [`AuthorizationRules`]: ruma::room_version_rules::AuthorizationRules
110    Creator,
111    /// The member is an administrator.
112    Administrator,
113    /// The member is a moderator.
114    Moderator,
115    /// The member is a regular user.
116    User,
117}
118
119impl RoomMemberRole {
120    /// Creates the suggested role for a given power level.
121    pub fn suggested_role_for_power_level(power_level: UserPowerLevel) -> Self {
122        match power_level {
123            UserPowerLevel::Infinite => RoomMemberRole::Creator,
124            UserPowerLevel::Int(value) => {
125                if value >= int!(100) {
126                    Self::Administrator
127                } else if value >= int!(50) {
128                    Self::Moderator
129                } else {
130                    Self::User
131                }
132            }
133            // This branch is only necessary because the enum is non-exhaustive.
134            // TODO: Use the `non_exhaustive_omitted_patterns` lint when it
135            // becomes stable to be warned when a variant is added. Tracking
136            // issue: https://github.com/rust-lang/rust/issues/89554
137            _ => unimplemented!(),
138        }
139    }
140
141    /// Get the suggested power level for this role.
142    pub fn suggested_power_level(&self) -> UserPowerLevel {
143        match self {
144            Self::Creator => UserPowerLevel::Infinite,
145            Self::Administrator => UserPowerLevel::Int(int!(100)),
146            Self::Moderator => UserPowerLevel::Int(int!(50)),
147            Self::User => UserPowerLevel::Int(int!(0)),
148        }
149    }
150}
151
152#[cfg(test)]
153mod tests {
154    use super::*;
155
156    #[test]
157    fn test_suggested_roles() {
158        assert_eq!(
159            RoomMemberRole::Administrator,
160            RoomMemberRole::suggested_role_for_power_level(int!(100).into())
161        );
162        assert_eq!(
163            RoomMemberRole::Moderator,
164            RoomMemberRole::suggested_role_for_power_level(int!(50).into())
165        );
166        assert_eq!(
167            RoomMemberRole::User,
168            RoomMemberRole::suggested_role_for_power_level(int!(0).into())
169        );
170    }
171
172    #[test]
173    fn test_unexpected_power_levels() {
174        assert_eq!(
175            RoomMemberRole::Administrator,
176            RoomMemberRole::suggested_role_for_power_level(int!(200).into())
177        );
178        assert_eq!(
179            RoomMemberRole::Administrator,
180            RoomMemberRole::suggested_role_for_power_level(int!(101).into())
181        );
182        assert_eq!(
183            RoomMemberRole::Moderator,
184            RoomMemberRole::suggested_role_for_power_level(int!(99).into())
185        );
186        assert_eq!(
187            RoomMemberRole::Moderator,
188            RoomMemberRole::suggested_role_for_power_level(int!(51).into())
189        );
190        assert_eq!(
191            RoomMemberRole::User,
192            RoomMemberRole::suggested_role_for_power_level(int!(-1).into())
193        );
194        assert_eq!(
195            RoomMemberRole::User,
196            RoomMemberRole::suggested_role_for_power_level(int!(-100).into())
197        );
198    }
199
200    #[test]
201    fn test_default_power_levels() {
202        assert_eq!(int!(100), RoomMemberRole::Administrator.suggested_power_level());
203        assert_eq!(int!(50), RoomMemberRole::Moderator.suggested_power_level());
204        assert_eq!(int!(0), RoomMemberRole::User.suggested_power_level());
205
206        assert_eq!(
207            RoomMemberRole::Administrator,
208            RoomMemberRole::suggested_role_for_power_level(
209                RoomMemberRole::Administrator.suggested_power_level()
210            )
211        );
212        assert_eq!(
213            RoomMemberRole::Moderator,
214            RoomMemberRole::suggested_role_for_power_level(
215                RoomMemberRole::Moderator.suggested_power_level()
216            )
217        );
218        assert_eq!(
219            RoomMemberRole::User,
220            RoomMemberRole::suggested_role_for_power_level(
221                RoomMemberRole::User.suggested_power_level()
222            )
223        );
224    }
225}