1use std::collections::BTreeSet;
18
19use eyeball::Subscriber as EyeballSubscriber;
20use matrix_sdk::{
21 event_cache::{
22 EventFocusThreadMode, EventFocusedCache, EventsOrigin, PinnedEventsCache, RoomEventCache,
23 RoomEventCacheUpdate, Subscriber, ThreadEventCache, ThreadEventCacheUpdate,
24 TimelineVectorDiffs,
25 },
26 send_queue::RoomSendQueueUpdate,
27};
28use matrix_sdk_base::RoomInfo;
29use ruma::OwnedEventId;
30#[cfg(feature = "unstable-msc4426")]
31use ruma::{OwnedUserId, UserId};
32use tokio::sync::broadcast::{Receiver, error::RecvError};
33use tracing::{error, instrument, trace, warn};
34
35use crate::timeline::{
36 TimelineController, TimelineFocus, controller::ActiveCallInfo, event_item::RemoteEventOrigin,
37 traits::RoomDataProvider,
38};
39
40#[instrument(
43 skip_all,
44 fields(
45 room_id = %timeline_controller.room().room_id(),
46 )
47)]
48pub(in crate::timeline) async fn pinned_events_task(
49 pinned_events_cache: PinnedEventsCache,
50 timeline_controller: TimelineController,
51 mut pinned_events_recv: Receiver<TimelineVectorDiffs>,
52) {
53 loop {
54 trace!("Waiting for an event.");
55
56 let update = match pinned_events_recv.recv().await {
57 Ok(up) => up,
58 Err(RecvError::Closed) => break,
59 Err(RecvError::Lagged(num_skipped)) => {
60 warn!(num_skipped, "Lagged behind pinned-event cache updates, resetting timeline");
61
62 let (initial_events, _) = match pinned_events_cache.subscribe().await {
66 Ok(initial_events) => initial_events,
67 Err(err) => {
68 error!(
69 ?err,
70 "Failed to replace the initial remote events in the event cache"
71 );
72 break;
73 }
74 };
75
76 timeline_controller
77 .replace_with_initial_remote_events(initial_events, RemoteEventOrigin::Cache)
78 .await;
79
80 continue;
81 }
82 };
83
84 trace!("Received new timeline events diffs");
85 let origin = match update.origin {
86 EventsOrigin::Sync => RemoteEventOrigin::Sync,
87 EventsOrigin::Pagination => RemoteEventOrigin::Pagination,
88 EventsOrigin::Cache => RemoteEventOrigin::Cache,
89 };
90 timeline_controller.handle_remote_events_with_diffs(update.diffs, origin).await;
91 }
92}
93
94#[instrument(
97 skip_all,
98 fields(
99 room_id = %timeline_controller.room().room_id(),
100 focused_event_id = %focused_event,
101 ?thread_mode
102 )
103)]
104pub(in crate::timeline) async fn event_focused_task(
105 focused_event: OwnedEventId,
106 thread_mode: EventFocusThreadMode,
107 event_cache: EventFocusedCache,
108 timeline_controller: TimelineController,
109 mut event_focused_events_recv: Receiver<TimelineVectorDiffs>,
110) {
111 loop {
112 trace!("Waiting for an event.");
113
114 let update = match event_focused_events_recv.recv().await {
115 Ok(up) => up,
116 Err(RecvError::Closed) => break,
117 Err(RecvError::Lagged(num_skipped)) => {
118 warn!(num_skipped, "Lagged behind focused-event cache updates, resetting timeline");
119
120 let Ok((initial_events, _)) = event_cache.subscribe().await else {
124 error!("Failed to subscribe to the event-focused cache");
125 break;
126 };
127
128 timeline_controller
129 .replace_with_initial_remote_events(initial_events, RemoteEventOrigin::Cache)
130 .await;
131
132 continue;
133 }
134 };
135
136 trace!("Received new timeline events diffs");
137 let origin = match update.origin {
138 EventsOrigin::Sync => RemoteEventOrigin::Sync,
139 EventsOrigin::Pagination => RemoteEventOrigin::Pagination,
140 EventsOrigin::Cache => RemoteEventOrigin::Cache,
141 };
142 timeline_controller.handle_remote_events_with_diffs(update.diffs, origin).await;
143 }
144}
145
146pub(in crate::timeline) async fn thread_updates_task(
149 mut thread_event_cache_subscriber: Subscriber<ThreadEventCacheUpdate>,
150 event_cache: ThreadEventCache,
151 timeline_controller: TimelineController,
152) {
153 trace!("Spawned the thread event subscriber task.");
154
155 loop {
156 trace!("Waiting for an event.");
157
158 let update = match thread_event_cache_subscriber.recv().await {
159 Ok(up) => up,
160 Err(RecvError::Closed) => break,
161 Err(RecvError::Lagged(num_skipped)) => {
162 warn!(num_skipped, "Lagged behind event cache updates, resetting timeline");
163
164 _ = timeline_controller.init_with_thread_root(&event_cache).await;
168
169 continue;
170 }
171 };
172
173 match update {
174 ThreadEventCacheUpdate::UpdateTimelineEvents(TimelineVectorDiffs { diffs, origin }) => {
175 trace!("Received new timeline events diffs");
176
177 let origin = match origin {
178 EventsOrigin::Sync => RemoteEventOrigin::Sync,
179 EventsOrigin::Pagination => RemoteEventOrigin::Pagination,
180 EventsOrigin::Cache => RemoteEventOrigin::Cache,
181 };
182
183 let has_diffs = !diffs.is_empty();
184
185 timeline_controller.handle_remote_events_with_diffs(diffs, origin).await;
186
187 if has_diffs && matches!(origin, RemoteEventOrigin::Cache) {
188 timeline_controller.retry_event_decryption(None).await;
189 }
190 }
191
192 ThreadEventCacheUpdate::AddReadReceiptEvent { event } => {
193 trace!("Received a new read receipt event from sync.");
194
195 timeline_controller.handle_read_receipt_event(event).await;
197 }
198 }
199 }
200
201 trace!("Thread event subscriber task finished.");
202}
203
204pub(in crate::timeline) async fn room_event_cache_updates_task(
207 room_event_cache: RoomEventCache,
208 timeline_controller: TimelineController,
209 mut room_event_cache_subscriber: Subscriber<RoomEventCacheUpdate>,
210 timeline_focus: TimelineFocus,
211) {
212 trace!("Spawned the event subscriber task.");
213
214 loop {
215 trace!("Waiting for an event.");
216
217 let update = match room_event_cache_subscriber.recv().await {
218 Ok(up) => up,
219 Err(RecvError::Closed) => break,
220 Err(RecvError::Lagged(num_skipped)) => {
221 warn!(num_skipped, "Lagged behind event cache updates, resetting timeline");
222
223 let initial_events = match room_event_cache.events().await {
227 Ok(initial_events) => initial_events,
228 Err(err) => {
229 error!(
230 ?err,
231 "Failed to replace the initial remote events in the event cache"
232 );
233 break;
234 }
235 };
236
237 timeline_controller
238 .replace_with_initial_remote_events(initial_events, RemoteEventOrigin::Cache)
239 .await;
240
241 continue;
242 }
243 };
244
245 match update {
246 RoomEventCacheUpdate::MoveReadMarkerTo { event_id } => {
247 trace!(target = %event_id, "Handling fully read marker.");
248 timeline_controller.handle_fully_read_marker(event_id).await;
249 }
250
251 RoomEventCacheUpdate::UpdateTimelineEvents(TimelineVectorDiffs { diffs, origin }) => {
252 trace!("Received new timeline events diffs");
253 let origin = match origin {
254 EventsOrigin::Sync => RemoteEventOrigin::Sync,
255 EventsOrigin::Pagination => RemoteEventOrigin::Pagination,
256 EventsOrigin::Cache => RemoteEventOrigin::Cache,
257 };
258
259 let has_diffs = !diffs.is_empty();
260
261 if matches!(timeline_focus, TimelineFocus::Live { .. }) {
262 timeline_controller.handle_remote_events_with_diffs(diffs, origin).await;
263 } else if matches!(timeline_focus, TimelineFocus::Event { .. }) {
264 timeline_controller.handle_remote_aggregations(diffs, origin).await;
266 }
267
268 if has_diffs && matches!(origin, RemoteEventOrigin::Cache) {
269 timeline_controller.retry_event_decryption(None).await;
270 }
271 }
272
273 RoomEventCacheUpdate::AddReadReceiptEvent { event } => {
274 trace!("Received a new read receipt event from sync.");
275
276 timeline_controller.handle_read_receipt_event(event).await;
278 }
279
280 RoomEventCacheUpdate::UpdateMembers { ambiguity_changes, avatar_changes } => {
281 if !ambiguity_changes.is_empty()
282 || !avatar_changes.as_ref().is_none_or(|avatars| avatars.is_empty())
283 {
284 let member_ambiguity_changes = ambiguity_changes
285 .values()
286 .flat_map(|change| change.user_ids())
287 .collect::<BTreeSet<_>>();
288
289 let mut user_ids_to_update = member_ambiguity_changes;
290
291 if let Some(avatar_changes) = &avatar_changes {
292 let mut user_ids =
293 avatar_changes.keys().map(|u| u.as_ref()).collect::<BTreeSet<_>>();
294 user_ids_to_update.append(&mut user_ids)
295 } else {
296 warn!(
297 "No avatar changes to update for {}, ignoring",
298 room_event_cache.room_id()
299 );
300 }
301 timeline_controller.force_update_sender_profiles(&user_ids_to_update).await;
302 }
303 }
304 }
305 }
306}
307
308#[cfg(feature = "unstable-msc4426")]
311pub(in crate::timeline) async fn global_profile_updates_task(
312 mut global_profile_updates_stream: Receiver<BTreeSet<OwnedUserId>>,
313 timeline_controller: TimelineController,
314) {
315 trace!("spawned the global profile updates task!");
316
317 loop {
318 match global_profile_updates_stream.recv().await {
319 Ok(user_ids) => {
320 let sender_ids: BTreeSet<&UserId> =
321 user_ids.iter().map(|user_id| user_id.as_ref()).collect();
322 timeline_controller.force_update_sender_profiles(&sender_ids).await;
323 }
324
325 Err(RecvError::Lagged(num_missed)) => {
326 warn!("missed {num_missed} global profile updates, ignoring those missed");
327 }
328
329 Err(RecvError::Closed) => {
330 trace!("channel closed, exiting the global profile updates handler");
331 break;
332 }
333 }
334 }
335}
336
337pub(in crate::timeline) async fn room_send_queue_update_task(
340 mut send_queue_stream: Receiver<RoomSendQueueUpdate>,
341 timeline_controller: TimelineController,
342) {
343 trace!("spawned the local echo task!");
344
345 loop {
346 match send_queue_stream.recv().await {
347 Ok(update) => timeline_controller.handle_room_send_queue_update(update).await,
348
349 Err(RecvError::Lagged(num_missed)) => {
350 warn!("missed {num_missed} local echoes, ignoring those missed");
351 }
352
353 Err(RecvError::Closed) => {
354 trace!("channel closed, exiting the local echo handler");
355 break;
356 }
357 }
358 }
359}
360
361pub(in crate::timeline) async fn rtc_membership_update_task(
364 mut room_info: EyeballSubscriber<RoomInfo>,
365 timeline_controller: TimelineController,
366 initial_call_info: Option<ActiveCallInfo>,
367) {
368 let mut prev_info = initial_call_info;
369 let own_user = timeline_controller.room().own_user_id().to_owned();
370
371 while let Some(info) = room_info.next().await {
372 let active_call = ActiveCallInfo::from_info(info, own_user.clone());
373 if active_call != prev_info {
376 prev_info = active_call.clone();
377 timeline_controller.handle_active_call_update(active_call).await;
378 }
379 }
380}