Merge pull request #1 from matrix-org/feat-ui-roomlist

Feat UI roomlist
This commit is contained in:
Ivan Enderlin
2023-06-01 15:46:00 +02:00
committed by GitHub
3 changed files with 34 additions and 63 deletions

View File

@@ -1,17 +1,13 @@
//! `RoomList` API.
use std::{
fmt,
future::ready,
sync::{Arc, Mutex, RwLock, RwLockWriteGuard},
};
use std::future::ready;
use async_stream::stream;
use async_trait::async_trait;
use eyeball::shared::Observable;
use eyeball_im::VectorDiff;
use eyeball_im_util::FilteredVectorSubscriber;
use futures_util::{pin_mut, Stream, StreamExt};
use imbl::Vector;
pub use matrix_sdk::RoomListEntry;
use matrix_sdk::{
Client, Error as SlidingSyncError, SlidingSync, SlidingSyncList, SlidingSyncMode,
@@ -22,43 +18,20 @@ use thiserror::Error;
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,
entries_stream: RwLock<
FilteredVectorSubscriber<RoomListEntry, Box<dyn Fn(&RoomListEntry) -> bool + Sync + Send>>,
>,
state: Observable<State>,
}
impl fmt::Debug for RoomList {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter
.debug_struct("RoomList")
.field("sliding_sync", &self.sliding_sync)
.field("state", &self.state)
.finish_non_exhaustive()
}
}
impl RoomList {
pub async fn new(client: Client) -> Result<Self, Error> {
let entries = Arc::new(Mutex::new(None));
let 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().add_range(0..=19))
.once_built({
let entries = entries.clone();
move |list| {
*entries.lock().unwrap() =
Some(list.room_list_filtered_stream(Box::new(|_| true)));
list
}
}),
.sync_mode(SlidingSyncMode::new_selective().add_range(0..=19)),
)
.await
.map_err(Error::SlidingSync)?
@@ -66,14 +39,7 @@ impl RoomList {
.await
.map_err(Error::SlidingSync)?;
let entries =
Arc::try_unwrap(entries).map_err(|_| Error::Entries)?.into_inner().unwrap().unwrap();
Ok(Self {
sliding_sync,
entries_stream: RwLock::new(entries),
state: Observable::new(State::Init),
})
Ok(Self { sliding_sync, state: Observable::new(State::Init) })
}
pub fn sync(&self) -> impl Stream<Item = Result<(), Error>> + '_ {
@@ -127,26 +93,23 @@ impl RoomList {
Observable::subscribe(&self.state)
}
pub fn entries_stream(
pub async fn entries(
&self,
) -> RwLockWriteGuard<impl Stream<Item = VectorDiff<RoomListEntry>>> {
self.entries_stream.write().unwrap()
) -> Result<(Vector<RoomListEntry>, impl Stream<Item = VectorDiff<RoomListEntry>>), Error> {
self.sliding_sync
.on_list(ALL_ROOMS_LIST_NAME, |list| ready(list.room_list_stream()))
.await
.ok_or_else(|| Error::UnknownList(ALL_ROOMS_LIST_NAME.to_string()))
}
pub async fn update_entries_stream_filter(
pub async fn entries_filtered(
&self,
filter: Box<dyn Fn(&RoomListEntry) -> bool + Sync + Send>,
) -> Result<(), Error> {
let mut entries_stream =
self.entries_stream.try_write().map_err(|_| Error::CannotUpdateEntriesFilter)?;
*entries_stream = self
.sliding_sync
filter: impl Fn(&RoomListEntry) -> bool + Send + Sync + 'static,
) -> Result<(Vector<RoomListEntry>, impl Stream<Item = VectorDiff<RoomListEntry>>), Error> {
self.sliding_sync
.on_list(ALL_ROOMS_LIST_NAME, |list| ready(list.room_list_filtered_stream(filter)))
.await
.ok_or_else(|| Error::UnknownList(ALL_ROOMS_LIST_NAME.to_string()))?;
Ok(())
.ok_or_else(|| Error::UnknownList(ALL_ROOMS_LIST_NAME.to_string()))
}
#[cfg(any(test, feature = "testing"))]

View File

@@ -646,12 +646,12 @@ async fn test_entries_stream_with_updated_filter() -> Result<(), Error> {
// Second, update the filter.
room_list
.update_entries_stream_filter(Box::new(|room_list_entry| {
.update_entries_stream_filter(|room_list_entry| {
matches!(
room_list_entry.as_room_id(),
Some(room_id) if room_id.server_name() == "bar.org"
)
}))
})
.await?;
// Third, let's get a new entries stream.

View File

@@ -17,6 +17,7 @@ use eyeball_im::{ObservableVector, VectorDiff};
use eyeball_im_util::{FilteredVectorSubscriber, VectorExt};
pub(super) use frozen::FrozenSlidingSyncList;
use futures_core::Stream;
use imbl::Vector;
pub(super) use request_generator::*;
pub use room_list_entry::RoomListEntry;
use ruma::{api::client::sync::sync_events::v4, assign, events::StateEventType, OwnedRoomId};
@@ -49,6 +50,8 @@ pub struct SlidingSyncList {
inner: Arc<SlidingSyncListInner>,
}
type BoxedRoomListEntryFilter = Box<dyn Fn(&RoomListEntry) -> bool + Sync + Send>;
impl SlidingSyncList {
/// Create a new [`SlidingSyncListBuilder`] with the given name.
pub fn builder(name: impl Into<String>) -> SlidingSyncListBuilder {
@@ -125,8 +128,13 @@ impl SlidingSyncList {
///
/// There's no guarantee of ordering between items emitted by this stream
/// and those emitted by other streams exposed on this structure.
pub fn room_list_stream(&self) -> impl Stream<Item = VectorDiff<RoomListEntry>> {
ObservableVector::subscribe(&self.inner.room_list.read().unwrap())
pub fn room_list_stream(
&self,
) -> (Vector<RoomListEntry>, impl Stream<Item = VectorDiff<RoomListEntry>>) {
let read_lock = self.inner.room_list.read().unwrap();
let values = (*read_lock).clone();
let subscriber = ObservableVector::subscribe(&read_lock);
(values, subscriber)
}
/// Get a stream of room list, but filtered.
@@ -135,13 +143,13 @@ impl SlidingSyncList {
/// by `filter`.
pub fn room_list_filtered_stream(
&self,
filter: Box<dyn Fn(&RoomListEntry) -> bool + Sync + Send>,
) -> FilteredVectorSubscriber<RoomListEntry, Box<dyn Fn(&RoomListEntry) -> bool + Sync + Send>>
filter: impl Fn(&RoomListEntry) -> bool + Sync + Send + 'static,
) -> (Vector<RoomListEntry>, FilteredVectorSubscriber<RoomListEntry, BoxedRoomListEntryFilter>)
{
let (_, stream) =
ObservableVector::subscribe_filtered(&self.inner.room_list.read().unwrap(), filter);
stream
ObservableVector::subscribe_filtered(
&self.inner.room_list.read().unwrap(),
Box::new(filter),
)
}
/// Get the maximum number of rooms. See [`Self::maximum_number_of_rooms`]