This commit is contained in:
Ivan Enderlin
2023-05-24 17:53:58 +02:00
parent 7706b0096b
commit f47c2ba125
7 changed files with 294 additions and 38 deletions

View File

@@ -4,17 +4,20 @@ version = "0.6.0"
edition = "2021"
[features]
default = ["e2e-encryption", "native-tls", "experimental-roomlist"]
default = ["e2e-encryption", "native-tls", "experimental-room-list"]
e2e-encryption = ["matrix-sdk/e2e-encryption"]
native-tls = ["matrix-sdk/native-tls"]
rustls-tls = ["matrix-sdk/rustls-tls"]
experimental-roomlist = ["experimental-sliding-sync"]
experimental-room-list = ["experimental-sliding-sync", "dep:async-stream"]
experimental-sliding-sync = ["matrix-sdk/experimental-sliding-sync"]
testing = []
[dependencies]
async-stream = { workspace = true, optional = true }
async-trait = { workspace = true }
chrono = "0.4.23"
eyeball-im = { workspace = true }
@@ -23,6 +26,7 @@ futures-util = { workspace = true }
imbl = { version = "2.0.0", features = ["serde"] }
indexmap = "1.9.1"
matrix-sdk = { version = "0.6.2", path = "../matrix-sdk", default-features = false }
matrix-sdk-base = { version = "0.6.1", path = "../matrix-sdk-base" }
mime = "0.3.16"
once_cell = { workspace = true }
pin-project-lite = "0.2.9"
@@ -35,8 +39,13 @@ tracing = { workspace = true, features = ["attributes"] }
[dev-dependencies]
anyhow = { workspace = true }
assert-json-diff = "2.0"
assert_matches = { workspace = true }
ctor = { workspace = true }
matrix-sdk-test = { version = "0.6.0", path = "../../testing/matrix-sdk-test" }
tracing-subscriber = { version = "0.3.11", features = ["env-filter"] }
wiremock = "0.5.13"
[[test]]
name = "integration"
required-features = ["testing"]

View File

@@ -14,8 +14,10 @@
mod events;
#[cfg(feature = "experimental-roomlist")]
pub mod roomlist;
#[cfg(feature = "experimental-room-list")]
pub mod room_list;
pub mod timeline;
#[cfg(feature = "experimental-room-list")]
pub use self::room_list::RoomList;
pub use self::timeline::Timeline;

View File

@@ -0,0 +1,169 @@
//! `RoomList` API.
use async_stream::stream;
use async_trait::async_trait;
use futures_util::{pin_mut, Stream, StreamExt};
use matrix_sdk::{
Client, Error, Result, SlidingSync, SlidingSyncList, SlidingSyncMode, UpdateSummary,
};
use once_cell::sync::Lazy;
pub const ALL_ROOMS_LIST_NAME: &str = "all_rooms";
pub const VISIBLE_ROOMS_LIST_NAME: &str = "visible_rooms";
#[derive(Debug)]
pub struct RoomList {
sliding_sync: SlidingSync,
}
impl RoomList {
pub async fn new(client: Client) -> Result<Self> {
Ok(Self {
sliding_sync: client
.sliding_sync()
.storage_key(Some("matrix-sdk-ui-roomlist".to_string()))
.add_cached_list(
SlidingSyncList::builder(ALL_ROOMS_LIST_NAME)
.sync_mode(SlidingSyncMode::new_selective()),
)
.await?
.build()
.await?,
})
}
pub fn sync(&self) -> impl Stream<Item = Result<(), Error>> + '_ {
stream! {
let sync = self.sliding_sync.sync();
pin_mut!(sync);
let mut state = State::LoadFirstRooms;
while let Some(update_summary) = sync.next().await {
state = state.next(update_summary, &self.sliding_sync).await?;
// if let State::Failure = state {
// yield Err(());
// // transform into a custom `MyError::SyncFailure`
// } else {
yield Ok(());
// }
}
}
}
#[cfg(feature = "testing")]
pub fn sliding_sync(&self) -> &SlidingSync {
&self.sliding_sync
}
}
enum State {
LoadFirstRooms,
LoadAllRooms,
Enjoy,
Failure,
}
impl State {
async fn next(
self,
update_summary: Result<UpdateSummary, Error>,
sliding_sync: &SlidingSync,
) -> Result<Self, Error> {
use State::*;
if update_summary.is_err() {
return Ok(Failure);
}
let (next_state, actions) = match self {
LoadFirstRooms => (LoadAllRooms, Actions::first_rooms_are_loaded()),
LoadAllRooms => (Enjoy, Actions::none()),
Enjoy => (Enjoy, Actions::none()),
Failure => (Failure, Actions::none()),
};
for action in actions.iter() {
action.run(sliding_sync).await?;
}
Ok(next_state)
}
}
#[async_trait]
trait Action {
async fn run(&self, sliding_sync: &SlidingSync) -> Result<(), Error>;
}
struct AddVisibleRoomsList;
#[async_trait]
impl Action for AddVisibleRoomsList {
async fn run(&self, sliding_sync: &SlidingSync) -> Result<(), Error> {
sliding_sync
.add_list(
SlidingSyncList::builder(VISIBLE_ROOMS_LIST_NAME)
.sync_mode(SlidingSyncMode::new_selective()),
)
.await?;
Ok(())
}
}
struct ChangeAllRoomsListToSelectiveSyncMode;
#[async_trait]
impl Action for ChangeAllRoomsListToSelectiveSyncMode {
async fn run(&self, sliding_sync: &SlidingSync) -> Result<(), Error> {
sliding_sync
.on_list("all_rooms", |list| list.set_sync_mode(SlidingSyncMode::new_growing(20, None)))
.unwrap() // transform into a custom `MyError::UnknownList`
?;
Ok(())
}
}
type OneAction = Box<dyn Action + Send + Sync>;
type ManyActions = Vec<OneAction>;
struct Actions {
actions: &'static Lazy<ManyActions>,
}
macro_rules! actions {
(
$(
$action_group_name:ident => [
$( $action_name:ident ),* $(,)?
]
),*
$(,)?
) => {
$(
fn $action_group_name () -> Self {
static ACTIONS: Lazy<ManyActions> = Lazy::new(|| {
vec![
$( Box::new( $action_name ) ),*
]
});
Self { actions: &ACTIONS }
}
)*
};
}
impl Actions {
actions! {
none => [],
first_rooms_are_loaded => [ChangeAllRoomsListToSelectiveSyncMode, AddVisibleRoomsList],
}
fn iter(&self) -> &[OneAction] {
self.actions.as_slice()
}
}

