Skip to content

Commit

Permalink
ffi: Add methods to get and reset the power levels of a Room
Browse files Browse the repository at this point in the history
- `Room::build_power_level_changes_from_current()` was replaced by `Room::get_power_levels()`, which now returns an SDK/Ruma `RoomPowerLevels` value containing all the data we need to display these values in UI and not only the customised values.
- `Room::reset_power_levels()` was added to the FFI layer.
  • Loading branch information
jmartinesp authored Mar 7, 2024
1 parent f14c00d commit 0469c27
Show file tree
Hide file tree
Showing 4 changed files with 250 additions and 8 deletions.
66 changes: 61 additions & 5 deletions bindings/matrix-sdk-ffi/src/room.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,13 @@ use mime::Mime;
use ruma::{
api::client::room::report_content,
assign,
events::room::{avatar::ImageInfo as RumaAvatarImageInfo, MediaSource},
events::{
room::{
avatar::ImageInfo as RumaAvatarImageInfo,
power_levels::RoomPowerLevels as RumaPowerLevels, MediaSource,
},
TimelineEventType,
},
EventId, Int, UserId,
};
use tokio::sync::RwLock;
Expand Down Expand Up @@ -561,11 +567,9 @@ impl Room {
Ok(())
}

pub async fn build_power_level_changes_from_current(
&self,
) -> Result<RoomPowerLevelChanges, ClientError> {
pub async fn get_power_levels(&self) -> Result<RoomPowerLevels, ClientError> {
let power_levels = self.inner.room_power_levels().await?;
Ok(power_levels.into())
Ok(RoomPowerLevels::from(power_levels))
}

pub async fn apply_power_level_changes(
Expand Down Expand Up @@ -603,6 +607,58 @@ impl Room {
let user_id = UserId::parse(&user_id)?;
Ok(self.inner.get_suggested_user_role(&user_id).await?)
}

pub async fn reset_power_levels(&self) -> Result<RoomPowerLevels, ClientError> {
Ok(RoomPowerLevels::from(self.inner.reset_power_levels().await?))
}
}

#[derive(uniffi::Record)]
pub struct RoomPowerLevels {
/// The level required to ban a user.
pub ban: i64,
/// The level required to invite a user.
pub invite: i64,
/// The level required to kick a user.
pub kick: i64,
/// The level required to redact an event.
pub redact: i64,
/// The default level required to send message events.
pub events_default: i64,
/// The default level required to send state events.
pub state_default: i64,
/// The default power level for every user in the room.
pub users_default: i64,
/// The level required to change the room's name.
pub room_name: i64,
/// The level required to change the room's avatar.
pub room_avatar: i64,
/// The level required to change the room's topic.
pub room_topic: i64,
}

impl From<RumaPowerLevels> for RoomPowerLevels {
fn from(value: RumaPowerLevels) -> Self {
fn state_event_level_for(
power_levels: &RumaPowerLevels,
event_type: &TimelineEventType,
) -> i64 {
let default_state: i64 = power_levels.state_default.into();
power_levels.events.get(event_type).map_or(default_state, |&level| level.into())
}
Self {
ban: value.ban.into(),
invite: value.invite.into(),
kick: value.kick.into(),
redact: value.redact.into(),
events_default: value.events_default.into(),
state_default: value.state_default.into(),
users_default: value.users_default.into(),
room_name: state_event_level_for(&value, &TimelineEventType::RoomName),
room_avatar: state_event_level_for(&value, &TimelineEventType::RoomAvatar),
room_topic: state_event_level_for(&value, &TimelineEventType::RoomTopic),
}
}
}

#[uniffi::export(callback_interface)]
Expand Down
10 changes: 10 additions & 0 deletions crates/matrix-sdk/src/room/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1860,6 +1860,16 @@ impl Room {
.power_levels())
}

/// Resets the room's power levels to the default values
///
/// [spec]: https://spec.matrix.org/v1.9/client-server-api/#mroompower_levels
pub async fn reset_power_levels(&self) -> Result<RoomPowerLevels> {
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
}

/// Gets the suggested role for the user with the provided `user_id`.
///
/// This method checks the `RoomPowerLevels` events instead of loading the
Expand Down
41 changes: 38 additions & 3 deletions crates/matrix-sdk/tests/integration/room/joined.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,13 @@ use matrix_sdk::{
};
use matrix_sdk_base::RoomState;
use matrix_sdk_test::{
async_test, test_json, EphemeralTestEvent, JoinedRoomBuilder, SyncResponseBuilder,
DEFAULT_TEST_ROOM_ID,
async_test, test_json, test_json::sync::CUSTOM_ROOM_POWER_LEVELS, EphemeralTestEvent,
JoinedRoomBuilder, SyncResponseBuilder, DEFAULT_TEST_ROOM_ID,
};
use ruma::{
api::client::{membership::Invite3pidInit, receipt::create_receipt::v3::ReceiptType},
assign, event_id,
events::{receipt::ReceiptThread, room::message::RoomMessageEventContent},
events::{receipt::ReceiptThread, room::message::RoomMessageEventContent, TimelineEventType},
int, mxc_uri, owned_event_id, room_id, thirdparty, uint, user_id, OwnedUserId, TransactionId,
};
use serde_json::json;
Expand Down Expand Up @@ -817,3 +817,38 @@ async fn get_users_with_power_levels_is_empty_if_power_level_info_is_not_availab

