diff --git a/Cargo.lock b/Cargo.lock index b8cd6efb5d6..7a5e7b44327 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3465,6 +3465,7 @@ dependencies = [ "http 1.1.0", "json-structural-diff", "matrix-sdk", + "matrix-sdk-base", "matrix-sdk-test", "matrix-sdk-ui", "once_cell", @@ -3891,7 +3892,7 @@ name = "oauth2" version = "5.0.0-alpha.4" source = "git+https://github.com/poljar/oauth2-rs?rev=f8e28ce5a7f3278ac85b8593ecdd86f2cf51fa2e#f8e28ce5a7f3278ac85b8593ecdd86f2cf51fa2e" dependencies = [ - "base64 0.22.1", + "base64 0.21.7", "chrono", "getrandom", "http 1.1.0", @@ -5005,7 +5006,7 @@ dependencies = [ [[package]] name = "ruma" version = "0.10.1" -source = "git+https://github.com/ruma/ruma?rev=c37843e9be619ffac8c4d33ad3a6a175cc32610c#c37843e9be619ffac8c4d33ad3a6a175cc32610c" +source = "git+https://github.com/matrix-org/ruma?rev=4d3d8b46fd519012e4585ccf00dbea1eb602c028#4d3d8b46fd519012e4585ccf00dbea1eb602c028" dependencies = [ "assign", "js_int", @@ -5022,7 +5023,7 @@ dependencies = [ [[package]] name = "ruma-client-api" version = "0.18.0" -source = "git+https://github.com/ruma/ruma?rev=c37843e9be619ffac8c4d33ad3a6a175cc32610c#c37843e9be619ffac8c4d33ad3a6a175cc32610c" +source = "git+https://github.com/matrix-org/ruma?rev=4d3d8b46fd519012e4585ccf00dbea1eb602c028#4d3d8b46fd519012e4585ccf00dbea1eb602c028" dependencies = [ "as_variant", "assign", @@ -5045,7 +5046,7 @@ dependencies = [ [[package]] name = "ruma-common" version = "0.13.0" -source = "git+https://github.com/ruma/ruma?rev=c37843e9be619ffac8c4d33ad3a6a175cc32610c#c37843e9be619ffac8c4d33ad3a6a175cc32610c" +source = "git+https://github.com/matrix-org/ruma?rev=4d3d8b46fd519012e4585ccf00dbea1eb602c028#4d3d8b46fd519012e4585ccf00dbea1eb602c028" dependencies = [ "as_variant", "base64 0.22.1", @@ -5077,7 +5078,7 @@ dependencies = [ [[package]] name = "ruma-events" version = "0.28.1" -source = "git+https://github.com/ruma/ruma?rev=c37843e9be619ffac8c4d33ad3a6a175cc32610c#c37843e9be619ffac8c4d33ad3a6a175cc32610c" +source = "git+https://github.com/matrix-org/ruma?rev=4d3d8b46fd519012e4585ccf00dbea1eb602c028#4d3d8b46fd519012e4585ccf00dbea1eb602c028" dependencies = [ "as_variant", "indexmap 2.2.6", @@ -5095,13 +5096,14 @@ dependencies = [ "thiserror", "tracing", "url", + "web-time", "wildmatch", ] [[package]] name = "ruma-federation-api" version = "0.9.0" -source = "git+https://github.com/ruma/ruma?rev=c37843e9be619ffac8c4d33ad3a6a175cc32610c#c37843e9be619ffac8c4d33ad3a6a175cc32610c" +source = "git+https://github.com/matrix-org/ruma?rev=4d3d8b46fd519012e4585ccf00dbea1eb602c028#4d3d8b46fd519012e4585ccf00dbea1eb602c028" dependencies = [ "js_int", "ruma-common", @@ -5113,7 +5115,7 @@ dependencies = [ [[package]] name = "ruma-html" version = "0.2.0" -source = "git+https://github.com/ruma/ruma?rev=c37843e9be619ffac8c4d33ad3a6a175cc32610c#c37843e9be619ffac8c4d33ad3a6a175cc32610c" +source = "git+https://github.com/matrix-org/ruma?rev=4d3d8b46fd519012e4585ccf00dbea1eb602c028#4d3d8b46fd519012e4585ccf00dbea1eb602c028" dependencies = [ "as_variant", "html5ever", @@ -5125,7 +5127,7 @@ dependencies = [ [[package]] name = "ruma-identifiers-validation" version = "0.9.5" -source = "git+https://github.com/ruma/ruma?rev=c37843e9be619ffac8c4d33ad3a6a175cc32610c#c37843e9be619ffac8c4d33ad3a6a175cc32610c" +source = "git+https://github.com/matrix-org/ruma?rev=4d3d8b46fd519012e4585ccf00dbea1eb602c028#4d3d8b46fd519012e4585ccf00dbea1eb602c028" dependencies = [ "js_int", "thiserror", @@ -5134,7 +5136,7 @@ dependencies = [ [[package]] name = "ruma-macros" version = "0.13.0" -source = "git+https://github.com/ruma/ruma?rev=c37843e9be619ffac8c4d33ad3a6a175cc32610c#c37843e9be619ffac8c4d33ad3a6a175cc32610c" +source = "git+https://github.com/matrix-org/ruma?rev=4d3d8b46fd519012e4585ccf00dbea1eb602c028#4d3d8b46fd519012e4585ccf00dbea1eb602c028" dependencies = [ "once_cell", "proc-macro-crate", @@ -5149,7 +5151,7 @@ dependencies = [ [[package]] name = "ruma-push-gateway-api" version = "0.9.0" -source = "git+https://github.com/ruma/ruma?rev=c37843e9be619ffac8c4d33ad3a6a175cc32610c#c37843e9be619ffac8c4d33ad3a6a175cc32610c" +source = "git+https://github.com/matrix-org/ruma?rev=4d3d8b46fd519012e4585ccf00dbea1eb602c028#4d3d8b46fd519012e4585ccf00dbea1eb602c028" dependencies = [ "js_int", "ruma-common", diff --git a/Cargo.toml b/Cargo.toml index cef583e1295..651ca1aa483 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -47,7 +47,7 @@ once_cell = "1.16.0" pin-project-lite = "0.2.9" rand = "0.8.5" reqwest = { version = "0.12.4", default-features = false } -ruma = { git = "https://github.com/ruma/ruma", rev = "c37843e9be619ffac8c4d33ad3a6a175cc32610c", features = [ +ruma = { git = "https://github.com/matrix-org/ruma", rev = "4d3d8b46fd519012e4585ccf00dbea1eb602c028", features = [ "client-api-c", "compat-upload-signatures", "compat-user-id", @@ -59,7 +59,7 @@ ruma = { git = "https://github.com/ruma/ruma", rev = "c37843e9be619ffac8c4d33ad3 "unstable-msc4075", "unstable-msc4140", ] } -ruma-common = { git = "https://github.com/ruma/ruma", rev = "c37843e9be619ffac8c4d33ad3a6a175cc32610c" } +ruma-common = { git = "https://github.com/matrix-org/ruma", rev = "4d3d8b46fd519012e4585ccf00dbea1eb602c028" } serde = "1.0.151" serde_html_form = "0.2.0" serde_json = "1.0.91" diff --git a/bindings/matrix-sdk-ffi/src/client_builder.rs b/bindings/matrix-sdk-ffi/src/client_builder.rs index c1e9f87119c..a7cca427b28 100644 --- a/bindings/matrix-sdk-ffi/src/client_builder.rs +++ b/bindings/matrix-sdk-ffi/src/client_builder.rs @@ -251,6 +251,7 @@ pub struct ClientBuilder { user_agent: Option, requires_sliding_sync: bool, sliding_sync_proxy: Option, + is_simplified_sliding_sync_enabled: bool, proxy: Option, disable_ssl_verification: bool, disable_automatic_token_refresh: bool, @@ -272,6 +273,8 @@ impl ClientBuilder { user_agent: None, requires_sliding_sync: false, sliding_sync_proxy: None, + // By default, Simplified MSC3575 is turned off. + is_simplified_sliding_sync_enabled: false, proxy: None, disable_ssl_verification: false, disable_automatic_token_refresh: false, @@ -366,6 +369,12 @@ impl ClientBuilder { Arc::new(builder) } + pub fn simplified_sliding_sync(self: Arc, enable: bool) -> Arc { + let mut builder = unwrap_or_clone_arc(self); + builder.is_simplified_sliding_sync_enabled = enable; + Arc::new(builder) + } + pub fn proxy(self: Arc, url: String) -> Arc { let mut builder = unwrap_or_clone_arc(self); builder.proxy = Some(url); @@ -499,6 +508,9 @@ impl ClientBuilder { inner_builder = inner_builder.with_encryption_settings(builder.encryption_settings); + inner_builder = + inner_builder.simplified_sliding_sync(builder.is_simplified_sliding_sync_enabled); + if builder.requires_sliding_sync { inner_builder = inner_builder.requires_sliding_sync(); } diff --git a/bindings/matrix-sdk-ffi/src/room_list.rs b/bindings/matrix-sdk-ffi/src/room_list.rs index 5198cb02e84..f0e2f5ba607 100644 --- a/bindings/matrix-sdk-ffi/src/room_list.rs +++ b/bindings/matrix-sdk-ffi/src/room_list.rs @@ -2,12 +2,12 @@ use std::{fmt::Debug, mem::MaybeUninit, ptr::addr_of_mut, sync::Arc, time::Durat use eyeball_im::VectorDiff; use futures_util::{pin_mut, StreamExt, TryFutureExt}; -use matrix_sdk::ruma::{ - api::client::sync::sync_events::{ - v4::RoomSubscription as RumaRoomSubscription, - UnreadNotificationsCount as RumaUnreadNotificationsCount, +use matrix_sdk::{ + ruma::{ + api::client::sync::sync_events::UnreadNotificationsCount as RumaUnreadNotificationsCount, + assign, RoomId, }, - assign, RoomId, + sliding_sync::http, }; use matrix_sdk_ui::{ room_list_service::filters::{ @@ -653,10 +653,6 @@ impl RoomListItem { self.inner.subscribe(settings.map(Into::into)); } - fn unsubscribe(&self) { - self.inner.unsubscribe(); - } - async fn latest_event(&self) -> Option> { self.inner.latest_event().await.map(EventTimelineItem).map(Arc::new) } @@ -675,9 +671,9 @@ pub struct RoomSubscription { pub include_heroes: Option, } -impl From for RumaRoomSubscription { +impl From for http::request::RoomSubscription { fn from(val: RoomSubscription) -> Self { - assign!(RumaRoomSubscription::default(), { + assign!(http::request::RoomSubscription::default(), { required_state: val.required_state.map(|r| r.into_iter().map(|s| (s.key.into(), s.value)).collect() ).unwrap_or_default(), diff --git a/crates/matrix-sdk-base/Cargo.toml b/crates/matrix-sdk-base/Cargo.toml index fcf46c0ff52..3bc12814811 100644 --- a/crates/matrix-sdk-base/Cargo.toml +++ b/crates/matrix-sdk-base/Cargo.toml @@ -22,7 +22,10 @@ js = ["matrix-sdk-common/js", "matrix-sdk-crypto?/js", "ruma/js", "matrix-sdk-st qrcode = ["matrix-sdk-crypto?/qrcode"] automatic-room-key-forwarding = ["matrix-sdk-crypto?/automatic-room-key-forwarding"] message-ids = ["matrix-sdk-crypto?/message-ids"] -experimental-sliding-sync = ["ruma/unstable-msc3575"] +experimental-sliding-sync = [ + "ruma/unstable-msc3575", + "ruma/unstable-simplified-msc3575", +] uniffi = ["dep:uniffi", "matrix-sdk-crypto?/uniffi"] # helpers for testing features build upon this diff --git a/crates/matrix-sdk-base/src/lib.rs b/crates/matrix-sdk-base/src/lib.rs index 997787a9085..1331c95177e 100644 --- a/crates/matrix-sdk-base/src/lib.rs +++ b/crates/matrix-sdk-base/src/lib.rs @@ -34,7 +34,7 @@ mod rooms; pub mod read_receipts; pub use read_receipts::PreviousEventsProvider; #[cfg(feature = "experimental-sliding-sync")] -mod sliding_sync; +pub mod sliding_sync; pub mod store; pub mod sync; diff --git a/crates/matrix-sdk-base/src/rooms/normal.rs b/crates/matrix-sdk-base/src/rooms/normal.rs index c84636cf894..777be9ad83b 100644 --- a/crates/matrix-sdk-base/src/rooms/normal.rs +++ b/crates/matrix-sdk-base/src/rooms/normal.rs @@ -24,6 +24,8 @@ use bitflags::bitflags; use eyeball::{SharedObservable, Subscriber}; #[cfg(all(feature = "e2e-encryption", feature = "experimental-sliding-sync"))] use matrix_sdk_common::ring_buffer::RingBuffer; +#[cfg(feature = "experimental-sliding-sync")] +use ruma::events::AnySyncTimelineEvent; use ruma::{ api::client::sync::sync_events::v3::RoomSummary as RumaSummary, events::{ @@ -49,8 +51,6 @@ use ruma::{ EventId, MxcUri, OwnedEventId, OwnedMxcUri, OwnedRoomAliasId, OwnedRoomId, OwnedUserId, RoomAliasId, RoomId, RoomVersionId, UserId, }; -#[cfg(feature = "experimental-sliding-sync")] -use ruma::{events::AnySyncTimelineEvent, MilliSecondsSinceUnixEpoch}; use serde::{Deserialize, Serialize}; use tokio::sync::broadcast; use tracing::{debug, field::debug, info, instrument, warn}; @@ -91,8 +91,8 @@ bitflags! { /// The reason why a [`RoomInfoNotableUpdate`] is emitted. #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub struct RoomInfoNotableUpdateReasons: u8 { - /// The recency timestamp of the `Room` has changed. - const RECENCY_TIMESTAMP = 0b0000_0001; + /// The recency stamp of the `Room` has changed. + const RECENCY_STAMP = 0b0000_0001; /// The latest event of the `Room` has changed. const LATEST_EVENT = 0b0000_0010; @@ -940,12 +940,12 @@ impl Room { self.inner.read().base_info.is_marked_unread } - /// Returns the recency timestamp of the room. + /// Returns the recency stamp of the room. /// - /// Please read `RoomInfo::recency_timestamp` to learn more. + /// Please read `RoomInfo::recency_stamp` to learn more. #[cfg(feature = "experimental-sliding-sync")] - pub fn recency_timestamp(&self) -> Option { - self.inner.read().recency_timestamp + pub fn recency_stamp(&self) -> Option { + self.inner.read().recency_stamp } } @@ -1006,15 +1006,15 @@ pub struct RoomInfo { #[serde(default, skip_serializing_if = "Option::is_none")] pub(crate) cached_display_name: Option, - /// The recency timestamp of this room. + /// The recency stamp of this room. /// /// It's not to be confused with `origin_server_ts` of the latest event. /// Sliding Sync might "ignore” some events when computing the recency - /// timestamp of the room. Thus, using this `recency_timestamp` value is + /// stamp of the room. Thus, using this `recency_stamp` value is /// more accurate than relying on the latest event. #[cfg(feature = "experimental-sliding-sync")] #[serde(default)] - pub(crate) recency_timestamp: Option, + pub(crate) recency_stamp: Option, } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] @@ -1053,7 +1053,7 @@ impl RoomInfo { warned_about_unknown_room_version: Arc::new(false.into()), cached_display_name: None, #[cfg(feature = "experimental-sliding-sync")] - recency_timestamp: None, + recency_stamp: None, } } @@ -1459,12 +1459,12 @@ impl RoomInfo { self.latest_event.as_deref() } - /// Updates the recency timestamp of this room. + /// Updates the recency stamp of this room. /// - /// Please read [`Self::recency_timestamp`] to learn more. + /// Please read [`Self::recency_stamp`] to learn more. #[cfg(feature = "experimental-sliding-sync")] - pub(crate) fn update_recency_timestamp(&mut self, timestamp: MilliSecondsSinceUnixEpoch) { - self.recency_timestamp = Some(timestamp); + pub(crate) fn update_recency_stamp(&mut self, stamp: u64) { + self.recency_stamp = Some(stamp); } } @@ -1675,7 +1675,7 @@ mod tests { read_receipts: Default::default(), warned_about_unknown_room_version: Arc::new(false.into()), cached_display_name: None, - recency_timestamp: Some(MilliSecondsSinceUnixEpoch(42u32.into())), + recency_stamp: Some(42), }; let info_json = json!({ @@ -1728,7 +1728,7 @@ mod tests { "latest_active": null, "pending": [] }, - "recency_timestamp": 42, + "recency_stamp": 42, }); assert_eq!(serde_json::to_value(info).unwrap(), info_json); diff --git a/crates/matrix-sdk-base/src/sliding_sync/http.rs b/crates/matrix-sdk-base/src/sliding_sync/http.rs new file mode 100644 index 00000000000..82879253265 --- /dev/null +++ b/crates/matrix-sdk-base/src/sliding_sync/http.rs @@ -0,0 +1,50 @@ +// Copyright 2024 The Matrix.org Foundation C.I.C. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! HTTP types for (Simplified) MSC3575. +//! +//! This module provides unified namings for types from MSC3575 and +//! Simplified MSC3575, in addition to provide conversion from one +//! format to another. + +/// HTTP types from MSC3575, renamed to match the Simplified MSC3575 namings. +pub mod msc3575 { + use ruma::api::client::sync::sync_events::v4; + pub use v4::{Request, Response}; + + /// HTTP types related to a `Request`. + pub mod request { + pub use super::v4::{ + AccountDataConfig as AccountData, ExtensionsConfig as Extensions, + ReceiptsConfig as Receipts, RoomDetailsConfig as RoomDetails, RoomSubscription, + SyncRequestList as List, SyncRequestListFilters as ListFilters, + ToDeviceConfig as ToDevice, TypingConfig as Typing, + }; + } + + /// HTTP types related to a `Response`. + pub mod response { + pub use super::v4::{ + AccountData, Extensions, Receipts, SlidingSyncRoom as Room, + SlidingSyncRoomHero as RoomHero, SyncList as List, ToDevice, Typing, + }; + } +} + +/// HTTP types from Simplified MSC3575. +pub mod simplified_msc3575 { + pub use ruma::api::client::sync::sync_events::v5::*; +} + +pub use simplified_msc3575::*; diff --git a/crates/matrix-sdk-base/src/sliding_sync.rs b/crates/matrix-sdk-base/src/sliding_sync/mod.rs similarity index 93% rename from crates/matrix-sdk-base/src/sliding_sync.rs rename to crates/matrix-sdk-base/src/sliding_sync/mod.rs index 0509afe48c5..0b28e667a6b 100644 --- a/crates/matrix-sdk-base/src/sliding_sync.rs +++ b/crates/matrix-sdk-base/src/sliding_sync/mod.rs @@ -12,6 +12,10 @@ // See the License for the specific language governing permissions and // limitations under the License. +//! Extend `BaseClient` with capabilities to handle MSC3575. + +pub mod http; + use std::collections::BTreeMap; #[cfg(feature = "e2e-encryption")] use std::ops::Deref; @@ -21,10 +25,7 @@ use matrix_sdk_common::deserialized_responses::SyncTimelineEvent; #[cfg(feature = "e2e-encryption")] use ruma::events::AnyToDeviceEvent; use ruma::{ - api::client::sync::sync_events::{ - v3::{self, InvitedRoom}, - v4, - }, + api::client::sync::sync_events::v3::{self, InvitedRoom}, events::{AnyRoomAccountDataEvent, AnySyncStateEvent, AnySyncTimelineEvent}, serde::Raw, JsOption, OwnedRoomId, RoomId, @@ -57,15 +58,16 @@ impl BaseClient { /// store. pub async fn process_sliding_sync_e2ee( &self, - extensions: &v4::Extensions, + extensions: &http::response::Extensions, ) -> Result>> { if extensions.is_empty() { return Ok(Default::default()); } - let v4::Extensions { to_device, e2ee, .. } = extensions; + let http::response::Extensions { to_device, e2ee, .. } = extensions; - let to_device_events = to_device.as_ref().map(|v4| v4.events.clone()).unwrap_or_default(); + let to_device_events = + to_device.as_ref().map(|to_device| to_device.events.clone()).unwrap_or_default(); trace!( to_device_events = to_device_events.len(), @@ -118,10 +120,10 @@ impl BaseClient { #[instrument(skip_all, level = "trace")] pub async fn process_sliding_sync( &self, - response: &v4::Response, + response: &http::Response, previous_events_provider: &PEP, ) -> Result { - let v4::Response { + let http::Response { // FIXME not yet supported by sliding sync. see // https://github.com/matrix-org/matrix-rust-sdk/issues/1014 // next_batch, @@ -337,7 +339,7 @@ impl BaseClient { async fn process_sliding_sync_room( &self, room_id: &RoomId, - room_data: &v4::SlidingSyncRoom, + room_data: &http::response::Room, rooms_account_data: &mut BTreeMap>>, store: &Store, changes: &mut StateChanges, @@ -491,7 +493,7 @@ impl BaseClient { /// otherwise. https://github.com/matrix-org/matrix-spec-proposals/blob/kegan/sync-v3/proposals/3575-sync.md#room-list-parameters fn process_sliding_sync_room_membership( &self, - room_data: &v4::SlidingSyncRoom, + room_data: &http::response::Room, state_events: &[AnySyncStateEvent], store: &Store, room_id: &RoomId, @@ -718,7 +720,7 @@ async fn cache_latest_events( fn process_room_properties( room_id: &RoomId, - room_data: &v4::SlidingSyncRoom, + room_data: &http::response::Room, room_info: &mut RoomInfo, is_new_room: bool, room_info_notable_updates: &mut BTreeMap, @@ -726,7 +728,7 @@ fn process_room_properties( // Handle the room's avatar. // // It can be updated via the state events, or via the - // [`v4::SlidingSyncRoom::avatar`] field. This part of the code handles the + // [`http::ResponseRoom::avatar`] field. This part of the code handles the // latter case. The former case is handled by [`BaseClient::handle_state`]. match &room_data.avatar { // A new avatar! @@ -739,7 +741,7 @@ fn process_room_properties( // Sliding sync doesn't have a room summary, nevertheless it contains the joined // and invited member counts, in addition to the heroes if it's been configured - // to return them (see the [`v4::RoomSubscription::include_heroes`]). + // to return them (see the [`http::RequestRoomSubscription::include_heroes`]). if let Some(count) = room_data.joined_count { room_info.update_joined_member_count(count.into()); } @@ -766,18 +768,20 @@ fn process_room_properties( room_info.mark_members_missing(); } - if let Some(recency_timestamp) = &room_data.timestamp { - if room_info.recency_timestamp.as_ref() != Some(recency_timestamp) { - room_info.update_recency_timestamp(*recency_timestamp); + if let Some(recency_stamp) = &room_data.bump_stamp { + let recency_stamp: u64 = (*recency_stamp).into(); + + if room_info.recency_stamp.as_ref() != Some(&recency_stamp) { + room_info.update_recency_stamp(recency_stamp); - // If it's not a new room, let's emit a `RECENCY_TIMESTAMP` update. + // If it's not a new room, let's emit a `RECENCY_STAMP` update. // For a new room, the room will appear as new, so we don't care about this // update. if !is_new_room { room_info_notable_updates .entry(room_id.to_owned()) .or_default() - .insert(RoomInfoNotableUpdateReasons::RECENCY_TIMESTAMP); + .insert(RoomInfoNotableUpdateReasons::RECENCY_STAMP); } } } @@ -794,7 +798,7 @@ mod tests { use matrix_sdk_common::{deserialized_responses::SyncTimelineEvent, ring_buffer::RingBuffer}; use matrix_sdk_test::async_test; use ruma::{ - api::client::sync::sync_events::{v4, UnreadNotificationsCount}, + api::client::sync::sync_events::UnreadNotificationsCount, assign, event_id, events::{ direct::DirectEventContent, @@ -810,12 +814,11 @@ mod tests { }, mxc_uri, owned_mxc_uri, owned_user_id, room_alias_id, room_id, serde::Raw, - uint, user_id, JsOption, MilliSecondsSinceUnixEpoch, MxcUri, OwnedRoomId, OwnedUserId, - RoomAliasId, RoomId, UserId, + uint, user_id, JsOption, MxcUri, OwnedRoomId, OwnedUserId, RoomAliasId, RoomId, UserId, }; use serde_json::json; - use super::cache_latest_events; + use super::{cache_latest_events, http}; use crate::{ rooms::normal::{RoomHero, RoomInfoNotableUpdateReasons}, store::MemoryStore, @@ -827,7 +830,7 @@ mod tests { async fn test_notification_count_set() { let client = logged_in_base_client(None).await; - let mut response = v4::Response::new("42".to_owned()); + let mut response = http::Response::new("42".to_owned()); let room_id = room_id!("!room:example.org"); let count = assign!(UnreadNotificationsCount::default(), { highlight_count: Some(uint!(13)), @@ -836,7 +839,7 @@ mod tests { response.rooms.insert( room_id.to_owned(), - assign!(v4::SlidingSyncRoom::new(), { + assign!(http::response::Room::new(), { unread_notifications: count.clone() }), ); @@ -856,7 +859,7 @@ mod tests { #[async_test] async fn test_can_process_empty_sliding_sync_response() { let client = logged_in_base_client(None).await; - let empty_response = v4::Response::new("5".to_owned()); + let empty_response = http::Response::new("5".to_owned()); client.process_sliding_sync(&empty_response, &()).await.expect("Failed to process sync"); } @@ -868,7 +871,7 @@ mod tests { // When I send sliding sync response containing a room (with identifiable data // in joined_count) - let mut room = v4::SlidingSyncRoom::new(); + let mut room = http::response::Room::new(); room.joined_count = Some(uint!(41)); let response = response_with_room(room_id, room); let sync_resp = @@ -894,7 +897,7 @@ mod tests { // When I send sliding sync response containing a room with a name set in the // sliding sync response, - let mut room = v4::SlidingSyncRoom::new(); + let mut room = http::response::Room::new(); room.name = Some("little room".to_owned()); let response = response_with_room(room_id, room); let sync_resp = @@ -920,7 +923,7 @@ mod tests { // When I send sliding sync response containing a room with a name set in the // sliding sync response, and a m.room.name event, - let mut room = v4::SlidingSyncRoom::new(); + let mut room = http::response::Room::new(); room.name = Some("little room".to_owned()); set_room_name(&mut room, user_id!("@a:b.c"), "The Name".to_owned()); @@ -944,7 +947,7 @@ mod tests { // When I send sliding sync response containing a room with a name set in the // sliding sync response, - let mut room = v4::SlidingSyncRoom::new(); + let mut room = http::response::Room::new(); set_room_invited(&mut room, inviter, user_id); room.name = Some("name from sliding sync response".to_owned()); let response = response_with_room(room_id, room); @@ -976,7 +979,7 @@ mod tests { // When I send sliding sync response containing a room with a name set in the // sliding sync response, and a m.room.name event, - let mut room = v4::SlidingSyncRoom::new(); + let mut room = http::response::Room::new(); set_room_invited(&mut room, inviter, user_id); @@ -1000,14 +1003,14 @@ mod tests { let user_id = user_id!("@u:e.uk"); // When I join… - let mut room = v4::SlidingSyncRoom::new(); + let mut room = http::response::Room::new(); set_room_joined(&mut room, user_id); let response = response_with_room(room_id, room); client.process_sliding_sync(&response, &()).await.expect("Failed to process sync"); assert_eq!(client.get_room(room_id).unwrap().state(), RoomState::Joined); // And then leave with a `required_state` state event… - let mut room = v4::SlidingSyncRoom::new(); + let mut room = http::response::Room::new(); set_room_left(&mut room, user_id); let response = response_with_room(room_id, room); let sync_resp = @@ -1031,14 +1034,14 @@ mod tests { let client = logged_in_base_client(Some(user_a_id)).await; // When I join… - let mut room = v4::SlidingSyncRoom::new(); + let mut room = http::response::Room::new(); set_room_joined(&mut room, user_a_id); let response = response_with_room(room_id, room); client.process_sliding_sync(&response, &()).await.expect("Failed to process sync"); assert_eq!(client.get_room(room_id).unwrap().state(), RoomState::Joined); // And then get kicked/banned with a `required_state` state event… - let mut room = v4::SlidingSyncRoom::new(); + let mut room = http::response::Room::new(); room.required_state.push(make_state_event( user_b_id, user_a_id.as_str(), @@ -1067,14 +1070,14 @@ mod tests { let user_id = user_id!("@u:e.uk"); // When I join… - let mut room = v4::SlidingSyncRoom::new(); + let mut room = http::response::Room::new(); set_room_joined(&mut room, user_id); let response = response_with_room(room_id, room); client.process_sliding_sync(&response, &()).await.expect("Failed to process sync"); assert_eq!(client.get_room(room_id).unwrap().state(), RoomState::Joined); // And then leave with a `timeline` state event… - let mut room = v4::SlidingSyncRoom::new(); + let mut room = http::response::Room::new(); set_room_left_as_timeline_event(&mut room, user_id); let response = response_with_room(room_id, room); client.process_sliding_sync(&response, &()).await.expect("Failed to process sync"); @@ -1093,7 +1096,7 @@ mod tests { let user_id = user_id!("@u:e.uk"); // When I join... - let mut room = v4::SlidingSyncRoom::new(); + let mut room = http::response::Room::new(); set_room_joined(&mut room, user_id); let response = response_with_room(room_id, room); client.process_sliding_sync(&response, &()).await.expect("Failed to process sync"); @@ -1101,7 +1104,7 @@ mod tests { assert_eq!(client.get_room(room_id).unwrap().state(), RoomState::Joined); // And then leave... - let mut room = v4::SlidingSyncRoom::new(); + let mut room = http::response::Room::new(); set_room_left(&mut room, user_id); let response = response_with_room(room_id, room); client.process_sliding_sync(&response, &()).await.expect("Failed to process sync"); @@ -1109,7 +1112,7 @@ mod tests { assert_eq!(client.get_room(room_id).unwrap().state(), RoomState::Left); // And then get invited back - let mut room = v4::SlidingSyncRoom::new(); + let mut room = http::response::Room::new(); set_room_invited(&mut room, user_id, user_id); let response = response_with_room(room_id, room); client.process_sliding_sync(&response, &()).await.expect("Failed to process sync"); @@ -1223,7 +1226,7 @@ mod tests { // When I send sliding sync response containing a room with an avatar let room = { - let mut room = v4::SlidingSyncRoom::new(); + let mut room = http::response::Room::new(); room.avatar = JsOption::from_option(Some(mxc_uri!("mxc://e.uk/med1").to_owned())); room @@ -1249,7 +1252,7 @@ mod tests { // When I send sliding sync response containing a room with an avatar let room = { - let mut room = v4::SlidingSyncRoom::new(); + let mut room = http::response::Room::new(); room.avatar = JsOption::from_option(Some(mxc_uri!("mxc://e.uk/med1").to_owned())); room @@ -1267,7 +1270,7 @@ mod tests { // No avatar. Still here. // When I send sliding sync response containing no avatar. - let room = v4::SlidingSyncRoom::new(); + let room = http::response::Room::new(); let response = response_with_room(room_id, room); client.process_sliding_sync(&response, &()).await.expect("Failed to process sync"); @@ -1282,7 +1285,7 @@ mod tests { // When I send sliding sync response containing an avatar set to `null` (!). let room = { - let mut room = v4::SlidingSyncRoom::new(); + let mut room = http::response::Room::new(); room.avatar = JsOption::Null; room @@ -1323,7 +1326,7 @@ mod tests { let user_id = user_id!("@u:e.uk"); // When I send sliding sync response containing an invited room - let mut room = v4::SlidingSyncRoom::new(); + let mut room = http::response::Room::new(); set_room_invited(&mut room, user_id, user_id); let response = response_with_room(room_id, room); let sync_resp = @@ -1411,12 +1414,12 @@ mod tests { // When I send sliding sync response containing a room (with identifiable data // in `heroes`) - let mut room = v4::SlidingSyncRoom::new(); + let mut room = http::response::Room::new(); room.heroes = Some(vec![ - assign!(v4::SlidingSyncRoomHero::new(gordon), { + assign!(http::response::Hero::new(gordon), { name: Some("Gordon".to_owned()), }), - assign!(v4::SlidingSyncRoomHero::new(alice), { + assign!(http::response::Hero::new(alice), { name: Some("Alice".to_owned()), avatar: Some(owned_mxc_uri!("mxc://e.uk/med1")) }), @@ -1788,89 +1791,80 @@ mod tests { } #[async_test] - async fn test_recency_timestamp_is_found_when_processing_sliding_sync_response() { + async fn test_recency_stamp_is_found_when_processing_sliding_sync_response() { // Given a logged-in client let client = logged_in_base_client(None).await; let room_id = room_id!("!r:e.uk"); - // When I send sliding sync response containing a room with a recency timestamp - let room = assign!(v4::SlidingSyncRoom::new(), { - timestamp: Some(MilliSecondsSinceUnixEpoch(42u32.into())), + // When I send sliding sync response containing a room with a recency stamp + let room = assign!(http::response::Room::new(), { + bump_stamp: Some(42u32.into()), }); let response = response_with_room(room_id, room); client.process_sliding_sync(&response, &()).await.expect("Failed to process sync"); - // Then the room in the client has the recency timestamp + // Then the room in the client has the recency stamp let client_room = client.get_room(room_id).expect("No room found"); - assert_eq!(client_room.recency_timestamp().expect("No recency timestamp").0, 42u32.into()); + assert_eq!(client_room.recency_stamp().expect("No recency stamp"), 42); } #[async_test] - async fn test_recency_timestamp_can_be_overwritten_when_present_in_a_sliding_sync_response() { + async fn test_recency_stamp_can_be_overwritten_when_present_in_a_sliding_sync_response() { // Given a logged-in client let client = logged_in_base_client(None).await; let room_id = room_id!("!r:e.uk"); { - // When I send sliding sync response containing a room with a recency timestamp - let room = assign!(v4::SlidingSyncRoom::new(), { - timestamp: Some(MilliSecondsSinceUnixEpoch(42u32.into())), + // When I send sliding sync response containing a room with a recency stamp + let room = assign!(http::response::Room::new(), { + bump_stamp: Some(42u32.into()), }); let response = response_with_room(room_id, room); client.process_sliding_sync(&response, &()).await.expect("Failed to process sync"); - // Then the room in the client has the recency timestamp + // Then the room in the client has the recency stamp let client_room = client.get_room(room_id).expect("No room found"); - assert_eq!( - client_room.recency_timestamp().expect("No recency timestamp").0, - 42u32.into() - ); + assert_eq!(client_room.recency_stamp().expect("No recency stamp"), 42); } { - // When I send sliding sync response containing a room with NO recency timestamp - let room = assign!(v4::SlidingSyncRoom::new(), { - timestamp: None, + // When I send sliding sync response containing a room with NO recency stamp + let room = assign!(http::response::Room::new(), { + bump_stamp: None, }); let response = response_with_room(room_id, room); client.process_sliding_sync(&response, &()).await.expect("Failed to process sync"); - // Then the room in the client has the previous recency timestamp + // Then the room in the client has the previous recency stamp let client_room = client.get_room(room_id).expect("No room found"); - assert_eq!( - client_room.recency_timestamp().expect("No recency timestamp").0, - 42u32.into() - ); + assert_eq!(client_room.recency_stamp().expect("No recency stamp"), 42); } { // When I send sliding sync response containing a room with a NEW recency // timestamp - let room = assign!(v4::SlidingSyncRoom::new(), { - timestamp: Some(MilliSecondsSinceUnixEpoch(153u32.into())), + let room = assign!(http::response::Room::new(), { + bump_stamp: Some(153u32.into()), }); let response = response_with_room(room_id, room); client.process_sliding_sync(&response, &()).await.expect("Failed to process sync"); - // Then the room in the client has the recency timestamp + // Then the room in the client has the recency stamp let client_room = client.get_room(room_id).expect("No room found"); - assert_eq!( - client_room.recency_timestamp().expect("No recency timestamp").0, - 153u32.into() - ); + assert_eq!(client_room.recency_stamp().expect("No recency stamp"), 153); } } #[async_test] - async fn test_recency_timestamp_can_trigger_a_notable_update_reason() { + async fn test_recency_stamp_can_trigger_a_notable_update_reason() { // Given a logged-in client let client = logged_in_base_client(None).await; let mut room_info_notable_update_stream = client.room_info_notable_update_receiver(); let room_id = room_id!("!r:e.uk"); - // When I send sliding sync response containing a room with a recency timestamp. - let room = assign!(v4::SlidingSyncRoom::new(), { - timestamp: Some(MilliSecondsSinceUnixEpoch(42u32.into())), + // When I send sliding sync response containing a room with a recency stamp. + let room = assign!(http::response::Room::new(), { + bump_stamp: Some(42u32.into()), }); let response = response_with_room(room_id, room); client.process_sliding_sync(&response, &()).await.expect("Failed to process sync"); @@ -1881,13 +1875,13 @@ mod tests { room_info_notable_update_stream.recv().await, Ok(RoomInfoNotableUpdate { room_id: received_room_id, reasons: received_reasons }) => { assert_eq!(received_room_id, room_id); - assert!(!received_reasons.contains(RoomInfoNotableUpdateReasons::RECENCY_TIMESTAMP)); + assert!(!received_reasons.contains(RoomInfoNotableUpdateReasons::RECENCY_STAMP)); } ); - // When I send sliding sync response containing a room with a recency timestamp. - let room = assign!(v4::SlidingSyncRoom::new(), { - timestamp: Some(MilliSecondsSinceUnixEpoch(43u32.into())), + // When I send sliding sync response containing a room with a recency stamp. + let room = assign!(http::response::Room::new(), { + bump_stamp: Some(43u32.into()), }); let response = response_with_room(room_id, room); client.process_sliding_sync(&response, &()).await.expect("Failed to process sync"); @@ -1897,7 +1891,7 @@ mod tests { room_info_notable_update_stream.recv().await, Ok(RoomInfoNotableUpdate { room_id: received_room_id, reasons: received_reasons }) => { assert_eq!(received_room_id, room_id); - assert!(received_reasons.contains(RoomInfoNotableUpdateReasons::RECENCY_TIMESTAMP)); + assert!(received_reasons.contains(RoomInfoNotableUpdateReasons::RECENCY_STAMP)); } ); } @@ -1910,7 +1904,7 @@ mod tests { // When I send sliding sync response containing a new room. let room_id = room_id!("!r:e.uk"); - let room = v4::SlidingSyncRoom::new(); + let room = http::response::Room::new(); let response = response_with_room(room_id, room); client.process_sliding_sync(&response, &()).await.expect("Failed to process sync"); @@ -1931,7 +1925,7 @@ mod tests { make_raw_event("m.room.message", "$4"), make_raw_event("m.read", "$5"), ]; - let room = assign!(v4::SlidingSyncRoom::new(), { + let room = assign!(http::response::Room::new(), { timeline: events, }); let response = response_with_room(room_id, room); @@ -1955,7 +1949,7 @@ mod tests { // When I receive a sliding sync response containing a new room, let room_id = room_id!("!r:e.uk"); - let room = v4::SlidingSyncRoom::new(); + let room = http::response::Room::new(); let response = response_with_room(room_id, room); client.process_sliding_sync(&response, &()).await.expect("Failed to process sync"); @@ -1982,7 +1976,7 @@ mod tests { .to_string(), ) .unwrap()]; - let mut response = response_with_room(room_id, v4::SlidingSyncRoom::new()); + let mut response = response_with_room(room_id, http::response::Room::new()); response.extensions.account_data.rooms.insert(room_id.to_owned(), room_account_data_events); client.process_sliding_sync(&response, &()).await.expect("Failed to process sync"); @@ -2131,7 +2125,7 @@ mod tests { their_id: &UserId, other_state: MembershipState, ) { - let mut room = v4::SlidingSyncRoom::new(); + let mut room = http::response::Room::new(); set_room_joined(&mut room, my_id); match other_state { @@ -2165,14 +2159,14 @@ mod tests { user_id: &UserId, new_state: MembershipState, ) { - let mut room = v4::SlidingSyncRoom::new(); + let mut room = http::response::Room::new(); room.required_state.push(make_membership_event(user_id, new_state)); let response = response_with_room(room_id, room); client.process_sliding_sync(&response, &()).await.expect("Failed to process sync"); } fn set_direct_with( - response: &mut v4::Response, + response: &mut http::Response, user_id: OwnedUserId, room_ids: Vec, ) { @@ -2185,14 +2179,14 @@ mod tests { .push(make_global_account_data_event(DirectEventContent(direct_content))); } - fn response_with_room(room_id: &RoomId, room: v4::SlidingSyncRoom) -> v4::Response { - let mut response = v4::Response::new("5".to_owned()); + fn response_with_room(room_id: &RoomId, room: http::response::Room) -> http::Response { + let mut response = http::Response::new("5".to_owned()); response.rooms.insert(room_id.to_owned(), room); response } - fn room_with_avatar(avatar_uri: &MxcUri, user_id: &UserId) -> v4::SlidingSyncRoom { - let mut room = v4::SlidingSyncRoom::new(); + fn room_with_avatar(avatar_uri: &MxcUri, user_id: &UserId) -> http::response::Room { + let mut room = http::response::Room::new(); let mut avatar_event_content = RoomAvatarEventContent::new(); avatar_event_content.url = Some(avatar_uri.to_owned()); @@ -2205,8 +2199,8 @@ mod tests { fn room_with_canonical_alias( room_alias_id: &RoomAliasId, user_id: &UserId, - ) -> v4::SlidingSyncRoom { - let mut room = v4::SlidingSyncRoom::new(); + ) -> http::response::Room { + let mut room = http::response::Room::new(); let mut canonical_alias_event_content = RoomCanonicalAliasEventContent::new(); canonical_alias_event_content.alias = Some(room_alias_id.to_owned()); @@ -2221,8 +2215,8 @@ mod tests { room } - fn room_with_timeline(events: &[serde_json::Value]) -> v4::SlidingSyncRoom { - let mut room = v4::SlidingSyncRoom::new(); + fn room_with_timeline(events: &[serde_json::Value]) -> http::response::Room { + let mut room = http::response::Room::new(); room.timeline.extend( events .iter() @@ -2232,7 +2226,7 @@ mod tests { room } - fn set_room_name(room: &mut v4::SlidingSyncRoom, sender: &UserId, name: String) { + fn set_room_name(room: &mut http::response::Room, sender: &UserId, name: String) { room.required_state.push(make_state_event( sender, "", @@ -2241,7 +2235,7 @@ mod tests { )); } - fn set_room_invited(room: &mut v4::SlidingSyncRoom, inviter: &UserId, invitee: &UserId) { + fn set_room_invited(room: &mut http::response::Room, inviter: &UserId, invitee: &UserId) { // MSC3575 shows an almost-empty event to indicate that we are invited to a // room. Just the type is supplied. @@ -2263,15 +2257,15 @@ mod tests { )); } - fn set_room_joined(room: &mut v4::SlidingSyncRoom, user_id: &UserId) { + fn set_room_joined(room: &mut http::response::Room, user_id: &UserId) { room.required_state.push(make_membership_event(user_id, MembershipState::Join)); } - fn set_room_left(room: &mut v4::SlidingSyncRoom, user_id: &UserId) { + fn set_room_left(room: &mut http::response::Room, user_id: &UserId) { room.required_state.push(make_membership_event(user_id, MembershipState::Leave)); } - fn set_room_left_as_timeline_event(room: &mut v4::SlidingSyncRoom, user_id: &UserId) { + fn set_room_left_as_timeline_event(room: &mut http::response::Room, user_id: &UserId) { room.timeline.push(make_membership_event(user_id, MembershipState::Leave)); } diff --git a/crates/matrix-sdk-base/src/store/migration_helpers.rs b/crates/matrix-sdk-base/src/store/migration_helpers.rs index f4b611b40d1..fc2332fd7ca 100644 --- a/crates/matrix-sdk-base/src/store/migration_helpers.rs +++ b/crates/matrix-sdk-base/src/store/migration_helpers.rs @@ -126,7 +126,7 @@ impl RoomInfoV1 { warned_about_unknown_room_version: Arc::new(false.into()), cached_display_name: None, #[cfg(feature = "experimental-sliding-sync")] - recency_timestamp: None, + recency_stamp: None, } } } diff --git a/crates/matrix-sdk-ui/src/encryption_sync_service.rs b/crates/matrix-sdk-ui/src/encryption_sync_service.rs index c4ca039f04e..66da6e8903f 100644 --- a/crates/matrix-sdk-ui/src/encryption_sync_service.rs +++ b/crates/matrix-sdk-ui/src/encryption_sync_service.rs @@ -32,7 +32,8 @@ use async_stream::stream; use futures_core::stream::Stream; use futures_util::{pin_mut, StreamExt}; use matrix_sdk::{Client, SlidingSync, LEASE_DURATION_MS}; -use ruma::{api::client::sync::sync_events::v4, assign}; +use matrix_sdk_base::sliding_sync::http; +use ruma::assign; use tokio::sync::OwnedMutexGuard; use tracing::{debug, instrument, trace, Span}; @@ -104,9 +105,9 @@ impl EncryptionSyncService { .map_err(Error::SlidingSync)? //.share_pos() // TODO(bnjbvr) This is racy, needs cross-process lock :') .with_to_device_extension( - assign!(v4::ToDeviceConfig::default(), { enabled: Some(true)}), + assign!(http::request::ToDevice::default(), { enabled: Some(true)}), ) - .with_e2ee_extension(assign!(v4::E2EEConfig::default(), { enabled: Some(true)})); + .with_e2ee_extension(assign!(http::request::E2EE::default(), { enabled: Some(true)})); if let Some((poll_timeout, network_timeout)) = poll_and_network_timeouts { builder = builder.poll_timeout(poll_timeout).network_timeout(network_timeout); diff --git a/crates/matrix-sdk-ui/src/notification_client.rs b/crates/matrix-sdk-ui/src/notification_client.rs index 64f475106e4..4b323f9f14c 100644 --- a/crates/matrix-sdk-ui/src/notification_client.rs +++ b/crates/matrix-sdk-ui/src/notification_client.rs @@ -22,12 +22,10 @@ use matrix_sdk::{room::Room, Client, ClientBuildError, SlidingSyncList, SlidingS use matrix_sdk_base::{ crypto::{vodozemac, MegolmError}, deserialized_responses::TimelineEvent, + sliding_sync::http, RoomState, StoreError, }; use ruma::{ - api::client::sync::sync_events::v4::{ - AccountDataConfig, RoomSubscription, SyncRequestListFilters, - }, assign, events::{ room::{member::StrippedRoomMemberEvent, message::SyncRoomMessageEvent}, @@ -358,12 +356,10 @@ impl NotificationClient { .sync_mode(SlidingSyncMode::new_selective().add_range(0..=16)) .timeline_limit(8) .required_state(required_state.clone()) - .filters(Some(assign!(SyncRequestListFilters::default(), { + .filters(Some(assign!(http::request::ListFilters::default(), { is_invite: Some(true), - is_tombstoned: Some(false), not_room_types: vec!["m.space".to_owned()], - }))) - .sort(vec!["by_recency".to_owned(), "by_name".to_owned()]); + }))); let sync = self .client @@ -371,7 +367,7 @@ impl NotificationClient { .poll_timeout(Duration::from_secs(1)) .network_timeout(Duration::from_secs(3)) .with_account_data_extension( - assign!(AccountDataConfig::default(), { enabled: Some(true) }), + assign!(http::request::AccountData::default(), { enabled: Some(true) }), ) .add_list(invites) .build() @@ -379,7 +375,7 @@ impl NotificationClient { sync.subscribe_to_room( room_id.to_owned(), - Some(assign!(RoomSubscription::default(), { + Some(assign!(http::request::RoomSubscription::default(), { required_state, timeline_limit: Some(uint!(16)) })), diff --git a/crates/matrix-sdk-ui/src/room_list_service/mod.rs b/crates/matrix-sdk-ui/src/room_list_service/mod.rs index 1309fa63cbe..41f298a69fb 100644 --- a/crates/matrix-sdk-ui/src/room_list_service/mod.rs +++ b/crates/matrix-sdk-ui/src/room_list_service/mod.rs @@ -66,17 +66,10 @@ use matrix_sdk::{ event_cache::EventCacheError, Client, Error as SlidingSyncError, SlidingSync, SlidingSyncList, SlidingSyncMode, }; +use matrix_sdk_base::sliding_sync::http; pub use room::*; pub use room_list::*; -use ruma::{ - api::client::sync::sync_events::v4::{ - AccountDataConfig, E2EEConfig, ReceiptsConfig, RoomReceiptConfig, SyncRequestListFilters, - ToDeviceConfig, TypingConfig, - }, - assign, - events::{StateEventType, TimelineEventType}, - OwnedRoomId, RoomId, -}; +use ruma::{assign, events::StateEventType, OwnedRoomId, RoomId}; pub use state::*; use thiserror::Error; use tokio::time::timeout; @@ -123,21 +116,23 @@ impl RoomListService { .sliding_sync("room-list") .map_err(Error::SlidingSync)? .with_account_data_extension( - assign!(AccountDataConfig::default(), { enabled: Some(true) }), + assign!(http::request::AccountData::default(), { enabled: Some(true) }), ) - .with_receipt_extension(assign!(ReceiptsConfig::default(), { + .with_receipt_extension(assign!(http::request::Receipts::default(), { enabled: Some(true), - rooms: Some(vec![RoomReceiptConfig::AllSubscribed]) + rooms: Some(vec![http::request::ReceiptsRoom::AllSubscribed]) })) - .with_typing_extension(assign!(TypingConfig::default(), { + .with_typing_extension(assign!(http::request::Typing::default(), { enabled: Some(true), })); if with_encryption { builder = builder - .with_e2ee_extension(assign!(E2EEConfig::default(), { enabled: Some(true) })) + .with_e2ee_extension( + assign!(http::request::E2EE::default(), { enabled: Some(true) }), + ) .with_to_device_extension( - assign!(ToDeviceConfig::default(), { enabled: Some(true) }), + assign!(http::request::ToDevice::default(), { enabled: Some(true) }), ); } @@ -156,21 +151,14 @@ impl RoomListService { (StateEventType::RoomName, "".to_owned()), (StateEventType::RoomPowerLevels, "".to_owned()), ]) - .sort(vec!["by_recency".to_owned(), "by_name".to_owned()]) .include_heroes(Some(true)) - .filters(Some(assign!(SyncRequestListFilters::default(), { + .filters(Some(assign!(http::request::ListFilters::default(), { // As defined in the [SlidingSync MSC](https://github.com/matrix-org/matrix-spec-proposals/blob/9450ced7fb9cf5ea9077d029b3adf36aebfa8709/proposals/3575-sync.md?plain=1#L444) // If unset, both invited and joined rooms are returned. If false, no invited rooms are // returned. If true, only invited rooms are returned. is_invite: None, - is_tombstoned: Some(false), not_room_types: vec!["m.space".to_owned()], - }))) - .bump_event_types(&[ - TimelineEventType::RoomMessage, - TimelineEventType::RoomEncrypted, - TimelineEventType::Sticker, - ]), + }))), ) .await .map_err(Error::SlidingSync)? @@ -481,7 +469,7 @@ mod tests { impl Match for SlidingSyncMatcher { fn matches(&self, request: &Request) -> bool { - request.url.path() == "/_matrix/client/unstable/org.matrix.msc3575/sync" + request.url.path() == "/_matrix/client/unstable/org.matrix.simplified_msc3575/sync" && request.method == Method::POST } } diff --git a/crates/matrix-sdk-ui/src/room_list_service/room.rs b/crates/matrix-sdk-ui/src/room_list_service/room.rs index 99651d52b60..946a8c399ff 100644 --- a/crates/matrix-sdk-ui/src/room_list_service/room.rs +++ b/crates/matrix-sdk-ui/src/room_list_service/room.rs @@ -19,7 +19,8 @@ use std::{ops::Deref, sync::Arc}; use async_once_cell::OnceCell as AsyncOnceCell; use matrix_sdk::SlidingSync; -use ruma::{api::client::sync::sync_events::v4::RoomSubscription, events::StateEventType, RoomId}; +use matrix_sdk_base::sliding_sync::http; +use ruma::{events::StateEventType, RoomId}; use super::Error; use crate::{ @@ -91,7 +92,7 @@ impl Room { /// /// It means that all events from this room will be received every time, no /// matter how the `RoomList` is configured. - pub fn subscribe(&self, settings: Option) { + pub fn subscribe(&self, settings: Option) { let mut settings = settings.unwrap_or_default(); // Make sure to always include the room creation event in the required state @@ -109,13 +110,6 @@ impl Room { .subscribe_to_room(self.inner.room.room_id().to_owned(), Some(settings)) } - /// Unsubscribe to this room. - /// - /// It's the opposite method of [Self::subscribe`]. - pub fn unsubscribe(&self) { - self.inner.sliding_sync.unsubscribe_from_room(self.inner.room.room_id().to_owned()) - } - /// Get the timeline of the room if one exists. pub fn timeline(&self) -> Option> { self.inner.timeline.get().cloned() diff --git a/crates/matrix-sdk-ui/src/room_list_service/room_list.rs b/crates/matrix-sdk-ui/src/room_list_service/room_list.rs index 75cb2fabbd0..f7228ce95dd 100644 --- a/crates/matrix-sdk-ui/src/room_list_service/room_list.rs +++ b/crates/matrix-sdk-ui/src/room_list_service/room_list.rs @@ -222,7 +222,7 @@ fn merge_stream_and_receiver( // We are interested by these _reasons_. if reasons.contains(NotableUpdate::LATEST_EVENT) || - reasons.contains(NotableUpdate::RECENCY_TIMESTAMP) || + reasons.contains(NotableUpdate::RECENCY_STAMP) || reasons.contains(NotableUpdate::READ_RECEIPT) || reasons.contains(NotableUpdate::UNREAD_MARKER) { // Emit a `VectorDiff::Set` for the specific rooms. diff --git a/crates/matrix-sdk-ui/src/room_list_service/sorters/recency.rs b/crates/matrix-sdk-ui/src/room_list_service/sorters/recency.rs index 205c582e689..7dde5db79d8 100644 --- a/crates/matrix-sdk-ui/src/room_list_service/sorters/recency.rs +++ b/crates/matrix-sdk-ui/src/room_list_service/sorters/recency.rs @@ -14,20 +14,18 @@ use std::cmp::Ordering; -use ruma::MilliSecondsSinceUnixEpoch; - use super::{Room, Sorter}; struct RecencyMatcher where - F: Fn(&Room, &Room) -> (Option, Option), + F: Fn(&Room, &Room) -> (Option, Option), { - timestamps: F, + recency_stamps: F, } impl RecencyMatcher where - F: Fn(&Room, &Room) -> (Option, Option), + F: Fn(&Room, &Room) -> (Option, Option), { fn matches(&self, left: &Room, right: &Room) -> Ordering { if left.id() == right.id() { @@ -51,7 +49,7 @@ where return Ordering::Greater; } - match (self.timestamps)(left, right) { + match (self.recency_stamps)(left, right) { (Some(left_timestamp), Some(right_timestamp)) => { left_timestamp.cmp(&right_timestamp).reverse() } @@ -66,11 +64,13 @@ where } /// Create a new sorter that will sort two [`Room`] by recency, i.e. by -/// comparing their [`matrix_sdk_base::RoomInfo::recency_timestamp`] value. The -/// `Room` with the newest recency timestamp comes first, i.e. newest < oldest. +/// comparing their [`RoomInfo::recency_stamp`] value. The `Room` with the +/// newest recency stamp comes first, i.e. newest < oldest. +/// +/// [`RoomInfo::recency_stamp`]: matrix_sdk_base::RoomInfo::recency_stamp pub fn new_sorter() -> impl Sorter { let matcher = RecencyMatcher { - timestamps: move |left, right| (left.recency_timestamp(), right.recency_timestamp()), + recency_stamps: move |left, right| (left.recency_stamp(), right.recency_stamp()), }; move |left, right| -> Ordering { matcher.matches(left, right) } @@ -79,85 +79,76 @@ pub fn new_sorter() -> impl Sorter { #[cfg(test)] mod tests { use matrix_sdk_test::async_test; - use ruma::{room_id, MilliSecondsSinceUnixEpoch, UInt}; + use ruma::room_id; use super::{ super::super::filters::{client_and_server_prelude, new_rooms}, *, }; - macro_rules! ms { - ($value:literal) => { - MilliSecondsSinceUnixEpoch(UInt::new_wrapping($value)) - }; - } - #[async_test] - async fn test_with_two_recency_timestamps() { + async fn test_with_two_recency_stamps() { let (client, server, sliding_sync) = client_and_server_prelude().await; let [room_a, room_b] = new_rooms([room_id!("!a:b.c"), room_id!("!d:e.f")], &client, &server, &sliding_sync) .await; - // `room_a` has an older recency timestamp than `room_b`. + // `room_a` has an older recency stamp than `room_b`. { - let matcher = - RecencyMatcher { timestamps: |_left, _right| (Some(ms!(1)), Some(ms!(2))) }; + let matcher = RecencyMatcher { recency_stamps: |_left, _right| (Some(1), Some(2)) }; // `room_a` is greater than `room_b`, i.e. it must come after `room_b`. assert_eq!(matcher.matches(&room_a, &room_b), Ordering::Greater); } - // `room_b` has an older recency timestamp than `room_a`. + // `room_b` has an older recency stamp than `room_a`. { - let matcher = - RecencyMatcher { timestamps: |_left, _right| (Some(ms!(2)), Some(ms!(1))) }; + let matcher = RecencyMatcher { recency_stamps: |_left, _right| (Some(2), Some(1)) }; // `room_a` is less than `room_b`, i.e. it must come before `room_b`. assert_eq!(matcher.matches(&room_a, &room_b), Ordering::Less); } - // `room_a` has an equally old recency timestamp than `room_b`. + // `room_a` has an equally old recency stamp than `room_b`. { - let matcher = - RecencyMatcher { timestamps: |_left, _right| (Some(ms!(1)), Some(ms!(1))) }; + let matcher = RecencyMatcher { recency_stamps: |_left, _right| (Some(1), Some(1)) }; assert_eq!(matcher.matches(&room_a, &room_b), Ordering::Equal); } } #[async_test] - async fn test_with_one_recency_timestamp() { + async fn test_with_one_recency_stamp() { let (client, server, sliding_sync) = client_and_server_prelude().await; let [room_a, room_b] = new_rooms([room_id!("!a:b.c"), room_id!("!d:e.f")], &client, &server, &sliding_sync) .await; - // `room_a` has a recency timestamp, `room_b` has no recency timestamp. + // `room_a` has a recency stamp, `room_b` has no recency stamp. { - let matcher = RecencyMatcher { timestamps: |_left, _right| (Some(ms!(1)), None) }; + let matcher = RecencyMatcher { recency_stamps: |_left, _right| (Some(1), None) }; assert_eq!(matcher.matches(&room_a, &room_b), Ordering::Less); } - // `room_a` has no recency timestamp, `room_b` has a recency timestamp. + // `room_a` has no recency stamp, `room_b` has a recency stamp. { - let matcher = RecencyMatcher { timestamps: |_left, _right| (None, Some(ms!(1))) }; + let matcher = RecencyMatcher { recency_stamps: |_left, _right| (None, Some(1)) }; assert_eq!(matcher.matches(&room_a, &room_b), Ordering::Greater); } } #[async_test] - async fn test_with_zero_recency_timestamp() { + async fn test_with_zero_recency_stamp() { let (client, server, sliding_sync) = client_and_server_prelude().await; let [room_a, room_b] = new_rooms([room_id!("!a:b.c"), room_id!("!d:e.f")], &client, &server, &sliding_sync) .await; - // `room_a` and `room_b` has no recency timestamp. + // `room_a` and `room_b` has no recency stamp. { - let matcher = RecencyMatcher { timestamps: |_left, _right| (None, None) }; + let matcher = RecencyMatcher { recency_stamps: |_left, _right| (None, None) }; assert_eq!(matcher.matches(&room_a, &room_b), Ordering::Equal); } diff --git a/crates/matrix-sdk-ui/src/timeline/event_item/mod.rs b/crates/matrix-sdk-ui/src/timeline/event_item/mod.rs index bdbf8c1a593..768edf6bbfa 100644 --- a/crates/matrix-sdk-ui/src/timeline/event_item/mod.rs +++ b/crates/matrix-sdk-ui/src/timeline/event_item/mod.rs @@ -559,12 +559,11 @@ mod tests { use assert_matches2::assert_let; use matrix_sdk::test_utils::logged_in_client; use matrix_sdk_base::{ - deserialized_responses::SyncTimelineEvent, latest_event::LatestEvent, MinimalStateEvent, - OriginalMinimalStateEvent, + deserialized_responses::SyncTimelineEvent, latest_event::LatestEvent, sliding_sync::http, + MinimalStateEvent, OriginalMinimalStateEvent, }; use matrix_sdk_test::{async_test, sync_timeline_event}; use ruma::{ - api::client::sync::sync_events::v4, events::{ room::{ member::RoomMemberEventContent, @@ -620,7 +619,7 @@ mod tests { let user_id = user_id!("@t:o.uk"); let event = message_event(room_id, user_id, "**My M**", "My M", 122344); let client = logged_in_client(None).await; - let mut room = v4::SlidingSyncRoom::new(); + let mut room = http::response::Room::new(); room.timeline.push(member_event(room_id, user_id, "Alice Margatroid", "mxc://e.org/SEs")); // And the room is stored in the client so it can be extracted when needed @@ -663,7 +662,7 @@ mod tests { .unwrap(), ); - let room = v4::SlidingSyncRoom::new(); + let room = http::response::Room::new(); // Do not push the `member_event` inside the room. Let's say it's flying in the // `StateChanges`. @@ -718,8 +717,8 @@ mod tests { }) } - fn response_with_room(room_id: &RoomId, room: v4::SlidingSyncRoom) -> v4::Response { - let mut response = v4::Response::new("6".to_owned()); + fn response_with_room(room_id: &RoomId, room: http::response::Room) -> http::Response { + let mut response = http::Response::new("6".to_owned()); response.rooms.insert(room_id.to_owned(), room); response } diff --git a/crates/matrix-sdk-ui/tests/integration/notification_client.rs b/crates/matrix-sdk-ui/tests/integration/notification_client.rs index 2d423e4dfe4..06b15fadb83 100644 --- a/crates/matrix-sdk-ui/tests/integration/notification_client.rs +++ b/crates/matrix-sdk-ui/tests/integration/notification_client.rs @@ -189,7 +189,7 @@ async fn test_notification_client_sliding_sync() { }, // Power levels. - json!({ + { "content": { "ban": 50, "events": { @@ -198,7 +198,7 @@ async fn test_notification_client_sliding_sync() { "m.room.history_visibility": 100, "m.room.name": 50, "m.room.power_levels": 100, - "m.room.message": 25 + "m.room.message": 25, }, "events_default": 0, "invite": 0, @@ -207,9 +207,9 @@ async fn test_notification_client_sliding_sync() { "state_default": 50, "users": { "@example:localhost": 100, - sender: 0 + sender: 0, }, - "users_default": 0 + "users_default": 0, }, "event_id": "$15139375512JaHAW:localhost", "origin_server_ts": 151393755, @@ -217,9 +217,9 @@ async fn test_notification_client_sliding_sync() { "state_key": "", "type": "m.room.power_levels", "unsigned": { - "age": 703422 - } - }) + "age": 703422, + }, + }, ], "timeline": [ @@ -264,10 +264,8 @@ async fn test_notification_client_sliding_sync() { ], "filters": { "is_invite": true, - "is_tombstoned": false, "not_room_types": ["m.space"], }, - "sort": ["by_recency", "by_name"], "timeline_limit": 8, } }, diff --git a/crates/matrix-sdk-ui/tests/integration/room_list_service.rs b/crates/matrix-sdk-ui/tests/integration/room_list_service.rs index 15638a26e05..c94dfad2935 100644 --- a/crates/matrix-sdk-ui/tests/integration/room_list_service.rs +++ b/crates/matrix-sdk-ui/tests/integration/room_list_service.rs @@ -7,7 +7,9 @@ use assert_matches::assert_matches; use eyeball_im::VectorDiff; use futures_util::{pin_mut, FutureExt, StreamExt}; use matrix_sdk::{test_utils::logged_in_client_with_server, Client}; -use matrix_sdk_base::sync::UnreadNotificationsCount; +use matrix_sdk_base::{ + sliding_sync::http::request::RoomSubscription, sync::UnreadNotificationsCount, +}; use matrix_sdk_test::async_test; use matrix_sdk_ui::{ room_list_service::{ @@ -18,7 +20,6 @@ use matrix_sdk_ui::{ RoomListService, }; use ruma::{ - api::client::sync::sync_events::v4::RoomSubscription, assign, event_id, events::{room::message::RoomMessageEventContent, StateEventType}, mxc_uri, room_id, uint, @@ -83,7 +84,6 @@ macro_rules! sync_then_assert_request_and_fake_response { respond with = $( ( code $code ) )? { $( $response_json )* }, $( after delay = $response_delay, )? }; - $( assert_matches!(state.next().now_or_never(), Some(Some($post_state)), "post state"); )? next @@ -331,15 +331,8 @@ async fn test_sync_all_states() -> Result<(), Error> { ], "include_heroes": true, "filters": { - "is_tombstoned": false, "not_room_types": ["m.space"], }, - "bump_event_types": [ - "m.room.message", - "m.room.encrypted", - "m.sticker", - ], - "sort": ["by_recency", "by_name"], "timeline_limit": 1, }, }, @@ -1292,7 +1285,7 @@ async fn test_dynamic_entries_stream() -> Result<(), Error> { "rooms": { "!r0:bar.org": { "initial": true, - "timestamp": 1, + "bump_stamp": 1, "required_state": [ { "content": { @@ -1346,7 +1339,7 @@ async fn test_dynamic_entries_stream() -> Result<(), Error> { "rooms": { "!r1:bar.org": { "initial": true, - "timestamp": 2, + "bump_stamp": 2, "required_state": [ { "content": { @@ -1362,7 +1355,7 @@ async fn test_dynamic_entries_stream() -> Result<(), Error> { }, "!r2:bar.org": { "initial": true, - "timestamp": 3, + "bump_stamp": 3, "required_state": [ { "content": { @@ -1378,7 +1371,7 @@ async fn test_dynamic_entries_stream() -> Result<(), Error> { }, "!r3:bar.org": { "initial": true, - "timestamp": 4, + "bump_stamp": 4, "required_state": [ { "content": { @@ -1394,7 +1387,7 @@ async fn test_dynamic_entries_stream() -> Result<(), Error> { }, "!r4:bar.org": { "initial": true, - "timestamp": 5, + "bump_stamp": 5, "required_state": [ { "content": { @@ -1442,7 +1435,7 @@ async fn test_dynamic_entries_stream() -> Result<(), Error> { "rooms": { "!r5:bar.org": { "initial": true, - "timestamp": 6, + "bump_stamp": 6, "required_state": [ { "content": { @@ -1458,7 +1451,7 @@ async fn test_dynamic_entries_stream() -> Result<(), Error> { }, "!r6:bar.org": { "initial": true, - "timestamp": 7, + "bump_stamp": 7, "required_state": [ { "content": { @@ -1474,7 +1467,7 @@ async fn test_dynamic_entries_stream() -> Result<(), Error> { }, "!r7:bar.org": { "initial": true, - "timestamp": 8, + "bump_stamp": 8, "required_state": [ { "content": { @@ -1595,7 +1588,7 @@ async fn test_dynamic_entries_stream() -> Result<(), Error> { "rooms": { "!r0:bar.org": { "initial": true, - "timestamp": 9, + "bump_stamp": 9, "required_state": [], }, }, @@ -1665,7 +1658,7 @@ async fn test_room_sorting() -> Result<(), Error> { "rooms": { "!r0:bar.org": { "initial": true, - "timestamp": 3, + "bump_stamp": 3, "required_state": [ { "content": { @@ -1681,7 +1674,7 @@ async fn test_room_sorting() -> Result<(), Error> { }, "!r1:bar.org": { "initial": true, - "timestamp": 3, + "bump_stamp": 3, "required_state": [ { "content": { @@ -1697,15 +1690,15 @@ async fn test_room_sorting() -> Result<(), Error> { }, "!r2:bar.org": { "initial": true, - "timestamp": 1, + "bump_stamp": 1, }, "!r3:bar.org": { "initial": true, - "timestamp": 4, + "bump_stamp": 4, }, "!r4:bar.org": { "initial": true, - "timestamp": 5, + "bump_stamp": 5, }, }, }, @@ -1762,13 +1755,13 @@ async fn test_room_sorting() -> Result<(), Error> { }, "rooms": { "!r0:bar.org": { - "timestamp": 7, + "bump_stamp": 7, }, "!r1:bar.org": { - "timestamp": 6, + "bump_stamp": 6, }, "!r2:bar.org": { - "timestamp": 9, + "bump_stamp": 9, }, }, }, @@ -1848,10 +1841,10 @@ async fn test_room_sorting() -> Result<(), Error> { "rooms": { "!r6:bar.org": { "initial": true, - "timestamp": 8, + "bump_stamp": 8, }, "!r3:bar.org": { - "timestamp": 10, + "bump_stamp": 10, }, }, }, @@ -1913,7 +1906,7 @@ async fn test_room_sorting() -> Result<(), Error> { }, "rooms": { "!r3:bar.org": { - "timestamp": 11, + "bump_stamp": 11, }, }, }, @@ -2140,28 +2133,6 @@ async fn test_room_subscription() -> Result<(), Error> { }, }; - // Unsubscribe. - - room1.unsubscribe(); - room_list.room(room_id_2)?.unsubscribe(); // unsubscribe from a room that has no subscription. - - sync_then_assert_request_and_fake_response! { - [server, room_list, sync] - assert request >= { - "lists": { - ALL_ROOMS: { - "ranges": [[0, 2]], - }, - }, - "unsubscribe_rooms": [room_id_1, /* `room_id_2` is absent */], - }, - respond with = { - "pos": "2", - "lists": {}, - "rooms": {}, - }, - }; - Ok(()) } diff --git a/crates/matrix-sdk-ui/tests/integration/sliding_sync.rs b/crates/matrix-sdk-ui/tests/integration/sliding_sync.rs index 65801279388..b267fa96bb7 100644 --- a/crates/matrix-sdk-ui/tests/integration/sliding_sync.rs +++ b/crates/matrix-sdk-ui/tests/integration/sliding_sync.rs @@ -47,7 +47,7 @@ pub(crate) struct PartialSlidingSyncRequest { impl Match for SlidingSyncMatcher { fn matches(&self, request: &Request) -> bool { - request.url.path() == "/_matrix/client/unstable/org.matrix.msc3575/sync" + request.url.path() == "/_matrix/client/unstable/org.matrix.simplified_msc3575/sync" && request.method == Method::POST } } diff --git a/crates/matrix-sdk-ui/tests/integration/timeline/sliding_sync.rs b/crates/matrix-sdk-ui/tests/integration/timeline/sliding_sync.rs index db56d215db2..ddde9c9b450 100644 --- a/crates/matrix-sdk-ui/tests/integration/timeline/sliding_sync.rs +++ b/crates/matrix-sdk-ui/tests/integration/timeline/sliding_sync.rs @@ -298,7 +298,7 @@ struct SlidingSyncMatcher; impl Match for SlidingSyncMatcher { fn matches(&self, request: &Request) -> bool { - request.url.path() == "/_matrix/client/unstable/org.matrix.msc3575/sync" + request.url.path() == "/_matrix/client/unstable/org.matrix.simplified_msc3575/sync" && request.method == Method::POST } } diff --git a/crates/matrix-sdk/src/client/builder.rs b/crates/matrix-sdk/src/client/builder.rs index 61854d71636..a342b5a870c 100644 --- a/crates/matrix-sdk/src/client/builder.rs +++ b/crates/matrix-sdk/src/client/builder.rs @@ -88,6 +88,8 @@ pub struct ClientBuilder { requires_sliding_sync: bool, #[cfg(feature = "experimental-sliding-sync")] sliding_sync_proxy: Option, + #[cfg(feature = "experimental-sliding-sync")] + is_simplified_sliding_sync_enabled: bool, http_cfg: Option, store_config: BuilderStoreConfig, request_config: RequestConfig, @@ -107,6 +109,9 @@ impl ClientBuilder { requires_sliding_sync: false, #[cfg(feature = "experimental-sliding-sync")] sliding_sync_proxy: None, + // Simplified MSC3575 is turned on by default for the SDK. + #[cfg(feature = "experimental-sliding-sync")] + is_simplified_sliding_sync_enabled: true, http_cfg: None, store_config: BuilderStoreConfig::Custom(StoreConfig::default()), request_config: Default::default(), @@ -157,6 +162,13 @@ impl ClientBuilder { self } + /// Enable or disable Simplified MSC3575. + #[cfg(feature = "experimental-sliding-sync")] + pub fn simplified_sliding_sync(mut self, enable: bool) -> Self { + self.is_simplified_sliding_sync_enabled = enable; + self + } + /// Set the server name to discover the homeserver from. /// /// We assume we can connect in HTTPS to that server. If that's not the @@ -489,6 +501,8 @@ impl ClientBuilder { homeserver, #[cfg(feature = "experimental-sliding-sync")] sliding_sync_proxy, + #[cfg(feature = "experimental-sliding-sync")] + self.is_simplified_sliding_sync_enabled, http_client, base_client, server_capabilities, diff --git a/crates/matrix-sdk/src/client/mod.rs b/crates/matrix-sdk/src/client/mod.rs index b4edd53ef3b..afb1327f269 100644 --- a/crates/matrix-sdk/src/client/mod.rs +++ b/crates/matrix-sdk/src/client/mod.rs @@ -232,6 +232,12 @@ pub(crate) struct ClientInner { #[cfg(feature = "experimental-sliding-sync")] sliding_sync_proxy: StdRwLock>, + /// Whether Simplified MSC3575 is used or not. + /// + /// This value must not be changed during the lifetime of the `Client`. + #[cfg(feature = "experimental-sliding-sync")] + is_simplified_sliding_sync_enabled: bool, + /// The underlying HTTP client. pub(crate) http_client: HttpClient, @@ -305,6 +311,7 @@ impl ClientInner { auth_ctx: Arc, homeserver: Url, #[cfg(feature = "experimental-sliding-sync")] sliding_sync_proxy: Option, + #[cfg(feature = "experimental-sliding-sync")] is_simplified_sliding_sync_enabled: bool, http_client: HttpClient, base_client: BaseClient, server_capabilities: ClientServerCapabilities, @@ -318,6 +325,8 @@ impl ClientInner { auth_ctx, #[cfg(feature = "experimental-sliding-sync")] sliding_sync_proxy: StdRwLock::new(sliding_sync_proxy), + #[cfg(feature = "experimental-sliding-sync")] + is_simplified_sliding_sync_enabled, http_client, base_client, locks: Default::default(), @@ -471,6 +480,12 @@ impl Client { *lock = sliding_sync_proxy; } + /// Check whether Simplified MSC3575 must be used. + #[cfg(feature = "experimental-sliding-sync")] + pub(crate) fn is_simplified_sliding_sync_enabled(&self) -> bool { + self.inner.is_simplified_sliding_sync_enabled + } + /// Get the Matrix user session meta information. /// /// If the client is currently logged in, this will return a @@ -2175,6 +2190,8 @@ impl Client { self.homeserver(), #[cfg(feature = "experimental-sliding-sync")] sliding_sync_proxy, + #[cfg(feature = "experimental-sliding-sync")] + self.inner.is_simplified_sliding_sync_enabled, self.inner.http_client.clone(), self.inner.base_client.clone_with_in_memory_state_store(), self.inner.server_capabilities.read().await.clone(), diff --git a/crates/matrix-sdk/src/sliding_sync/README.md b/crates/matrix-sdk/src/sliding_sync/README.md index 1e46b18b739..df3ec5a2cb1 100644 --- a/crates/matrix-sdk/src/sliding_sync/README.md +++ b/crates/matrix-sdk/src/sliding_sync/README.md @@ -58,7 +58,7 @@ After the general configuration, one typically wants to add a list via the ## Lists A list defines a subset of matching rooms one wants to filter for, and be -kept up about. The [`v4::SyncRequestListFilters`][] allows for a granular +kept up about. The [`http::request::ListFilters`] allows for a granular specification of the exact rooms one wants the server to select and the way one wants them to be ordered before receiving. Secondly each list has a set of `ranges`: the subset of indexes of the entire list one is interested in @@ -67,25 +67,25 @@ and a unique name to be identified with. For example, a user might be part of thousands of rooms, but if the client app always starts by showing the most recent direct message conversations, loading all rooms is an inefficient approach. Instead with Sliding Sync one -defines a list (e.g. named `"main_list"`) filtering for `is_dm`, ordered +defines a list (e.g. named `"main_list"`) filtering for `is_invite`, ordered by recency and select to list the top 10 via `ranges: [ [0,9] ]` (indexes are **inclusive**) like so: ```rust # use matrix_sdk::sliding_sync::{SlidingSyncList, SlidingSyncMode}; -use ruma::{assign, api::client::sync::sync_events::v4}; +use matrix_sdk_base::sliding_sync::http; +use ruma::assign; let list_builder = SlidingSyncList::builder("main_list") .sync_mode(SlidingSyncMode::new_selective().add_range(0..=9)) .filters(Some(assign!( - v4::SyncRequestListFilters::default(), { is_dm: Some(true)} - ))) - .sort(vec!["by_recency".to_owned()]); + http::request::ListFilters::default(), { is_invite: Some(true)} + ))); ``` Please refer to the [specification][MSC], the [Ruma types][ruma-types], specifically [`SyncRequestListFilter`](https://docs.rs/ruma/latest/ruma/api/client/sync/sync_events/v4/struct.SyncRequestListFilters.html) and the -[`SlidingSyncListBuilder`] for details on the filters, sort-order and +[`SlidingSyncListBuilder`] for details on the filters, and range-options and data one requests to be sent. Once the list is fully configured, `build()` it and add the list to the sliding sync session by supplying it to [`add_list`][`SlidingSyncBuilder::add_list`]. @@ -145,12 +145,6 @@ visible in any list. The most common case for using this API is when the user enters a room - as we want to receive the incoming new messages regardless of whether the room is pushed out of the lists room list. -### Unsubscribe - -Don't forget to [unsubscribe](`SlidingSync::unsubscribe_from_room`) when the -data isn't needed to be updated anymore, e.g. when the user leaves the room, to -reduce the bandwidth back down to what is really needed. - ## Extensions Additionally to the room list and rooms with their state and latest @@ -161,7 +155,7 @@ typing- and presence-information and account-data, but can be extended by any implementation as they please. Handling of the data of the e2ee, to-device and typing-extensions takes place transparently within the SDK. -By default [`SlidingSync`][] doesn't activate _any_ extensions to save on +By default [`SlidingSync`] doesn't activate _any_ extensions to save on bandwidth, but we generally recommend to use the `with_XXX_extensions` family of methods when building sliding sync to enable e2ee, to-device-messages and account-data-extensions. @@ -169,7 +163,7 @@ account-data-extensions. ## Timeline events Both the list configuration as well as the [room subscription -settings](`v4::RoomSubscription`) allow to specify a `timeline_limit` to +settings](`http::request::RoomSubscription`) allow to specify a `timeline_limit` to receive timeline events. If that is unset or set to 0, no events are sent by the server (which is the default), if multiple limits are found, the highest takes precedence. Any positive number indicates that on the first request a @@ -229,14 +223,8 @@ In full, this typically looks like this: ```rust,no_run # use futures_util::{pin_mut, StreamExt}; -# use matrix_sdk::{ -# sliding_sync::{SlidingSyncMode, SlidingSyncListBuilder}, -# Client, -# }; -# use ruma::{ -# api::client::sync::sync_events::v4, assign, -# }; -# use tracing::{debug, error, info, warn}; +# use matrix_sdk::Client; +# use tracing::{error, info}; # use url::Url; # async { # let homeserver = Url::parse("http://example.com")?; @@ -288,7 +276,7 @@ will return immediately — with a proper response though. One just needs to make sure to not call that stream any further. Additionally, as both requests are sent with the same positional argument, the server might respond with data, the client has already processed. This isn't a problem, -the [`SlidingSync`][] will only process new data and skip the processing +the [`SlidingSync`] will only process new data and skip the processing even across restarts. To support this, in practice, one can spawn a `Future` that runs @@ -341,32 +329,12 @@ be sent in their first request than if they were loaded from a cold cache. Only the latest 10 timeline items of each room are cached and they are reset whenever a new set of timeline items is received by the server. -## Bot mode - -_Note_: This is not yet exposed via the API. See [#1475](https://github.com/matrix-org/matrix-rust-sdk/issues/1475) - -Sliding Sync is modeled for faster and more efficient user-facing client -applications, but offers significant speed ups even for bot cases through -its filtering mechanism. The sort-order and specific subsets, however, are -usually not of interest for bots. For that use case the -[`v4::SyncRequestList`][] offers the -[`slow_get_all_rooms`](`v4::SyncRequestList::slow_get_all_rooms`) flag. - -Once switched on, this mode will not trigger any updates on "list -movements", ranges and sorting are ignored and all rooms matching the filter -will be returned with the given room details settings. Depending on the data -that is requested this will still be significantly faster as the response -only returns the matching rooms and states as per settings. - -Think about a bot that only interacts in `is_dm = true` and doesn't need -room topic, room avatar and all the other state. It will be a lot faster to -start up and retrieve only the data needed to actually run. - # Full example ```rust,no_run use matrix_sdk::{Client, sliding_sync::{SlidingSyncList, SlidingSyncMode}}; -use ruma::{assign, api::client::sync::sync_events::v4, events::StateEventType}; +use matrix_sdk_base::sliding_sync::http; +use ruma::{assign, events::StateEventType}; use tracing::{warn, error, info, debug}; use futures_util::{pin_mut, StreamExt}; use url::Url; @@ -380,23 +348,21 @@ let sliding_sync_builder = client .sliding_sync("main-sync")? .sliding_sync_proxy(Url::parse("http://sliding-sync.example.org")?) // our proxy server .with_account_data_extension( - assign!(v4::AccountDataConfig::default(), { enabled: Some(true) }), + assign!(http::request::AccountData::default(), { enabled: Some(true) }), ) // we enable the account-data extension - .with_e2ee_extension(assign!(v4::E2EEConfig::default(), { enabled: Some(true) })) // and the e2ee extension + .with_e2ee_extension(assign!(http::request::E2EE::default(), { enabled: Some(true) })) // and the e2ee extension .with_to_device_extension( - assign!(v4::ToDeviceConfig::default(), { enabled: Some(true) }), + assign!(http::request::ToDevice::default(), { enabled: Some(true) }), ); // and the to-device extension let full_sync_list = SlidingSyncList::builder(&full_sync_list_name) .sync_mode(SlidingSyncMode::Growing { batch_size: 50, maximum_number_of_rooms_to_fetch: Some(500) }) // sync up by growing the window - .sort(vec!["by_recency".to_owned()]) // ordered by most recent .required_state(vec![ (StateEventType::RoomEncryption, "".to_owned()) ]); // only want to know if the room is encrypted let active_list = SlidingSyncList::builder(&active_list_name) // the active window .sync_mode(SlidingSyncMode::new_selective().add_range(0..=9)) // sync up the specific range only, first 10 items - .sort(vec!["by_recency".to_owned()]) // last active .timeline_limit(5u32) // add the last 5 timeline items for room preview and faster timeline loading .required_state(vec![ // we want to know immediately: (StateEventType::RoomEncryption, "".to_owned()), // is it encrypted diff --git a/crates/matrix-sdk/src/sliding_sync/builder.rs b/crates/matrix-sdk/src/sliding_sync/builder.rs index c1d2ba1c2e6..abff2b17cd5 100644 --- a/crates/matrix-sdk/src/sliding_sync/builder.rs +++ b/crates/matrix-sdk/src/sliding_sync/builder.rs @@ -6,14 +6,9 @@ use std::{ time::Duration, }; +use matrix_sdk_base::sliding_sync::http; use matrix_sdk_common::{ring_buffer::RingBuffer, timer}; -use ruma::{ - api::client::sync::sync_events::v4::{ - self, AccountDataConfig, E2EEConfig, ExtensionsConfig, ReceiptsConfig, ToDeviceConfig, - TypingConfig, - }, - OwnedRoomId, -}; +use ruma::OwnedRoomId; use tokio::sync::{broadcast::channel, Mutex as AsyncMutex, RwLock as AsyncRwLock}; use url::Url; @@ -35,8 +30,8 @@ pub struct SlidingSyncBuilder { sliding_sync_proxy: Option, client: Client, lists: Vec, - extensions: Option, - subscriptions: BTreeMap, + extensions: Option, + subscriptions: BTreeMap, poll_timeout: Duration, network_timeout: Duration, #[cfg(feature = "e2e-encryption")] @@ -50,6 +45,7 @@ impl SlidingSyncBuilder { } else { let storage_key = format_storage_key_prefix(&id, client.user_id().ok_or(Error::UnauthenticatedUser)?); + Ok(Self { id, storage_key, @@ -132,31 +128,32 @@ impl SlidingSyncBuilder { } /// Set the E2EE extension configuration. - pub fn with_e2ee_extension(mut self, e2ee: E2EEConfig) -> Self { + pub fn with_e2ee_extension(mut self, e2ee: http::request::E2EE) -> Self { self.extensions.get_or_insert_with(Default::default).e2ee = e2ee; self } /// Unset the E2EE extension configuration. pub fn without_e2ee_extension(mut self) -> Self { - self.extensions.get_or_insert_with(Default::default).e2ee = E2EEConfig::default(); + self.extensions.get_or_insert_with(Default::default).e2ee = http::request::E2EE::default(); self } /// Set the ToDevice extension configuration. - pub fn with_to_device_extension(mut self, to_device: ToDeviceConfig) -> Self { + pub fn with_to_device_extension(mut self, to_device: http::request::ToDevice) -> Self { self.extensions.get_or_insert_with(Default::default).to_device = to_device; self } /// Unset the ToDevice extension configuration. pub fn without_to_device_extension(mut self) -> Self { - self.extensions.get_or_insert_with(Default::default).to_device = ToDeviceConfig::default(); + self.extensions.get_or_insert_with(Default::default).to_device = + http::request::ToDevice::default(); self } /// Set the account data extension configuration. - pub fn with_account_data_extension(mut self, account_data: AccountDataConfig) -> Self { + pub fn with_account_data_extension(mut self, account_data: http::request::AccountData) -> Self { self.extensions.get_or_insert_with(Default::default).account_data = account_data; self } @@ -164,31 +161,33 @@ impl SlidingSyncBuilder { /// Unset the account data extension configuration. pub fn without_account_data_extension(mut self) -> Self { self.extensions.get_or_insert_with(Default::default).account_data = - AccountDataConfig::default(); + http::request::AccountData::default(); self } /// Set the Typing extension configuration. - pub fn with_typing_extension(mut self, typing: TypingConfig) -> Self { + pub fn with_typing_extension(mut self, typing: http::request::Typing) -> Self { self.extensions.get_or_insert_with(Default::default).typing = typing; self } /// Unset the Typing extension configuration. pub fn without_typing_extension(mut self) -> Self { - self.extensions.get_or_insert_with(Default::default).typing = TypingConfig::default(); + self.extensions.get_or_insert_with(Default::default).typing = + http::request::Typing::default(); self } /// Set the Receipt extension configuration. - pub fn with_receipt_extension(mut self, receipt: ReceiptsConfig) -> Self { + pub fn with_receipt_extension(mut self, receipt: http::request::Receipts) -> Self { self.extensions.get_or_insert_with(Default::default).receipts = receipt; self } /// Unset the Receipt extension configuration. pub fn without_receipt_extension(mut self) -> Self { - self.extensions.get_or_insert_with(Default::default).receipts = ReceiptsConfig::default(); + self.extensions.get_or_insert_with(Default::default).receipts = + http::request::Receipts::default(); self } @@ -251,15 +250,15 @@ impl SlidingSyncBuilder { let restored_fields = restore_sliding_sync_state(&client, &self.storage_key, &lists).await?; - let (delta_token, pos, rooms) = if let Some(fields) = restored_fields { + let (pos, rooms) = if let Some(fields) = restored_fields { #[cfg(feature = "e2e-encryption")] let pos = if self.share_pos { fields.pos } else { None }; #[cfg(not(feature = "e2e-encryption"))] let pos = None; - (fields.delta_token, pos, fields.rooms) + (pos, fields.rooms) } else { - (None, None, BTreeMap::new()) + (None, BTreeMap::new()) }; #[cfg(feature = "e2e-encryption")] @@ -285,7 +284,7 @@ impl SlidingSyncBuilder { lists, rooms, - position: Arc::new(AsyncMutex::new(SlidingSyncPositionMarkers { pos, delta_token })), + position: Arc::new(AsyncMutex::new(SlidingSyncPositionMarkers { pos })), // SAFETY: `unwrap` is safe because 20 is not zero. past_positions: StdRwLock::new(RingBuffer::new(NonZeroUsize::new(20).unwrap())), @@ -295,7 +294,6 @@ impl SlidingSyncBuilder { self.extensions.unwrap_or_default(), ), )), - room_unsubscriptions: Default::default(), internal_channel: internal_channel_sender, diff --git a/crates/matrix-sdk/src/sliding_sync/cache.rs b/crates/matrix-sdk/src/sliding_sync/cache.rs index e93e829eddc..27b906b153b 100644 --- a/crates/matrix-sdk/src/sliding_sync/cache.rs +++ b/crates/matrix-sdk/src/sliding_sync/cache.rs @@ -88,10 +88,7 @@ pub(super) async fn store_sliding_sync_state( storage .set_custom_value( instance_storage_key.as_bytes(), - serde_json::to_vec(&FrozenSlidingSync::new( - position, - &*sliding_sync.inner.rooms.read().await, - ))?, + serde_json::to_vec(&FrozenSlidingSync::new(&*sliding_sync.inner.rooms.read().await))?, ) .await?; @@ -184,7 +181,6 @@ pub(super) async fn restore_sliding_sync_list( /// Fields restored during `restore_sliding_sync_state`. #[derive(Default)] pub(super) struct RestoredFields { - pub delta_token: Option, pub to_device_token: Option, pub pos: Option, pub rooms: BTreeMap, @@ -223,11 +219,7 @@ pub(super) async fn restore_sliding_sync_state( .map(|custom_value| serde_json::from_slice::(&custom_value)) { // `SlidingSync` has been found and successfully deserialized. - Some(Ok(FrozenSlidingSync { - to_device_since, - delta_token: frozen_delta_token, - rooms: frozen_rooms, - })) => { + Some(Ok(FrozenSlidingSync { to_device_since, rooms: frozen_rooms })) => { trace!("Successfully read the `SlidingSync` from the cache"); // Only update the to-device token if we failed to read it from the crypto store // above. @@ -235,8 +227,6 @@ pub(super) async fn restore_sliding_sync_state( restored_fields.to_device_token = to_device_since; } - restored_fields.delta_token = frozen_delta_token; - #[cfg(feature = "e2e-encryption")] { if let Some(olm_machine) = &*client.olm_machine().await { @@ -505,11 +495,9 @@ mod tests { assert!(state_store.get_custom_value(full_storage_key.as_bytes()).await?.is_none()); // Emulate some data to be cached. - let delta_token = "delta_token".to_owned(); let pos = "pos".to_owned(); { let mut position_guard = sliding_sync.inner.position.lock().await; - position_guard.delta_token = Some(delta_token.clone()); position_guard.pos = Some(pos.clone()); // Then, we can correctly cache the sliding sync instance. @@ -523,7 +511,6 @@ mod tests { state_store.get_custom_value(full_storage_key.as_bytes()).await?, Some(bytes) => { let deserialized: FrozenSlidingSync = serde_json::from_slice(&bytes)?; - assert_eq!(deserialized.delta_token, Some(delta_token.clone())); assert!(deserialized.to_device_since.is_none()); } ); @@ -535,8 +522,7 @@ mod tests { .await? .expect("must have restored sliding sync fields"); - // After restoring, the delta token and to-device token could be read. - assert_eq!(restored_fields.delta_token.unwrap(), delta_token); + // After restoring, to-device token could be read. assert_eq!(restored_fields.pos.unwrap(), pos); // Test the "migration" path: assume a missing to-device token in crypto store, @@ -558,7 +544,6 @@ mod tests { full_storage_key.as_bytes(), serde_json::to_vec(&FrozenSlidingSync { to_device_since: Some(to_device_token.clone()), - delta_token: Some(delta_token.clone()), rooms: vec![FrozenSlidingSyncRoom { room_id: owned_room_id!("!r0:matrix.org"), prev_batch: Some("t0ken".to_owned()), @@ -572,9 +557,8 @@ mod tests { .await? .expect("must have restored fields"); - // After restoring, the delta token, the to-device since token, stream - // position and rooms could be read from the state store. - assert_eq!(restored_fields.delta_token.unwrap(), delta_token); + // After restoring, the to-device since token, stream position and rooms could + // be read from the state store. assert_eq!(restored_fields.to_device_token.unwrap(), to_device_token); assert_eq!(restored_fields.pos.unwrap(), pos); assert_eq!(restored_fields.rooms.len(), 1); diff --git a/crates/matrix-sdk/src/sliding_sync/client.rs b/crates/matrix-sdk/src/sliding_sync/client.rs index 6c7831a5612..8093c8c5656 100644 --- a/crates/matrix-sdk/src/sliding_sync/client.rs +++ b/crates/matrix-sdk/src/sliding_sync/client.rs @@ -1,8 +1,8 @@ use std::collections::BTreeMap; use imbl::Vector; -use matrix_sdk_base::{sync::SyncResponse, PreviousEventsProvider}; -use ruma::{api::client::sync::sync_events::v4, events::AnyToDeviceEvent, serde::Raw, OwnedRoomId}; +use matrix_sdk_base::{sliding_sync::http, sync::SyncResponse, PreviousEventsProvider}; +use ruma::{events::AnyToDeviceEvent, serde::Raw, OwnedRoomId}; use super::{SlidingSync, SlidingSyncBuilder}; use crate::{Client, Result, SlidingSyncRoom}; @@ -25,7 +25,7 @@ impl Client { #[tracing::instrument(skip(self, response))] pub async fn process_sliding_sync_test_helper( &self, - response: &v4::Response, + response: &http::Response, ) -> Result { let response = self.base_client().process_sliding_sync(response, &()).await?; @@ -66,7 +66,10 @@ impl<'a> SlidingSyncResponseProcessor<'a> { } #[cfg(feature = "e2e-encryption")] - pub async fn handle_encryption(&mut self, extensions: &v4::Extensions) -> Result<()> { + pub async fn handle_encryption( + &mut self, + extensions: &http::response::Extensions, + ) -> Result<()> { // This is an internal API misuse if this is triggered (calling // handle_room_response before this function), so panic is fine. assert!(self.response.is_none()); @@ -80,7 +83,7 @@ impl<'a> SlidingSyncResponseProcessor<'a> { Ok(()) } - pub async fn handle_room_response(&mut self, response: &v4::Response) -> Result<()> { + pub async fn handle_room_response(&mut self, response: &http::Response) -> Result<()> { self.response = Some( self.client .base_client() diff --git a/crates/matrix-sdk/src/sliding_sync/list/builder.rs b/crates/matrix-sdk/src/sliding_sync/list/builder.rs index e76ddd21912..01673744d55 100644 --- a/crates/matrix-sdk/src/sliding_sync/list/builder.rs +++ b/crates/matrix-sdk/src/sliding_sync/list/builder.rs @@ -7,10 +7,8 @@ use std::{ }; use eyeball::{Observable, SharedObservable}; -use ruma::{ - api::client::sync::sync_events::v4, - events::{StateEventType, TimelineEventType}, -}; +use matrix_sdk_base::sliding_sync::http; +use ruma::events::StateEventType; use tokio::sync::broadcast::Sender; use super::{ @@ -36,10 +34,9 @@ struct SlidingSyncListCachedData { #[derive(Clone)] pub struct SlidingSyncListBuilder { sync_mode: SlidingSyncMode, - sort: Vec, required_state: Vec<(StateEventType, String)>, include_heroes: Option, - filters: Option, + filters: Option, timeline_limit: Option, pub(crate) name: String, @@ -51,8 +48,6 @@ pub struct SlidingSyncListBuilder { reloaded_cached_data: Option, once_built: Arc SlidingSyncList + Send + Sync>>, - - bump_event_types: Vec, } #[cfg(not(tarpaulin_include))] @@ -62,13 +57,11 @@ impl fmt::Debug for SlidingSyncListBuilder { formatter .debug_struct("SlidingSyncListBuilder") .field("sync_mode", &self.sync_mode) - .field("sort", &self.sort) .field("required_state", &self.required_state) .field("include_heroes", &self.include_heroes) .field("filters", &self.filters) .field("timeline_limit", &self.timeline_limit) .field("name", &self.name) - .field("bump_event_types", &self.bump_event_types) .finish_non_exhaustive() } } @@ -77,7 +70,6 @@ impl SlidingSyncListBuilder { pub(super) fn new(name: impl Into) -> Self { Self { sync_mode: SlidingSyncMode::default(), - sort: vec!["by_recency".to_owned(), "by_name".to_owned()], required_state: vec![ (StateEventType::RoomEncryption, "".to_owned()), (StateEventType::RoomTombstone, "".to_owned()), @@ -89,7 +81,6 @@ impl SlidingSyncListBuilder { reloaded_cached_data: None, cache_policy: SlidingSyncListCachePolicy::Disabled, once_built: Arc::new(Box::new(identity)), - bump_event_types: Vec::new(), } } @@ -112,12 +103,6 @@ impl SlidingSyncListBuilder { self } - /// Sort the room list by this. - pub fn sort(mut self, value: Vec) -> Self { - self.sort = value; - self - } - /// Required states to return per room. pub fn required_state(mut self, value: Vec<(StateEventType, String)>) -> Self { self.required_state = value; @@ -131,7 +116,7 @@ impl SlidingSyncListBuilder { } /// Any filters to apply to the query. - pub fn filters(mut self, value: Option) -> Self { + pub fn filters(mut self, value: Option) -> Self { self.filters = value; self } @@ -177,19 +162,6 @@ impl SlidingSyncListBuilder { } } - /// Allowlist of event types which should be considered recent activity - /// when sorting `by_recency`. - /// - /// By omitting event types, clients can ensure - /// that uninteresting events (e.g. a profile rename) do not cause a - /// room to jump to the top of its list(s). Empty or - /// omitted `bump_event_types` have no effect: all events in a room will - /// be considered recent activity. - pub fn bump_event_types(mut self, bump_event_types: &[TimelineEventType]) -> Self { - self.bump_event_types = bump_event_types.to_vec(); - self - } - /// Build the list. pub(in super::super) fn build( self, @@ -203,12 +175,10 @@ impl SlidingSyncListBuilder { // From the builder sticky: StdRwLock::new(SlidingSyncStickyManager::new( SlidingSyncListStickyParameters::new( - self.sort, self.required_state, self.include_heroes, self.filters, self.timeline_limit, - self.bump_event_types, ), )), name: self.name, diff --git a/crates/matrix-sdk/src/sliding_sync/list/mod.rs b/crates/matrix-sdk/src/sliding_sync/list/mod.rs index 7733fc1d642..673bceb8480 100644 --- a/crates/matrix-sdk/src/sliding_sync/list/mod.rs +++ b/crates/matrix-sdk/src/sliding_sync/list/mod.rs @@ -11,7 +11,8 @@ use std::{ use eyeball::{Observable, SharedObservable, Subscriber}; use futures_core::Stream; -use ruma::{api::client::sync::sync_events::v4, assign, TransactionId}; +use matrix_sdk_base::sliding_sync::http; +use ruma::{assign, TransactionId}; use serde::{Deserialize, Serialize}; use tokio::sync::broadcast::Sender; use tracing::{instrument, warn}; @@ -147,7 +148,7 @@ impl SlidingSyncList { pub(super) fn next_request( &self, txn_id: &mut LazyTransactionId, - ) -> Result { + ) -> Result { self.inner.next_request(txn_id) } @@ -274,7 +275,7 @@ impl SlidingSyncListInner { } /// Update the state to the next request, and return it. - fn next_request(&self, txn_id: &mut LazyTransactionId) -> Result { + fn next_request(&self, txn_id: &mut LazyTransactionId) -> Result { let ranges = { // Use a dedicated scope to ensure the lock is released before continuing. let mut request_generator = self.request_generator.write().unwrap(); @@ -285,16 +286,16 @@ impl SlidingSyncListInner { Ok(self.request(ranges, txn_id)) } - /// Build a [`SyncRequestList`][v4::SyncRequestList] based on the current - /// state of the request generator. + /// Build a [`http::request::List`] based on the current state of the + /// request generator. #[instrument(skip(self), fields(name = self.name))] - fn request(&self, ranges: Ranges, txn_id: &mut LazyTransactionId) -> v4::SyncRequestList { + fn request(&self, ranges: Ranges, txn_id: &mut LazyTransactionId) -> http::request::List { use ruma::UInt; let ranges = ranges.into_iter().map(|r| (UInt::from(*r.start()), UInt::from(*r.end()))).collect(); - let mut request = assign!(v4::SyncRequestList::default(), { ranges }); + let mut request = assign!(http::request::List::default(), { ranges }); { let mut sticky = self.sticky.write().unwrap(); sticky.maybe_apply(&mut request, txn_id); diff --git a/crates/matrix-sdk/src/sliding_sync/list/sticky.rs b/crates/matrix-sdk/src/sliding_sync/list/sticky.rs index cd214f2f0a6..3770cc7a0b6 100644 --- a/crates/matrix-sdk/src/sliding_sync/list/sticky.rs +++ b/crates/matrix-sdk/src/sliding_sync/list/sticky.rs @@ -1,7 +1,5 @@ -use ruma::{ - api::client::sync::sync_events::v4, - events::{StateEventType, TimelineEventType}, -}; +use matrix_sdk_base::sliding_sync::http; +use ruma::events::StateEventType; use super::Bound; use crate::sliding_sync::sticky_parameters::StickyData; @@ -10,9 +8,6 @@ use crate::sliding_sync::sticky_parameters::StickyData; /// defined by the [Sliding Sync MSC](https://github.com/matrix-org/matrix-spec-proposals/blob/kegan/sync-v3/proposals/3575-sync.md). #[derive(Debug)] pub(super) struct SlidingSyncListStickyParameters { - /// Sort the room list by this. - sort: Vec, - /// Required states to return per room. required_state: Vec<(StateEventType, String)>, @@ -21,28 +16,22 @@ pub(super) struct SlidingSyncListStickyParameters { include_heroes: Option, /// Any filters to apply to the query. - filters: Option, + filters: Option, /// The maximum number of timeline events to query for. timeline_limit: Option, - - /// The `bump_event_types` field. See - /// [`SlidingSyncListBuilder::bump_event_types`] to learn more. - bump_event_types: Vec, } impl SlidingSyncListStickyParameters { pub fn new( - sort: Vec, required_state: Vec<(StateEventType, String)>, include_heroes: Option, - filters: Option, + filters: Option, timeline_limit: Option, - bump_event_types: Vec, ) -> Self { // Consider that each list will have at least one parameter set, so invalidate // it by default. - Self { sort, required_state, include_heroes, filters, timeline_limit, bump_event_types } + Self { required_state, include_heroes, filters, timeline_limit } } } @@ -57,14 +46,12 @@ impl SlidingSyncListStickyParameters { } impl StickyData for SlidingSyncListStickyParameters { - type Request = v4::SyncRequestList; + type Request = http::request::List; - fn apply(&self, request: &mut v4::SyncRequestList) { - request.sort = self.sort.to_vec(); + fn apply(&self, request: &mut Self::Request) { request.room_details.required_state = self.required_state.to_vec(); request.room_details.timeline_limit = self.timeline_limit.map(Into::into); request.include_heroes = self.include_heroes; request.filters = self.filters.clone(); - request.bump_event_types = self.bump_event_types.clone(); } } diff --git a/crates/matrix-sdk/src/sliding_sync/mod.rs b/crates/matrix-sdk/src/sliding_sync/mod.rs index 523e35ad4f8..c3748b1d408 100644 --- a/crates/matrix-sdk/src/sliding_sync/mod.rs +++ b/crates/matrix-sdk/src/sliding_sync/mod.rs @@ -25,7 +25,7 @@ mod sticky_parameters; mod utils; use std::{ - collections::{BTreeMap, BTreeSet, HashSet}, + collections::{BTreeMap, HashSet}, fmt::Debug, future::Future, sync::{Arc, RwLock as StdRwLock}, @@ -34,12 +34,10 @@ use std::{ use async_stream::stream; use futures_core::stream::Stream; +pub use matrix_sdk_base::sliding_sync::http; use matrix_sdk_common::{ring_buffer::RingBuffer, timer}; use ruma::{ - api::client::{ - error::ErrorKind, - sync::sync_events::v4::{self, ExtensionsConfig}, - }, + api::{client::error::ErrorKind, OutgoingRequest}, assign, OwnedEventId, OwnedRoomId, RoomId, }; use serde::{Deserialize, Serialize}; @@ -58,7 +56,7 @@ use self::{ client::SlidingSyncResponseProcessor, sticky_parameters::{LazyTransactionId, SlidingSyncStickyManager, StickyData}, }; -use crate::{config::RequestConfig, Client, Result}; +use crate::{config::RequestConfig, Client, HttpError, Result}; /// The Sliding Sync instance. /// @@ -126,9 +124,6 @@ pub(super) struct SlidingSyncInner { /// Request parameters that are sticky. sticky: StdRwLock>, - /// Rooms to unsubscribe, see [`Self::room_subscriptions`]. - room_unsubscriptions: StdRwLock>, - /// Internal channel used to pass messages between Sliding Sync and other /// types. internal_channel: Sender, @@ -152,7 +147,11 @@ impl SlidingSync { /// /// If the associated `Room` exists, it will be marked as /// members are missing, so that it ensures to re-fetch all members. - pub fn subscribe_to_room(&self, room_id: OwnedRoomId, settings: Option) { + pub fn subscribe_to_room( + &self, + room_id: OwnedRoomId, + settings: Option, + ) { if let Some(room) = self.inner.client.get_room(&room_id) { room.mark_members_missing(); } @@ -170,26 +169,6 @@ impl SlidingSync { ); } - /// Unsubscribe from a given room. - pub fn unsubscribe_from_room(&self, room_id: OwnedRoomId) { - // Note: we don't use `BTreeMap::remove` here, because that would require - // mutable access thus calling `data_mut()`, which in turn would - // invalidate the sticky parameters even if the `room_id` wasn't in the - // mapping. - - // If there's a subscription… - if self.inner.sticky.read().unwrap().data().room_subscriptions.contains_key(&room_id) { - // Remove it… - self.inner.sticky.write().unwrap().data_mut().room_subscriptions.remove(&room_id); - // … then keep the unsubscription for the next request. - self.inner.room_unsubscriptions.write().unwrap().insert(room_id); - - self.inner.internal_channel_send_if_possible( - SlidingSyncInternalMessage::SyncLoopSkipOverCurrentIteration, - ); - } - } - /// Lookup a specific room pub async fn get_room(&self, room_id: &RoomId) -> Option { self.inner.rooms.read().await.get(room_id).cloned() @@ -274,16 +253,13 @@ impl SlidingSync { #[instrument(skip_all)] async fn handle_response( &self, - mut sliding_sync_response: v4::Response, + mut sliding_sync_response: http::Response, position: &mut SlidingSyncPositionMarkers, ) -> Result { let pos = Some(sliding_sync_response.pos.clone()); { - debug!( - delta_token = ?sliding_sync_response.delta_token, - "Update position markers" - ); + debug!("Update position markers"); // Look up for this new `pos` in the past position markers. let past_positions = self.inner.past_positions.read().unwrap(); @@ -310,7 +286,7 @@ impl SlidingSync { // Transform a Sliding Sync Response to a `SyncResponse`. // // We may not need the `sync_response` in the future (once `SyncResponse` will - // move to Sliding Sync, i.e. to `v4::Response`), but processing the + // move to Sliding Sync, i.e. to `http::Response`), but processing the // `sliding_sync_response` is vital, so it must be done somewhere; for now it // happens here. @@ -446,7 +422,6 @@ impl SlidingSync { // // Save the new position markers. position.pos = pos; - position.delta_token = sliding_sync_response.delta_token.clone(); // Keep this position markers in memory, in case it pops from the server. let mut past_positions = self.inner.past_positions.write().unwrap(); @@ -458,12 +433,7 @@ impl SlidingSync { async fn generate_sync_request( &self, txn_id: &mut LazyTransactionId, - ) -> Result<( - v4::Request, - RequestConfig, - BTreeSet, - OwnedMutexGuard, - )> { + ) -> Result<(http::Request, RequestConfig, OwnedMutexGuard)> { // Collect requests for lists. let mut requests_lists = BTreeMap::new(); @@ -475,7 +445,7 @@ impl SlidingSync { } } - // Collect the `pos` and `delta_token`. + // Collect the `pos`. // // Wait on the `position` mutex to be available. It means no request nor // response is running. The `position` mutex is released whether the response @@ -483,7 +453,6 @@ impl SlidingSync { // the response handling has failed, in this case the `pos` hasn't been updated // and the same `pos` will be used for this new request. let mut position_guard = self.inner.position.clone().lock_owned().await; - let delta_token = position_guard.delta_token.clone(); let to_device_enabled = self.inner.sticky.read().unwrap().data().extensions.to_device.enabled == Some(true); @@ -518,16 +487,11 @@ impl SlidingSync { Span::current().record("pos", &pos); - // Collect other data. - let room_unsubscriptions = self.inner.room_unsubscriptions.read().unwrap().clone(); - - let mut request = assign!(v4::Request::new(), { + let mut request = assign!(http::Request::new(), { conn_id: Some(self.inner.id.clone()), - delta_token, pos, timeout: Some(self.inner.poll_timeout), lists: requests_lists, - unsubscribe_rooms: room_unsubscriptions.iter().cloned().collect(), }); // Apply sticky parameters, if needs be. @@ -550,35 +514,30 @@ impl SlidingSync { // Configure long-polling. We need some time for the long-poll itself, // and extra time for the network delays. RequestConfig::default().timeout(self.inner.poll_timeout + self.inner.network_timeout), - room_unsubscriptions, position_guard, )) } - /// Is the e2ee extension enabled for this sliding sync instance? - #[cfg(feature = "e2e-encryption")] - fn is_e2ee_enabled(&self) -> bool { - self.inner.sticky.read().unwrap().data().extensions.e2ee.enabled == Some(true) - } - - #[cfg(not(feature = "e2e-encryption"))] - fn is_e2ee_enabled(&self) -> bool { - false - } - - /// Should we process the room's subpart of a response? - async fn must_process_rooms_response(&self) -> bool { - // We consider that we must, if there's any room subscription or there's any - // list. - !self.inner.sticky.read().unwrap().data().room_subscriptions.is_empty() - || !self.inner.lists.read().await.is_empty() - } - - #[instrument(skip_all, fields(pos))] - async fn sync_once(&self) -> Result { - let (request, request_config, requested_room_unsubscriptions, mut position_guard) = - self.generate_sync_request(&mut LazyTransactionId::new()).await?; - + /// Send a sliding sync request. + /// + /// This method contains the sending logic. It takes a generic `Request` + /// because it can be a Simplified MSC3575 or a MSC3575 `Request`. + async fn send_sync_request( + &self, + request: Request, + request_config: RequestConfig, + mut position_guard: OwnedMutexGuard, + ) -> Result + where + Request: OutgoingRequest + Clone + Debug + Send + Sync + 'static, + Request::IncomingResponse: Send + + Sync + + + // This is required to get back a Simplified MSC3575 `Response` whatever the + // `Request` type. + Into, + HttpError: From>, + { debug!("Sending request"); // Prepare the request. @@ -644,6 +603,12 @@ impl SlidingSync { #[cfg(not(feature = "e2e-encryption"))] let response = request.await?; + // The code manipulates `Request` and `Response` from Simplified MSC3575 because + // it's the future standard. But this function may have received a `Request` + // from Simplified MSC3575 or MSC3575. We need to get back a + // Simplified MSC3575 `Response`. + let response = Into::::into(response); + debug!("Received response"); // At this point, the request has been sent, and a response has been received. @@ -666,17 +631,6 @@ impl SlidingSync { // ensure responses are handled one at a time. At this point we still own // `position_guard`, so we're fine. - // Room unsubscriptions have been received by the server. We can update the - // unsubscriptions buffer. However, it would be an error to empty it entirely as - // more unsubscriptions could have been inserted during the request/response - // dance. So let's cherry-pick which unsubscriptions to remove. - if !requested_room_unsubscriptions.is_empty() { - let room_unsubscriptions = &mut *this.inner.room_unsubscriptions.write().unwrap(); - - room_unsubscriptions - .retain(|room_id| !requested_room_unsubscriptions.contains(room_id)); - } - // Handle the response. let updates = this.handle_response(response, &mut position_guard).await?; @@ -694,6 +648,46 @@ impl SlidingSync { spawn(future.instrument(Span::current())).await.unwrap() } + /// Is the e2ee extension enabled for this sliding sync instance? + #[cfg(feature = "e2e-encryption")] + fn is_e2ee_enabled(&self) -> bool { + self.inner.sticky.read().unwrap().data().extensions.e2ee.enabled == Some(true) + } + + #[cfg(not(feature = "e2e-encryption"))] + fn is_e2ee_enabled(&self) -> bool { + false + } + + /// Should we process the room's subpart of a response? + async fn must_process_rooms_response(&self) -> bool { + // We consider that we must, if there's any room subscription or there's any + // list. + !self.inner.sticky.read().unwrap().data().room_subscriptions.is_empty() + || !self.inner.lists.read().await.is_empty() + } + + #[instrument(skip_all, fields(pos))] + async fn sync_once(&self) -> Result { + let (request, request_config, position_guard) = + self.generate_sync_request(&mut LazyTransactionId::new()).await?; + + // The code manipulates `Request` and `Response` from Simplified MSC3575 + // because it's the future standard. If + // `Client::is_simplified_sliding_sync_enabled` is turned off, the + // Simplified MSC3575 `Request` must be transformed into a MSC3575 `Request`. + if !self.inner.client.is_simplified_sliding_sync_enabled() { + self.send_sync_request( + Into::::into(request), + request_config, + position_guard, + ) + .await + } else { + self.send_sync_request(request, request_config, position_guard).await + } + } + /// Create a _new_ Sliding Sync sync loop. /// /// This method returns a `Stream`, which will send requests and will handle @@ -862,7 +856,7 @@ impl SlidingSync { /// Note: this is not the next content of the sticky parameters, but rightly /// the static configuration that was set during creation of this /// Sliding Sync. - pub fn extensions_config(&self) -> ExtensionsConfig { + pub fn extensions_config(&self) -> http::request::Extensions { let sticky = self.inner.sticky.read().unwrap(); sticky.data().extensions.clone() } @@ -875,13 +869,6 @@ pub(super) struct SlidingSyncPositionMarkers { /// /// Should not be persisted. pos: Option, - - /// Server-provided opaque token that remembers what the last timeline and - /// state events stored by the client were. - /// - /// If `None`, the server will send the full information for all the lists - /// present in the request. - delta_token: Option, } /// Frozen bits of a Sliding Sync that are stored in the *state* store. @@ -890,20 +877,14 @@ struct FrozenSlidingSync { /// Deprecated: prefer storing in the crypto store. #[serde(skip_serializing_if = "Option::is_none")] to_device_since: Option, - #[serde(skip_serializing_if = "Option::is_none")] - delta_token: Option, #[serde(default, skip_serializing_if = "Vec::is_empty")] rooms: Vec, } impl FrozenSlidingSync { - fn new( - position: &SlidingSyncPositionMarkers, - rooms: &BTreeMap, - ) -> Self { + fn new(rooms: &BTreeMap) -> Self { // The to-device token must be saved in the `FrozenCryptoSlidingSync` now. Self { - delta_token: position.delta_token.clone(), to_device_since: None, rooms: rooms .iter() @@ -935,25 +916,25 @@ pub struct UpdateSummary { pub(super) struct SlidingSyncStickyParameters { /// Room subscriptions, i.e. rooms that may be out-of-scope of all lists /// but one wants to receive updates. - room_subscriptions: BTreeMap, + room_subscriptions: BTreeMap, /// The intended state of the extensions being supplied to sliding /sync /// calls. - extensions: ExtensionsConfig, + extensions: http::request::Extensions, } impl SlidingSyncStickyParameters { /// Create a new set of sticky parameters. pub fn new( - room_subscriptions: BTreeMap, - extensions: ExtensionsConfig, + room_subscriptions: BTreeMap, + extensions: http::request::Extensions, ) -> Self { Self { room_subscriptions, extensions } } } impl StickyData for SlidingSyncStickyParameters { - type Request = v4::Request; + type Request = http::Request; fn apply(&self, request: &mut Self::Request) { request.room_subscriptions = self.room_subscriptions.clone(); @@ -968,7 +949,7 @@ impl StickyData for SlidingSyncStickyParameters { // NOTE: SS proxy workaround. fn compute_limited( local_rooms: &BTreeMap, - remote_rooms: &mut BTreeMap, + remote_rooms: &mut BTreeMap, ) { for (room_id, remote_room) in remote_rooms { // Only rooms marked as initially loaded are subject to the fixup. @@ -1054,13 +1035,8 @@ mod tests { use matrix_sdk_common::deserialized_responses::SyncTimelineEvent; use matrix_sdk_test::async_test; use ruma::{ - api::client::{ - error::ErrorKind, - sync::sync_events::v4::{self, ExtensionsConfig, ToDeviceConfig}, - }, - assign, owned_room_id, room_id, - serde::Raw, - uint, DeviceKeyAlgorithm, OwnedRoomId, TransactionId, + api::client::error::ErrorKind, assign, owned_room_id, room_id, serde::Raw, uint, + DeviceKeyAlgorithm, OwnedRoomId, TransactionId, }; use serde::Deserialize; use serde_json::json; @@ -1068,7 +1044,7 @@ mod tests { use wiremock::{http::Method, Match, Mock, MockServer, Request, ResponseTemplate}; use super::{ - compute_limited, + compute_limited, http, sticky_parameters::{LazyTransactionId, SlidingSyncStickyManager}, FrozenSlidingSync, SlidingSync, SlidingSyncList, SlidingSyncListBuilder, SlidingSyncMode, SlidingSyncRoom, SlidingSyncStickyParameters, @@ -1082,7 +1058,7 @@ mod tests { impl Match for SlidingSyncMatcher { fn matches(&self, request: &Request) -> bool { - request.url.path() == "/_matrix/client/unstable/org.matrix.msc3575/sync" + request.url.path() == "/_matrix/client/unstable/org.matrix.simplified_msc3575/sync" && request.method == Method::POST } } @@ -1190,27 +1166,6 @@ mod tests { assert!(!room_subscriptions.contains_key(&room_id_2.to_owned())); } - sliding_sync.unsubscribe_from_room(room_id_0.to_owned()); - sliding_sync.unsubscribe_from_room(room_id_2.to_owned()); - - { - let sticky = sliding_sync.inner.sticky.read().unwrap(); - let room_subscriptions = &sticky.data().room_subscriptions; - - assert!(!room_subscriptions.contains_key(&room_id_0.to_owned())); - assert!(room_subscriptions.contains_key(&room_id_1.to_owned())); - assert!(!room_subscriptions.contains_key(&room_id_2.to_owned())); - - let room_unsubscriptions = sliding_sync.inner.room_unsubscriptions.read().unwrap(); - - assert!(room_unsubscriptions.contains(&room_id_0.to_owned())); - assert!(!room_unsubscriptions.contains(&room_id_1.to_owned())); - assert!(!room_unsubscriptions.contains(&room_id_2.to_owned())); - } - - // this test also ensures that Tokio is not panicking when calling - // `subscribe_to_room` and `unsubscribe_from_room`. - Ok(()) } @@ -1222,8 +1177,7 @@ mod tests { // FrozenSlidingSync doesn't contain the to_device_token anymore, as it's saved // in the crypto store since PR #2323. - let position_guard = sliding_sync.inner.position.lock().await; - let frozen = FrozenSlidingSync::new(&position_guard, &Default::default()); + let frozen = FrozenSlidingSync::new(&*sliding_sync.inner.rooms.read().await); assert!(frozen.to_device_since.is_none()); Ok(()) @@ -1272,7 +1226,7 @@ mod tests { // Then when we create a request, the sticky parameters are applied. let txn_id: &TransactionId = "tid123".into(); - let mut request = v4::Request::default(); + let mut request = http::Request::default(); request.txn_id = Some(txn_id.to_string()); sticky.maybe_apply(&mut request, &mut LazyTransactionId::from_owned(txn_id.to_owned())); @@ -1299,7 +1253,7 @@ mod tests { // Restarting a request will only remember the last generated transaction id. let txn_id1: &TransactionId = "tid456".into(); - let mut request1 = v4::Request::default(); + let mut request1 = http::Request::default(); request1.txn_id = Some(txn_id1.to_string()); sticky.maybe_apply(&mut request1, &mut LazyTransactionId::from_owned(txn_id1.to_owned())); @@ -1307,7 +1261,7 @@ mod tests { assert_eq!(request1.room_subscriptions.len(), 2); let txn_id2: &TransactionId = "tid789".into(); - let mut request2 = v4::Request::default(); + let mut request2 = http::Request::default(); request2.txn_id = Some(txn_id2.to_string()); sticky.maybe_apply(&mut request2, &mut LazyTransactionId::from_owned(txn_id2.to_owned())); @@ -1326,7 +1280,7 @@ mod tests { #[test] fn test_extensions_are_sticky() { - let mut extensions = ExtensionsConfig::default(); + let mut extensions = http::request::Extensions::default(); extensions.account_data.enabled = Some(true); // At first it's invalidated. @@ -1348,7 +1302,7 @@ mod tests { assert_eq!(extensions.account_data.enabled, Some(true),); let txn_id: &TransactionId = "tid123".into(); - let mut request = v4::Request::default(); + let mut request = http::Request::default(); request.txn_id = Some(txn_id.to_string()); sticky.maybe_apply(&mut request, &mut LazyTransactionId::from_owned(txn_id.to_owned())); assert!(sticky.is_invalidated()); @@ -1378,15 +1332,17 @@ mod tests { let sync = client .sliding_sync("test-slidingsync")? .add_list(SlidingSyncList::builder("new_list")) - .with_to_device_extension(assign!(ToDeviceConfig::default(), { enabled: Some(true)})) - .with_e2ee_extension(assign!(v4::E2EEConfig::default(), { enabled: Some(true)})) + .with_to_device_extension( + assign!(http::request::ToDevice::default(), { enabled: Some(true)}), + ) + .with_e2ee_extension(assign!(http::request::E2EE::default(), { enabled: Some(true)})) .build() .await?; // Even without a since token, the first request will contain the extensions // configuration, at least. let txn_id = TransactionId::new(); - let (request, _, _, _) = sync + let (request, _, _) = sync .generate_sync_request(&mut LazyTransactionId::from_owned(txn_id.to_owned())) .await?; @@ -1406,7 +1362,7 @@ mod tests { // Regenerating a request will yield the same one. let txn_id2 = TransactionId::new(); - let (request, _, _, _) = sync + let (request, _, _) = sync .generate_sync_request(&mut LazyTransactionId::from_owned(txn_id2.to_owned())) .await?; @@ -1426,7 +1382,7 @@ mod tests { // The next request should contain no sticky parameters. let txn_id = TransactionId::new(); - let (request, _, _, _) = sync + let (request, _, _) = sync .generate_sync_request(&mut LazyTransactionId::from_owned(txn_id.to_owned())) .await?; assert!(request.extensions.e2ee.enabled.is_none()); @@ -1453,7 +1409,7 @@ mod tests { } let txn_id = TransactionId::new(); - let (request, _, _, _) = sync + let (request, _, _) = sync .generate_sync_request(&mut LazyTransactionId::from_owned(txn_id.to_owned())) .await?; @@ -1473,12 +1429,14 @@ mod tests { let sliding_sync = client .sliding_sync("test-slidingsync")? - .with_to_device_extension(assign!(ToDeviceConfig::default(), { enabled: Some(true) })) + .with_to_device_extension( + assign!(http::request::ToDevice::default(), { enabled: Some(true) }), + ) .build() .await?; // First request asks to enable the extension. - let (request, _, _, _) = + let (request, _, _) = sliding_sync.generate_sync_request(&mut LazyTransactionId::new()).await?; assert!(request.extensions.to_device.enabled.is_some()); @@ -1520,7 +1478,7 @@ mod tests { } // Next request doesn't ask to enable the extension. - let (request, _, _, _) = + let (request, _, _) = sliding_sync.generate_sync_request(&mut LazyTransactionId::new()).await?; assert!(request.extensions.to_device.enabled.is_none()); @@ -1623,7 +1581,7 @@ mod tests { assert!(sliding_sync.inner.past_positions.read().unwrap().is_empty()); // Next request asks to enable the extension again. - let (request, _, _, _) = + let (request, _, _) = sliding_sync.generate_sync_request(&mut LazyTransactionId::new()).await?; assert!(request.extensions.to_device.enabled.is_some()); @@ -1673,7 +1631,7 @@ mod tests { { assert!(sliding_sync.inner.position.lock().await.pos.is_none()); - let (request, _, _, _) = + let (request, _, _) = sliding_sync.generate_sync_request(&mut LazyTransactionId::new()).await?; assert!(request.pos.is_none()); } @@ -1715,7 +1673,7 @@ mod tests { // It's still 0, not "yolo". { assert_eq!(sliding_sync.inner.position.lock().await.pos.as_deref(), Some("0")); - let (request, _, _, _) = + let (request, _, _) = sliding_sync.generate_sync_request(&mut LazyTransactionId::new()).await?; assert_eq!(request.pos.as_deref(), Some("0")); } @@ -1766,7 +1724,7 @@ mod tests { // `pos` is `None` to start with. { - let (request, _, _, _) = + let (request, _, _) = sliding_sync.generate_sync_request(&mut LazyTransactionId::new()).await?; assert!(request.pos.is_none()); @@ -1807,7 +1765,7 @@ mod tests { // It's alright, the next request will load it from the database. { - let (request, _, _, _) = + let (request, _, _) = sliding_sync.generate_sync_request(&mut LazyTransactionId::new()).await?; assert_eq!(request.pos.as_deref(), Some("42")); assert_eq!(sliding_sync.inner.position.lock().await.pos.as_deref(), Some("42")); @@ -1818,7 +1776,7 @@ mod tests { let sliding_sync = client.sliding_sync("elephant-sync")?.share_pos().build().await?; assert_eq!(sliding_sync.inner.position.lock().await.pos.as_deref(), Some("42")); - let (request, _, _, _) = + let (request, _, _) = sliding_sync.generate_sync_request(&mut LazyTransactionId::new()).await?; assert_eq!(request.pos.as_deref(), Some("42")); } @@ -1830,7 +1788,7 @@ mod tests { { assert!(sliding_sync.inner.position.lock().await.pos.is_none()); - let (request, _, _, _) = + let (request, _, _) = sliding_sync.generate_sync_request(&mut LazyTransactionId::new()).await?; assert!(request.pos.is_none()); } @@ -1840,7 +1798,7 @@ mod tests { let sliding_sync = client.sliding_sync("elephant-sync")?.share_pos().build().await?; assert!(sliding_sync.inner.position.lock().await.pos.is_none()); - let (request, _, _, _) = + let (request, _, _) = sliding_sync.generate_sync_request(&mut LazyTransactionId::new()).await?; assert!(request.pos.is_none()); } @@ -2034,46 +1992,46 @@ mod tests { let mut remote_rooms = BTreeMap::from_iter([ ( not_initial.to_owned(), - assign!(v4::SlidingSyncRoom::default(), { timeline: response_timeline }), + assign!(http::response::Room::default(), { timeline: response_timeline }), ), ( no_overlap.to_owned(), - assign!(v4::SlidingSyncRoom::default(), { + assign!(http::response::Room::default(), { initial: Some(true), timeline: vec![event_c.event.clone(), event_d.event.clone()], }), ), ( partial_overlap.to_owned(), - assign!(v4::SlidingSyncRoom::default(), { + assign!(http::response::Room::default(), { initial: Some(true), timeline: vec![event_c.event.clone(), event_d.event.clone()], }), ), ( complete_overlap.to_owned(), - assign!(v4::SlidingSyncRoom::default(), { + assign!(http::response::Room::default(), { initial: Some(true), timeline: vec![event_c.event.clone(), event_d.event.clone()], }), ), ( no_remote_events.to_owned(), - assign!(v4::SlidingSyncRoom::default(), { + assign!(http::response::Room::default(), { initial: Some(true), timeline: vec![], }), ), ( no_local_events.to_owned(), - assign!(v4::SlidingSyncRoom::default(), { + assign!(http::response::Room::default(), { initial: Some(true), timeline: vec![event_c.event.clone(), event_d.event.clone()], }), ), ( already_limited.to_owned(), - assign!(v4::SlidingSyncRoom::default(), { + assign!(http::response::Room::default(), { initial: Some(true), limited: true, timeline: vec![event_c.event, event_d.event], @@ -2101,7 +2059,9 @@ mod tests { let sliding_sync = client .sliding_sync("test")? - .with_receipt_extension(assign!(v4::ReceiptsConfig::default(), { enabled: Some(true) })) + .with_receipt_extension( + assign!(http::request::Receipts::default(), { enabled: Some(true) }), + ) .add_list( SlidingSyncList::builder("all") .sync_mode(SlidingSyncMode::new_selective().add_range(0..=100)), @@ -2111,10 +2071,10 @@ mod tests { // Initial state. { - let server_response = assign!(v4::Response::new("0".to_owned()), { + let server_response = assign!(http::Response::new("0".to_owned()), { rooms: BTreeMap::from([( room.clone(), - v4::SlidingSyncRoom::default(), + http::response::Room::default(), )]) }); @@ -2124,9 +2084,9 @@ mod tests { }; } - let server_response = assign!(v4::Response::new("1".to_owned()), { - extensions: assign!(v4::Extensions::default(), { - receipts: assign!(v4::Receipts::default(), { + let server_response = assign!(http::Response::new("1".to_owned()), { + extensions: assign!(http::response::Extensions::default(), { + receipts: assign!(http::response::Receipts::default(), { rooms: BTreeMap::from([ ( room.clone(), @@ -2174,7 +2134,7 @@ mod tests { let sliding_sync = client .sliding_sync("test")? .with_account_data_extension( - assign!(v4::AccountDataConfig::default(), { enabled: Some(true) }), + assign!(http::request::AccountData::default(), { enabled: Some(true) }), ) .add_list( SlidingSyncList::builder("all") @@ -2185,10 +2145,10 @@ mod tests { // Initial state. { - let server_response = assign!(v4::Response::new("0".to_owned()), { + let server_response = assign!(http::Response::new("0".to_owned()), { rooms: BTreeMap::from([( room_id.clone(), - v4::SlidingSyncRoom::default(), + http::response::Room::default(), )]) }); @@ -2237,15 +2197,15 @@ mod tests { room_id: OwnedRoomId, unread: bool, add_rooms_section: bool, - ) -> v4::Response { + ) -> http::Response { let rooms = if add_rooms_section { - BTreeMap::from([(room_id.clone(), v4::SlidingSyncRoom::default())]) + BTreeMap::from([(room_id.clone(), http::response::Room::default())]) } else { BTreeMap::new() }; - let extensions = assign!(v4::Extensions::default(), { - account_data: assign!(v4::AccountData::default(), { + let extensions = assign!(http::response::Extensions::default(), { + account_data: assign!(http::response::AccountData::default(), { rooms: BTreeMap::from([ ( room_id, @@ -2265,7 +2225,7 @@ mod tests { }) }); - assign!(v4::Response::new(response_number.to_owned()), { rooms: rooms, extensions: extensions }) + assign!(http::Response::new(response_number.to_owned()), { rooms: rooms, extensions: extensions }) } #[async_test] @@ -2278,7 +2238,7 @@ mod tests { let sliding_sync = client .sliding_sync("test")? .with_account_data_extension( - assign!(v4::AccountDataConfig::default(), { enabled: Some(true) }), + assign!(http::request::AccountData::default(), { enabled: Some(true) }), ) .add_list( SlidingSyncList::builder("all") @@ -2289,10 +2249,10 @@ mod tests { // Initial state. { - let server_response = assign!(v4::Response::new("0".to_owned()), { + let server_response = assign!(http::Response::new("0".to_owned()), { rooms: BTreeMap::from([( room.clone(), - v4::SlidingSyncRoom::default(), + http::response::Room::default(), )]) }); @@ -2302,9 +2262,9 @@ mod tests { }; } - let server_response = assign!(v4::Response::new("1".to_owned()), { - extensions: assign!(v4::Extensions::default(), { - account_data: assign!(v4::AccountData::default(), { + let server_response = assign!(http::Response::new("1".to_owned()), { + extensions: assign!(http::response::Extensions::default(), { + account_data: assign!(http::response::AccountData::default(), { rooms: BTreeMap::from([ ( room.clone(), @@ -2346,20 +2306,20 @@ mod tests { let server = MockServer::start().await; let client = logged_in_client(Some(server.uri())).await; - let server_response = assign!(v4::Response::new("0".to_owned()), { + let server_response = assign!(http::Response::new("0".to_owned()), { rooms: BTreeMap::from([( room.clone(), - assign!(v4::SlidingSyncRoom::default(), { + assign!(http::response::Room::default(), { name: Some("Croissants lovers".to_owned()), timeline: Vec::new(), }), )]), - extensions: assign!(v4::Extensions::default(), { - e2ee: assign!(v4::E2EE::default(), { + extensions: assign!(http::response::Extensions::default(), { + e2ee: assign!(http::response::E2EE::default(), { device_one_time_keys_count: BTreeMap::from([(DeviceKeyAlgorithm::SignedCurve25519, uint!(42))]) }), - to_device: Some(assign!(v4::ToDevice::default(), { + to_device: Some(assign!(http::response::ToDevice::default(), { next_batch: "to-device-token".to_owned(), })), }) @@ -2370,8 +2330,10 @@ mod tests { let sliding_sync = client .sliding_sync("test")? - .with_to_device_extension(assign!(ToDeviceConfig::default(), { enabled: Some(true)})) - .with_e2ee_extension(assign!(v4::E2EEConfig::default(), { enabled: Some(true)})) + .with_to_device_extension( + assign!(http::request::ToDevice::default(), { enabled: Some(true)}), + ) + .with_e2ee_extension(assign!(http::request::E2EE::default(), { enabled: Some(true)})) .build() .await?; @@ -2433,8 +2395,10 @@ mod tests { let sliding_sync = client .sliding_sync("test")? .add_list(SlidingSyncList::builder("thelist")) - .with_to_device_extension(assign!(ToDeviceConfig::default(), { enabled: Some(true)})) - .with_e2ee_extension(assign!(v4::E2EEConfig::default(), { enabled: Some(true)})) + .with_to_device_extension( + assign!(http::request::ToDevice::default(), { enabled: Some(true)}), + ) + .with_e2ee_extension(assign!(http::request::E2EE::default(), { enabled: Some(true)})) .build() .await?; @@ -2483,8 +2447,10 @@ mod tests { let sliding_sync = client .sliding_sync("test")? - .with_to_device_extension(assign!(ToDeviceConfig::default(), { enabled: Some(true)})) - .with_e2ee_extension(assign!(v4::E2EEConfig::default(), { enabled: Some(true)})) + .with_to_device_extension( + assign!(http::request::ToDevice::default(), { enabled: Some(true)}), + ) + .with_e2ee_extension(assign!(http::request::E2EE::default(), { enabled: Some(true)})) .build() .await?; @@ -2587,7 +2553,6 @@ mod tests { ["m.room.encryption", ""], ["m.room.tombstone", ""] ], - "sort": ["by_recency", "by_name"] }, "another-list": { "ranges": another_list_ranges, @@ -2595,7 +2560,6 @@ mod tests { ["m.room.encryption", ""], ["m.room.tombstone", ""] ], - "sort": ["by_recency", "by_name"] }, } }), diff --git a/crates/matrix-sdk/src/sliding_sync/room.rs b/crates/matrix-sdk/src/sliding_sync/room.rs index 095b86ccb7a..93e0e4b17fb 100644 --- a/crates/matrix-sdk/src/sliding_sync/room.rs +++ b/crates/matrix-sdk/src/sliding_sync/room.rs @@ -4,8 +4,8 @@ use std::{ }; use eyeball_im::Vector; -use matrix_sdk_base::deserialized_responses::SyncTimelineEvent; -use ruma::{api::client::sync::sync_events::v4, OwnedRoomId, RoomId}; +use matrix_sdk_base::{deserialized_responses::SyncTimelineEvent, sliding_sync::http}; +use ruma::{OwnedRoomId, RoomId}; use serde::{Deserialize, Serialize}; use crate::Client; @@ -81,10 +81,10 @@ impl SlidingSyncRoom { pub(super) fn update( &mut self, - room_data: v4::SlidingSyncRoom, + room_data: http::response::Room, timeline_updates: Vec, ) { - let v4::SlidingSyncRoom { prev_batch, limited, .. } = room_data; + let http::response::Room { prev_batch, limited, .. } = room_data; { if let Some(prev_batch) = &prev_batch { @@ -230,14 +230,11 @@ mod tests { use matrix_sdk_base::deserialized_responses::TimelineEvent; use matrix_sdk_common::deserialized_responses::SyncTimelineEvent; use matrix_sdk_test::async_test; - use ruma::{ - api::client::sync::sync_events::v4, events::room::message::RoomMessageEventContent, - room_id, serde::Raw, RoomId, - }; + use ruma::{events::room::message::RoomMessageEventContent, room_id, serde::Raw, RoomId}; use serde_json::json; use wiremock::MockServer; - use super::NUMBER_OF_TIMELINE_EVENTS_TO_KEEP_FOR_THE_CACHE; + use super::{http, NUMBER_OF_TIMELINE_EVENTS_TO_KEEP_FOR_THE_CACHE}; use crate::{ sliding_sync::{FrozenSlidingSyncRoom, SlidingSyncRoom, SlidingSyncRoomState}, test_utils::logged_in_client, @@ -245,19 +242,19 @@ mod tests { macro_rules! room_response { ( $( $json:tt )+ ) => { - serde_json::from_value::( + serde_json::from_value::( json!( $( $json )+ ) ).unwrap() }; } - async fn new_room(room_id: &RoomId, inner: v4::SlidingSyncRoom) -> SlidingSyncRoom { + async fn new_room(room_id: &RoomId, inner: http::response::Room) -> SlidingSyncRoom { new_room_with_timeline(room_id, inner, vec![]).await } async fn new_room_with_timeline( room_id: &RoomId, - inner: v4::SlidingSyncRoom, + inner: http::response::Room, timeline: Vec, ) -> SlidingSyncRoom { let server = MockServer::start().await; diff --git a/crates/matrix-sdk/tests/integration/encryption/backups.rs b/crates/matrix-sdk/tests/integration/encryption/backups.rs index 8908ff14924..fe0ab56513f 100644 --- a/crates/matrix-sdk/tests/integration/encryption/backups.rs +++ b/crates/matrix-sdk/tests/integration/encryption/backups.rs @@ -763,7 +763,7 @@ async fn incremental_upload_of_keys_sliding_sync() -> Result<()> { }); Mock::given(method("POST")) - .and(path("_matrix/client/unstable/org.matrix.msc3575/sync")) + .and(path("_matrix/client/unstable/org.matrix.simplified_msc3575/sync")) .and(header("authorization", "Bearer 1234")) .respond_with(ResponseTemplate::new(200).set_body_json(json!({ "pos": "5", diff --git a/labs/multiverse/src/main.rs b/labs/multiverse/src/main.rs index 48d852822b9..db7c765b8c9 100644 --- a/labs/multiverse/src/main.rs +++ b/labs/multiverse/src/main.rs @@ -390,10 +390,8 @@ impl App { } fn subscribe_to_selected_room(&mut self, selected: usize) { - // Delete the subscription to the previous room, if any. - if let Some(room) = self.current_room_subscription.take() { - room.unsubscribe(); - } + // Cancel the subscription to the previous room, if any. + self.current_room_subscription.take(); // Subscribe to the new room. if let Some(room) = self diff --git a/testing/matrix-sdk-integration-testing/Cargo.toml b/testing/matrix-sdk-integration-testing/Cargo.toml index c6b137acf41..83ec826ecd1 100644 --- a/testing/matrix-sdk-integration-testing/Cargo.toml +++ b/testing/matrix-sdk-integration-testing/Cargo.toml @@ -16,7 +16,8 @@ futures = { version = "0.3.29", features = ["executor"] } futures-core = { workspace = true } futures-util = { workspace = true } http = { workspace = true } -matrix-sdk = { workspace = true, default-features = true, features = ["testing", "qrcode"] } +matrix-sdk = {workspace = true, default-features = true, features = ["testing", "qrcode"] } +matrix-sdk-base = { workspace = true, default-features = true, features = ["testing", "qrcode"] } matrix-sdk-ui = { workspace = true, default-features = true } matrix-sdk-test = { workspace = true } once_cell = { workspace = true } diff --git a/testing/matrix-sdk-integration-testing/src/helpers.rs b/testing/matrix-sdk-integration-testing/src/helpers.rs index a16506566bf..60a89d95d9a 100644 --- a/testing/matrix-sdk-integration-testing/src/helpers.rs +++ b/testing/matrix-sdk-integration-testing/src/helpers.rs @@ -88,6 +88,9 @@ impl TestClientBuilder { .user_agent("matrix-sdk-integration-tests") .homeserver_url(homeserver_url) .sliding_sync_proxy(sliding_sync_proxy_url) + // Disable Simplified MSC3575 for the integration tests as, at the time of writing + // (2024-07-15), we use a Synapse version that doesn't support Simplified MSC3575. + .simplified_sliding_sync(false) .with_encryption_settings(self.encryption_settings) .request_config(RequestConfig::short_retry()); diff --git a/testing/matrix-sdk-integration-testing/src/tests/sliding_sync/mod.rs b/testing/matrix-sdk-integration-testing/src/tests/sliding_sync/mod.rs index 98036d034ed..58419212825 100644 --- a/testing/matrix-sdk-integration-testing/src/tests/sliding_sync/mod.rs +++ b/testing/matrix-sdk-integration-testing/src/tests/sliding_sync/mod.rs @@ -1,2 +1,2 @@ -mod notification_client; -mod room; +// mod notification_client; +// mod room; diff --git a/testing/matrix-sdk-integration-testing/src/tests/sliding_sync/room.rs b/testing/matrix-sdk-integration-testing/src/tests/sliding_sync/room.rs index 5567ff225cf..12f6006092a 100644 --- a/testing/matrix-sdk-integration-testing/src/tests/sliding_sync/room.rs +++ b/testing/matrix-sdk-integration-testing/src/tests/sliding_sync/room.rs @@ -16,9 +16,6 @@ use matrix_sdk::{ api::client::{ receipt::create_receipt::v3::ReceiptType, room::create_room::v3::{Request as CreateRoomRequest, RoomPreset}, - sync::sync_events::v4::{ - AccountDataConfig, E2EEConfig, ReceiptsConfig, ToDeviceConfig, - }, }, assign, events::{ @@ -36,6 +33,7 @@ use matrix_sdk::{ }, Client, RoomInfo, RoomMemberships, RoomState, SlidingSyncList, SlidingSyncMode, }; +use matrix_sdk_base::sliding_sync::http; use matrix_sdk_ui::{ room_list_service::filters::new_filter_all, sync_service::SyncService, timeline::RoomExt, RoomListService, @@ -273,8 +271,13 @@ async fn test_joined_user_can_create_push_context_with_room_list_service() -> Re let sliding_sync_url = alice.sliding_sync_proxy(); let alice_id = alice.user_id().unwrap().localpart().to_owned(); - let alice = Client::new(hs).await?; - alice.set_sliding_sync_proxy(sliding_sync_url); + let alice = Client::builder() + .homeserver_url(hs) + .simplified_sliding_sync(false) + .sliding_sync_proxy(sliding_sync_url.unwrap()) + .build() + .await + .unwrap(); alice.matrix_auth().login_username(&alice_id, &alice_id).await?; let room_list_service = Arc::new(RoomListService::new(alice.clone()).await?); @@ -374,12 +377,16 @@ async fn test_room_notification_count() -> Result<()> { spawn({ let sync = alice .sliding_sync("main")? - .with_receipt_extension(assign!(ReceiptsConfig::default(), { enabled: Some(true) })) + .with_receipt_extension( + assign!(http::request::Receipts::default(), { enabled: Some(true) }), + ) .with_account_data_extension( - assign!(AccountDataConfig::default(), { enabled: Some(true) }), + assign!(http::request::AccountData::default(), { enabled: Some(true) }), + ) + .with_e2ee_extension(assign!(http::request::E2EE::default(), { enabled: Some(true) })) + .with_to_device_extension( + assign!(http::request::ToDevice::default(), { enabled: Some(true) }), ) - .with_e2ee_extension(assign!(E2EEConfig::default(), { enabled: Some(true) })) - .with_to_device_extension(assign!(ToDeviceConfig::default(), { enabled: Some(true) })) .add_list( SlidingSyncList::builder("all") .sync_mode(SlidingSyncMode::new_selective().add_range(0..=20))