Skip to content

Commit

Permalink
room preview: rejigger public API to pass a RoomOrAliasId in place …
Browse files Browse the repository at this point in the history
…of a `RoomId` to `get_room_preview`
  • Loading branch information
bnjbvr committed May 16, 2024
1 parent 1fd29f7 commit 7ae0bce
Show file tree
Hide file tree
Showing 5 changed files with 90 additions and 36 deletions.
48 changes: 30 additions & 18 deletions bindings/matrix-sdk-ffi/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -771,36 +771,48 @@ impl Client {
Ok(response.into())
}

/// Get the preview of a room, to interact with it.
/// Given a room id, get the preview of a room, to interact with it.
///
/// The (optional) list of `via_servers` must be a list of servers that know
/// The list of `via_servers` must be a list of servers that know
/// about the room and can resolve it, and that may appear as a `via`
/// parameter in e.g. a permalink URL. It can be empty.
pub async fn get_room_preview(
/// parameter in e.g. a permalink URL. This list can be empty.
pub async fn get_room_preview_from_room_id(
&self,
room_id_or_alias: String,
room_id: String,
via_servers: Vec<String>,
) -> Result<RoomPreview, ClientError> {
let room_id = if let Ok(parsed_room_id) = RoomId::parse(&room_id_or_alias) {
parsed_room_id
} else {
// Try to resolve it as an alias.
let Ok(room_alias) = RoomAliasId::parse(&room_id_or_alias) else {
return Err(anyhow!("room_id_or_alias is neither a room id or an alias").into());
};
let response = self.inner.resolve_room_alias(&room_alias).await?;
response.room_id
};
let room_id = RoomId::parse(&room_id).context("room_id is not a valid room id")?;

let via_servers = via_servers
.into_iter()
.map(ServerName::parse)
.collect::<Result<Vec<_>, _>>()
.context("invalid via server name")?;
.context("at least one `via` server name is invalid")?;

// The `into()` call below doesn't work if I do `(&room_id).into()`, so I let
// rustc win that one fight.
let room_id: &RoomId = &room_id;

let sdk_room_preview = self.inner.get_room_preview(room_id.into(), via_servers).await?;

Ok(RoomPreview::from_sdk(sdk_room_preview))
}

/// Given a room alias, get the preview of a room, to interact with it.
pub async fn get_room_preview_from_room_alias(
&self,
room_alias: String,
) -> Result<RoomPreview, ClientError> {
let room_alias =
RoomAliasId::parse(&room_alias).context("room_alias is not a valid room alias")?;

// The `into()` call below doesn't work if I do `(&room_id).into()`, so I let
// rustc win that one fight.
let room_alias: &RoomAliasId = &room_alias;

let sdk_room_preview = self.inner.get_room_preview(&room_id, via_servers).await?;
let sdk_room_preview = self.inner.get_room_preview(room_alias.into(), Vec::new()).await?;

Ok(RoomPreview::from_sdk(room_id, sdk_room_preview))
Ok(RoomPreview::from_sdk(sdk_room_preview))
}
}

Expand Down
6 changes: 3 additions & 3 deletions bindings/matrix-sdk-ffi/src/room_preview.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use matrix_sdk::{room_preview::RoomPreview as SdkRoomPreview, RoomState};
use ruma::{space::SpaceRoomJoinRule, OwnedRoomId};
use ruma::space::SpaceRoomJoinRule;

/// The preview of a room, be it invited/joined/left, or not.
#[derive(uniffi::Record)]
Expand Down Expand Up @@ -31,9 +31,9 @@ pub struct RoomPreview {
}

