Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(room): add methods to customise a Room's privacy settings #4401

Merged
merged 9 commits into from
Jan 13, 2025
Merged
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

25 changes: 14 additions & 11 deletions bindings/matrix-sdk-ffi/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1152,17 +1152,6 @@ impl Client {
let alias = RoomAliasId::parse(alias)?;
self.inner.is_room_alias_available(&alias).await.map_err(Into::into)
}

/// Creates a new room alias associated with the provided room id.
pub async fn create_room_alias(
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was just moved to a different file, to have the functions grouped.

&self,
room_alias: String,
room_id: String,
) -> Result<(), ClientError> {
let room_alias = RoomAliasId::parse(room_alias)?;
let room_id = RoomId::parse(room_id)?;
self.inner.create_room_alias(&room_alias, &room_id).await.map_err(Into::into)
}
}

#[matrix_sdk_ffi_macros::export(callback_interface)]
Expand Down Expand Up @@ -1462,13 +1451,27 @@ pub enum RoomVisibility {

/// Indicates that the room will not be shown in the published room list.
Private,

/// A custom value that's not present in the spec.
Custom { value: String },
}

impl From<RoomVisibility> for Visibility {
fn from(value: RoomVisibility) -> Self {
match value {
RoomVisibility::Public => Self::Public,
RoomVisibility::Private => Self::Private,
RoomVisibility::Custom { value } => value.as_str().into(),
}
}
}

impl From<Visibility> for RoomVisibility {
fn from(value: Visibility) -> Self {
match value {
Visibility::Public => Self::Public,
Visibility::Private => Self::Private,
_ => Self::Custom { value: value.as_str().to_owned() },
}
}
}
Expand Down
6 changes: 6 additions & 0 deletions bindings/matrix-sdk-ffi/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,12 @@ impl From<RoomSendQueueError> for ClientError {
}
}

impl From<NotYetImplemented> for ClientError {
fn from(_: NotYetImplemented) -> Self {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we're starting to use that more and more, I wonder if NotYetImplemented should contain more information, i.e. so we know which exact feature isn't yet implemented. (if you agree, this can be part of a separate PR)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good, but as you suggest I'd rather make this change in a new PR.

Self::new("This functionality is not implemented yet.")
}
}

