mirror of
https://github.com/matrix-org/matrix-rust-sdk.git
synced 2026-02-16 10:32:43 -05:00
feat(ui): Moaaar filters: add all, any, not, unread and category filters
feat(ui): Moaaar filters: add `all`, `any`, `not`, `unread` and `category` filters
This commit is contained in:
@@ -13,9 +13,13 @@ use matrix_sdk::{
|
||||
RoomListEntry as MatrixRoomListEntry,
|
||||
};
|
||||
use matrix_sdk_ui::{
|
||||
room_list_service::filters::{
|
||||
new_filter_all, new_filter_all_non_left, new_filter_fuzzy_match_room_name, new_filter_none,
|
||||
new_filter_normalized_match_room_name,
|
||||
room_list_service::{
|
||||
filters::{
|
||||
new_filter_all, new_filter_any, new_filter_category, new_filter_fuzzy_match_room_name,
|
||||
new_filter_non_left, new_filter_none, new_filter_normalized_match_room_name,
|
||||
new_filter_unread, RoomCategory,
|
||||
},
|
||||
BoxedFilterFn,
|
||||
},
|
||||
timeline::default_event_filter,
|
||||
};
|
||||
@@ -391,19 +395,8 @@ impl RoomListDynamicEntriesController {
|
||||
#[uniffi::export]
|
||||
impl RoomListDynamicEntriesController {
|
||||
fn set_filter(&self, kind: RoomListEntriesDynamicFilterKind) -> bool {
|
||||
use RoomListEntriesDynamicFilterKind as Kind;
|
||||
|
||||
match kind {
|
||||
Kind::All => self.inner.set_filter(new_filter_all()),
|
||||
Kind::AllNonLeft => self.inner.set_filter(new_filter_all_non_left(&self.client)),
|
||||
Kind::None => self.inner.set_filter(new_filter_none()),
|
||||
Kind::NormalizedMatchRoomName { pattern } => {
|
||||
self.inner.set_filter(new_filter_normalized_match_room_name(&self.client, &pattern))
|
||||
}
|
||||
Kind::FuzzyMatchRoomName { pattern } => {
|
||||
self.inner.set_filter(new_filter_fuzzy_match_room_name(&self.client, &pattern))
|
||||
}
|
||||
}
|
||||
let FilterWrapper(filter) = FilterWrapper::from(&self.client, kind);
|
||||
self.inner.set_filter(filter)
|
||||
}
|
||||
|
||||
fn add_one_page(&self) {
|
||||
@@ -417,13 +410,60 @@ impl RoomListDynamicEntriesController {
|
||||
|
||||
#[derive(uniffi::Enum)]
|
||||
pub enum RoomListEntriesDynamicFilterKind {
|
||||
All,
|
||||
AllNonLeft,
|
||||
All { filters: Vec<RoomListEntriesDynamicFilterKind> },
|
||||
Any { filters: Vec<RoomListEntriesDynamicFilterKind> },
|
||||
NonLeft,
|
||||
Unread,
|
||||
Category { expect: RoomListFilterCategory },
|
||||
None,
|
||||
NormalizedMatchRoomName { pattern: String },
|
||||
FuzzyMatchRoomName { pattern: String },
|
||||
}
|
||||
|
||||
#[derive(uniffi::Enum)]
|
||||
pub enum RoomListFilterCategory {
|
||||
Group,
|
||||
People,
|
||||
}
|
||||
|
||||
impl From<RoomListFilterCategory> for RoomCategory {
|
||||
fn from(value: RoomListFilterCategory) -> Self {
|
||||
match value {
|
||||
RoomListFilterCategory::Group => Self::Group,
|
||||
RoomListFilterCategory::People => Self::People,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Custom internal type to transform a `RoomListEntriesDynamicFilterKind` into
|
||||
/// a `BoxedFilterFn`.
|
||||
struct FilterWrapper(BoxedFilterFn);
|
||||
|
||||
impl FilterWrapper {
|
||||
fn from(client: &matrix_sdk::Client, value: RoomListEntriesDynamicFilterKind) -> Self {
|
||||
use RoomListEntriesDynamicFilterKind as Kind;
|
||||
|
||||
match value {
|
||||
Kind::All { filters } => Self(Box::new(new_filter_all(
|
||||
filters.into_iter().map(|filter| FilterWrapper::from(client, filter).0).collect(),
|
||||
))),
|
||||
Kind::Any { filters } => Self(Box::new(new_filter_any(
|
||||
filters.into_iter().map(|filter| FilterWrapper::from(client, filter).0).collect(),
|
||||
))),
|
||||
Kind::NonLeft => Self(Box::new(new_filter_non_left(client))),
|
||||
Kind::Unread => Self(Box::new(new_filter_unread(client))),
|
||||
Kind::Category { expect } => Self(Box::new(new_filter_category(client, expect.into()))),
|
||||
Kind::None => Self(Box::new(new_filter_none())),
|
||||
Kind::NormalizedMatchRoomName { pattern } => {
|
||||
Self(Box::new(new_filter_normalized_match_room_name(client, &pattern)))
|
||||
}
|
||||
Kind::FuzzyMatchRoomName { pattern } => {
|
||||
Self(Box::new(new_filter_fuzzy_match_room_name(client, &pattern)))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(uniffi::Object)]
|
||||
pub struct RoomListItem {
|
||||
inner: Arc<matrix_sdk_ui::room_list_service::Room>,
|
||||
|
||||
@@ -31,7 +31,7 @@ pub mod latest_event;
|
||||
pub mod media;
|
||||
mod rooms;
|
||||
|
||||
mod read_receipts;
|
||||
pub mod read_receipts;
|
||||
pub use read_receipts::PreviousEventsProvider;
|
||||
#[cfg(feature = "experimental-sliding-sync")]
|
||||
mod sliding_sync;
|
||||
|
||||
@@ -15,9 +15,9 @@
|
||||
//! # Client-side read receipts computation
|
||||
//!
|
||||
//! While Matrix servers have the ability to provide basic information about the
|
||||
//! unread status of rooms, via [`matrix_sdk::ruma::UnreadNotificationCounts`],
|
||||
//! it's not reliable for encrypted rooms. Indeed, the server doesn't have
|
||||
//! access to the content of encrypted events, so it can only makes guesses when
|
||||
//! unread status of rooms, via [`crate::sync::UnreadNotificationsCount`], it's
|
||||
//! not reliable for encrypted rooms. Indeed, the server doesn't have access to
|
||||
//! the content of encrypted events, so it can only makes guesses when
|
||||
//! estimating unread and highlight counts.
|
||||
//!
|
||||
//! Instead, this module provides facilities to compute the number of unread
|
||||
@@ -36,8 +36,8 @@
|
||||
//! `marks_as_unread` function shows the opiniated set of rules that will filter
|
||||
//! out uninterested events.
|
||||
//!
|
||||
//! The only public method in that module is [`compute_unread_counts`], which
|
||||
//! updates the `RoomInfo` in place according to the new counts.
|
||||
//! The only `pub(crate)` method in that module is `compute_unread_counts`,
|
||||
//! which updates the `RoomInfo` in place according to the new counts.
|
||||
//!
|
||||
//! ## Implementation details: How to get the latest receipt?
|
||||
//!
|
||||
|
||||
@@ -347,6 +347,12 @@ impl Room {
|
||||
self.inner.read().base_info.dm_targets.clone()
|
||||
}
|
||||
|
||||
/// If this room is a direct message, returns the number of members that
|
||||
/// we're sharing the room with.
|
||||
pub fn direct_targets_length(&self) -> usize {
|
||||
self.inner.read().base_info.dm_targets.len()
|
||||
}
|
||||
|
||||
/// Is the room encrypted.
|
||||
pub fn is_encrypted(&self) -> bool {
|
||||
self.inner.read().is_encrypted()
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
use matrix_sdk::RoomListEntry;
|
||||
use super::{super::room_list::BoxedFilterFn, Filter};
|
||||
|
||||
/// Create a new filter that will accept all filled or invalidated entries.
|
||||
pub fn new_filter() -> impl Fn(&RoomListEntry) -> bool {
|
||||
|room_list_entry| -> bool {
|
||||
matches!(room_list_entry, RoomListEntry::Filled(_) | RoomListEntry::Invalidated(_))
|
||||
}
|
||||
/// Create a new filter that will run multiple filters. It returns `false` if at
|
||||
/// least one of the filter returns `false`.
|
||||
pub fn new_filter(filters: Vec<BoxedFilterFn>) -> impl Filter {
|
||||
move |room_list_entry| -> bool { filters.iter().all(|filter| filter(room_list_entry)) }
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -17,11 +16,58 @@ mod tests {
|
||||
use super::new_filter;
|
||||
|
||||
#[test]
|
||||
fn test_all_kind_of_room_list_entry() {
|
||||
let all = new_filter();
|
||||
fn test_one_filter() {
|
||||
let room_list_entry = RoomListEntry::Filled(room_id!("!r0:bar.org").to_owned());
|
||||
|
||||
assert!(all(&RoomListEntry::Empty).not());
|
||||
assert!(all(&RoomListEntry::Filled(room_id!("!r0:bar.org").to_owned())));
|
||||
assert!(all(&RoomListEntry::Invalidated(room_id!("!r0:bar.org").to_owned())));
|
||||
{
|
||||
let filter = |_: &_| true;
|
||||
let all = new_filter(vec![Box::new(filter)]);
|
||||
|
||||
assert!(all(&room_list_entry));
|
||||
}
|
||||
|
||||
{
|
||||
let filter = |_: &_| false;
|
||||
let all = new_filter(vec![Box::new(filter)]);
|
||||
|
||||
assert!(all(&room_list_entry).not());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_two_filters() {
|
||||
let room_list_entry = RoomListEntry::Filled(room_id!("!r0:bar.org").to_owned());
|
||||
|
||||
{
|
||||
let filter1 = |_: &_| true;
|
||||
let filter2 = |_: &_| true;
|
||||
let all = new_filter(vec![Box::new(filter1), Box::new(filter2)]);
|
||||
|
||||
assert!(all(&room_list_entry));
|
||||
}
|
||||
|
||||
{
|
||||
let filter1 = |_: &_| true;
|
||||
let filter2 = |_: &_| false;
|
||||
let all = new_filter(vec![Box::new(filter1), Box::new(filter2)]);
|
||||
|
||||
assert!(all(&room_list_entry).not());
|
||||
}
|
||||
|
||||
{
|
||||
let filter1 = |_: &_| false;
|
||||
let filter2 = |_: &_| true;
|
||||
let all = new_filter(vec![Box::new(filter1), Box::new(filter2)]);
|
||||
|
||||
assert!(all(&room_list_entry).not());
|
||||
}
|
||||
|
||||
{
|
||||
let filter1 = |_: &_| false;
|
||||
let filter2 = |_: &_| false;
|
||||
let all = new_filter(vec![Box::new(filter1), Box::new(filter2)]);
|
||||
|
||||
assert!(all(&room_list_entry).not());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
81
crates/matrix-sdk-ui/src/room_list_service/filters/any.rs
Normal file
81
crates/matrix-sdk-ui/src/room_list_service/filters/any.rs
Normal file
@@ -0,0 +1,81 @@
|
||||
use super::{super::room_list::BoxedFilterFn, Filter};
|
||||
|
||||
/// Create a new filter that will run multiple filters. It returns `true` if at
|
||||
/// least one of the filter returns `true`.
|
||||
pub fn new_filter(filters: Vec<BoxedFilterFn>) -> impl Filter {
|
||||
move |room_list_entry| -> bool { filters.iter().any(|filter| filter(room_list_entry)) }
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::ops::Not;
|
||||
|
||||
use matrix_sdk::RoomListEntry;
|
||||
use ruma::room_id;
|
||||
|
||||
use super::new_filter;
|
||||
|
||||
#[test]
|
||||
fn test_one_filter_is_true() {
|
||||
let room_list_entry = RoomListEntry::Filled(room_id!("!r0:bar.org").to_owned());
|
||||
|
||||
let filter = |_: &_| true;
|
||||
let any = new_filter(vec![Box::new(filter)]);
|
||||
|
||||
assert!(any(&room_list_entry));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_one_filter_is_false() {
|
||||
let room_list_entry = RoomListEntry::Filled(room_id!("!r0:bar.org").to_owned());
|
||||
|
||||
let filter = |_: &_| false;
|
||||
let any = new_filter(vec![Box::new(filter)]);
|
||||
|
||||
assert!(any(&room_list_entry).not());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_two_filters_with_true_true() {
|
||||
let room_list_entry = RoomListEntry::Filled(room_id!("!r0:bar.org").to_owned());
|
||||
|
||||
let filter1 = |_: &_| true;
|
||||
let filter2 = |_: &_| true;
|
||||
let any = new_filter(vec![Box::new(filter1), Box::new(filter2)]);
|
||||
|
||||
assert!(any(&room_list_entry));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_two_filters_with_true_false() {
|
||||
let room_list_entry = RoomListEntry::Filled(room_id!("!r0:bar.org").to_owned());
|
||||
|
||||
let filter1 = |_: &_| true;
|
||||
let filter2 = |_: &_| false;
|
||||
let any = new_filter(vec![Box::new(filter1), Box::new(filter2)]);
|
||||
|
||||
assert!(any(&room_list_entry));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_two_filters_with_false_true() {
|
||||
let room_list_entry = RoomListEntry::Filled(room_id!("!r0:bar.org").to_owned());
|
||||
|
||||
let filter1 = |_: &_| false;
|
||||
let filter2 = |_: &_| true;
|
||||
let any = new_filter(vec![Box::new(filter1), Box::new(filter2)]);
|
||||
|
||||
assert!(any(&room_list_entry));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_two_filters_with_false_false() {
|
||||
let room_list_entry = RoomListEntry::Filled(room_id!("!r0:bar.org").to_owned());
|
||||
|
||||
let filter1 = |_: &_| false;
|
||||
let filter2 = |_: &_| false;
|
||||
let any = new_filter(vec![Box::new(filter1), Box::new(filter2)]);
|
||||
|
||||
assert!(any(&room_list_entry).not());
|
||||
}
|
||||
}
|
||||
186
crates/matrix-sdk-ui/src/room_list_service/filters/category.rs
Normal file
186
crates/matrix-sdk-ui/src/room_list_service/filters/category.rs
Normal file
@@ -0,0 +1,186 @@
|
||||
use matrix_sdk::{Client, RoomListEntry};
|
||||
|
||||
use super::Filter;
|
||||
|
||||
/// An enum to represent whether a room is about “people” (strictly 2 users) or
|
||||
/// “group” (1 or more than 2 users).
|
||||
///
|
||||
/// Ideally, we would only want to rely on the
|
||||
/// [`matrix_sdk::BaseRoom::is_direct`] method, but the rules are a little bit
|
||||
/// different for this high-level UI API.
|
||||
///
|
||||
/// This is implemented this way so that it's impossible to filter by “group”
|
||||
/// and by “people” at the same time: these criteria are mutually
|
||||
/// exclusive by design per filter.
|
||||
#[derive(Copy, Clone, PartialEq)]
|
||||
pub enum RoomCategory {
|
||||
Group,
|
||||
People,
|
||||
}
|
||||
|
||||
type DirectTargetsLength = usize;
|
||||
|
||||
struct CategoryRoomMatcher<F>
|
||||
where
|
||||
F: Fn(&RoomListEntry) -> Option<DirectTargetsLength>,
|
||||
{
|
||||
/// _Direct targets_ mean the number of users in a direct room, except us.
|
||||
/// So if it returns 1, it means there are 2 users in the direct room.
|
||||
number_of_direct_targets: F,
|
||||
}
|
||||
|
||||
impl<F> CategoryRoomMatcher<F>
|
||||
where
|
||||
F: Fn(&RoomListEntry) -> Option<DirectTargetsLength>,
|
||||
{
|
||||
fn matches(&self, room_list_entry: &RoomListEntry, expected_kind: RoomCategory) -> bool {
|
||||
if !matches!(room_list_entry, RoomListEntry::Filled(_) | RoomListEntry::Invalidated(_)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let kind = match (self.number_of_direct_targets)(room_list_entry) {
|
||||
// If 1, we are sure it's a direct room between two users. It's the strict
|
||||
// definition of the `People` category, all good.
|
||||
Some(1) => RoomCategory::People,
|
||||
|
||||
// If smaller than 1, we are not sure it's a direct room, it's then a `Group`.
|
||||
// If greater than 1, we are sure it's a direct room but not between
|
||||
// two users, so it's a `Group` based on our expectation.
|
||||
Some(_) => RoomCategory::Group,
|
||||
|
||||
// Don't know.
|
||||
None => return false,
|
||||
};
|
||||
|
||||
kind == expected_kind
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a new filter that will accept all filled or invalidated entries, and
|
||||
/// if the associated rooms fit in the `expected_category`. The category is
|
||||
/// defined by [`RoomCategory`], see this type to learn more.
|
||||
pub fn new_filter(client: &Client, expected_category: RoomCategory) -> impl Filter {
|
||||
let client = client.clone();
|
||||
|
||||
let matcher = CategoryRoomMatcher {
|
||||
number_of_direct_targets: move |room| {
|
||||
let room_id = room.as_room_id()?;
|
||||
let room = client.get_room(room_id)?;
|
||||
|
||||
Some(room.direct_targets_length())
|
||||
},
|
||||
};
|
||||
|
||||
move |room_list_entry| -> bool { matcher.matches(room_list_entry, expected_category) }
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::ops::Not;
|
||||
|
||||
use matrix_sdk::RoomListEntry;
|
||||
use ruma::room_id;
|
||||
|
||||
use super::{CategoryRoomMatcher, RoomCategory};
|
||||
|
||||
#[test]
|
||||
fn test_kind_is_group() {
|
||||
let matcher = CategoryRoomMatcher { number_of_direct_targets: |_| Some(42) };
|
||||
|
||||
// Expect `People`.
|
||||
{
|
||||
let expected_kind = RoomCategory::People;
|
||||
|
||||
assert!(matcher.matches(&RoomListEntry::Empty, expected_kind).not());
|
||||
assert!(
|
||||
matcher
|
||||
.matches(
|
||||
&RoomListEntry::Filled(room_id!("!r0:bar.org").to_owned(),),
|
||||
expected_kind,
|
||||
)
|
||||
.not()
|
||||
);
|
||||
assert!(matcher
|
||||
.matches(
|
||||
&RoomListEntry::Invalidated(room_id!("!r0:bar.org").to_owned()),
|
||||
expected_kind
|
||||
)
|
||||
.not());
|
||||
}
|
||||
|
||||
// Expect `Group`.
|
||||
{
|
||||
let expected_kind = RoomCategory::Group;
|
||||
|
||||
assert!(matcher.matches(&RoomListEntry::Empty, expected_kind).not());
|
||||
assert!(matcher.matches(
|
||||
&RoomListEntry::Filled(room_id!("!r0:bar.org").to_owned(),),
|
||||
expected_kind,
|
||||
));
|
||||
assert!(matcher.matches(
|
||||
&RoomListEntry::Invalidated(room_id!("!r0:bar.org").to_owned()),
|
||||
expected_kind,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_kind_is_people() {
|
||||
let matcher = CategoryRoomMatcher { number_of_direct_targets: |_| Some(1) };
|
||||
|
||||
// Expect `People`.
|
||||
{
|
||||
let expected_kind = RoomCategory::People;
|
||||
|
||||
assert!(matcher.matches(&RoomListEntry::Empty, expected_kind).not());
|
||||
assert!(matcher.matches(
|
||||
&RoomListEntry::Filled(room_id!("!r0:bar.org").to_owned()),
|
||||
expected_kind,
|
||||
));
|
||||
assert!(matcher.matches(
|
||||
&RoomListEntry::Invalidated(room_id!("!r0:bar.org").to_owned()),
|
||||
expected_kind
|
||||
));
|
||||
}
|
||||
|
||||
// Expect `Group`.
|
||||
{
|
||||
let expected_kind = RoomCategory::Group;
|
||||
|
||||
assert!(matcher.matches(&RoomListEntry::Empty, expected_kind).not());
|
||||
assert!(
|
||||
matcher
|
||||
.matches(
|
||||
&RoomListEntry::Filled(room_id!("!r0:bar.org").to_owned(),),
|
||||
expected_kind,
|
||||
)
|
||||
.not()
|
||||
);
|
||||
assert!(matcher
|
||||
.matches(
|
||||
&RoomListEntry::Invalidated(room_id!("!r0:bar.org").to_owned()),
|
||||
expected_kind,
|
||||
)
|
||||
.not());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_room_kind_cannot_be_found() {
|
||||
let matcher = CategoryRoomMatcher { number_of_direct_targets: |_| None };
|
||||
|
||||
assert!(matcher.matches(&RoomListEntry::Empty, RoomCategory::Group).not());
|
||||
assert!(matcher
|
||||
.matches(
|
||||
&RoomListEntry::Filled(room_id!("!r0:bar.org").to_owned()),
|
||||
RoomCategory::Group
|
||||
)
|
||||
.not());
|
||||
assert!(matcher
|
||||
.matches(
|
||||
&RoomListEntry::Invalidated(room_id!("!r0:bar.org").to_owned()),
|
||||
RoomCategory::Group
|
||||
)
|
||||
.not());
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
pub use fuzzy_matcher::{skim::SkimMatcherV2, FuzzyMatcher as _};
|
||||
use matrix_sdk::{Client, RoomListEntry};
|
||||
use matrix_sdk::Client;
|
||||
|
||||
use super::normalize_string;
|
||||
use super::{normalize_string, Filter};
|
||||
|
||||
struct FuzzyMatcher {
|
||||
matcher: SkimMatcherV2,
|
||||
@@ -19,7 +19,7 @@ impl FuzzyMatcher {
|
||||
self
|
||||
}
|
||||
|
||||
fn fuzzy_match(&self, subject: &str) -> bool {
|
||||
fn matches(&self, subject: &str) -> bool {
|
||||
// No pattern means there is a match.
|
||||
let Some(pattern) = self.pattern.as_ref() else { return true };
|
||||
|
||||
@@ -31,7 +31,7 @@ impl FuzzyMatcher {
|
||||
///
|
||||
/// Rooms are fetched from the `Client`. The pattern and the room names are
|
||||
/// normalized with `normalize_string`.
|
||||
pub fn new_filter(client: &Client, pattern: &str) -> impl Fn(&RoomListEntry) -> bool {
|
||||
pub fn new_filter(client: &Client, pattern: &str) -> impl Filter {
|
||||
let searcher = FuzzyMatcher::new().with_pattern(pattern);
|
||||
|
||||
let client = client.clone();
|
||||
@@ -41,7 +41,7 @@ pub fn new_filter(client: &Client, pattern: &str) -> impl Fn(&RoomListEntry) ->
|
||||
let Some(room) = client.get_room(room_id) else { return false };
|
||||
let Some(room_name) = room.name() else { return false };
|
||||
|
||||
searcher.fuzzy_match(&room_name)
|
||||
searcher.matches(&room_name)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,14 +55,14 @@ mod tests {
|
||||
fn test_no_pattern() {
|
||||
let matcher = FuzzyMatcher::new();
|
||||
|
||||
assert!(matcher.fuzzy_match("hello"));
|
||||
assert!(matcher.matches("hello"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_empty_pattern() {
|
||||
let matcher = FuzzyMatcher::new();
|
||||
|
||||
assert!(matcher.fuzzy_match("hello"));
|
||||
assert!(matcher.matches("hello"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -70,10 +70,10 @@ mod tests {
|
||||
let matcher = FuzzyMatcher::new();
|
||||
|
||||
let matcher = matcher.with_pattern("mtx");
|
||||
assert!(matcher.fuzzy_match("matrix"));
|
||||
assert!(matcher.matches("matrix"));
|
||||
|
||||
let matcher = matcher.with_pattern("mxt");
|
||||
assert!(matcher.fuzzy_match("matrix").not());
|
||||
assert!(matcher.matches("matrix").not());
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -81,10 +81,10 @@ mod tests {
|
||||
let matcher = FuzzyMatcher::new();
|
||||
|
||||
let matcher = matcher.with_pattern("mtx");
|
||||
assert!(matcher.fuzzy_match("MaTrIX"));
|
||||
assert!(matcher.matches("MaTrIX"));
|
||||
|
||||
let matcher = matcher.with_pattern("mxt");
|
||||
assert!(matcher.fuzzy_match("MaTrIX").not());
|
||||
assert!(matcher.matches("MaTrIX").not());
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -92,12 +92,12 @@ mod tests {
|
||||
let matcher = FuzzyMatcher::new();
|
||||
|
||||
let matcher = matcher.with_pattern("mtx");
|
||||
assert!(matcher.fuzzy_match("matrix"));
|
||||
assert!(matcher.fuzzy_match("Matrix"));
|
||||
assert!(matcher.matches("matrix"));
|
||||
assert!(matcher.matches("Matrix"));
|
||||
|
||||
let matcher = matcher.with_pattern("Mtx");
|
||||
assert!(matcher.fuzzy_match("matrix").not());
|
||||
assert!(matcher.fuzzy_match("Matrix"));
|
||||
assert!(matcher.matches("matrix").not());
|
||||
assert!(matcher.matches("Matrix"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -110,10 +110,10 @@ mod tests {
|
||||
assert_eq!(matcher.pattern, Some("ubete".to_owned()));
|
||||
|
||||
// Second, assert that the subject is normalized too.
|
||||
assert!(matcher.fuzzy_match("un bel été"));
|
||||
assert!(matcher.matches("un bel été"));
|
||||
|
||||
// Another concrete test.
|
||||
let matcher = matcher.with_pattern("stf");
|
||||
assert!(matcher.fuzzy_match("Ștefan"));
|
||||
assert!(matcher.matches("Ștefan"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +1,32 @@
|
||||
mod all;
|
||||
mod all_non_left;
|
||||
mod any;
|
||||
mod category;
|
||||
mod fuzzy_match_room_name;
|
||||
mod non_left;
|
||||
mod none;
|
||||
mod normalized_match_room_name;
|
||||
mod not;
|
||||
mod unread;
|
||||
|
||||
pub use all::new_filter as new_filter_all;
|
||||
pub use all_non_left::new_filter as new_filter_all_non_left;
|
||||
pub use any::new_filter as new_filter_any;
|
||||
pub use category::{new_filter as new_filter_category, RoomCategory};
|
||||
pub use fuzzy_match_room_name::new_filter as new_filter_fuzzy_match_room_name;
|
||||
use matrix_sdk::RoomListEntry;
|
||||
pub use non_left::new_filter as new_filter_non_left;
|
||||
pub use none::new_filter as new_filter_none;
|
||||
pub use normalized_match_room_name::new_filter as new_filter_normalized_match_room_name;
|
||||
pub use not::new_filter as new_filter_not;
|
||||
use unicode_normalization::{char::is_combining_mark, UnicodeNormalization};
|
||||
pub use unread::new_filter as new_filter_unread;
|
||||
|
||||
/// A trait “alias” that represents a _filter_.
|
||||
///
|
||||
/// A filter is simply a function that receives a `&RoomListEntry` and returns a
|
||||
/// `bool`.
|
||||
pub trait Filter: Fn(&RoomListEntry) -> bool {}
|
||||
|
||||
impl<F> Filter for F where F: Fn(&RoomListEntry) -> bool {}
|
||||
|
||||
/// Normalize a string, i.e. decompose it into NFD (Normalization Form D, i.e. a
|
||||
/// canonical decomposition, see http://www.unicode.org/reports/tr15/) and
|
||||
|
||||
@@ -1,17 +1,25 @@
|
||||
use matrix_sdk::{Client, RoomListEntry};
|
||||
use matrix_sdk_base::RoomState;
|
||||
|
||||
struct NonLeftRoomMatcher<F: Fn(&RoomListEntry) -> Option<RoomState>> {
|
||||
get_state: F,
|
||||
use super::Filter;
|
||||
|
||||
struct NonLeftRoomMatcher<F>
|
||||
where
|
||||
F: Fn(&RoomListEntry) -> Option<RoomState>,
|
||||
{
|
||||
state: F,
|
||||
}
|
||||
|
||||
impl<F: Fn(&RoomListEntry) -> Option<RoomState>> NonLeftRoomMatcher<F> {
|
||||
impl<F> NonLeftRoomMatcher<F>
|
||||
where
|
||||
F: Fn(&RoomListEntry) -> Option<RoomState>,
|
||||
{
|
||||
fn matches(&self, room: &RoomListEntry) -> bool {
|
||||
if !matches!(room, RoomListEntry::Filled(_) | RoomListEntry::Invalidated(_)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if let Some(state) = (self.get_state)(room) {
|
||||
if let Some(state) = (self.state)(room) {
|
||||
state != RoomState::Left
|
||||
} else {
|
||||
false
|
||||
@@ -21,11 +29,11 @@ impl<F: Fn(&RoomListEntry) -> Option<RoomState>> NonLeftRoomMatcher<F> {
|
||||
|
||||
/// Create a new filter that will accept all filled or invalidated entries, but
|
||||
/// filters out left rooms.
|
||||
pub fn new_filter(client: &Client) -> impl Fn(&RoomListEntry) -> bool {
|
||||
pub fn new_filter(client: &Client) -> impl Filter {
|
||||
let client = client.clone();
|
||||
|
||||
let matcher = NonLeftRoomMatcher {
|
||||
get_state: move |room| {
|
||||
state: move |room| {
|
||||
let room_id = room.as_room_id()?;
|
||||
let room = client.get_room(room_id)?;
|
||||
Some(room.state())
|
||||
@@ -41,24 +49,24 @@ mod tests {
|
||||
use matrix_sdk_base::RoomState;
|
||||
use ruma::room_id;
|
||||
|
||||
use crate::room_list_service::filters::all_non_left::NonLeftRoomMatcher;
|
||||
use super::NonLeftRoomMatcher;
|
||||
|
||||
#[test]
|
||||
fn test_all_non_left_kind_of_room_list_entry() {
|
||||
// When we can't figure out the room state, nothing matches.
|
||||
let matcher = NonLeftRoomMatcher { get_state: |_| None };
|
||||
let matcher = NonLeftRoomMatcher { state: |_| None };
|
||||
assert!(!matcher.matches(&RoomListEntry::Empty));
|
||||
assert!(!matcher.matches(&RoomListEntry::Filled(room_id!("!r0:bar.org").to_owned())));
|
||||
assert!(!matcher.matches(&RoomListEntry::Invalidated(room_id!("!r0:bar.org").to_owned())));
|
||||
|
||||
// When a room has been left, it doesn't match.
|
||||
let matcher = NonLeftRoomMatcher { get_state: |_| Some(RoomState::Left) };
|
||||
let matcher = NonLeftRoomMatcher { state: |_| Some(RoomState::Left) };
|
||||
assert!(!matcher.matches(&RoomListEntry::Empty));
|
||||
assert!(!matcher.matches(&RoomListEntry::Filled(room_id!("!r0:bar.org").to_owned())));
|
||||
assert!(!matcher.matches(&RoomListEntry::Invalidated(room_id!("!r0:bar.org").to_owned())));
|
||||
|
||||
// When a room has been joined, it does match (unless it's empty).
|
||||
let matcher = NonLeftRoomMatcher { get_state: |_| Some(RoomState::Joined) };
|
||||
let matcher = NonLeftRoomMatcher { state: |_| Some(RoomState::Joined) };
|
||||
assert!(!matcher.matches(&RoomListEntry::Empty));
|
||||
assert!(matcher.matches(&RoomListEntry::Filled(room_id!("!r0:bar.org").to_owned())));
|
||||
assert!(matcher.matches(&RoomListEntry::Invalidated(room_id!("!r0:bar.org").to_owned())));
|
||||
@@ -1,7 +1,7 @@
|
||||
use matrix_sdk::RoomListEntry;
|
||||
use super::Filter;
|
||||
|
||||
/// Create a new filter that will reject all entries.
|
||||
pub fn new_filter() -> impl Fn(&RoomListEntry) -> bool {
|
||||
pub fn new_filter() -> impl Filter {
|
||||
|_room_list_entry| -> bool { false }
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use matrix_sdk::{Client, RoomListEntry};
|
||||
use matrix_sdk::Client;
|
||||
|
||||
use super::normalize_string;
|
||||
use super::{normalize_string, Filter};
|
||||
|
||||
struct NormalizedMatcher {
|
||||
pattern: Option<String>,
|
||||
@@ -17,7 +17,7 @@ impl NormalizedMatcher {
|
||||
self
|
||||
}
|
||||
|
||||
fn normalized_match(&self, subject: &str) -> bool {
|
||||
fn matches(&self, subject: &str) -> bool {
|
||||
// No pattern means there is a match.
|
||||
let Some(pattern) = self.pattern.as_ref() else { return true };
|
||||
|
||||
@@ -31,7 +31,7 @@ impl NormalizedMatcher {
|
||||
///
|
||||
/// Rooms are fetched from the `Client`. The pattern and the room names are
|
||||
/// normalized with `normalize_string`.
|
||||
pub fn new_filter(client: &Client, pattern: &str) -> impl Fn(&RoomListEntry) -> bool {
|
||||
pub fn new_filter(client: &Client, pattern: &str) -> impl Filter {
|
||||
let searcher = NormalizedMatcher::new().with_pattern(pattern);
|
||||
|
||||
let client = client.clone();
|
||||
@@ -41,7 +41,7 @@ pub fn new_filter(client: &Client, pattern: &str) -> impl Fn(&RoomListEntry) ->
|
||||
let Some(room) = client.get_room(room_id) else { return false };
|
||||
let Some(room_name) = room.name() else { return false };
|
||||
|
||||
searcher.normalized_match(&room_name)
|
||||
searcher.matches(&room_name)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,14 +55,14 @@ mod tests {
|
||||
fn test_no_pattern() {
|
||||
let matcher = NormalizedMatcher::new();
|
||||
|
||||
assert!(matcher.normalized_match("hello"));
|
||||
assert!(matcher.matches("hello"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_empty_pattern() {
|
||||
let matcher = NormalizedMatcher::new();
|
||||
|
||||
assert!(matcher.normalized_match("hello"));
|
||||
assert!(matcher.matches("hello"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -70,10 +70,10 @@ mod tests {
|
||||
let matcher = NormalizedMatcher::new();
|
||||
|
||||
let matcher = matcher.with_pattern("matrix");
|
||||
assert!(matcher.normalized_match("matrix"));
|
||||
assert!(matcher.matches("matrix"));
|
||||
|
||||
let matcher = matcher.with_pattern("matrxi");
|
||||
assert!(matcher.normalized_match("matrix").not());
|
||||
assert!(matcher.matches("matrix").not());
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -81,10 +81,10 @@ mod tests {
|
||||
let matcher = NormalizedMatcher::new();
|
||||
|
||||
let matcher = matcher.with_pattern("matrix");
|
||||
assert!(matcher.normalized_match("MaTrIX"));
|
||||
assert!(matcher.matches("MaTrIX"));
|
||||
|
||||
let matcher = matcher.with_pattern("matrxi");
|
||||
assert!(matcher.normalized_match("MaTrIX").not());
|
||||
assert!(matcher.matches("MaTrIX").not());
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -97,10 +97,10 @@ mod tests {
|
||||
assert_eq!(matcher.pattern, Some("un ete".to_owned()));
|
||||
|
||||
// Second, assert that the subject is normalized too.
|
||||
assert!(matcher.normalized_match("un été magnifique"));
|
||||
assert!(matcher.matches("un été magnifique"));
|
||||
|
||||
// Another concrete test.
|
||||
let matcher = matcher.with_pattern("stefan");
|
||||
assert!(matcher.normalized_match("Ștefan"));
|
||||
assert!(matcher.matches("Ștefan"));
|
||||
}
|
||||
}
|
||||
|
||||
39
crates/matrix-sdk-ui/src/room_list_service/filters/not.rs
Normal file
39
crates/matrix-sdk-ui/src/room_list_service/filters/not.rs
Normal file
@@ -0,0 +1,39 @@
|
||||
use std::ops::Not;
|
||||
|
||||
use super::{super::room_list::BoxedFilterFn, Filter};
|
||||
|
||||
/// Create a new filter that will negate the inner filter. It returns `false` if
|
||||
/// the inner filter returns `true`, otherwise it returns `true`.
|
||||
pub fn new_filter(filter: BoxedFilterFn) -> impl Filter {
|
||||
move |room_list_entry| -> bool { filter(room_list_entry).not() }
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::ops::Not;
|
||||
|
||||
use matrix_sdk::RoomListEntry;
|
||||
use ruma::room_id;
|
||||
|
||||
use super::new_filter;
|
||||
|
||||
#[test]
|
||||
fn test_true() {
|
||||
let room_list_entry = RoomListEntry::Filled(room_id!("!r0:bar.org").to_owned());
|
||||
|
||||
let filter = Box::new(|_: &_| true);
|
||||
let not = new_filter(filter);
|
||||
|
||||
assert!(not(&room_list_entry).not());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_false() {
|
||||
let room_list_entry = RoomListEntry::Filled(room_id!("!r0:bar.org").to_owned());
|
||||
|
||||
let filter = Box::new(|_: &_| false);
|
||||
let not = new_filter(filter);
|
||||
|
||||
assert!(not(&room_list_entry));
|
||||
}
|
||||
}
|
||||
153
crates/matrix-sdk-ui/src/room_list_service/filters/unread.rs
Normal file
153
crates/matrix-sdk-ui/src/room_list_service/filters/unread.rs
Normal file
@@ -0,0 +1,153 @@
|
||||
use matrix_sdk::{Client, RoomListEntry};
|
||||
use matrix_sdk_base::read_receipts::RoomReadReceipts;
|
||||
|
||||
use super::Filter;
|
||||
|
||||
type IsMarkedUnread = bool;
|
||||
|
||||
struct UnreadRoomMatcher<F>
|
||||
where
|
||||
F: Fn(&RoomListEntry) -> Option<(RoomReadReceipts, IsMarkedUnread)>,
|
||||
{
|
||||
read_receipts_and_unread: F,
|
||||
}
|
||||
|
||||
impl<F> UnreadRoomMatcher<F>
|
||||
where
|
||||
F: Fn(&RoomListEntry) -> Option<(RoomReadReceipts, IsMarkedUnread)>,
|
||||
{
|
||||
fn matches(&self, room_list_entry: &RoomListEntry) -> bool {
|
||||
if !matches!(room_list_entry, RoomListEntry::Filled(_) | RoomListEntry::Invalidated(_)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let Some((read_receipts, is_marked_unread)) =
|
||||
(self.read_receipts_and_unread)(room_list_entry)
|
||||
else {
|
||||
return false;
|
||||
};
|
||||
|
||||
read_receipts.num_notifications > 0 || is_marked_unread
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a new filter that will accept all filled or invalidated entries, but
|
||||
/// filters out rooms that have no unread notifications (different from unread
|
||||
/// messages), or is not marked as unread.
|
||||
pub fn new_filter(client: &Client) -> impl Filter {
|
||||
let client = client.clone();
|
||||
|
||||
let matcher = UnreadRoomMatcher {
|
||||
read_receipts_and_unread: move |room| {
|
||||
let room_id = room.as_room_id()?;
|
||||
let room = client.get_room(room_id)?;
|
||||
|
||||
Some((room.read_receipts(), room.is_marked_unread()))
|
||||
},
|
||||
};
|
||||
|
||||
move |room_list_entry| -> bool { matcher.matches(room_list_entry) }
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::ops::Not;
|
||||
|
||||
use matrix_sdk::RoomListEntry;
|
||||
use matrix_sdk_base::read_receipts::RoomReadReceipts;
|
||||
use ruma::room_id;
|
||||
|
||||
use super::UnreadRoomMatcher;
|
||||
|
||||
#[test]
|
||||
fn test_has_unread_notifications() {
|
||||
for is_marked_as_unread in [true, false] {
|
||||
let matcher = UnreadRoomMatcher {
|
||||
read_receipts_and_unread: |_| {
|
||||
let mut read_receipts = RoomReadReceipts::default();
|
||||
read_receipts.num_unread = 42;
|
||||
read_receipts.num_notifications = 42;
|
||||
|
||||
Some((read_receipts, is_marked_as_unread))
|
||||
},
|
||||
};
|
||||
|
||||
assert!(matcher.matches(&RoomListEntry::Empty).not());
|
||||
assert!(matcher.matches(&RoomListEntry::Filled(room_id!("!r0:bar.org").to_owned())));
|
||||
assert!(
|
||||
matcher.matches(&RoomListEntry::Invalidated(room_id!("!r0:bar.org").to_owned()))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_has_unread_messages_but_no_unread_notifications_and_is_not_marked_as_unread() {
|
||||
let matcher = UnreadRoomMatcher {
|
||||
read_receipts_and_unread: |_| {
|
||||
let mut read_receipts = RoomReadReceipts::default();
|
||||
read_receipts.num_unread = 42;
|
||||
read_receipts.num_notifications = 0;
|
||||
|
||||
Some((read_receipts, false))
|
||||
},
|
||||
};
|
||||
|
||||
assert!(matcher.matches(&RoomListEntry::Empty).not());
|
||||
assert!(matcher.matches(&RoomListEntry::Filled(room_id!("!r0:bar.org").to_owned())).not());
|
||||
assert!(matcher
|
||||
.matches(&RoomListEntry::Invalidated(room_id!("!r0:bar.org").to_owned()))
|
||||
.not());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_has_unread_messages_but_no_unread_notifications_and_is_marked_as_unread() {
|
||||
let matcher = UnreadRoomMatcher {
|
||||
read_receipts_and_unread: |_| {
|
||||
let mut read_receipts = RoomReadReceipts::default();
|
||||
read_receipts.num_unread = 42;
|
||||
read_receipts.num_notifications = 0;
|
||||
|
||||
Some((read_receipts, true))
|
||||
},
|
||||
};
|
||||
|
||||
assert!(matcher.matches(&RoomListEntry::Empty).not());
|
||||
assert!(matcher.matches(&RoomListEntry::Filled(room_id!("!r0:bar.org").to_owned())));
|
||||
assert!(matcher.matches(&RoomListEntry::Invalidated(room_id!("!r0:bar.org").to_owned())));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_has_no_unread_notifications_and_is_not_marked_as_unread() {
|
||||
let matcher = UnreadRoomMatcher {
|
||||
read_receipts_and_unread: |_| Some((RoomReadReceipts::default(), false)),
|
||||
};
|
||||
|
||||
assert!(matcher.matches(&RoomListEntry::Empty).not());
|
||||
assert!(matcher.matches(&RoomListEntry::Filled(room_id!("!r0:bar.org").to_owned())).not());
|
||||
assert!(matcher
|
||||
.matches(&RoomListEntry::Invalidated(room_id!("!r0:bar.org").to_owned()))
|
||||
.not());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_has_no_unread_notifications_and_is_marked_as_unread() {
|
||||
let matcher = UnreadRoomMatcher {
|
||||
read_receipts_and_unread: |_| Some((RoomReadReceipts::default(), true)),
|
||||
};
|
||||
|
||||
assert!(matcher.matches(&RoomListEntry::Empty).not());
|
||||
assert!(matcher.matches(&RoomListEntry::Filled(room_id!("!r0:bar.org").to_owned())));
|
||||
assert!(matcher.matches(&RoomListEntry::Invalidated(room_id!("!r0:bar.org").to_owned())));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_read_receipts_cannot_be_found() {
|
||||
let matcher = UnreadRoomMatcher { read_receipts_and_unread: |_| None };
|
||||
|
||||
assert!(matcher.matches(&RoomListEntry::Empty).not());
|
||||
assert!(matcher.matches(&RoomListEntry::Filled(room_id!("!r0:bar.org").to_owned())).not());
|
||||
assert!(matcher
|
||||
.matches(&RoomListEntry::Invalidated(room_id!("!r0:bar.org").to_owned()))
|
||||
.not());
|
||||
}
|
||||
}
|
||||
@@ -26,7 +26,7 @@ use matrix_sdk::{
|
||||
RoomListEntry, SlidingSync, SlidingSyncList,
|
||||
};
|
||||
|
||||
use super::{Error, State};
|
||||
use super::{filters::Filter, Error, State};
|
||||
|
||||
/// A `RoomList` represents a list of rooms, from a
|
||||
/// [`RoomListService`](super::RoomListService).
|
||||
@@ -199,7 +199,8 @@ pub enum RoomListLoadingState {
|
||||
},
|
||||
}
|
||||
|
||||
type BoxedFilterFn = Box<dyn Fn(&RoomListEntry) -> bool + Send + Sync>;
|
||||
/// Type alias for a boxed filter function.
|
||||
pub type BoxedFilterFn = Box<dyn Filter + Send + Sync>;
|
||||
|
||||
/// Controller for the [`RoomList`] dynamic entries.
|
||||
///
|
||||
@@ -226,17 +227,14 @@ impl RoomListDynamicEntriesController {
|
||||
///
|
||||
/// If the associated stream has been dropped, returns `false` to indicate
|
||||
/// the operation didn't have an effect.
|
||||
pub fn set_filter(
|
||||
&self,
|
||||
filter: impl Fn(&RoomListEntry) -> bool + Send + Sync + 'static,
|
||||
) -> bool {
|
||||
pub fn set_filter(&self, filter: BoxedFilterFn) -> bool {
|
||||
if Arc::strong_count(&self.filter) == 1 {
|
||||
// there is no other reference to the boxed filter fn, setting it
|
||||
// would be pointless (no new references can be created from self,
|
||||
// either)
|
||||
false
|
||||
} else {
|
||||
self.filter.set(Box::new(filter));
|
||||
self.filter.set(filter);
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ use matrix_sdk_base::sync::UnreadNotificationsCount;
|
||||
use matrix_sdk_test::async_test;
|
||||
use matrix_sdk_ui::{
|
||||
room_list_service::{
|
||||
filters::{new_filter_all, new_filter_fuzzy_match_room_name, new_filter_none},
|
||||
filters::{new_filter_fuzzy_match_room_name, new_filter_non_left, new_filter_none},
|
||||
Error, Input, InputResult, RoomListEntry, RoomListLoadingState, State, SyncIndicator,
|
||||
ALL_ROOMS_LIST_NAME as ALL_ROOMS, INVITES_LIST_NAME as INVITES,
|
||||
VISIBLE_ROOMS_LIST_NAME as VISIBLE_ROOMS,
|
||||
@@ -1643,7 +1643,7 @@ async fn test_dynamic_entries_stream() -> Result<(), Error> {
|
||||
assert_pending!(dynamic_entries_stream);
|
||||
|
||||
// Now, let's define a filter.
|
||||
dynamic_entries.set_filter(new_filter_fuzzy_match_room_name(&client, "mat ba"));
|
||||
dynamic_entries.set_filter(Box::new(new_filter_fuzzy_match_room_name(&client, "mat ba")));
|
||||
|
||||
// Assert the dynamic entries.
|
||||
assert_entries_batch! {
|
||||
@@ -1799,7 +1799,7 @@ async fn test_dynamic_entries_stream() -> Result<(), Error> {
|
||||
assert_pending!(dynamic_entries_stream);
|
||||
|
||||
// Now, let's change the dynamic entries!
|
||||
dynamic_entries.set_filter(new_filter_fuzzy_match_room_name(&client, "hell"));
|
||||
dynamic_entries.set_filter(Box::new(new_filter_fuzzy_match_room_name(&client, "hell")));
|
||||
|
||||
// Assert the dynamic entries.
|
||||
assert_entries_batch! {
|
||||
@@ -1811,7 +1811,7 @@ async fn test_dynamic_entries_stream() -> Result<(), Error> {
|
||||
assert_pending!(dynamic_entries_stream);
|
||||
|
||||
// Now, let's change again the dynamic filter!
|
||||
dynamic_entries.set_filter(new_filter_none());
|
||||
dynamic_entries.set_filter(Box::new(new_filter_none()));
|
||||
|
||||
// Assert the dynamic entries.
|
||||
assert_entries_batch! {
|
||||
@@ -1822,7 +1822,7 @@ async fn test_dynamic_entries_stream() -> Result<(), Error> {
|
||||
};
|
||||
|
||||
// Now, let's change again the dynamic filter!
|
||||
dynamic_entries.set_filter(new_filter_all());
|
||||
dynamic_entries.set_filter(Box::new(new_filter_non_left(&client)));
|
||||
|
||||
// Assert the dynamic entries.
|
||||
assert_entries_batch! {
|
||||
|
||||
Reference in New Issue
Block a user