impl RoomPreview {
pub(crate) fn from_sdk(room_id: OwnedRoomId, preview: SdkRoomPreview) -> Self {
pub(crate) fn from_sdk(preview: SdkRoomPreview) -> Self {
Self {
room_id: room_id.to_string(),
room_id: preview.room_id.to_string(),
canonical_alias: preview.canonical_alias.map(|alias| alias.to_string()),
name: preview.name,
topic: preview.topic,
Expand Down
12 changes: 9 additions & 3 deletions crates/matrix-sdk/src/client/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -946,13 +946,19 @@ impl Client {
/// they've joined/left/been invited to it) or not.
pub async fn get_room_preview(
&self,
room_id: &RoomId,
room_or_alias_id: &RoomOrAliasId,
via: Vec<OwnedServerName>,
) -> Result<RoomPreview> {
if let Some(room) = self.get_room(room_id) {
let room_id = match <&RoomId>::try_from(room_or_alias_id) {
Ok(room_id) => room_id.to_owned(),
Err(alias) => self.resolve_room_alias(alias).await?.room_id,
};

if let Some(room) = self.get_room(&room_id) {
return Ok(RoomPreview::from_known(&room));
}
RoomPreview::from_unknown(self, room_id, via).await

RoomPreview::from_unknown(self, room_id, room_or_alias_id, via).await
}

/// Resolve a room alias to a room id and a list of servers which know
Expand Down
24 changes: 17 additions & 7 deletions crates/matrix-sdk/src/room_preview.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ use ruma::{
events::room::{history_visibility::HistoryVisibility, join_rules::JoinRule},
room::RoomType,
space::SpaceRoomJoinRule,
OwnedMxcUri, OwnedRoomAliasId, OwnedServerName, RoomId,
OwnedMxcUri, OwnedRoomAliasId, OwnedRoomId, OwnedServerName, RoomId, RoomOrAliasId,
};
use tokio::try_join;
use tracing::{instrument, warn};
Expand All @@ -34,6 +34,12 @@ use crate::{Client, Room};
/// The preview of a room, be it invited/joined/left, or not.
#[derive(Debug)]
pub struct RoomPreview {
/// The actual room id for this room.
///
/// Remember the room preview can be fetched from a room alias id, so we
/// might not know ahead of time what the room id is.
pub room_id: OwnedRoomId,

/// The canonical alias for the room.
pub canonical_alias: Option<OwnedRoomAliasId>,

Expand Down Expand Up @@ -76,6 +82,7 @@ impl RoomPreview {
state: Option<RoomState>,
) -> Self {
RoomPreview {
room_id: room_info.room_id().to_owned(),
canonical_alias: room_info.canonical_alias().map(ToOwned::to_owned),
name: room_info.name().map(ToOwned::to_owned),
topic: room_info.topic().map(ToOwned::to_owned),
Expand Down Expand Up @@ -110,12 +117,13 @@ impl RoomPreview {
#[instrument(skip(client))]
pub(crate) async fn from_unknown(
client: &Client,
room_id: &RoomId,
room_id: OwnedRoomId,
room_or_alias_id: &RoomOrAliasId,
via: Vec<OwnedServerName>,
) -> crate::Result<Self> {
// Use the room summary endpoint, if available, as described in
// https://github.com/deepbluev7/matrix-doc/blob/room-summaries/proposals/3266-room-summary.md
match Self::from_room_summary(client, room_id, via).await {
match Self::from_room_summary(client, room_id.clone(), room_or_alias_id, via).await {
Ok(res) => return Ok(res),
Err(err) => {
warn!("error when previewing room from the room summary endpoint: {err}");
Expand All @@ -127,7 +135,7 @@ impl RoomPreview {
// - then use a public room filter set to this room id

// Resort to using the room state endpoint, as well as the joined members one.
Self::from_state_events(client, room_id).await
Self::from_state_events(client, &room_id).await
}

/// Get a [`RoomPreview`] using MSC3266, if available on the remote server.
Expand All @@ -138,11 +146,12 @@ impl RoomPreview {
/// `Client::get_room_preview` in general over this.
pub async fn from_room_summary(
client: &Client,
room_id: &RoomId,
room_id: OwnedRoomId,
room_or_alias_id: &RoomOrAliasId,
via: Vec<OwnedServerName>,
) -> crate::Result<Self> {
let request = ruma::api::client::room::get_summary::msc3266::Request::new(
room_id.to_owned().into(),
room_or_alias_id.to_owned(),
via,
);

Expand All @@ -151,13 +160,14 @@ impl RoomPreview {
// The server returns a `Left` room state for rooms the user has not joined. Be
// more precise than that, and set it to `None` if we haven't joined
// that room.
let state = if client.get_room(room_id).is_none() {
let state = if client.get_room(&room_id).is_none() {
None
} else {
response.membership.map(|membership| RoomState::from(&membership))
};

Ok(RoomPreview {
room_id,
canonical_alias: response.canonical_alias,
name: response.name,
topic: response.topic,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1118,7 +1118,7 @@ async fn test_room_preview() -> Result<()> {
{
// Dummy test for `Client::get_room_preview` which may call one or the other
// methods.
let preview = alice.get_room_preview(room_id, Vec::new()).await.unwrap();
let preview = alice.get_room_preview(room_id.into(), Vec::new()).await.unwrap();
assert_room_preview(&preview, &room_alias);
assert_eq!(preview.state, Some(RoomState::Joined));
}
Expand Down Expand Up @@ -1169,21 +1169,47 @@ async fn get_room_preview_with_room_summary(
public_no_history_room_id: &RoomId,
) {
// Alice has joined the room, so they get the full details.
let preview = RoomPreview::from_room_summary(alice, room_id, Vec::new()).await.unwrap();
let preview =
RoomPreview::from_room_summary(alice, room_id.to_owned(), room_id.into(), Vec::new())
.await
.unwrap();

assert_room_preview(&preview, room_alias);
assert_eq!(preview.state, Some(RoomState::Joined));

// The preview also works when using the room alias parameter.
let full_alias = format!("#{room_alias}:{}", alice.user_id().unwrap().server_name());
let preview = RoomPreview::from_room_summary(
alice,
room_id.to_owned(),
<_>::try_from(full_alias.as_str()).unwrap(),
Vec::new(),
)
.await
.unwrap();

assert_room_preview(&preview, room_alias);
assert_eq!(preview.state, Some(RoomState::Joined));

// Bob definitely doesn't know about the room, but they can get a preview of the
// room too.
let preview = RoomPreview::from_room_summary(bob, room_id, Vec::new()).await.unwrap();
let preview =
RoomPreview::from_room_summary(bob, room_id.to_owned(), room_id.into(), Vec::new())
.await
.unwrap();
assert_room_preview(&preview, room_alias);
assert!(preview.state.is_none());

// Bob can preview the second room with the room summary (because its join rule
// is set to public, or because Alice is a member of that room).
let preview =
RoomPreview::from_room_summary(bob, public_no_history_room_id, Vec::new()).await.unwrap();
let preview = RoomPreview::from_room_summary(
bob,
public_no_history_room_id.to_owned(),
public_no_history_room_id.into(),
Vec::new(),
)
.await
.unwrap();
assert_eq!(preview.name.unwrap(), "Alice's Room 2");
assert!(preview.state.is_none());
}

0 comments on commit 7ae0bce

Please sign in to comment.