Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 8 additions & 8 deletions Cargo.lock

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

5 changes: 3 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ proptest = { version = "1.6.0", default-features = false, features = ["std"] }
rand = "0.8.5"
reqwest = { version = "0.12.12", default-features = false }
rmp-serde = "1.3.0"
ruma = { git = "https://github.com/ruma/ruma", rev = "de19ebaf71af620eb17abaefd92e43153f9d041d", features = [
ruma = { git = "https://github.com/ruma/ruma", rev = "184d4f85b201bc1e932a8344d78575e826f31efa", features = [
"client-api-c",
"compat-upload-signatures",
"compat-arbitrary-length-ids",
Expand All @@ -76,8 +76,9 @@ ruma = { git = "https://github.com/ruma/ruma", rev = "de19ebaf71af620eb17abaefd9
"unstable-msc4171",
"unstable-msc4278",
"unstable-msc4286",
"unstable-msc4306"
] }
ruma-common = { git = "https://github.com/ruma/ruma", rev = "de19ebaf71af620eb17abaefd92e43153f9d041d" }
ruma-common = { git = "https://github.com/ruma/ruma", rev = "184d4f85b201bc1e932a8344d78575e826f31efa" }
sentry = "0.36.0"
sentry-tracing = "0.36.0"
serde = { version = "1.0.217", features = ["rc"] }
Expand Down
4 changes: 4 additions & 0 deletions bindings/matrix-sdk-ffi/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ All notable changes to this project will be documented in this file.

### Features:

- Add experimental support for
[MSC4306](https://github.com/matrix-org/matrix-spec-proposals/pull/4306), with the
`Room::fetch_thread_subscription()` and `Room::set_thread_subscription()` methods.
([#5442](https://github.com/matrix-org/matrix-rust-sdk/pull/5442))
- [**breaking**] [`GalleryUploadParameters::reply`] and [`UploadParameters::reply`] have been both
replaced with a new optional `in_reply_to` field, that's a string which will be parsed into an
`OwnedEventId` when sending the event. The thread relationship will be automatically filled in,
Expand Down
53 changes: 53 additions & 0 deletions bindings/matrix-sdk-ffi/src/room/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1139,6 +1139,59 @@ impl Room {

Ok(Arc::new(RoomPreview::new(AsyncRuntimeDropped::new(client), room_preview)))
}

/// Toggle a MSC4306 subscription to a thread in this room, based on the
/// thread root event id.
///
/// If `subscribed` is `true`, it will subscribe to the thread, with a
/// precision that the subscription was manually requested by the user
/// (i.e. not automatic).
///
/// If the thread was already subscribed to (resp. unsubscribed from), while
/// trying to subscribe to it (resp. unsubscribe from it), it will do
/// nothing, i.e. subscribing (resp. unsubscribing) to a thread is an
/// idempotent operation.
pub async fn set_thread_subscription(
&self,
thread_root_event_id: String,
subscribed: bool,
) -> Result<(), ClientError> {
let thread_root = EventId::parse(thread_root_event_id)?;
if subscribed {
// This is a manual subscription.
let automatic = false;
self.inner.subscribe_thread(thread_root, automatic).await?;
} else {
self.inner.unsubscribe_thread(thread_root).await?;
}
Ok(())
}

/// Return the current MSC4306 thread subscription for the given thread root
/// in this room.
///
/// Returns `None` if the thread doesn't exist, or isn't subscribed to, or
/// the server can't handle MSC4306; otherwise, returns the thread
/// subscription status.
pub async fn fetch_thread_subscription(
&self,
thread_root_event_id: String,
) -> Result<Option<ThreadSubscription>, ClientError> {
let thread_root = EventId::parse(thread_root_event_id)?;
Ok(self
.inner
.fetch_thread_subscription(thread_root)
.await?
.map(|sub| ThreadSubscription { automatic: sub.automatic }))
}
}

/// Status of a thread subscription (MSC4306).
#[derive(uniffi::Record)]
pub struct ThreadSubscription {
/// Whether the thread subscription happened automatically (e.g. after a
/// mention) or if it was manually requested by the user.
automatic: bool,
}

/// A listener for receiving new live location shares in a room.
Expand Down
5 changes: 5 additions & 0 deletions crates/matrix-sdk/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@ All notable changes to this project will be documented in this file.

### Features

- Add experimental support for
[MSC4306](https://github.com/matrix-org/matrix-spec-proposals/pull/4306), with the
`Room::fetch_thread_subscription()`, `Room::subscribe_thread()` and `Room::unsubscribe_thread()`
methods.
([#5442](https://github.com/matrix-org/matrix-rust-sdk/pull/5442))
Copy link
Member

Choose a reason for hiding this comment

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

It's 5439, not 5442 :-D.

- [**breaking**] `RoomMemberRole` has a new `Creator` variant, that
differentiates room creators with infinite power levels, as introduced in room
version 12.
Expand Down
109 changes: 104 additions & 5 deletions crates/matrix-sdk/src/room/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,8 +78,9 @@ use ruma::{
receipt::create_receipt,
redact::redact_event,
room::{get_room_event, report_content, report_room},
state::{get_state_events_for_key, send_state_event},
state::{get_state_event_for_key, send_state_event},
tag::{create_tag, delete_tag},
threads::{get_thread_subscription, subscribe_thread, unsubscribe_thread},
typing::create_typing_event::{self, v3::Typing},
},
assign,
Expand Down Expand Up @@ -814,15 +815,15 @@ impl Room {
.encryption_state_deduplicated_handler
.run(self.room_id().to_owned(), async move {
// Request the event from the server.
let request = get_state_events_for_key::v3::Request::new(
let request = get_state_event_for_key::v3::Request::new(
self.room_id().to_owned(),
StateEventType::RoomEncryption,
"".to_owned(),
);
let response = match self.client.send(request).await {
Ok(response) => Some(
response
.content
.into_content()
.deserialize_as_unchecked::<RoomEncryptionEventContent>()?,
),
Err(err) if err.client_api_error_kind() == Some(&ErrorKind::NotFound) => None,
Expand Down Expand Up @@ -3490,7 +3491,7 @@ impl Room {
pub async fn load_pinned_events(&self) -> Result<Option<Vec<OwnedEventId>>> {
let response = self
.client
.send(get_state_events_for_key::v3::Request::new(
.send(get_state_event_for_key::v3::Request::new(
self.room_id().to_owned(),
StateEventType::RoomPinnedEvents,
"".to_owned(),
Expand All @@ -3499,7 +3500,10 @@ impl Room {

match response {
Ok(response) => Ok(Some(
response.content.deserialize_as_unchecked::<RoomPinnedEventsEventContent>()?.pinned,
response
.into_content()
.deserialize_as_unchecked::<RoomPinnedEventsEventContent>()?
.pinned,
)),
Err(http_error) => match http_error.as_client_api_error() {
Some(error) if error.status_code == StatusCode::NOT_FOUND => Ok(None),
Expand Down Expand Up @@ -3708,6 +3712,101 @@ impl Room {
) -> Result<Relations> {
opts.send(self, event_id).await
}

/// Subscribe to a given thread in this room.
///
/// This will subscribe the user to the thread, so that they will receive
/// notifications for that thread specifically.
///
/// # Arguments
///
/// - `thread_root`: The ID of the thread root event to subscribe to.
/// - `automatic`: Whether the subscription was made automatically by a
/// client, not by manual user choice. If there was a previous automatic
/// subscription, and that's set to `true` (i.e. we're now subscribing
/// manually), the subscription will be overridden to a manual one
/// instead.
///
/// # Returns
///
/// - A 404 error if the event isn't known, or isn't a thread root.
/// - An `Ok` result if the subscription was successful.
pub async fn subscribe_thread(&self, thread_root: OwnedEventId, automatic: bool) -> Result<()> {
self.client
.send(subscribe_thread::unstable::Request::new(
self.room_id().to_owned(),
thread_root,
automatic,
))
.await?;
Ok(())
}

/// Unsubscribe from a given thread in this room.
///
/// # Arguments
///
/// - `thread_root`: The ID of the thread root event to unsubscribe to.
///
/// # Returns
///
/// - An `Ok` result if the unsubscription was successful, or the thread was
/// already unsubscribed.
/// - A 404 error if the event isn't known, or isn't a thread root.
pub async fn unsubscribe_thread(&self, thread_root: OwnedEventId) -> Result<()> {
self.client
.send(unsubscribe_thread::unstable::Request::new(
self.room_id().to_owned(),
thread_root,
))
.await?;
Ok(())
}

/// Return the current thread subscription for the given thread root in this
/// room.
///
/// # Arguments
///
/// - `thread_root`: The ID of the thread root event to get the subscription
/// for.
///
/// # Returns
///
/// - An `Ok` result with `Some(ThreadSubscription)` if the subscription
/// exists.
/// - An `Ok` result with `None` if the subscription does not exist, or the
/// event couldn't be found, or the event isn't a thread.
/// - An error if the request fails for any other reason, such as a network
/// error.
pub async fn fetch_thread_subscription(
&self,
thread_root: OwnedEventId,
) -> Result<Option<ThreadSubscription>> {
let result = self
.client
.send(get_thread_subscription::unstable::Request::new(
self.room_id().to_owned(),
thread_root,
))
.await;

match result {
Ok(response) => Ok(Some(ThreadSubscription { automatic: response.automatic })),
Err(http_error) => match http_error.as_client_api_error() {
Some(error) if error.status_code == StatusCode::NOT_FOUND => Ok(None),
_ => Err(http_error.into()),
},
}
}
}

/// Status of a thread subscription.
#[derive(Debug, Clone, Copy)]
pub struct ThreadSubscription {
/// Whether the subscription was made automatically by a client, not by
/// manual user choice.
pub automatic: bool,
}

#[cfg(feature = "e2e-encryption")]
Expand Down
Loading
Loading