mirror of
https://github.com/matrix-org/matrix-rust-sdk.git
synced 2026-05-16 03:55:42 -04:00
feat(ui,ffi): Implement the category room list filter.
This patch implements the `category` room list filter. It introduces a new type: `RoomCategory`, to ensure that “group” and “people” are mutually exclusives.
This commit is contained in:
@@ -15,8 +15,9 @@ use matrix_sdk::{
|
||||
use matrix_sdk_ui::{
|
||||
room_list_service::{
|
||||
filters::{
|
||||
new_filter_all, new_filter_any, new_filter_fuzzy_match_room_name, new_filter_non_left,
|
||||
new_filter_none, new_filter_normalized_match_room_name, new_filter_unread,
|
||||
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,
|
||||
},
|
||||
@@ -413,11 +414,27 @@ pub enum 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);
|
||||
@@ -435,6 +452,7 @@ impl FilterWrapper {
|
||||
))),
|
||||
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)))
|
||||
|
||||
181
crates/matrix-sdk-ui/src/room_list_service/filters/category.rs
Normal file
181
crates/matrix-sdk-ui/src/room_list_service/filters/category.rs
Normal file
@@ -0,0 +1,181 @@
|
||||
use matrix_sdk::{Client, RoomListEntry};
|
||||
|
||||
use super::Filter;
|
||||
|
||||
/// An enum to represent whether a room is about “people” (1 or 2 users) or
|
||||
/// “group” (more than 2 users).
|
||||
///
|
||||
/// 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, but
|
||||
/// filters out rooms that have no unread messages.
|
||||
pub fn new_filter(client: &Client, expected_kind: 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_kind) }
|
||||
}
|
||||
|
||||
#[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,5 +1,6 @@
|
||||
mod all;
|
||||
mod any;
|
||||
mod category;
|
||||
mod fuzzy_match_room_name;
|
||||
mod non_left;
|
||||
mod none;
|
||||
@@ -9,6 +10,7 @@ mod unread;
|
||||
|
||||
pub use all::new_filter as new_filter_all;
|
||||
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;
|
||||
|
||||
Reference in New Issue
Block a user