assert!(room.users_with_power_levels().await.is_empty());
}

#[async_test]
async fn reset_power_levels() {
let (client, server) = logged_in_client_with_server().await;

mock_sync(&server, &*CUSTOM_ROOM_POWER_LEVELS, None).await;

let sync_settings = SyncSettings::new().timeout(Duration::from_millis(3000));
let _response = client.sync_once(sync_settings).await.unwrap();
let room = client.get_room(&DEFAULT_TEST_ROOM_ID).unwrap();

Mock::given(method("PUT"))
.and(path_regex(r"^/_matrix/client/r0/rooms/.*/state/m.room.power_levels/$"))
.and(header("authorization", "Bearer 1234"))
.and(body_partial_json(json!({
"events": {
// 'm.room.avatar' is 100 here, if we receive a value '50', the reset worked
"m.room.avatar": 50,
"m.room.canonical_alias": 50,
"m.room.history_visibility": 100,
"m.room.name": 50,
"m.room.power_levels": 100,
"m.room.topic": 50
},
})))
.respond_with(ResponseTemplate::new(200).set_body_json(&*test_json::EVENT_ID))
.expect(1)
.mount(&server)
.await;

let initial_power_levels = room.room_power_levels().await.unwrap();
assert_eq!(initial_power_levels.events[&TimelineEventType::RoomAvatar], int!(100));

room.reset_power_levels().await.unwrap();
}
141 changes: 141 additions & 0 deletions testing/matrix-sdk-test/src/test_json/sync.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1714,3 +1714,144 @@ pub static SYNC_ADMIN_AND_MOD: Lazy<JsonValue> = Lazy::new(|| {
}
})
});

pub static CUSTOM_ROOM_POWER_LEVELS: Lazy<JsonValue> = Lazy::new(|| {
json!({
"device_one_time_keys_count": {},
"next_batch": "s526_47314_0_7_1_1_1_11444_1",
"device_lists": {
"changed": [
"@admin:example.org"
],
"left": []
},
"rooms": {
"invite": {},
"join": {
*DEFAULT_TEST_ROOM_ID: {
"summary": {
"m.heroes": [
"@example2:localhost"
],
"m.joined_member_count": 1,
"m.invited_member_count": 0
},
"account_data": {
"events": []
},
"ephemeral": {
"events": []
},
"state": {
"events": [
{
"content": {
"join_rule": "public"
},
"event_id": "$15139375514WsgmR:localhost",
"origin_server_ts": 151393755000000_u64,
"sender": "@admin:localhost",
"state_key": "",
"type": "m.room.join_rules",
"unsigned": {
"age": 7034220
}
},
{
"content": {
"avatar_url": null,
"displayname": "admin",
"membership": "join"
},
"event_id": "$151800140517rfvjc:localhost",
"membership": "join",
"origin_server_ts": 151800140000000_u64,
"sender": "@admin:localhost",
"state_key": "@admin:localhost",
"type": "m.room.member",
"unsigned": {
"age": 297036,
"replaces_state": "$151800111315tsynI:localhost"
}
},
{
"content": {
"creator": "@example:localhost"
},
"event_id": "$15139375510KUZHi:localhost",
"origin_server_ts": 151393755000000_u64,
"sender": "@admin:localhost",
"state_key": "",
"type": "m.room.create",
"unsigned": {
"age": 703422
}
},
{
"content": {
"ban": 100,
"events": {
"m.room.avatar": 100,
"m.room.canonical_alias": 50,
"m.room.history_visibility": 100,
"m.room.name": 50,
"m.room.power_levels": 100
},
"events_default": 0,
"invite": 0,
"kick": 50,
"redact": 50,
"state_default": 50,
"users": {
"@admin:localhost": 100
},
"users_default": 0
},
"event_id": "$15139375512JaHAW:localhost",
"origin_server_ts": 151393755000000_u64,
"sender": "@admin:localhost",
"state_key": "",
"type": "m.room.power_levels",
"unsigned": {
"age": 703422
}
}
]
},
"timeline": {
"events": [
{
"content": {
"body": "baba",
"format": "org.matrix.custom.html",
"formatted_body": "<strong>baba</strong>",
"msgtype": "m.text"
},
"event_id": "$152037280074GZeOm:localhost",
"origin_server_ts": 152037280000000_u64,
"sender": "@admin:localhost",
"type": "m.room.message",
"unsigned": {
"age": 598971425
}
}
],
"limited": true,
"prev_batch": "t392-516_47314_0_7_1_1_1_11444_1"
},
"unread_notifications": {
"highlight_count": 0,
"notification_count": 11
}
}
},
"leave": {}
},
"to_device": {
"events": []
},
"presence": {
"events": []
}
})
});

0 comments on commit 0469c27

Please sign in to comment.