matrix_sdk_ui/timeline/
event_filter.rs

1// Copyright 2026 The Matrix.org Foundation C.I.C.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use ruma::events::{
16    AnySyncStateEvent, AnySyncTimelineEvent, SyncStateEvent, TimelineEventType,
17    room::member::MembershipChange,
18};
19
20/// A timeline filter that in- or excludes events based on their type or
21/// content.
22pub enum TimelineEventFilter {
23    /// Only return items whose event matches any of the conditions in the list.
24    Include(Vec<TimelineEventCondition>),
25    /// Return all items except the ones whose event matches any of the
26    /// conditions in the list
27    Exclude(Vec<TimelineEventCondition>),
28}
29
30impl TimelineEventFilter {
31    /// Filters any incoming `event` using the filter conditions.
32    ///
33    /// # Arguments
34    ///
35    /// * `event` - The event to run the filter on.
36    ///
37    /// # Returns
38    /// `true` if the filter allows the event or `false` otherwise.
39    pub fn filter(&self, event: &AnySyncTimelineEvent) -> bool {
40        match self {
41            Self::Include(conditions) => conditions.iter().any(|c| c.matches(event)),
42            Self::Exclude(conditions) => !conditions.iter().any(|c| c.matches(event)),
43        }
44    }
45}
46
47/// A condition that matches on an event's type or content.
48#[derive(Clone)]
49pub enum TimelineEventCondition {
50    /// The event has the specified event type.
51    EventType(TimelineEventType),
52    /// The event is an `m.room.member` event that represents a membership
53    /// change (join, leave, etc.).
54    MembershipChange,
55    /// The event is an `m.room.member` event that represents a profile
56    /// change (displayname or avatar URL).
57    ProfileChange,
58}
59
60impl TimelineEventCondition {
61    /// Evaluate the condition against an event.
62    ///
63    /// # Arguments
64    ///
65    /// * `event` - The event to test the condition against.
66    ///
67    /// # Returns
68    /// `true` if the condition matches or `false` otherwise.
69    fn matches(&self, event: &AnySyncTimelineEvent) -> bool {
70        match self {
71            Self::EventType(event_type) => event.event_type() == *event_type,
72            Self::MembershipChange => match event {
73                AnySyncTimelineEvent::State(AnySyncStateEvent::RoomMember(
74                    SyncStateEvent::Original(ev),
75                )) => {
76                    let change = ev.content.membership_change(
77                        ev.prev_content().as_ref().map(|c| c.details()),
78                        &ev.sender,
79                        &ev.state_key,
80                    );
81                    !matches!(change, MembershipChange::ProfileChanged { .. })
82                }
83                _ => false,
84            },
85            Self::ProfileChange => match event {
86                AnySyncTimelineEvent::State(AnySyncStateEvent::RoomMember(
87                    SyncStateEvent::Original(ev),
88                )) => {
89                    let change = ev.content.membership_change(
90                        ev.prev_content().as_ref().map(|c| c.details()),
91                        &ev.sender,
92                        &ev.state_key,
93                    );
94                    matches!(change, MembershipChange::ProfileChanged { .. })
95                }
96                _ => false,
97            },
98        }
99    }
100}