matrix_sdk_base/response_processors/account_data/
room.rs

1// Copyright 2025 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::{
16    events::{marked_unread::MarkedUnreadEventContent, AnyRoomAccountDataEvent},
17    serde::Raw,
18    RoomId,
19};
20use tracing::{instrument, warn};
21
22use super::super::{Context, RoomInfoNotableUpdates};
23use crate::{
24    room::AccountDataSource, store::BaseStateStore, RoomInfo, RoomInfoNotableUpdateReasons,
25    StateChanges,
26};
27
28#[instrument(skip_all, fields(?room_id))]
29pub async fn for_room(
30    context: &mut Context,
31    room_id: &RoomId,
32    events: &[Raw<AnyRoomAccountDataEvent>],
33    state_store: &BaseStateStore,
34) {
35    // Handle new events.
36    for raw_event in events {
37        match raw_event.deserialize() {
38            Ok(event) => {
39                context.state_changes.add_room_account_data(
40                    room_id,
41                    event.clone(),
42                    raw_event.clone(),
43                );
44
45                match event {
46                    AnyRoomAccountDataEvent::MarkedUnread(event) => {
47                        on_room_info(
48                            room_id,
49                            &mut context.state_changes,
50                            state_store,
51                            |room_info| {
52                                on_unread_marker(
53                                    room_id,
54                                    &event.content,
55                                    AccountDataSource::Stable,
56                                    room_info,
57                                    &mut context.room_info_notable_updates,
58                                );
59                            },
60                        );
61                    }
62                    AnyRoomAccountDataEvent::UnstableMarkedUnread(event) => {
63                        on_room_info(
64                            room_id,
65                            &mut context.state_changes,
66                            state_store,
67                            |room_info| {
68                                on_unread_marker(
69                                    room_id,
70                                    &event.content.0,
71                                    AccountDataSource::Unstable,
72                                    room_info,
73                                    &mut context.room_info_notable_updates,
74                                );
75                            },
76                        );
77                    }
78                    AnyRoomAccountDataEvent::Tag(event) => {
79                        on_room_info(
80                            room_id,
81                            &mut context.state_changes,
82                            state_store,
83                            |room_info| {
84                                room_info.base_info.handle_notable_tags(&event.content.tags);
85                            },
86                        );
87                    }
88
89                    // Nothing.
90                    _ => {}
91                }
92            }
93
94            Err(err) => {
95                warn!("unable to deserialize account data event: {err}");
96            }
97        }
98    }
99}
100
101// Small helper to make the code easier to read.
102//
103// It finds the appropriate `RoomInfo`, allowing the caller to modify it, and
104// save it in the correct place.
105fn on_room_info<F>(
106    room_id: &RoomId,
107    state_changes: &mut StateChanges,
108    state_store: &BaseStateStore,
109    mut on_room_info: F,
110) where
111    F: FnMut(&mut RoomInfo),
112{
113    // `StateChanges` has the `RoomInfo`.
114    if let Some(room_info) = state_changes.room_infos.get_mut(room_id) {
115        // Show time.
116        on_room_info(room_info);
117    }
118    // The `BaseStateStore` has the `Room`, which has the `RoomInfo`.
119    else if let Some(room) = state_store.room(room_id) {
120        // Clone the `RoomInfo`.
121        let mut room_info = room.clone_info();
122
123        // Show time.
124        on_room_info(&mut room_info);
125
126        // Update the `RoomInfo` via `StateChanges`.
127        state_changes.add_room(room_info);
128    }
129}
130
131// Helper to update the unread marker for stable and unstable prefixes.
132fn on_unread_marker(
133    room_id: &RoomId,
134    content: &MarkedUnreadEventContent,
135    source: AccountDataSource,
136    room_info: &mut RoomInfo,
137    room_info_notable_updates: &mut RoomInfoNotableUpdates,
138) {
139    if room_info.base_info.is_marked_unread_source == AccountDataSource::Stable
140        && source != AccountDataSource::Stable
141    {
142        // Ignore the unstable source if a stable source was used previously.
143        return;
144    }
145
146    if room_info.base_info.is_marked_unread != content.unread {
147        // Notify the room list about a manual read marker change if the
148        // value's changed.
149        room_info_notable_updates
150            .entry(room_id.to_owned())
151            .or_default()
152            .insert(RoomInfoNotableUpdateReasons::UNREAD_MARKER);
153    }
154
155    room_info.base_info.is_marked_unread = content.unread;
156    room_info.base_info.is_marked_unread_source = source;
157}