matrix_sdk_base/response_processors/account_data/
global.rs1use std::{
16 collections::{BTreeMap, HashMap, HashSet},
17 mem,
18};
19
20use ruma::{
21 events::{
22 direct::OwnedDirectUserIdentifier, AnyGlobalAccountDataEvent, GlobalAccountDataEventType,
23 },
24 serde::Raw,
25 RoomId,
26};
27use tracing::{debug, instrument, trace, warn};
28
29use super::super::Context;
30use crate::{store::BaseStateStore, RoomInfo, StateChanges};
31
32pub fn global(events: &[Raw<AnyGlobalAccountDataEvent>]) -> Global {
34 Global::process(events)
35}
36
37#[must_use]
38pub struct Global {
39 parsed_events: Vec<AnyGlobalAccountDataEvent>,
40 raw_by_type: BTreeMap<GlobalAccountDataEventType, Raw<AnyGlobalAccountDataEvent>>,
41}
42
43impl Global {
44 fn process(events: &[Raw<AnyGlobalAccountDataEvent>]) -> Self {
46 let mut raw_by_type = BTreeMap::new();
47 let mut parsed_events = Vec::new();
48
49 for raw_event in events {
50 let event = match raw_event.deserialize() {
51 Ok(e) => e,
52 Err(e) => {
53 let event_type: Option<String> = raw_event.get_field("type").ok().flatten();
54 warn!(event_type, "Failed to deserialize a global account data event: {e}");
55 continue;
56 }
57 };
58
59 raw_by_type.insert(event.event_type(), raw_event.clone());
60 parsed_events.push(event);
61 }
62
63 Self { raw_by_type, parsed_events }
64 }
65
66 pub fn push_rules(&self) -> Option<&Raw<AnyGlobalAccountDataEvent>> {
68 self.raw_by_type.get(&GlobalAccountDataEventType::PushRules)
69 }
70
71 #[instrument(skip_all)]
77 fn process_direct_rooms(
78 &self,
79 events: &[AnyGlobalAccountDataEvent],
80 state_store: &BaseStateStore,
81 state_changes: &mut StateChanges,
82 ) {
83 for event in events {
84 let AnyGlobalAccountDataEvent::Direct(direct_event) = event else { continue };
85
86 let mut new_dms = HashMap::<&RoomId, HashSet<OwnedDirectUserIdentifier>>::new();
87
88 for (user_identifier, rooms) in direct_event.content.iter() {
89 for room_id in rooms {
90 new_dms.entry(room_id).or_default().insert(user_identifier.clone());
91 }
92 }
93
94 let rooms = state_store.rooms();
95 let mut old_dms = rooms
96 .iter()
97 .filter_map(|r| {
98 let direct_targets = r.direct_targets();
99 (!direct_targets.is_empty()).then(|| (r.room_id(), direct_targets))
100 })
101 .collect::<HashMap<_, _>>();
102
103 for (room_id, new_direct_targets) in new_dms {
105 if let Some(old_direct_targets) = old_dms.remove(&room_id) {
106 if old_direct_targets == new_direct_targets {
107 continue;
108 }
109 }
110 trace!(?room_id, targets = ?new_direct_targets, "Marking room as direct room");
111 map_info(room_id, state_changes, state_store, |info| {
112 info.base_info.dm_targets = new_direct_targets;
113 });
114 }
115
116 for room_id in old_dms.keys() {
118 trace!(?room_id, "Unmarking room as direct room");
119 map_info(room_id, state_changes, state_store, |info| {
120 info.base_info.dm_targets.clear();
121 });
122 }
123 }
124 }
125
126 pub async fn apply(mut self, context: &mut Context, state_store: &BaseStateStore) {
128 mem::swap(&mut context.state_changes.account_data, &mut self.raw_by_type);
130
131 let has_new_direct_room_data = self
133 .parsed_events
134 .iter()
135 .any(|event| event.event_type() == GlobalAccountDataEventType::Direct);
136
137 if has_new_direct_room_data {
138 self.process_direct_rooms(&self.parsed_events, state_store, &mut context.state_changes);
139 } else if let Ok(Some(direct_account_data)) =
140 state_store.get_account_data_event(GlobalAccountDataEventType::Direct).await
141 {
142 debug!("Found direct room data in the Store, applying it");
143 if let Ok(direct_account_data) = direct_account_data.deserialize() {
144 self.process_direct_rooms(
145 &[direct_account_data],
146 state_store,
147 &mut context.state_changes,
148 );
149 } else {
150 warn!("Failed to deserialize direct room account data");
151 }
152 }
153 }
154}
155
156fn map_info<F: FnOnce(&mut RoomInfo)>(
159 room_id: &RoomId,
160 changes: &mut StateChanges,
161 store: &BaseStateStore,
162 f: F,
163) {
164 if let Some(info) = changes.room_infos.get_mut(room_id) {
165 f(info);
166 } else if let Some(room) = store.room(room_id) {
167 let mut info = room.clone_info();
168 f(&mut info);
169 changes.add_room(info);
170 } else {
171 debug!(room = %room_id, "couldn't find room in state changes or store");
172 }
173}