refactor(room_list): only display the knock state events if the current user can act on them
That is, if their power level allows them to either invite or kick users.
This commit is contained in:
committed by
Jorge Martin Espinosa
parent
f4a18989fb
commit
c143f981bd
@@ -634,7 +634,7 @@ impl Room {
|
||||
}
|
||||
|
||||
pub async fn get_power_levels(&self) -> Result<RoomPowerLevels, ClientError> {
|
||||
let power_levels = self.inner.room_power_levels().await?;
|
||||
let power_levels = self.inner.power_levels().await.map_err(matrix_sdk::Error::from)?;
|
||||
Ok(RoomPowerLevels::from(power_levels))
|
||||
}
|
||||
|
||||
|
||||
@@ -789,6 +789,8 @@ impl BaseClient {
|
||||
room: &Room,
|
||||
) -> Option<(Box<LatestEvent>, usize)> {
|
||||
let enc_events = room.latest_encrypted_events();
|
||||
let power_levels = room.power_levels().await.ok();
|
||||
let power_levels_info = Some(room.own_user_id()).zip(power_levels.as_ref());
|
||||
|
||||
// Walk backwards through the encrypted events, looking for one we can decrypt
|
||||
for (i, event) in enc_events.iter().enumerate().rev() {
|
||||
@@ -802,14 +804,13 @@ impl BaseClient {
|
||||
// We found an event we can decrypt
|
||||
if let Ok(any_sync_event) = decrypted.raw().deserialize() {
|
||||
// We can deserialize it to find its type
|
||||
match is_suitable_for_latest_event(&any_sync_event) {
|
||||
match is_suitable_for_latest_event(&any_sync_event, power_levels_info) {
|
||||
PossibleLatestEvent::YesRoomMessage(_)
|
||||
| PossibleLatestEvent::YesPoll(_)
|
||||
| PossibleLatestEvent::YesCallInvite(_)
|
||||
| PossibleLatestEvent::YesCallNotify(_)
|
||||
| PossibleLatestEvent::YesSticker(_)
|
||||
| PossibleLatestEvent::YesKnockedStateEvent(_) => {
|
||||
// The event is the right type for us to use as latest_event
|
||||
return Some((Box::new(LatestEvent::new(decrypted)), i));
|
||||
}
|
||||
_ => (),
|
||||
|
||||
@@ -61,4 +61,12 @@ pub enum Error {
|
||||
/// function with invalid parameters
|
||||
#[error("receive_all_members function was called with invalid parameters")]
|
||||
InvalidReceiveMembersParameters,
|
||||
|
||||
/// This request failed because the local data wasn't sufficient.
|
||||
#[error("Local cache doesn't contain all necessary data to perform the action.")]
|
||||
InsufficientData,
|
||||
|
||||
/// There was a [`serde_json`] deserialization error.
|
||||
#[error(transparent)]
|
||||
DeserializationError(#[from] serde_json::error::Error),
|
||||
}
|
||||
|
||||
@@ -14,11 +14,14 @@ use ruma::events::{
|
||||
};
|
||||
use ruma::{
|
||||
events::{
|
||||
room::member::{MembershipState, SyncRoomMemberEvent},
|
||||
room::{
|
||||
member::{MembershipState, SyncRoomMemberEvent},
|
||||
power_levels::RoomPowerLevels,
|
||||
},
|
||||
sticker::SyncStickerEvent,
|
||||
AnySyncStateEvent,
|
||||
},
|
||||
MxcUri, OwnedEventId,
|
||||
MxcUri, OwnedEventId, UserId,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
@@ -45,6 +48,7 @@ pub enum PossibleLatestEvent<'a> {
|
||||
YesCallNotify(&'a SyncCallNotifyEvent),
|
||||
|
||||
/// This state event is suitable - it's a knock membership change
|
||||
/// that can be handled by the current user.
|
||||
YesKnockedStateEvent(&'a SyncRoomMemberEvent),
|
||||
|
||||
// Later: YesState(),
|
||||
@@ -60,7 +64,10 @@ pub enum PossibleLatestEvent<'a> {
|
||||
/// Decide whether an event could be stored as the latest event in a room.
|
||||
/// Returns a LatestEvent representing our decision.
|
||||
#[cfg(feature = "e2e-encryption")]
|
||||
pub fn is_suitable_for_latest_event(event: &AnySyncTimelineEvent) -> PossibleLatestEvent<'_> {
|
||||
pub fn is_suitable_for_latest_event<'a>(
|
||||
event: &'a AnySyncTimelineEvent,
|
||||
power_levels_info: Option<(&'a UserId, &'a RoomPowerLevels)>,
|
||||
) -> PossibleLatestEvent<'a> {
|
||||
match event {
|
||||
// Suitable - we have an m.room.message that was not redacted or edited
|
||||
AnySyncTimelineEvent::MessageLike(AnySyncMessageLikeEvent::RoomMessage(message)) => {
|
||||
@@ -114,10 +121,23 @@ pub fn is_suitable_for_latest_event(event: &AnySyncTimelineEvent) -> PossibleLat
|
||||
|
||||
// We don't currently support most state events
|
||||
AnySyncTimelineEvent::State(state) => {
|
||||
// But we make an exception for knocked state events
|
||||
// But we make an exception for knocked state events *if* the current user
|
||||
// can either accept or decline them
|
||||
if let AnySyncStateEvent::RoomMember(member) = state {
|
||||
if matches!(member.membership(), MembershipState::Knock) {
|
||||
return PossibleLatestEvent::YesKnockedStateEvent(member);
|
||||
let can_accept_or_decline_knocks = match power_levels_info {
|
||||
Some((own_user_id, room_power_levels)) => {
|
||||
room_power_levels.user_can_invite(own_user_id)
|
||||
|| room_power_levels.user_can_kick(own_user_id)
|
||||
}
|
||||
_ => false,
|
||||
};
|
||||
|
||||
// The current user can act on the knock changes, so they should be
|
||||
// displayed
|
||||
if can_accept_or_decline_knocks {
|
||||
return PossibleLatestEvent::YesKnockedStateEvent(member);
|
||||
}
|
||||
}
|
||||
}
|
||||
PossibleLatestEvent::NoUnsupportedEventType
|
||||
@@ -345,7 +365,7 @@ mod tests {
|
||||
));
|
||||
assert_let!(
|
||||
PossibleLatestEvent::YesRoomMessage(SyncMessageLikeEvent::Original(m)) =
|
||||
is_suitable_for_latest_event(&event)
|
||||
is_suitable_for_latest_event(&event, None)
|
||||
);
|
||||
|
||||
assert_eq!(m.content.msgtype.msgtype(), "m.image");
|
||||
@@ -368,7 +388,7 @@ mod tests {
|
||||
));
|
||||
assert_let!(
|
||||
PossibleLatestEvent::YesPoll(SyncMessageLikeEvent::Original(m)) =
|
||||
is_suitable_for_latest_event(&event)
|
||||
is_suitable_for_latest_event(&event, None)
|
||||
);
|
||||
|
||||
assert_eq!(m.content.poll_start().question.text, "do you like rust?");
|
||||
@@ -392,7 +412,7 @@ mod tests {
|
||||
));
|
||||
assert_let!(
|
||||
PossibleLatestEvent::YesCallInvite(SyncMessageLikeEvent::Original(_)) =
|
||||
is_suitable_for_latest_event(&event)
|
||||
is_suitable_for_latest_event(&event, None)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -414,7 +434,7 @@ mod tests {
|
||||
));
|
||||
assert_let!(
|
||||
PossibleLatestEvent::YesCallNotify(SyncMessageLikeEvent::Original(_)) =
|
||||
is_suitable_for_latest_event(&event)
|
||||
is_suitable_for_latest_event(&event, None)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -435,7 +455,7 @@ mod tests {
|
||||
));
|
||||
|
||||
assert_matches!(
|
||||
is_suitable_for_latest_event(&event),
|
||||
is_suitable_for_latest_event(&event, None),
|
||||
PossibleLatestEvent::YesSticker(SyncStickerEvent::Original(_))
|
||||
);
|
||||
}
|
||||
@@ -457,7 +477,7 @@ mod tests {
|
||||
));
|
||||
|
||||
assert_matches!(
|
||||
is_suitable_for_latest_event(&event),
|
||||
is_suitable_for_latest_event(&event, None),
|
||||
PossibleLatestEvent::NoUnsupportedMessageLikeType
|
||||
);
|
||||
}
|
||||
@@ -485,7 +505,7 @@ mod tests {
|
||||
));
|
||||
|
||||
assert_matches!(
|
||||
is_suitable_for_latest_event(&event),
|
||||
is_suitable_for_latest_event(&event, None),
|
||||
PossibleLatestEvent::YesRoomMessage(SyncMessageLikeEvent::Redacted(_))
|
||||
);
|
||||
}
|
||||
@@ -507,7 +527,10 @@ mod tests {
|
||||
}),
|
||||
));
|
||||
|
||||
assert_matches!(is_suitable_for_latest_event(&event), PossibleLatestEvent::NoEncrypted);
|
||||
assert_matches!(
|
||||
is_suitable_for_latest_event(&event, None),
|
||||
PossibleLatestEvent::NoEncrypted
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -524,7 +547,7 @@ mod tests {
|
||||
));
|
||||
|
||||
assert_matches!(
|
||||
is_suitable_for_latest_event(&event),
|
||||
is_suitable_for_latest_event(&event, None),
|
||||
PossibleLatestEvent::NoUnsupportedEventType
|
||||
);
|
||||
}
|
||||
@@ -548,7 +571,7 @@ mod tests {
|
||||
));
|
||||
|
||||
assert_matches!(
|
||||
is_suitable_for_latest_event(&event),
|
||||
is_suitable_for_latest_event(&event, None),
|
||||
PossibleLatestEvent::NoUnsupportedMessageLikeType
|
||||
);
|
||||
}
|
||||
|
||||
@@ -43,6 +43,7 @@ use ruma::{
|
||||
join_rules::JoinRule,
|
||||
member::{MembershipState, RoomMemberEventContent},
|
||||
pinned_events::RoomPinnedEventsEventContent,
|
||||
power_levels::{RoomPowerLevels, RoomPowerLevelsEventContent},
|
||||
redaction::SyncRoomRedactionEvent,
|
||||
tombstone::RoomTombstoneEventContent,
|
||||
},
|
||||
@@ -71,7 +72,7 @@ use crate::{
|
||||
read_receipts::RoomReadReceipts,
|
||||
store::{DynStateStore, Result as StoreResult, StateStoreExt},
|
||||
sync::UnreadNotificationsCount,
|
||||
MinimalStateEvent, OriginalMinimalStateEvent, RoomMemberships,
|
||||
Error, MinimalStateEvent, OriginalMinimalStateEvent, RoomMemberships,
|
||||
};
|
||||
|
||||
/// Indicates that a notable update of `RoomInfo` has been applied, and why.
|
||||
@@ -508,6 +509,17 @@ impl Room {
|
||||
self.inner.read().base_info.max_power_level
|
||||
}
|
||||
|
||||
/// Get the current power levels of this room.
|
||||
pub async fn power_levels(&self) -> Result<RoomPowerLevels, Error> {
|
||||
Ok(self
|
||||
.store
|
||||
.get_state_event_static::<RoomPowerLevelsEventContent>(self.room_id())
|
||||
.await?
|
||||
.ok_or(Error::InsufficientData)?
|
||||
.deserialize()?
|
||||
.power_levels())
|
||||
}
|
||||
|
||||
/// Get the `m.room.name` of this room.
|
||||
///
|
||||
/// The returned string may be empty if the event has been redacted, or it's
|
||||
|
||||
@@ -30,7 +30,7 @@ use ruma::{
|
||||
api::client::sync::sync_events::v3::{self, InvitedRoom, KnockedRoom},
|
||||
events::{
|
||||
room::member::MembershipState, AnyRoomAccountDataEvent, AnyStrippedStateEvent,
|
||||
AnySyncStateEvent,
|
||||
AnySyncStateEvent, StateEventType,
|
||||
},
|
||||
serde::Raw,
|
||||
JsOption, OwnedRoomId, RoomId, UInt, UserId,
|
||||
@@ -698,9 +698,28 @@ async fn cache_latest_events(
|
||||
let mut encrypted_events =
|
||||
Vec::with_capacity(room.latest_encrypted_events.read().unwrap().capacity());
|
||||
|
||||
// Try to get room power levels from the current changes
|
||||
let power_levels_from_changes = || {
|
||||
let state_changes = changes?.state.get(room_info.room_id())?;
|
||||
let room_power_levels_state =
|
||||
state_changes.get(&StateEventType::RoomPowerLevels)?.values().next()?;
|
||||
match room_power_levels_state.deserialize().ok()? {
|
||||
AnySyncStateEvent::RoomPowerLevels(ev) => Some(ev.power_levels()),
|
||||
_ => None,
|
||||
}
|
||||
};
|
||||
|
||||
// If we didn't get any info, try getting it from local data
|
||||
let power_levels = match power_levels_from_changes() {
|
||||
Some(power_levels) => Some(power_levels),
|
||||
None => room.power_levels().await.ok(),
|
||||
};
|
||||
|
||||
let power_levels_info = Some(room.own_user_id()).zip(power_levels.as_ref());
|
||||
|
||||
for event in events.iter().rev() {
|
||||
if let Ok(timeline_event) = event.raw().deserialize() {
|
||||
match is_suitable_for_latest_event(&timeline_event) {
|
||||
match is_suitable_for_latest_event(&timeline_event, power_levels_info) {
|
||||
PossibleLatestEvent::YesRoomMessage(_)
|
||||
| PossibleLatestEvent::YesPoll(_)
|
||||
| PossibleLatestEvent::YesCallInvite(_)
|
||||
@@ -1740,10 +1759,23 @@ mod tests {
|
||||
}
|
||||
|
||||
#[async_test]
|
||||
async fn test_last_knock_member_state_event_from_sliding_sync_is_cached() {
|
||||
async fn test_last_knock_event_from_sliding_sync_is_cached_if_user_has_permissions() {
|
||||
let own_user_id = user_id!("@me:e.uk");
|
||||
// Given a logged-in client
|
||||
let client = logged_in_base_client(None).await;
|
||||
let client = logged_in_base_client(Some(own_user_id)).await;
|
||||
let room_id = room_id!("!r:e.uk");
|
||||
|
||||
// Give the current user invite or kick permissions in this room
|
||||
let power_levels = json!({
|
||||
"sender":"@alice:example.com",
|
||||
"state_key":"",
|
||||
"type":"m.room.power_levels",
|
||||
"event_id": "$idb",
|
||||
"origin_server_ts": 12344445,
|
||||
"content":{ "invite": 100, "kick": 100, "users": { own_user_id: 100 } },
|
||||
"room_id": room_id,
|
||||
});
|
||||
|
||||
// And a knock member state event
|
||||
let knock_event = json!({
|
||||
"sender":"@alice:example.com",
|
||||
@@ -1757,7 +1789,8 @@ mod tests {
|
||||
|
||||
// When the sliding sync response contains a timeline
|
||||
let events = &[knock_event];
|
||||
let room = room_with_timeline(events);
|
||||
let mut room = room_with_timeline(events);
|
||||
room.required_state.push(Raw::new(&power_levels).unwrap().cast());
|
||||
let response = response_with_room(room_id, room);
|
||||
client.process_sliding_sync(&response, &(), true).await.expect("Failed to process sync");
|
||||
|
||||
@@ -1770,7 +1803,49 @@ mod tests {
|
||||
}
|
||||
|
||||
#[async_test]
|
||||
async fn test_last_member_state_event_from_sliding_sync_is_not_cached() {
|
||||
async fn test_last_knock_event_from_sliding_sync_is_not_cached_without_permissions() {
|
||||
let own_user_id = user_id!("@me:e.uk");
|
||||
// Given a logged-in client
|
||||
let client = logged_in_base_client(Some(own_user_id)).await;
|
||||
let room_id = room_id!("!r:e.uk");
|
||||
|
||||
// Set the user as a user with no permission to invite or kick other users in
|
||||
// this room
|
||||
let power_levels = json!({
|
||||
"sender":"@alice:example.com",
|
||||
"state_key":"",
|
||||
"type":"m.room.power_levels",
|
||||
"event_id": "$idb",
|
||||
"origin_server_ts": 12344445,
|
||||
"content":{ "invite": 50, "kick": 50, "users": { own_user_id: 0 } },
|
||||
"room_id": room_id,
|
||||
});
|
||||
|
||||
// And a knock member state event
|
||||
let knock_event = json!({
|
||||
"sender":"@alice:example.com",
|
||||
"state_key":"@alice:example.com",
|
||||
"type":"m.room.member",
|
||||
"event_id": "$ida",
|
||||
"origin_server_ts": 12344446,
|
||||
"content":{"membership": "knock"},
|
||||
"room_id": room_id,
|
||||
});
|
||||
|
||||
// When the sliding sync response contains a timeline
|
||||
let events = &[knock_event];
|
||||
let mut room = room_with_timeline(events);
|
||||
room.required_state.push(Raw::new(&power_levels).unwrap().cast());
|
||||
let response = response_with_room(room_id, room);
|
||||
client.process_sliding_sync(&response, &(), true).await.expect("Failed to process sync");
|
||||
|
||||
// Then the room doesn't hold the knock state event as the latest event
|
||||
let client_room = client.get_room(room_id).expect("No room found");
|
||||
assert!(client_room.latest_event().is_none());
|
||||
}
|
||||
|
||||
#[async_test]
|
||||
async fn test_last_non_knock_member_state_event_from_sliding_sync_is_not_cached() {
|
||||
// Given a logged-in client
|
||||
let client = logged_in_base_client(None).await;
|
||||
let room_id = room_id!("!r:e.uk");
|
||||
|
||||
@@ -46,7 +46,7 @@ use ruma::{
|
||||
},
|
||||
name::RoomNameEventContent,
|
||||
pinned_events::RoomPinnedEventsEventContent,
|
||||
power_levels::RoomPowerLevelsEventContent,
|
||||
power_levels::{RoomPowerLevels, RoomPowerLevelsEventContent},
|
||||
server_acl::RoomServerAclEventContent,
|
||||
third_party_invite::RoomThirdPartyInviteEventContent,
|
||||
tombstone::RoomTombstoneEventContent,
|
||||
@@ -141,8 +141,9 @@ impl TimelineItemContent {
|
||||
/// `TimelineItemContent`.
|
||||
pub(crate) fn from_latest_event_content(
|
||||
event: AnySyncTimelineEvent,
|
||||
power_levels_info: Option<(&UserId, &RoomPowerLevels)>,
|
||||
) -> Option<TimelineItemContent> {
|
||||
match is_suitable_for_latest_event(&event) {
|
||||
match is_suitable_for_latest_event(&event, power_levels_info) {
|
||||
PossibleLatestEvent::YesRoomMessage(m) => {
|
||||
Some(Self::from_suitable_latest_event_content(m))
|
||||
}
|
||||
|
||||
@@ -158,9 +158,18 @@ impl EventTimelineItem {
|
||||
let event_id = event.event_id().to_owned();
|
||||
let is_own = client.user_id().map(|uid| uid == sender).unwrap_or(false);
|
||||
|
||||
// Get the room's power levels for calculating the latest event
|
||||
let power_levels = if let Some(room) = client.get_room(room_id) {
|
||||
room.power_levels().await.ok()
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let room_power_levels_info = client.user_id().zip(power_levels.as_ref());
|
||||
|
||||
// If we don't (yet) know how to handle this type of message, return `None`
|
||||
// here. If we do, convert it into a `TimelineItemContent`.
|
||||
let content = TimelineItemContent::from_latest_event_content(event)?;
|
||||
let content =
|
||||
TimelineItemContent::from_latest_event_content(event, room_power_levels_info)?;
|
||||
|
||||
// We don't currently bundle any reactions with the main event. This could
|
||||
// conceivably be wanted in the message preview in future.
|
||||
@@ -780,6 +789,27 @@ mod tests {
|
||||
);
|
||||
let client = logged_in_client(None).await;
|
||||
|
||||
// Add power levels state event, otherwise the knock state event can't be used
|
||||
// as the latest event
|
||||
let power_level_event = sync_state_event!({
|
||||
"type": "m.room.power_levels",
|
||||
"content": {},
|
||||
"event_id": "$143278582443PhrSn:example.org",
|
||||
"origin_server_ts": 143273581,
|
||||
"room_id": room_id,
|
||||
"sender": user_id,
|
||||
"state_key": "",
|
||||
"unsigned": {
|
||||
"age": 1234
|
||||
}
|
||||
});
|
||||
let mut room = http::response::Room::new();
|
||||
room.required_state.push(power_level_event);
|
||||
|
||||
// And the room is stored in the client so it can be extracted when needed
|
||||
let response = response_with_room(room_id, room);
|
||||
client.process_sliding_sync_test_helper(&response).await.unwrap();
|
||||
|
||||
// When we construct a timeline event from it
|
||||
let event = SyncTimelineEvent::new(raw_event.cast());
|
||||
let timeline_item =
|
||||
@@ -1040,7 +1070,6 @@ mod tests {
|
||||
"room_id": room_id,
|
||||
"sender": user_id,
|
||||
"state_key": user_id,
|
||||
"type": "m.room.member",
|
||||
"unsigned": {
|
||||
"age": 1234
|
||||
}
|
||||
|
||||
@@ -2139,7 +2139,7 @@ impl Room {
|
||||
&self,
|
||||
updates: Vec<(&UserId, Int)>,
|
||||
) -> Result<send_state_event::v3::Response> {
|
||||
let mut power_levels = self.room_power_levels().await?;
|
||||
let mut power_levels = self.power_levels().await?;
|
||||
|
||||
for (user_id, new_level) in updates {
|
||||
if new_level == power_levels.users_default {
|
||||
@@ -2157,7 +2157,7 @@ impl Room {
|
||||
/// Any values that are `None` in the given `RoomPowerLevelChanges` will
|
||||
/// remain unchanged.
|
||||
pub async fn apply_power_level_changes(&self, changes: RoomPowerLevelChanges) -> Result<()> {
|
||||
let mut power_levels = self.room_power_levels().await?;
|
||||
let mut power_levels = self.power_levels().await?;
|
||||
power_levels.apply(changes)?;
|
||||
self.send_state_event(RoomPowerLevelsEventContent::from(power_levels)).await?;
|
||||
Ok(())
|
||||
@@ -2180,7 +2180,7 @@ impl Room {
|
||||
let default_power_levels = RoomPowerLevels::from(RoomPowerLevelsEventContent::new());
|
||||
let changes = RoomPowerLevelChanges::from(default_power_levels);
|
||||
self.apply_power_level_changes(changes).await?;
|
||||
self.room_power_levels().await
|
||||
Ok(self.power_levels().await?)
|
||||
}
|
||||
|
||||
/// Gets the suggested role for the user with the provided `user_id`.
|
||||
@@ -2197,14 +2197,14 @@ impl Room {
|
||||
/// This method checks the `RoomPowerLevels` events instead of loading the
|
||||
/// member list and looking for the member.
|
||||
pub async fn get_user_power_level(&self, user_id: &UserId) -> Result<i64> {
|
||||
let event = self.room_power_levels().await?;
|
||||
let event = self.power_levels().await?;
|
||||
Ok(event.for_user(user_id).into())
|
||||
}
|
||||
|
||||
/// Gets a map with the `UserId` of users with power levels other than `0`
|
||||
/// and this power level.
|
||||
pub async fn users_with_power_levels(&self) -> HashMap<OwnedUserId, i64> {
|
||||
let power_levels = self.room_power_levels().await.ok();
|
||||
let power_levels = self.power_levels().await.ok();
|
||||
let mut user_power_levels = HashMap::<OwnedUserId, i64>::new();
|
||||
if let Some(power_levels) = power_levels {
|
||||
for (id, level) in power_levels.users.into_iter() {
|
||||
@@ -2487,7 +2487,7 @@ impl Room {
|
||||
///
|
||||
/// The call may fail if there is an error in getting the power levels.
|
||||
pub async fn can_user_redact_own(&self, user_id: &UserId) -> Result<bool> {
|
||||
Ok(self.room_power_levels().await?.user_can_redact_own_event(user_id))
|
||||
Ok(self.power_levels().await?.user_can_redact_own_event(user_id))
|
||||
}
|
||||
|
||||
/// Returns true if the user with the given user_id is able to redact
|
||||
@@ -2495,7 +2495,7 @@ impl Room {
|
||||
///
|
||||
/// The call may fail if there is an error in getting the power levels.
|
||||
pub async fn can_user_redact_other(&self, user_id: &UserId) -> Result<bool> {
|
||||
Ok(self.room_power_levels().await?.user_can_redact_event_of_other(user_id))
|
||||
Ok(self.power_levels().await?.user_can_redact_event_of_other(user_id))
|
||||
}
|
||||
|
||||
/// Returns true if the user with the given user_id is able to ban in the
|
||||
@@ -2503,7 +2503,7 @@ impl Room {
|
||||
///
|
||||
/// The call may fail if there is an error in getting the power levels.
|
||||
pub async fn can_user_ban(&self, user_id: &UserId) -> Result<bool> {
|
||||
Ok(self.room_power_levels().await?.user_can_ban(user_id))
|
||||
Ok(self.power_levels().await?.user_can_ban(user_id))
|
||||
}
|
||||
|
||||
/// Returns true if the user with the given user_id is able to kick in the
|
||||
@@ -2511,7 +2511,7 @@ impl Room {
|
||||
///
|
||||
/// The call may fail if there is an error in getting the power levels.
|
||||
pub async fn can_user_invite(&self, user_id: &UserId) -> Result<bool> {
|
||||
Ok(self.room_power_levels().await?.user_can_invite(user_id))
|
||||
Ok(self.power_levels().await?.user_can_invite(user_id))
|
||||
}
|
||||
|
||||
/// Returns true if the user with the given user_id is able to kick in the
|
||||
@@ -2519,7 +2519,7 @@ impl Room {
|
||||
///
|
||||
/// The call may fail if there is an error in getting the power levels.
|
||||
pub async fn can_user_kick(&self, user_id: &UserId) -> Result<bool> {
|
||||
Ok(self.room_power_levels().await?.user_can_kick(user_id))
|
||||
Ok(self.power_levels().await?.user_can_kick(user_id))
|
||||
}
|
||||
|
||||
/// Returns true if the user with the given user_id is able to send a
|
||||
@@ -2531,7 +2531,7 @@ impl Room {
|
||||
user_id: &UserId,
|
||||
state_event: StateEventType,
|
||||
) -> Result<bool> {
|
||||
Ok(self.room_power_levels().await?.user_can_send_state(user_id, state_event))
|
||||
Ok(self.power_levels().await?.user_can_send_state(user_id, state_event))
|
||||
}
|
||||
|
||||
/// Returns true if the user with the given user_id is able to send a
|
||||
@@ -2543,7 +2543,7 @@ impl Room {
|
||||
user_id: &UserId,
|
||||
message: MessageLikeEventType,
|
||||
) -> Result<bool> {
|
||||
Ok(self.room_power_levels().await?.user_can_send_message(user_id, message))
|
||||
Ok(self.power_levels().await?.user_can_send_message(user_id, message))
|
||||
}
|
||||
|
||||
/// Returns true if the user with the given user_id is able to pin or unpin
|
||||
@@ -2552,7 +2552,7 @@ impl Room {
|
||||
/// The call may fail if there is an error in getting the power levels.
|
||||
pub async fn can_user_pin_unpin(&self, user_id: &UserId) -> Result<bool> {
|
||||
Ok(self
|
||||
.room_power_levels()
|
||||
.power_levels()
|
||||
.await?
|
||||
.user_can_send_state(user_id, StateEventType::RoomPinnedEvents))
|
||||
}
|
||||
@@ -2562,7 +2562,7 @@ impl Room {
|
||||
///
|
||||
/// The call may fail if there is an error in getting the power levels.
|
||||
pub async fn can_user_trigger_room_notification(&self, user_id: &UserId) -> Result<bool> {
|
||||
Ok(self.room_power_levels().await?.user_can_trigger_room_notification(user_id))
|
||||
Ok(self.power_levels().await?.user_can_trigger_room_notification(user_id))
|
||||
}
|
||||
|
||||
/// Get a list of servers that should know this room.
|
||||
|
||||
@@ -624,7 +624,7 @@ async fn test_reset_power_levels() {
|
||||
.mount(&server)
|
||||
.await;
|
||||
|
||||
let initial_power_levels = room.room_power_levels().await.unwrap();
|
||||
let initial_power_levels = room.power_levels().await.unwrap();
|
||||
assert_eq!(initial_power_levels.events[&TimelineEventType::RoomAvatar], int!(100));
|
||||
|
||||
room.reset_power_levels().await.unwrap();
|
||||
|
||||
Reference in New Issue
Block a user