1use std::{collections::BTreeMap, fmt};
18
19use matrix_sdk_common::{debug::DebugRawEvent, deserialized_responses::TimelineEvent};
20use ruma::{
21 api::client::sync::sync_events::{
22 v3::{InvitedRoom as InvitedRoomUpdate, KnockedRoom as KnockedRoomUpdate},
23 UnreadNotificationsCount as RumaUnreadNotificationsCount,
24 },
25 events::{
26 presence::PresenceEvent, AnyGlobalAccountDataEvent, AnyRoomAccountDataEvent,
27 AnySyncEphemeralRoomEvent, AnySyncStateEvent, AnyToDeviceEvent,
28 },
29 push::Action,
30 serde::Raw,
31 OwnedEventId, OwnedRoomId,
32};
33use serde::{Deserialize, Serialize};
34
35use crate::{
36 debug::{DebugInvitedRoom, DebugKnockedRoom, DebugListOfRawEvents, DebugListOfRawEventsNoId},
37 deserialized_responses::{AmbiguityChange, RawAnySyncOrStrippedTimelineEvent},
38 store::Store,
39};
40
41#[derive(Clone, Default)]
46pub struct SyncResponse {
47 pub rooms: RoomUpdates,
49 pub presence: Vec<Raw<PresenceEvent>>,
51 pub account_data: Vec<Raw<AnyGlobalAccountDataEvent>>,
53 pub to_device: Vec<Raw<AnyToDeviceEvent>>,
55 pub notifications: BTreeMap<OwnedRoomId, Vec<Notification>>,
57}
58
59#[cfg(not(tarpaulin_include))]
60impl fmt::Debug for SyncResponse {
61 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
62 f.debug_struct("SyncResponse")
63 .field("rooms", &self.rooms)
64 .field("account_data", &DebugListOfRawEventsNoId(&self.account_data))
65 .field("to_device", &DebugListOfRawEventsNoId(&self.to_device))
66 .field("notifications", &self.notifications)
67 .finish_non_exhaustive()
68 }
69}
70
71#[derive(Clone, Default)]
73pub struct RoomUpdates {
74 pub leave: BTreeMap<OwnedRoomId, LeftRoomUpdate>,
76 pub join: BTreeMap<OwnedRoomId, JoinedRoomUpdate>,
78 pub invite: BTreeMap<OwnedRoomId, InvitedRoomUpdate>,
80 pub knocked: BTreeMap<OwnedRoomId, KnockedRoomUpdate>,
82}
83
84impl RoomUpdates {
85 pub(crate) async fn update_in_memory_caches(&self, store: &Store) {
89 for room in self
90 .leave
91 .keys()
92 .chain(self.join.keys())
93 .chain(self.invite.keys())
94 .chain(self.knocked.keys())
95 .filter_map(|room_id| store.room(room_id))
96 {
97 let _ = room.compute_display_name().await;
98 }
99 }
100}
101
102#[cfg(not(tarpaulin_include))]
103impl fmt::Debug for RoomUpdates {
104 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
105 f.debug_struct("RoomUpdates")
106 .field("leave", &self.leave)
107 .field("join", &self.join)
108 .field("invite", &DebugInvitedRoomUpdates(&self.invite))
109 .field("knocked", &DebugKnockedRoomUpdates(&self.knocked))
110 .finish()
111 }
112}
113
114#[derive(Clone, Default)]
116pub struct JoinedRoomUpdate {
117 pub unread_notifications: UnreadNotificationsCount,
119 pub timeline: Timeline,
121 pub state: Vec<Raw<AnySyncStateEvent>>,
126 pub account_data: Vec<Raw<AnyRoomAccountDataEvent>>,
128 pub ephemeral: Vec<Raw<AnySyncEphemeralRoomEvent>>,
131 pub ambiguity_changes: BTreeMap<OwnedEventId, AmbiguityChange>,
136}
137
138#[cfg(not(tarpaulin_include))]
139impl fmt::Debug for JoinedRoomUpdate {
140 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
141 f.debug_struct("JoinedRoomUpdate")
142 .field("unread_notifications", &self.unread_notifications)
143 .field("timeline", &self.timeline)
144 .field("state", &DebugListOfRawEvents(&self.state))
145 .field("account_data", &DebugListOfRawEventsNoId(&self.account_data))
146 .field("ephemeral", &self.ephemeral)
147 .field("ambiguity_changes", &self.ambiguity_changes)
148 .finish()
149 }
150}
151
152impl JoinedRoomUpdate {
153 pub(crate) fn new(
154 timeline: Timeline,
155 state: Vec<Raw<AnySyncStateEvent>>,
156 account_data: Vec<Raw<AnyRoomAccountDataEvent>>,
157 ephemeral: Vec<Raw<AnySyncEphemeralRoomEvent>>,
158 unread_notifications: UnreadNotificationsCount,
159 ambiguity_changes: BTreeMap<OwnedEventId, AmbiguityChange>,
160 ) -> Self {
161 Self { unread_notifications, timeline, state, account_data, ephemeral, ambiguity_changes }
162 }
163}
164
165#[derive(Copy, Clone, Debug, Default, Deserialize, Serialize, PartialEq)]
167pub struct UnreadNotificationsCount {
168 pub highlight_count: u64,
171 pub notification_count: u64,
173}
174
175impl From<RumaUnreadNotificationsCount> for UnreadNotificationsCount {
176 fn from(notifications: RumaUnreadNotificationsCount) -> Self {
177 Self {
178 highlight_count: notifications.highlight_count.map(|c| c.into()).unwrap_or(0),
179 notification_count: notifications.notification_count.map(|c| c.into()).unwrap_or(0),
180 }
181 }
182}
183
184#[derive(Clone, Default)]
186pub struct LeftRoomUpdate {
187 pub timeline: Timeline,
190 pub state: Vec<Raw<AnySyncStateEvent>>,
195 pub account_data: Vec<Raw<AnyRoomAccountDataEvent>>,
197 pub ambiguity_changes: BTreeMap<OwnedEventId, AmbiguityChange>,
202}
203
204impl LeftRoomUpdate {
205 pub(crate) fn new(
206 timeline: Timeline,
207 state: Vec<Raw<AnySyncStateEvent>>,
208 account_data: Vec<Raw<AnyRoomAccountDataEvent>>,
209 ambiguity_changes: BTreeMap<OwnedEventId, AmbiguityChange>,
210 ) -> Self {
211 Self { timeline, state, account_data, ambiguity_changes }
212 }
213}
214
215#[cfg(not(tarpaulin_include))]
216impl fmt::Debug for LeftRoomUpdate {
217 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
218 f.debug_struct("LeftRoomUpdate")
219 .field("timeline", &self.timeline)
220 .field("state", &DebugListOfRawEvents(&self.state))
221 .field("account_data", &DebugListOfRawEventsNoId(&self.account_data))
222 .field("ambiguity_changes", &self.ambiguity_changes)
223 .finish()
224 }
225}
226
227#[derive(Clone, Debug, Default)]
229pub struct Timeline {
230 pub limited: bool,
233
234 pub prev_batch: Option<String>,
237
238 pub events: Vec<TimelineEvent>,
240}
241
242impl Timeline {
243 pub(crate) fn new(limited: bool, prev_batch: Option<String>) -> Self {
244 Self { limited, prev_batch, ..Default::default() }
245 }
246}
247
248struct DebugInvitedRoomUpdates<'a>(&'a BTreeMap<OwnedRoomId, InvitedRoomUpdate>);
249
250#[cfg(not(tarpaulin_include))]
251impl fmt::Debug for DebugInvitedRoomUpdates<'_> {
252 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
253 f.debug_map().entries(self.0.iter().map(|(k, v)| (k, DebugInvitedRoom(v)))).finish()
254 }
255}
256
257struct DebugKnockedRoomUpdates<'a>(&'a BTreeMap<OwnedRoomId, KnockedRoomUpdate>);
258
259#[cfg(not(tarpaulin_include))]
260impl fmt::Debug for DebugKnockedRoomUpdates<'_> {
261 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
262 f.debug_map().entries(self.0.iter().map(|(k, v)| (k, DebugKnockedRoom(v)))).finish()
263 }
264}
265
266#[derive(Clone)]
268pub struct Notification {
269 pub actions: Vec<Action>,
271
272 pub event: RawAnySyncOrStrippedTimelineEvent,
274}
275
276#[cfg(not(tarpaulin_include))]
277impl fmt::Debug for Notification {
278 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
279 let event_debug = match &self.event {
280 RawAnySyncOrStrippedTimelineEvent::Sync(ev) => DebugRawEvent(ev),
281 RawAnySyncOrStrippedTimelineEvent::Stripped(ev) => DebugRawEvent(ev.cast_ref()),
282 };
283
284 f.debug_struct("Notification")
285 .field("actions", &self.actions)
286 .field("event", &event_debug)
287 .finish()
288 }
289}