From 4fa73a8d017bc96708e986d3e8a6d54e6ed10e8b Mon Sep 17 00:00:00 2001 From: maan2003 Date: Mon, 15 Apr 2024 22:11:35 +0530 Subject: [PATCH 1/6] backout of commit 7e3e8fff559105b2effb8f2df9e1cefc1b4d4237 Signed-off-by: Manmeet Singh --- crates/matrix-sdk-common/src/executor.rs | 39 +++++------------------- 1 file changed, 8 insertions(+), 31 deletions(-) diff --git a/crates/matrix-sdk-common/src/executor.rs b/crates/matrix-sdk-common/src/executor.rs index b1cb1a7bf13..e533d189fc4 100644 --- a/crates/matrix-sdk-common/src/executor.rs +++ b/crates/matrix-sdk-common/src/executor.rs @@ -15,6 +15,8 @@ //! Abstraction over an executor so we can spawn tasks under WASM the same way //! we do usually. +#[cfg(target_arch = "wasm32")] +pub use std::convert::Infallible as JoinError; #[cfg(target_arch = "wasm32")] use std::{ future::Future, @@ -23,12 +25,7 @@ use std::{ }; #[cfg(target_arch = "wasm32")] -pub use futures_util::future::Aborted as JoinError; -#[cfg(target_arch = "wasm32")] -use futures_util::{ - future::{AbortHandle, Abortable, RemoteHandle}, - FutureExt, -}; +use futures_util::{future::RemoteHandle, FutureExt}; #[cfg(not(target_arch = "wasm32"))] pub use tokio::task::{spawn, JoinError, JoinHandle}; @@ -37,31 +34,16 @@ pub fn spawn(future: F) -> JoinHandle where F: Future + 'static, { - let (future, remote_handle) = future.remote_handle(); - let (abort_handle, abort_registration) = AbortHandle::new_pair(); - let future = Abortable::new(future, abort_registration); - - wasm_bindgen_futures::spawn_local(async { - // Poll the future, and ignore the result (either it's `Ok(())`, or it's - // `Err(Aborted)`). - let _ = future.await; - }); + let (fut, handle) = future.remote_handle(); + wasm_bindgen_futures::spawn_local(fut); - JoinHandle { remote_handle, abort_handle } + JoinHandle { handle } } #[cfg(target_arch = "wasm32")] #[derive(Debug)] pub struct JoinHandle { - remote_handle: RemoteHandle, - abort_handle: AbortHandle, -} - -#[cfg(target_arch = "wasm32")] -impl JoinHandle { - pub fn abort(&self) { - self.abort_handle.abort(); - } + handle: RemoteHandle, } #[cfg(target_arch = "wasm32")] @@ -69,12 +51,7 @@ impl Future for JoinHandle { type Output = Result; fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - if self.abort_handle.is_aborted() { - // The future has been aborted. It is not possible to poll it again. - Poll::Ready(Err(JoinError)) - } else { - Pin::new(&mut self.remote_handle).poll(cx).map(Ok) - } + Pin::new(&mut self.handle).poll(cx).map(Ok) } } From 78d0a27227768a6255ef07fe2c32f990ec4f49d0 Mon Sep 17 00:00:00 2001 From: maan2003 Date: Wed, 21 Feb 2024 15:21:15 +0530 Subject: [PATCH 2/6] fix: spawn correctly on wasm Signed-off-by: Manmeet Singh --- crates/matrix-sdk-common/src/executor.rs | 34 +++++++++++++++++++----- 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/crates/matrix-sdk-common/src/executor.rs b/crates/matrix-sdk-common/src/executor.rs index e533d189fc4..a9b342a358c 100644 --- a/crates/matrix-sdk-common/src/executor.rs +++ b/crates/matrix-sdk-common/src/executor.rs @@ -15,8 +15,6 @@ //! Abstraction over an executor so we can spawn tasks under WASM the same way //! we do usually. -#[cfg(target_arch = "wasm32")] -pub use std::convert::Infallible as JoinError; #[cfg(target_arch = "wasm32")] use std::{ future::Future, @@ -24,6 +22,8 @@ use std::{ task::{Context, Poll}, }; +#[cfg(target_arch = "wasm32")] +pub use futures_util::future::Aborted as JoinError; #[cfg(target_arch = "wasm32")] use futures_util::{future::RemoteHandle, FutureExt}; #[cfg(not(target_arch = "wasm32"))] @@ -37,13 +37,30 @@ where let (fut, handle) = future.remote_handle(); wasm_bindgen_futures::spawn_local(fut); - JoinHandle { handle } + JoinHandle { handle: Some(handle) } } #[cfg(target_arch = "wasm32")] #[derive(Debug)] pub struct JoinHandle { - handle: RemoteHandle, + handle: Option>, +} + +#[cfg(target_arch = "wasm32")] +impl JoinHandle { + pub fn abort(&mut self) { + drop(self.handle.take()); + } +} + +#[cfg(target_arch = "wasm32")] +impl Drop for JoinHandle { + fn drop(&mut self) { + // don't abort the spawned future + if let Some(h) = self.handle.take() { + h.forget(); + } + } } #[cfg(target_arch = "wasm32")] @@ -51,7 +68,11 @@ impl Future for JoinHandle { type Output = Result; fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - Pin::new(&mut self.handle).poll(cx).map(Ok) + if let Some(handle) = self.handle.as_mut() { + Pin::new(handle).poll(cx).map(Ok) + } else { + Poll::Ready(Err(JoinError)) + } } } @@ -73,7 +94,8 @@ mod tests { #[async_test] async fn test_abort() { let future = async { 42 }; - let join_handle = spawn(future); + #[allow(unused_mut)] + let mut join_handle = spawn(future); join_handle.abort(); From deeea4945d2db415cb67607076a6380ba669cec2 Mon Sep 17 00:00:00 2001 From: maan2003 Date: Tue, 13 Feb 2024 10:06:15 +0530 Subject: [PATCH 3/6] fix(wasm): don't use std::time::Instant --- crates/matrix-sdk-common/src/tracing_timer.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/matrix-sdk-common/src/tracing_timer.rs b/crates/matrix-sdk-common/src/tracing_timer.rs index afd65cadce0..d11096993c6 100644 --- a/crates/matrix-sdk-common/src/tracing_timer.rs +++ b/crates/matrix-sdk-common/src/tracing_timer.rs @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -use std::time::Instant; +use crate::instant::Instant; use tracing::{callsite::DefaultCallsite, Callsite as _}; From e5e2aade30f547166b4ed963b4b551208f3a23c9 Mon Sep 17 00:00:00 2001 From: maan2003 Date: Thu, 22 Feb 2024 12:38:54 +0530 Subject: [PATCH 4/6] fix: don't use tokio::time::timeout --- crates/matrix-sdk-ui/src/room_list_service/mod.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) 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 cd9e0eac6b0..78c0ee12189 100644 --- a/crates/matrix-sdk-ui/src/room_list_service/mod.rs +++ b/crates/matrix-sdk-ui/src/room_list_service/mod.rs @@ -76,6 +76,7 @@ use std::{ use async_stream::stream; use eyeball::{SharedObservable, Subscriber}; use futures_util::{pin_mut, Stream, StreamExt}; +use matrix_sdk::timeout::timeout; pub use matrix_sdk::RoomListEntry; use matrix_sdk::{ event_cache::EventCacheError, sliding_sync::Ranges, Client, Error as SlidingSyncError, @@ -95,7 +96,7 @@ use ruma::{ }; pub use state::*; use thiserror::Error; -use tokio::{sync::Mutex, time::timeout}; +use tokio::sync::Mutex; use crate::timeline; @@ -347,7 +348,7 @@ impl RoomListService { }; // `state.next().await` has a maximum of `yield_delay` time to execute… - let next_state = match timeout(yield_delay, state.next()).await { + let next_state = match timeout(state.next(), yield_delay).await { // A new state has been received before `yield_delay` time. The new // `sync_indicator` value won't be yielded. Ok(next_state) => next_state, From 5923e75561a947d268284527dcb826bd5ec741a0 Mon Sep 17 00:00:00 2001 From: maan2003 Date: Fri, 9 Feb 2024 18:23:47 +0530 Subject: [PATCH 5/6] fix: matrix sdk ui on wasm --- Cargo.lock | 1 + Cargo.toml | 2 +- crates/matrix-sdk-base/src/read_receipts.rs | 7 +- crates/matrix-sdk-common/src/timeout.rs | 4 +- crates/matrix-sdk-common/src/tracing_timer.rs | 4 +- crates/matrix-sdk-ui/Cargo.toml | 3 + .../src/encryption_sync_service.rs | 3 + .../matrix-sdk-ui/src/notification_client.rs | 3 + .../src/room_list_service/mod.rs | 6 +- .../src/room_list_service/state.rs | 18 ++- crates/matrix-sdk-ui/src/sync_service.rs | 14 +- crates/matrix-sdk-ui/src/timeline/futures.rs | 27 +++- .../matrix-sdk-ui/src/timeline/inner/mod.rs | 4 +- .../src/timeline/sliding_sync_ext.rs | 151 ++++++++++++++++++ .../matrix-sdk-ui/src/timeline/tests/mod.rs | 3 +- crates/matrix-sdk-ui/src/timeline/traits.rs | 29 ++-- crates/matrix-sdk/src/sliding_sync/error.rs | 2 +- crates/matrix-sdk/src/sliding_sync/mod.rs | 4 +- crates/matrix-sdk/src/sliding_sync/utils.rs | 4 +- 19 files changed, 239 insertions(+), 50 deletions(-) create mode 100644 crates/matrix-sdk-ui/src/timeline/sliding_sync_ext.rs diff --git a/Cargo.lock b/Cargo.lock index 3c2bbb6aa53..0b7cfc845ce 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3583,6 +3583,7 @@ dependencies = [ "futures-core", "futures-util", "fuzzy-matcher", + "gloo-timers", "growable-bloom-filter", "imbl", "indexmap 2.2.6", diff --git a/Cargo.toml b/Cargo.toml index 5d37a6fa407..f71facf7715 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -63,7 +63,7 @@ sha2 = "0.10.8" similar-asserts = "1.5.0" stream_assert = "0.1.1" thiserror = "1.0.38" -tokio = { version = "1.30.0", default-features = false, features = ["sync"] } +tokio = { version = "1.30.0", default-features = false, features = ["sync", "macros"] } tokio-stream = "0.1.14" tracing = { version = "0.1.40", default-features = false, features = ["std"] } tracing-core = "0.1.32" diff --git a/crates/matrix-sdk-base/src/read_receipts.rs b/crates/matrix-sdk-base/src/read_receipts.rs index 1f77298045d..f7f2209d05e 100644 --- a/crates/matrix-sdk-base/src/read_receipts.rs +++ b/crates/matrix-sdk-base/src/read_receipts.rs @@ -123,7 +123,10 @@ use std::{ }; use eyeball_im::Vector; -use matrix_sdk_common::{deserialized_responses::SyncTimelineEvent, ring_buffer::RingBuffer}; +use matrix_sdk_common::{ + deserialized_responses::SyncTimelineEvent, ring_buffer::RingBuffer, SendOutsideWasm, + SyncOutsideWasm, +}; use ruma::{ events::{ poll::{start::PollStartEventContent, unstable_start::UnstablePollStartEventContent}, @@ -266,7 +269,7 @@ impl RoomReadReceipts { } /// Provider for timeline events prior to the current sync. -pub trait PreviousEventsProvider: Send + Sync { +pub trait PreviousEventsProvider: SendOutsideWasm + SyncOutsideWasm { /// Returns the list of known timeline events, in sync order, for the given /// room. fn for_room(&self, room_id: &RoomId) -> Vector; diff --git a/crates/matrix-sdk-common/src/timeout.rs b/crates/matrix-sdk-common/src/timeout.rs index 328711609e1..7f7ce68b787 100644 --- a/crates/matrix-sdk-common/src/timeout.rs +++ b/crates/matrix-sdk-common/src/timeout.rs @@ -41,7 +41,7 @@ impl Error for ElapsedError {} /// an error. pub async fn timeout(future: F, duration: Duration) -> Result where - F: Future + Unpin, + F: Future, { #[cfg(not(target_arch = "wasm32"))] return tokio_timeout(duration, future).await.map_err(|_| ElapsedError(())); @@ -51,7 +51,7 @@ where let timeout_future = TimeoutFuture::new(u32::try_from(duration.as_millis()).expect("Overlong duration")); - match select(future, timeout_future).await { + match select(std::pin::pin!(future), timeout_future).await { Either::Left((res, _)) => Ok(res), Either::Right((_, _)) => Err(ElapsedError(())), } diff --git a/crates/matrix-sdk-common/src/tracing_timer.rs b/crates/matrix-sdk-common/src/tracing_timer.rs index d11096993c6..d6586eee3fc 100644 --- a/crates/matrix-sdk-common/src/tracing_timer.rs +++ b/crates/matrix-sdk-common/src/tracing_timer.rs @@ -12,10 +12,10 @@ // See the License for the specific language governing permissions and // limitations under the License. -use crate::instant::Instant; - use tracing::{callsite::DefaultCallsite, Callsite as _}; +use crate::instant::Instant; + /// A named RAII that will show on Drop how long its covered section took to /// execute. pub struct TracingTimer { diff --git a/crates/matrix-sdk-ui/Cargo.toml b/crates/matrix-sdk-ui/Cargo.toml index a73328061d4..823d440ec6f 100644 --- a/crates/matrix-sdk-ui/Cargo.toml +++ b/crates/matrix-sdk-ui/Cargo.toml @@ -66,3 +66,6 @@ wiremock = { workspace = true } [lints] workspace = true + +[target.'cfg(target_arch = "wasm32")'.dependencies] +gloo-timers = { version = "0.3.0", features = ["futures"] } diff --git a/crates/matrix-sdk-ui/src/encryption_sync_service.rs b/crates/matrix-sdk-ui/src/encryption_sync_service.rs index c4ca039f04e..937383d7d2a 100644 --- a/crates/matrix-sdk-ui/src/encryption_sync_service.rs +++ b/crates/matrix-sdk-ui/src/encryption_sync_service.rs @@ -171,7 +171,10 @@ impl EncryptionSyncService { LEASE_DURATION_MS ); + #[cfg(not(target_arch = "wasm32"))] tokio::time::sleep(Duration::from_millis(LEASE_DURATION_MS.into())).await; + #[cfg(target_arch = "wasm32")] + gloo_timers::future::TimeoutFuture::new(LEASE_DURATION_MS).await; lock_guard = self .client diff --git a/crates/matrix-sdk-ui/src/notification_client.rs b/crates/matrix-sdk-ui/src/notification_client.rs index 64f475106e4..829cc3ed0ee 100644 --- a/crates/matrix-sdk-ui/src/notification_client.rs +++ b/crates/matrix-sdk-ui/src/notification_client.rs @@ -212,7 +212,10 @@ impl NotificationClient { for _ in 0..3 { trace!("waiting for decryption…"); + #[cfg(not(target_arch = "wasm32"))] tokio::time::sleep(Duration::from_millis(wait)).await; + #[cfg(target_arch = "wasm32")] + gloo_timers::future::TimeoutFuture::new(wait).await; match room.decrypt_event(raw_event.cast_ref()).await { Ok(new_event) => { 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 78c0ee12189..ccf22270d47 100644 --- a/crates/matrix-sdk-ui/src/room_list_service/mod.rs +++ b/crates/matrix-sdk-ui/src/room_list_service/mod.rs @@ -76,11 +76,11 @@ use std::{ use async_stream::stream; use eyeball::{SharedObservable, Subscriber}; use futures_util::{pin_mut, Stream, StreamExt}; -use matrix_sdk::timeout::timeout; pub use matrix_sdk::RoomListEntry; use matrix_sdk::{ - event_cache::EventCacheError, sliding_sync::Ranges, Client, Error as SlidingSyncError, - SlidingSync, SlidingSyncList, SlidingSyncListBuilder, SlidingSyncMode, + event_cache::EventCacheError, sliding_sync::Ranges, timeout::timeout, Client, + Error as SlidingSyncError, SlidingSync, SlidingSyncList, SlidingSyncListBuilder, + SlidingSyncMode, }; use matrix_sdk_base::ring_buffer::RingBuffer; pub use room::*; diff --git a/crates/matrix-sdk-ui/src/room_list_service/state.rs b/crates/matrix-sdk-ui/src/room_list_service/state.rs index 0ddc7461f9a..88962c033de 100644 --- a/crates/matrix-sdk-ui/src/room_list_service/state.rs +++ b/crates/matrix-sdk-ui/src/room_list_service/state.rs @@ -91,7 +91,8 @@ impl State { } /// A trait to define what an `Action` is. -#[async_trait] +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] trait Action { async fn run(&self, sliding_sync: &SlidingSync) -> Result<(), Error>; } @@ -104,7 +105,8 @@ pub const VISIBLE_ROOMS_DEFAULT_TIMELINE_LIMIT: Bound = 20; /// Default range for the `VISIBLE_ROOMS_LIST_NAME` list. pub const VISIBLE_ROOMS_DEFAULT_RANGE: Range = 0..=19; -#[async_trait] +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] impl Action for AddVisibleRooms { async fn run(&self, sliding_sync: &SlidingSync) -> Result<(), Error> { sliding_sync @@ -128,7 +130,8 @@ impl Action for AddVisibleRooms { struct SetVisibleRoomsToZeroTimelineLimit; -#[async_trait] +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] impl Action for SetVisibleRoomsToZeroTimelineLimit { async fn run(&self, sliding_sync: &SlidingSync) -> Result<(), Error> { sliding_sync @@ -146,7 +149,8 @@ impl Action for SetVisibleRoomsToZeroTimelineLimit { struct SetVisibleRoomsToDefaultTimelineLimit; -#[async_trait] +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] impl Action for SetVisibleRoomsToDefaultTimelineLimit { async fn run(&self, sliding_sync: &SlidingSync) -> Result<(), Error> { sliding_sync @@ -168,7 +172,8 @@ struct SetAllRoomsToSelectiveSyncMode; /// `ALL_ROOMS_LIST_NAME` list. pub const ALL_ROOMS_DEFAULT_SELECTIVE_RANGE: Range = 0..=19; -#[async_trait] +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] impl Action for SetAllRoomsToSelectiveSyncMode { async fn run(&self, sliding_sync: &SlidingSync) -> Result<(), Error> { sliding_sync @@ -192,7 +197,8 @@ struct SetAllRoomsToGrowingSyncMode; /// list. pub const ALL_ROOMS_DEFAULT_GROWING_BATCH_SIZE: u32 = 100; -#[async_trait] +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] impl Action for SetAllRoomsToGrowingSyncMode { async fn run(&self, sliding_sync: &SlidingSync) -> Result<(), Error> { sliding_sync diff --git a/crates/matrix-sdk-ui/src/sync_service.rs b/crates/matrix-sdk-ui/src/sync_service.rs index 23c8166cafe..f637886bb25 100644 --- a/crates/matrix-sdk-ui/src/sync_service.rs +++ b/crates/matrix-sdk-ui/src/sync_service.rs @@ -28,14 +28,14 @@ use std::sync::{Arc, Mutex}; use eyeball::{SharedObservable, Subscriber}; use futures_core::Future; use futures_util::{pin_mut, StreamExt as _}; -use matrix_sdk::Client; +use matrix_sdk::{ + executor::{spawn, JoinHandle}, + Client, +}; use thiserror::Error; -use tokio::{ - sync::{ - mpsc::{Receiver, Sender}, - Mutex as AsyncMutex, OwnedMutexGuard, - }, - task::{spawn, JoinHandle}, +use tokio::sync::{ + mpsc::{Receiver, Sender}, + Mutex as AsyncMutex, OwnedMutexGuard, }; use tracing::{error, info, instrument, trace, warn, Instrument, Level}; diff --git a/crates/matrix-sdk-ui/src/timeline/futures.rs b/crates/matrix-sdk-ui/src/timeline/futures.rs index 694a2203fb4..47b0f4514bc 100644 --- a/crates/matrix-sdk-ui/src/timeline/futures.rs +++ b/crates/matrix-sdk-ui/src/timeline/futures.rs @@ -1,7 +1,10 @@ use std::{fs, future::IntoFuture, path::PathBuf}; +#[cfg(not(target_arch = "wasm32"))] use eyeball::{SharedObservable, Subscriber}; -use matrix_sdk::{attachment::AttachmentConfig, TransmissionProgress}; +use matrix_sdk::attachment::AttachmentConfig; +#[cfg(not(target_arch = "wasm32"))] +use matrix_sdk::TransmissionProgress; use matrix_sdk_base::boxed_into_future; use mime::Mime; use tracing::{Instrument as _, Span}; @@ -14,6 +17,7 @@ pub struct SendAttachment<'a> { mime_type: Mime, config: AttachmentConfig, tracing_span: Span, + #[cfg(not(target_arch = "wasm32"))] pub(crate) send_progress: SharedObservable, } @@ -30,6 +34,7 @@ impl<'a> SendAttachment<'a> { mime_type, config, tracing_span: Span::current(), + #[cfg(not(target_arch = "wasm32"))] send_progress: Default::default(), } } @@ -47,7 +52,15 @@ impl<'a> IntoFuture for SendAttachment<'a> { boxed_into_future!(extra_bounds: 'a); fn into_future(self) -> Self::IntoFuture { - let Self { timeline, path, mime_type, config, tracing_span, send_progress } = self; + let Self { + timeline, + path, + mime_type, + config, + tracing_span, + #[cfg(not(target_arch = "wasm32"))] + send_progress, + } = self; let fut = async move { let filename = path .file_name() @@ -56,12 +69,10 @@ impl<'a> IntoFuture for SendAttachment<'a> { .ok_or(Error::InvalidAttachmentFileName)?; let data = fs::read(&path).map_err(|_| Error::InvalidAttachmentData)?; - timeline - .room() - .send_attachment(filename, &mime_type, data, config) - .with_send_progress_observable(send_progress) - .await - .map_err(|_| Error::FailedSendingAttachment)?; + let value = timeline.room().send_attachment(path, &mime_type, data, config); + #[cfg(not(target_arch = "wasm32"))] + let value = value.with_send_progress_observable(send_progress); + value.await.map_err(|_| Error::FailedSendingAttachment)?; Ok(()) }; diff --git a/crates/matrix-sdk-ui/src/timeline/inner/mod.rs b/crates/matrix-sdk-ui/src/timeline/inner/mod.rs index d6193e8b09b..31750247709 100644 --- a/crates/matrix-sdk-ui/src/timeline/inner/mod.rs +++ b/crates/matrix-sdk-ui/src/timeline/inner/mod.rs @@ -12,9 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -#[cfg(feature = "e2e-encryption")] -use std::collections::BTreeSet; -use std::{fmt, sync::Arc}; +use std::{collections::BTreeSet, fmt, sync::Arc}; use as_variant::as_variant; use eyeball_im::{ObservableVectorEntry, VectorDiff}; diff --git a/crates/matrix-sdk-ui/src/timeline/sliding_sync_ext.rs b/crates/matrix-sdk-ui/src/timeline/sliding_sync_ext.rs new file mode 100644 index 00000000000..3b0db505b9f --- /dev/null +++ b/crates/matrix-sdk-ui/src/timeline/sliding_sync_ext.rs @@ -0,0 +1,151 @@ +<<<<<<< ++++++++ +%%%%%%% + // Copyright 2023 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. + + use async_trait::async_trait; + use matrix_sdk::SlidingSyncRoom; + use tracing::instrument; + + use super::EventTimelineItem; + +-#[async_trait] ++#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] ++#[cfg_attr(not(target_arch = "wasm32"), async_trait)] + pub trait SlidingSyncRoomExt { + /// Get the latest timeline item of this room, if it is already cached. + /// + /// Use `Timeline::latest_event` instead if you already have a timeline for + /// this `SlidingSyncRoom`. + async fn latest_timeline_item(&self) -> Option; + } + +-#[async_trait] ++#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] ++#[cfg_attr(not(target_arch = "wasm32"), async_trait)] + impl SlidingSyncRoomExt for SlidingSyncRoom { + /// Get a timeline item representing the latest event in this room. + /// This method wraps latest_event, converting the event into an + /// EventTimelineItem. + #[instrument(skip_all)] + async fn latest_timeline_item(&self) -> Option { + let latest_event = + self.client().get_room(self.room_id()).and_then(|room| room.latest_event())?; + + EventTimelineItem::from_latest_event(self.client(), self.room_id(), latest_event).await + } + } + + #[cfg(test)] + mod tests { + use assert_matches::assert_matches; + use matrix_sdk::{test_utils::logged_in_client, Client, SlidingSyncRoom}; + use matrix_sdk_base::deserialized_responses::SyncTimelineEvent; + use matrix_sdk_test::async_test; + use ruma::{ + api::client::sync::sync_events::v4, + events::room::message::{MessageFormat, MessageType}, + room_id, + serde::Raw, + user_id, RoomId, UInt, UserId, + }; + use serde_json::json; + + use crate::timeline::{SlidingSyncRoomExt, TimelineDetails}; + + #[async_test] + async fn test_initially_latest_message_event_is_none() { + // Given a room with no latest event + let room_id = room_id!("!r:x.uk").to_owned(); + let client = logged_in_client(None).await; + let room = SlidingSyncRoom::new(client, room_id, None, Vec::new()); + + // When we ask for the latest event, it is None + assert!(room.latest_timeline_item().await.is_none()); + } + + #[async_test] + async fn test_latest_message_event_is_wrapped_as_a_timeline_item() { + // Given a room exists, and an event came in through a sync + let room_id = room_id!("!r:x.uk"); + let user_id = user_id!("@s:o.uk"); + let client = logged_in_client(None).await; + let event = message_event(room_id, user_id, "**My msg**", "My msg", 122343); + process_event_via_sync_test_helper(room_id, event, &client).await; + + // When we ask for the latest event in the room + let room = SlidingSyncRoom::new(client.clone(), room_id.to_owned(), None, Vec::new()); + let actual = room.latest_timeline_item().await.unwrap(); + + // Then it is wrapped as an EventTimelineItem + assert_eq!(actual.sender, user_id); + assert_matches!(actual.sender_profile, TimelineDetails::Unavailable); + assert_eq!(actual.timestamp.0, UInt::new(122343).unwrap()); + if let MessageType::Text(txt) = actual.content.as_message().unwrap().msgtype() { + assert_eq!(txt.body, "**My msg**"); + let formatted = txt.formatted.as_ref().unwrap(); + assert_eq!(formatted.format, MessageFormat::Html); + assert_eq!(formatted.body, "My msg"); + } else { + panic!("Unexpected message type"); + } + } + + async fn process_event_via_sync_test_helper( + room_id: &RoomId, + event: SyncTimelineEvent, + client: &Client, + ) { + let mut room = v4::SlidingSyncRoom::new(); + room.timeline.push(event.event); + let response = response_with_room(room_id, room).await; + client.process_sliding_sync_test_helper(&response).await.unwrap(); + } + + fn message_event( + room_id: &RoomId, + user_id: &UserId, + body: &str, + formatted_body: &str, + ts: u64, + ) -> SyncTimelineEvent { + SyncTimelineEvent::new( + Raw::from_json_string( + json!({ + "event_id": "$eventid6", + "sender": user_id, + "origin_server_ts": ts, + "type": "m.room.message", + "room_id": room_id.to_string(), + "content": { + "body": body, + "format": "org.matrix.custom.html", + "formatted_body": formatted_body, + "msgtype": "m.text" + }, + }) + .to_string(), + ) + .unwrap(), + ) + } + + async fn response_with_room(room_id: &RoomId, room: v4::SlidingSyncRoom) -> v4::Response { + let mut response = v4::Response::new("6".to_owned()); + response.rooms.insert(room_id.to_owned(), room); + response + } + } +>>>>>>> diff --git a/crates/matrix-sdk-ui/src/timeline/tests/mod.rs b/crates/matrix-sdk-ui/src/timeline/tests/mod.rs index 88b5f1cf8e8..bda17b06a6a 100644 --- a/crates/matrix-sdk-ui/src/timeline/tests/mod.rs +++ b/crates/matrix-sdk-ui/src/timeline/tests/mod.rs @@ -338,7 +338,8 @@ impl PaginableRoom for TestRoomDataProvider { } } -#[async_trait] +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] impl RoomDataProvider for TestRoomDataProvider { fn own_user_id(&self) -> &UserId { &ALICE diff --git a/crates/matrix-sdk-ui/src/timeline/traits.rs b/crates/matrix-sdk-ui/src/timeline/traits.rs index af8711cfe4b..655cecc757b 100644 --- a/crates/matrix-sdk-ui/src/timeline/traits.rs +++ b/crates/matrix-sdk-ui/src/timeline/traits.rs @@ -17,7 +17,7 @@ use indexmap::IndexMap; #[cfg(feature = "e2e-encryption")] use matrix_sdk::{deserialized_responses::TimelineEvent, Result}; use matrix_sdk::{event_cache::paginator::PaginableRoom, Room}; -use matrix_sdk_base::latest_event::LatestEvent; +use matrix_sdk_base::{latest_event::LatestEvent, SendOutsideWasm, SyncOutsideWasm}; #[cfg(feature = "e2e-encryption")] use ruma::{events::AnySyncTimelineEvent, serde::Raw}; use ruma::{ @@ -33,7 +33,8 @@ use tracing::{debug, error}; use super::{Profile, TimelineBuilder}; use crate::timeline::{self, Timeline}; -#[async_trait] +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] pub trait RoomExt { /// Get a [`Timeline`] for this room. /// @@ -55,7 +56,8 @@ pub trait RoomExt { fn timeline_builder(&self) -> TimelineBuilder; } -#[async_trait] +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] impl RoomExt for Room { async fn timeline(&self) -> Result { self.timeline_builder().build().await @@ -66,8 +68,11 @@ impl RoomExt for Room { } } -#[async_trait] -pub(super) trait RoomDataProvider: Clone + Send + Sync + 'static + PaginableRoom { +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] +pub(super) trait RoomDataProvider: + Clone + SendOutsideWasm + SyncOutsideWasm + 'static + PaginableRoom +{ fn own_user_id(&self) -> &UserId; fn room_version(&self) -> RoomVersionId; async fn profile_from_user_id(&self, user_id: &UserId) -> Option; @@ -90,7 +95,8 @@ pub(super) trait RoomDataProvider: Clone + Send + Sync + 'static + PaginableRoom async fn push_rules_and_context(&self) -> Option<(Ruleset, PushConditionRoomCtx)>; } -#[async_trait] +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] impl RoomDataProvider for Room { fn own_user_id(&self) -> &UserId { (**self).own_user_id() @@ -216,13 +222,15 @@ impl RoomDataProvider for Room { // Internal helper to make most of retry_event_decryption independent of a room // object, which is annoying to create for testing and not really needed #[cfg(feature = "e2e-encryption")] -#[async_trait] -pub(super) trait Decryptor: Clone + Send + Sync + 'static { +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] +pub(super) trait Decryptor: Clone + SendOutsideWasm + SyncOutsideWasm + 'static { async fn decrypt_event_impl(&self, raw: &Raw) -> Result; } #[cfg(feature = "e2e-encryption")] -#[async_trait] +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] impl Decryptor for Room { async fn decrypt_event_impl(&self, raw: &Raw) -> Result { self.decrypt_event(raw.cast_ref()).await @@ -230,7 +238,8 @@ impl Decryptor for Room { } #[cfg(all(test, feature = "e2e-encryption"))] -#[async_trait] +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] impl Decryptor for (matrix_sdk_base::crypto::OlmMachine, ruma::OwnedRoomId) { async fn decrypt_event_impl(&self, raw: &Raw) -> Result { let (olm_machine, room_id) = self; diff --git a/crates/matrix-sdk/src/sliding_sync/error.rs b/crates/matrix-sdk/src/sliding_sync/error.rs index 34431f21fdc..8eb71350608 100644 --- a/crates/matrix-sdk/src/sliding_sync/error.rs +++ b/crates/matrix-sdk/src/sliding_sync/error.rs @@ -1,7 +1,7 @@ //! Sliding Sync errors. +use matrix_sdk_common::executor::JoinError; use thiserror::Error; -use tokio::task::JoinError; /// Internal representation of errors in Sliding Sync. #[derive(Error, Debug)] diff --git a/crates/matrix-sdk/src/sliding_sync/mod.rs b/crates/matrix-sdk/src/sliding_sync/mod.rs index dff893e3a71..ffd199c94a5 100644 --- a/crates/matrix-sdk/src/sliding_sync/mod.rs +++ b/crates/matrix-sdk/src/sliding_sync/mod.rs @@ -44,7 +44,7 @@ use ruma::{ }; use serde::{Deserialize, Serialize}; use tokio::{ - select, spawn, + select, sync::{broadcast::Sender, Mutex as AsyncMutex, OwnedMutexGuard, RwLock as AsyncRwLock}, }; use tracing::{debug, error, info, instrument, trace, warn, Instrument, Span}; @@ -58,7 +58,7 @@ use self::{ client::SlidingSyncResponseProcessor, sticky_parameters::{LazyTransactionId, SlidingSyncStickyManager, StickyData}, }; -use crate::{config::RequestConfig, Client, Result}; +use crate::{config::RequestConfig, executor::spawn, Client, Result}; /// The Sliding Sync instance. /// diff --git a/crates/matrix-sdk/src/sliding_sync/utils.rs b/crates/matrix-sdk/src/sliding_sync/utils.rs index 0bc1e998d43..6d39f74a4f8 100644 --- a/crates/matrix-sdk/src/sliding_sync/utils.rs +++ b/crates/matrix-sdk/src/sliding_sync/utils.rs @@ -6,7 +6,7 @@ use std::{ task::{Context, Poll}, }; -use tokio::task::{JoinError, JoinHandle}; +use matrix_sdk_common::executor::{JoinError, JoinHandle}; /// Private type to ensure a task is aborted on drop. pub(crate) struct AbortOnDrop(JoinHandle); @@ -23,7 +23,7 @@ impl Drop for AbortOnDrop { } } -impl Future for AbortOnDrop { +impl Future for AbortOnDrop { type Output = Result; fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { From 54db4e77688975469fbe411e11a6f0326e21bd4d Mon Sep 17 00:00:00 2001 From: maan2003 Date: Tue, 16 Apr 2024 00:15:03 +0530 Subject: [PATCH 6/6] chore: wasm check in CI --- .github/workflows/ci.yml | 3 +++ xtask/src/ci.rs | 10 ++++++++++ 2 files changed, 13 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 83a56c7bcc8..67c8a566fbe 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -223,6 +223,9 @@ jobs: - name: '[m], indexeddb stores, no crypto' cmd: matrix-sdk-indexeddb-stores-no-crypto + - name: '[m]-sdk-ui' + cmd: matrix-sdk-ui + steps: - name: Checkout the repo uses: actions/checkout@v4 diff --git a/xtask/src/ci.rs b/xtask/src/ci.rs index 56ac0f9a679..ef7870ea107 100644 --- a/xtask/src/ci.rs +++ b/xtask/src/ci.rs @@ -97,6 +97,8 @@ enum WasmFeatureSet { /// Equivalent to `indexeddb-all-features`, `indexeddb-crypto` and /// `indexeddb-state` Indexeddb, + /// Check `matrix-sdk-ui` crate + MatrixSdkUi, } impl CiArgs { @@ -312,6 +314,10 @@ fn run_wasm_checks(cmd: Option) -> Result<()> { WasmFeatureSet::IndexeddbState, "-p matrix-sdk-indexeddb --no-default-features --features state-store", ), + ( + WasmFeatureSet::MatrixSdkUi, + "-p matrix-sdk-ui --no-default-features --features matrix-sdk/js,matrix-sdk/rustls-tls,e2e-encryption", + ), ]); let run = |arg_set: &str| { @@ -374,6 +380,10 @@ fn run_wasm_pack_tests(cmd: Option) -> Result<()> { WasmFeatureSet::IndexeddbState, ("crates/matrix-sdk-indexeddb", "--no-default-features --features state-store"), ), + ( + WasmFeatureSet::MatrixSdkUi, + ("crates/matrix-sdk-ui", "--no-default-features --features matrix-sdk/js,matrix-sdk/rustls-tls,e2e-encryption"), + ), ]); let run = |(folder, arg_set): (&str, &str)| {