From e673f85a825a66bfa01ff2b982f50bca9797489c Mon Sep 17 00:00:00 2001 From: Mauro Romito Date: Tue, 5 May 2026 18:22:54 +0200 Subject: [PATCH] feature (bindings): expose beacon and beacon_info power levels on the FFI --- .../matrix-sdk-ffi/src/room/power_levels.rs | 18 ++++- crates/matrix-sdk/src/room/power_levels.rs | 78 ++++++++++++++++++- 2 files changed, 94 insertions(+), 2 deletions(-) diff --git a/bindings/matrix-sdk-ffi/src/room/power_levels.rs b/bindings/matrix-sdk-ffi/src/room/power_levels.rs index 6f39d2b21..d26233af4 100644 --- a/bindings/matrix-sdk-ffi/src/room/power_levels.rs +++ b/bindings/matrix-sdk-ffi/src/room/power_levels.rs @@ -17,7 +17,10 @@ use std::collections::HashMap; use anyhow::Result; use ruma::{ OwnedUserId, UserId, - events::{TimelineEventType, room::power_levels::RoomPowerLevels as RumaPowerLevels}, + events::{ + MessageLikeEventType as RumaMessageLikeEventType, StateEventType as RumaStateEventType, + TimelineEventType, room::power_levels::RoomPowerLevels as RumaPowerLevels, + }, }; use crate::{ @@ -226,6 +229,10 @@ pub struct RoomPowerLevelsValues { pub room_topic: i64, /// The level required to change the space's children. pub space_child: i64, + /// The level required to send a beacon (live location) message event. + pub beacon: i64, + /// The level required to send a beacon info state event. + pub beacon_info: i64, } impl From for RoomPowerLevelsValues { @@ -237,6 +244,13 @@ impl From for RoomPowerLevelsValues { let default_state: i64 = power_levels.state_default.into(); power_levels.events.get(event_type).map_or(default_state, |&level| level.into()) } + fn message_event_level_for( + power_levels: &RumaPowerLevels, + event_type: &TimelineEventType, + ) -> i64 { + let default_events: i64 = power_levels.events_default.into(); + power_levels.events.get(event_type).map_or(default_events, |&level| level.into()) + } Self { ban: value.ban.into(), invite: value.invite.into(), @@ -249,6 +263,8 @@ impl From for RoomPowerLevelsValues { room_avatar: state_event_level_for(&value, &TimelineEventType::RoomAvatar), room_topic: state_event_level_for(&value, &TimelineEventType::RoomTopic), space_child: state_event_level_for(&value, &TimelineEventType::SpaceChild), + beacon: message_event_level_for(&value, &RumaMessageLikeEventType::Beacon.into()), + beacon_info: state_event_level_for(&value, &RumaStateEventType::BeaconInfo.into()), } } } diff --git a/crates/matrix-sdk/src/room/power_levels.rs b/crates/matrix-sdk/src/room/power_levels.rs index cb33c1ab2..ab2c91816 100644 --- a/crates/matrix-sdk/src/room/power_levels.rs +++ b/crates/matrix-sdk/src/room/power_levels.rs @@ -5,7 +5,7 @@ use std::collections::HashMap; use ruma::{ OwnedUserId, events::{ - StateEventType, + MessageLikeEventType, StateEventType, room::power_levels::{ PossiblyRedactedRoomPowerLevelsEventContent, RoomPowerLevels, RoomPowerLevelsEventContent, @@ -57,6 +57,12 @@ pub struct RoomPowerLevelChanges { /// The level required to change the space's children. #[cfg_attr(feature = "uniffi", uniffi(default = None))] pub space_child: Option, + /// The level required to send a beacon (live location) message event. + #[cfg_attr(feature = "uniffi", uniffi(default = None))] + pub beacon: Option, + /// The level required to send a beacon info state event. + #[cfg_attr(feature = "uniffi", uniffi(default = None))] + pub beacon_info: Option, } impl RoomPowerLevelChanges { @@ -74,6 +80,8 @@ impl RoomPowerLevelChanges { room_avatar: None, room_topic: None, space_child: None, + beacon: None, + beacon_info: None, } } } @@ -114,6 +122,16 @@ impl From for RoomPowerLevelChanges { .get(&StateEventType::SpaceChild.into()) .map(|v| (*v).into()) .or(Some(value.state_default.into())), + beacon: value + .events + .get(&MessageLikeEventType::Beacon.into()) + .map(|v| (*v).into()) + .or(Some(value.events_default.into())), + beacon_info: value + .events + .get(&StateEventType::BeaconInfo.into()) + .map(|v| (*v).into()) + .or(Some(value.state_default.into())), } } } @@ -162,6 +180,12 @@ impl RoomPowerLevelsExt for RoomPowerLevels { if let Some(space_child) = settings.space_child { self.events.insert(StateEventType::SpaceChild.into(), space_child.try_into()?); } + if let Some(beacon) = settings.beacon { + self.events.insert(MessageLikeEventType::Beacon.into(), beacon.try_into()?); + } + if let Some(beacon_info) = settings.beacon_info { + self.events.insert(StateEventType::BeaconInfo.into(), beacon_info.try_into()?); + } Ok(()) } @@ -236,6 +260,8 @@ mod tests { room_avatar: None, room_topic: None, space_child: None, + beacon: None, + beacon_info: None, }; // When applying the settings to the power levels. @@ -273,6 +299,8 @@ mod tests { room_avatar: Some(new_level.into()), room_topic: Some(new_level.into()), space_child: Some(new_level.into()), + beacon: None, + beacon_info: None, }; // When applying the settings to the power levels. @@ -324,6 +352,8 @@ mod tests { room_avatar: None, room_topic: None, space_child: None, + beacon: None, + beacon_info: None, }; // When applying the settings to the power levels. @@ -351,6 +381,52 @@ mod tests { assert_eq!(power_levels.users_default, original_levels.users_default); } + #[test] + fn test_apply_beacon_settings() { + // Given a set of power levels and some settings that only change the beacon + // and beacon_info event levels. + let mut power_levels = default_power_levels(); + + let new_level = int!(25); + let settings = RoomPowerLevelChanges { + ban: None, + invite: None, + kick: None, + redact: None, + events_default: None, + state_default: None, + users_default: None, + room_name: None, + room_avatar: None, + room_topic: None, + space_child: None, + beacon: Some(new_level.into()), + beacon_info: Some(new_level.into()), + }; + + // When applying the settings to the power levels. + let original_levels = power_levels.clone(); + power_levels.apply(settings).unwrap(); + + // Then levels for the beacon events should be added. + assert_eq!( + power_levels.events.get(&MessageLikeEventType::Beacon.into()).copied(), + Some(new_level) + ); + assert_eq!( + power_levels.events.get(&StateEventType::BeaconInfo.into()).copied(), + Some(new_level) + ); + // And the rest should remain unchanged. + assert_eq!(power_levels.ban, original_levels.ban); + assert_eq!(power_levels.invite, original_levels.invite); + assert_eq!(power_levels.kick, original_levels.kick); + assert_eq!(power_levels.redact, original_levels.redact); + assert_eq!(power_levels.events_default, original_levels.events_default); + assert_eq!(power_levels.state_default, original_levels.state_default); + assert_eq!(power_levels.users_default, original_levels.users_default); + } + #[test] fn test_user_power_level_changes_add_mod() { // Given a set of power levels and a new set of power levels that adds a new