/// Bindings version of the sdk type replacing OwnedUserId/DeviceIds with simple
/// String.
///
Expand Down
166 changes: 163 additions & 3 deletions bindings/matrix-sdk-ffi/src/room.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ use ruma::{
call::notify,
room::{
avatar::ImageInfo as RumaAvatarImageInfo,
message::RoomMessageEventContentWithoutRelation,
history_visibility::HistoryVisibility as RumaHistoryVisibility,
join_rules::JoinRule as RumaJoinRule, message::RoomMessageEventContentWithoutRelation,
power_levels::RoomPowerLevels as RumaPowerLevels, MediaSource,
},
AnyMessageLikeEventContent, AnySyncTimelineEvent, TimelineEventType,
Expand All @@ -33,7 +34,8 @@ use tracing::error;
use super::RUNTIME;
use crate::{
chunk_iterator::ChunkIterator,
error::{ClientError, MediaInfoError, RoomError},
client::{JoinRule, RoomVisibility},
error::{ClientError, MediaInfoError, NotYetImplemented, RoomError},
event::{MessageLikeEventType, RoomMessageEventMessageType, StateEventType},
identity_status_change::IdentityStatusChange,
room_info::RoomInfo,
Expand Down Expand Up @@ -345,7 +347,7 @@ impl Room {
}

pub async fn room_info(&self) -> Result<RoomInfo, ClientError> {
Ok(RoomInfo::new(&self.inner).await?)
RoomInfo::new(&self.inner).await
}

pub fn subscribe_to_room_info_updates(
Expand Down Expand Up @@ -941,6 +943,105 @@ impl Room {
let (cache, _drop_guards) = self.inner.event_cache().await?;
Ok(cache.debug_string().await)
}

/// Update the canonical alias of the room.
///
/// Note that publishing the alias in the room directory is done separately.
pub async fn update_canonical_alias(
&self,
alias: Option<String>,
alt_aliases: Vec<String>,
) -> Result<(), ClientError> {
let new_alias = alias.map(TryInto::try_into).transpose()?;
let new_alt_aliases =
alt_aliases.into_iter().map(RoomAliasId::parse).collect::<Result<_, _>>()?;
self.inner
.privacy_settings()
.update_canonical_alias(new_alias, new_alt_aliases)
.await
.map_err(Into::into)
}

/// Publish a new room alias for this room in the room directory.
///
/// Returns:
/// - `true` if the room alias didn't exist and it's now published.
/// - `false` if the room alias was already present so it couldn't be
/// published.
pub async fn publish_room_alias_in_room_directory(
&self,
alias: String,
) -> Result<bool, ClientError> {
let new_alias = RoomAliasId::parse(alias)?;
self.inner
.privacy_settings()
.publish_room_alias_in_room_directory(&new_alias)
.await
.map_err(Into::into)
}

/// Remove an existing room alias for this room in the room directory.
///
/// Returns:
/// - `true` if the room alias was present and it's now removed from the
/// room directory.
/// - `false` if the room alias didn't exist so it couldn't be removed.
pub async fn remove_room_alias_from_room_directory(
&self,
alias: String,
) -> Result<bool, ClientError> {
let alias = RoomAliasId::parse(alias)?;
self.inner
.privacy_settings()
.remove_room_alias_from_room_directory(&alias)
.await
.map_err(Into::into)
}

/// Enable End-to-end encryption in this room.
pub async fn enable_encryption(&self) -> Result<(), ClientError> {
self.inner.enable_encryption().await.map_err(Into::into)
}

/// Update room history visibility for this room.
pub async fn update_history_visibility(
Velin92 marked this conversation as resolved.
Show resolved Hide resolved
&self,
visibility: RoomHistoryVisibility,
) -> Result<(), ClientError> {
let visibility: RumaHistoryVisibility = visibility.try_into()?;
self.inner
.privacy_settings()
.update_room_history_visibility(visibility)
.await
.map_err(Into::into)
}

/// Update the join rule for this room.
pub async fn update_join_rules(&self, new_rule: JoinRule) -> Result<(), ClientError> {
let new_rule: RumaJoinRule = new_rule.try_into()?;
self.inner.privacy_settings().update_join_rule(new_rule).await.map_err(Into::into)
}

/// Update the room's visibility in the room directory.
pub async fn update_room_visibility(
&self,
visibility: RoomVisibility,
) -> Result<(), ClientError> {
self.inner
.privacy_settings()
.update_room_visibility(visibility.into())
.await
.map_err(Into::into)
}

/// Returns the visibility for this room in the room directory.
///
/// [Public](`RoomVisibility::Public`) rooms are listed in the room
/// directory and can be found using it.
pub async fn get_room_visibility(&self) -> Result<RoomVisibility, ClientError> {
let visibility = self.inner.privacy_settings().get_room_visibility().await?;
Ok(visibility.into())
}
}

impl From<matrix_sdk::room::knock_requests::KnockRequest> for KnockRequest {
Expand Down Expand Up @@ -1254,3 +1355,62 @@ impl TryFrom<ComposerDraftType> for SdkComposerDraftType {
Ok(draft_type)
}
}

#[derive(Debug, Clone, uniffi::Enum)]
pub enum RoomHistoryVisibility {
/// Previous events are accessible to newly joined members from the point
/// they were invited onwards.
///
/// Events stop being accessible when the member's state changes to
/// something other than *invite* or *join*.
Invited,

/// Previous events are accessible to newly joined members from the point
/// they joined the room onwards.
/// Events stop being accessible when the member's state changes to
/// something other than *join*.
Joined,

/// Previous events are always accessible to newly joined members.
///
/// All events in the room are accessible, even those sent when the member
/// was not a part of the room.
Shared,

/// All events while this is the `HistoryVisibility` value may be shared by
/// any participating homeserver with anyone, regardless of whether they
/// have ever joined the room.
WorldReadable,

/// A custom visibility value.
Custom { value: String },
}

impl TryFrom<RumaHistoryVisibility> for RoomHistoryVisibility {
type Error = NotYetImplemented;
fn try_from(value: RumaHistoryVisibility) -> Result<Self, Self::Error> {
match value {
RumaHistoryVisibility::Invited => Ok(RoomHistoryVisibility::Invited),
RumaHistoryVisibility::Shared => Ok(RoomHistoryVisibility::Shared),
RumaHistoryVisibility::WorldReadable => Ok(RoomHistoryVisibility::WorldReadable),
RumaHistoryVisibility::Joined => Ok(RoomHistoryVisibility::Joined),
RumaHistoryVisibility::_Custom(_) => {
Ok(RoomHistoryVisibility::Custom { value: value.to_string() })
}
_ => Err(NotYetImplemented),
}
}
}

impl TryFrom<RoomHistoryVisibility> for RumaHistoryVisibility {
type Error = NotYetImplemented;
fn try_from(value: RoomHistoryVisibility) -> Result<Self, Self::Error> {
match value {
RoomHistoryVisibility::Invited => Ok(RumaHistoryVisibility::Invited),
RoomHistoryVisibility::Shared => Ok(RumaHistoryVisibility::Shared),
RoomHistoryVisibility::Joined => Ok(RumaHistoryVisibility::Joined),
RoomHistoryVisibility::WorldReadable => Ok(RumaHistoryVisibility::WorldReadable),
RoomHistoryVisibility::Custom { .. } => Err(NotYetImplemented),
}
}
}
8 changes: 6 additions & 2 deletions bindings/matrix-sdk-ffi/src/room_info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@ use tracing::warn;

use crate::{
client::JoinRule,
error::ClientError,
notification_settings::RoomNotificationMode,
room::{Membership, RoomHero},
room::{Membership, RoomHero, RoomHistoryVisibility},
room_member::RoomMember,
};

Expand Down Expand Up @@ -60,10 +61,12 @@ pub struct RoomInfo {
pinned_event_ids: Vec<String>,
/// The join rule for this room, if known.
join_rule: Option<JoinRule>,
/// The history visibility for this room, if known.
history_visibility: RoomHistoryVisibility,
}

impl RoomInfo {
pub(crate) async fn new(room: &matrix_sdk::Room) -> matrix_sdk::Result<Self> {
pub(crate) async fn new(room: &matrix_sdk::Room) -> Result<Self, ClientError> {
let unread_notification_counts = room.unread_notification_counts();

let power_levels_map = room.users_with_power_levels().await;
Expand Down Expand Up @@ -128,6 +131,7 @@ impl RoomInfo {
num_unread_mentions: room.num_unread_mentions(),
pinned_event_ids,
join_rule: join_rule.ok(),
history_visibility: room.history_visibility_or_default().try_into()?,
})
}
}
2 changes: 1 addition & 1 deletion bindings/matrix-sdk-ffi/src/room_list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -566,7 +566,7 @@ impl RoomListItem {
}

async fn room_info(&self) -> Result<RoomInfo, ClientError> {
Ok(RoomInfo::new(self.inner.inner_room()).await?)
RoomInfo::new(self.inner.inner_room()).await
}

/// The room's current membership state.
Expand Down
1 change: 1 addition & 0 deletions crates/matrix-sdk/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ All notable changes to this project will be documented in this file.
- Expose `Client::server_versions()` publicly to allow users of the library to
get the versions of Matrix supported by the homeserver.
([#4519](https://github.com/matrix-org/matrix-rust-sdk/pull/4519))
- Create `RoomPrivacySettings` helper to group room settings functionality related to room access and visibility ([#4401](https://github.com/matrix-org/matrix-rust-sdk/pull/4401)).

### Refactor

Expand Down
1 change: 1 addition & 0 deletions crates/matrix-sdk/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ matrix-sdk-test = { workspace = true, optional = true }
mime = { workspace = true }
mime2ext = "0.1.53"
once_cell = { workspace = true }
percent-encoding = "2.3.1"
pin-project-lite = { workspace = true }
rand = { workspace = true , optional = true }
ruma = { workspace = true, features = [
Expand Down
13 changes: 10 additions & 3 deletions crates/matrix-sdk/src/client/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ use ruma::{
api::{
client::{
account::whoami,
alias::{create_alias, get_alias},
alias::{create_alias, delete_alias, get_alias},
device::{delete_devices, get_devices, update_device},
directory::{get_public_rooms, get_public_rooms_filtered},
discovery::{
Expand Down Expand Up @@ -1201,13 +1201,20 @@ impl Client {
}
}

/// Creates a new room alias associated with a room.
/// Adds a new room alias associated with a room to the room directory.
pub async fn create_room_alias(&self, alias: &RoomAliasId, room_id: &RoomId) -> HttpResult<()> {
let request = create_alias::v3::Request::new(alias.to_owned(), room_id.to_owned());
self.send(request).await?;
Ok(())
}

/// Removes a room alias from the room directory.
pub async fn remove_room_alias(&self, alias: &RoomAliasId) -> HttpResult<()> {
let request = delete_alias::v3::Request::new(alias.to_owned());
self.send(request).await?;
Ok(())
}

/// Update the homeserver from the login response well-known if needed.
///
/// # Arguments
Expand Down Expand Up @@ -3163,7 +3170,7 @@ pub(crate) mod tests {
let server = MatrixMockServer::new().await;
let client = server.client_builder().build().await;

server.mock_create_room_alias().ok().expect(1).mount().await;
server.mock_room_directory_create_room_alias().ok().expect(1).mount().await;

let ret = client
.create_room_alias(
Expand Down
9 changes: 9 additions & 0 deletions crates/matrix-sdk/src/room/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,7 @@ use crate::{
room::{
knock_requests::{KnockRequest, KnockRequestMemberInfo},
power_levels::{RoomPowerLevelChanges, RoomPowerLevelsExt},
privacy_settings::RoomPrivacySettings,
},
sync::RoomUpdate,
utils::{IntoRawMessageLikeEventContent, IntoRawStateEventContent},
Expand All @@ -162,6 +163,9 @@ mod member;
mod messages;
pub mod power_levels;

/// Contains all the functionality for modifying the privacy settings in a room.
pub mod privacy_settings;

/// A struct containing methods that are common for Joined, Invited and Left
/// Rooms
#[derive(Debug, Clone)]
Expand Down Expand Up @@ -3357,6 +3361,11 @@ impl Room {
})
.collect())
}

/// Access the room settings related to privacy and visibility.
pub fn privacy_settings(&self) -> RoomPrivacySettings<'_> {
RoomPrivacySettings::new(&self.inner, &self.client)
}
}

#[cfg(all(feature = "e2e-encryption", not(target_arch = "wasm32")))]
Expand Down
Loading
Loading