Skip to content

Commit

Permalink
feat(ui): Moaaar filters: add all, any, not, unread and `cate…
Browse files Browse the repository at this point in the history
…gory` filters

feat(ui): Moaaar filters: add `all`, `any`, `not`, `unread` and `category` filters
  • Loading branch information
Hywan authored Feb 8, 2024
2 parents 2e9f362 + fce1140 commit 9bf48ef
Show file tree
Hide file tree
Showing 16 changed files with 665 additions and 91 deletions.
76 changes: 58 additions & 18 deletions bindings/matrix-sdk-ffi/src/room_list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
};
Expand Down Expand Up @@ -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) {
Expand All @@ -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>,
Expand Down
2 changes: 1 addition & 1 deletion crates/matrix-sdk-base/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
10 changes: 5 additions & 5 deletions crates/matrix-sdk-base/src/read_receipts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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?
//!
Expand Down
6 changes: 6 additions & 0 deletions crates/matrix-sdk-base/src/rooms/normal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
68 changes: 57 additions & 11 deletions crates/matrix-sdk-ui/src/room_list_service/filters/all.rs
Original file line number Diff line number Diff line change
@@ -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)]
Expand All @@ -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());

{
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(&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())));
assert!(all(&room_list_entry).not());
}
}
}
81 changes: 81 additions & 0 deletions crates/matrix-sdk-ui/src/room_list_service/filters/any.rs
Original file line number Diff line number Diff line change
@@ -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());
}
}
Loading

0 comments on commit 9bf48ef

Please sign in to comment.