room preview: rejigger public API to pass a RoomOrAliasId in place of a RoomId to get_room_preview

This commit is contained in:
Benjamin Bouvier
2024-05-14 18:22:48 +02:00
parent 1fd29f7b6d
commit 7ae0bcecfd
5 changed files with 90 additions and 36 deletions

View File

@@ -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")?;
let sdk_room_preview = self.inner.get_room_preview(&room_id, via_servers).await?;
// 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;
Ok(RoomPreview::from_sdk(room_id, sdk_room_preview))
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_alias.into(), Vec::new()).await?;
Ok(RoomPreview::from_sdk(sdk_room_preview))
}
}

View File

@@ -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)]
@@ -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,

View File

@@ -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

View File

@@ -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};
@@ -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>,
@@ -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),
@@ -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}");
@@ -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.
@@ -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,
);
@@ -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,

View File

@@ -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));
}
@@ -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());
}