View File

@@ -1,33 +0,0 @@
//! `RoomList` API.
use matrix_sdk::{Client, Result, SlidingSync, SlidingSyncList};
#[derive(Debug)]
pub struct RoomList {
sliding_sync: SlidingSync,
}
impl RoomList {
pub async fn new(client: Client) -> Result<Self> {
Ok(Self {
sliding_sync: client
.sliding_sync()
.storage_key(Some("matrix-sdk-ui-roomlist".to_string()))
.add_cached_list(SlidingSyncList::builder("all_rooms"))
.await?
.add_list(SlidingSyncList::builder("visible_rooms"))
.build()
.await?,
})
}
pub fn sync(&self) {}
}
#[cfg(test)]
mod tests {
#[tokio::test]
async fn test_has_two_lists() {
// let
}
}

View File

@@ -7,6 +7,8 @@ use wiremock::{
Mock, MockServer, ResponseTemplate,
};
#[cfg(feature = "experimental-room-list")]
mod room_list;
mod timeline;
#[cfg(all(test, not(target_arch = "wasm32")))]

View File

@@ -0,0 +1,107 @@
use anyhow::{Context, Result};
use futures_util::{pin_mut, StreamExt};
use matrix_sdk_test::async_test;
use matrix_sdk_ui::{room_list, RoomList};
use serde_json::json;
use wiremock::{http::Method, Match, Mock, MockServer, Request, ResponseTemplate};
use crate::logged_in_client;
async fn new_room_list() -> Result<(MockServer, RoomList)> {
let (client, server) = logged_in_client().await;
let room_list = RoomList::new(client).await?;
Ok((server, room_list))
}
#[derive(Copy, Clone)]
struct SlidingSyncMatcher;
impl Match for SlidingSyncMatcher {
fn matches(&self, request: &Request) -> bool {
request.url.path() == "/_matrix/client/unstable/org.matrix.msc3575/sync"
&& request.method == Method::Post
}
}
macro_rules! sync_then_assert_request_and_fake_response {
(
[$server:ident, $room_list_sync_stream:ident]
request = { $( $request_json:tt )* },
response = { $( $response_json:tt )* }
$(,)?
) => {
{
let matcher = SlidingSyncMatcher;
let _mock_guard = Mock::given(matcher)
.respond_with(ResponseTemplate::new(200).set_body_json(
json!({ $( $response_json )* })
))
.mount_as_scoped(&$server)
.await;
let next = $room_list_sync_stream.next().await.context("`sync` trip")??;
for request in $server.received_requests().await.expect("Request recording has been disabled").iter().rev() {
if matcher.matches(request) {
let json_value = serde_json::from_slice::<serde_json::Value>(&request.body).unwrap();
if let Err(error) = assert_json_diff::assert_json_matches_no_panic(
&json_value,
&json!({ $( $response_json )* }),
assert_json_diff::Config::new(assert_json_diff::CompareMode::Inclusive),
) {
dbg!(json_value);
panic!("{}", error);
}
}
}
next
}
};
}
#[async_test]
async fn test_one_list() -> Result<()> {
let (_, room_list) = new_room_list().await?;
let sliding_sync = room_list.sliding_sync();
assert_eq!(sliding_sync.on_list(room_list::ALL_ROOMS_LIST_NAME, |_list| ()), Some(()));
assert_eq!(sliding_sync.on_list(room_list::VISIBLE_ROOMS_LIST_NAME, |_list| ()), None);
Ok(())
}
#[async_test]
async fn test_foo() -> Result<()> {
let (server, room_list) = new_room_list().await?;
let sync = room_list.sync();
pin_mut!(sync);
sync_then_assert_request_and_fake_response! {
[server, sync]
request = {
"lists": {
"all_rooms": {
"ranges": [],
}
},
},
response = {
"pos": "foo",
"lists": {},
"rooms": {},
"extensions": {},
},
};
assert_eq!(
room_list.sliding_sync().on_list(room_list::VISIBLE_ROOMS_LIST_NAME, |_list| ()),
Some(())
);
Ok(())
}

View File

@@ -28,7 +28,7 @@ macro_rules! receive_response {
.mount_as_scoped(&$server)
.await;
let next = $sliding_sync_stream.next().await.context("`stream` trip")??;
let next = $sliding_sync_stream.next().await.context("`sync` trip")??;
next
}