mirror of
https://github.com/matrix-org/matrix-rust-sdk.git
synced 2026-05-18 21:52:30 -04:00
!bar
This commit is contained in:
@@ -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"]
|
||||
@@ -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;
|
||||
|
||||
169
crates/matrix-sdk-ui/src/room_list/mod.rs
Normal file
169
crates/matrix-sdk-ui/src/room_list/mod.rs
Normal 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()
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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")))]
|
||||
|
||||
107
crates/matrix-sdk-ui/tests/integration/room_list.rs
Normal file
107
crates/matrix-sdk-ui/tests/integration/room_list.rs
Normal 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(())
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user