Skip to main content

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