matrix_ui_serializable/init/
singletons.rs1use anyhow::anyhow;
2use std::{
3 collections::HashMap,
4 path::PathBuf,
5 sync::{Arc, Condvar, LazyLock, Mutex, OnceLock},
6 time::Duration,
7};
8
9use matrix_sdk::{
10 Client, RoomMemberships,
11 ruma::{OwnedRoomId, OwnedUserId},
12};
13use matrix_sdk_ui::sync_service::SyncService;
14use tokio::{
15 sync::{
16 broadcast,
17 mpsc::{Receiver, UnboundedSender},
18 },
19 time::{Instant, interval},
20};
21
22use crate::{
23 events::timeline::TimelineKind,
24 init::session::ClientSession,
25 models::{
26 async_requests::MatrixRequest, event_bridge::EventBridge,
27 events::MatrixVerificationResponse,
28 },
29 submit_async_request,
30};
31
32pub static REQUEST_SENDER: OnceLock<UnboundedSender<MatrixRequest>> = OnceLock::new();
35
36pub static SYNC_SERVICE: OnceLock<SyncService> = OnceLock::new();
38
39pub static ALL_ROOMS_LOADED: OnceLock<bool> = OnceLock::new();
42
43pub(crate) struct TempClientSlot {
44 pub(crate) lock: Mutex<Option<Client>>,
45 pub(crate) cvar: Condvar,
46}
47
48pub static TEMP_CLIENT: LazyLock<TempClientSlot> = LazyLock::new(|| TempClientSlot {
51 lock: Mutex::new(None),
52 cvar: Condvar::new(),
53});
54
55pub static TEMP_CLIENT_SESSION: LazyLock<Mutex<Option<ClientSession>>> =
57 LazyLock::new(|| Mutex::new(None));
58
59pub static CLIENT: OnceLock<Client> = OnceLock::new();
61
62pub static CURRENT_USER_ID: OnceLock<OwnedUserId> = OnceLock::new();
64
65pub static LOGIN_STORE_READY: OnceLock<bool> = OnceLock::new();
67
68pub static HAS_SESSION_STORED: OnceLock<bool> = OnceLock::new();
69
70#[derive(Debug, Clone)]
71pub enum UIUpdateMessage {
72 RefreshUI,
73}
74
75static GLOBAL_BROADCASTER: OnceLock<GlobalBroadcaster> = OnceLock::new();
77
78pub struct GlobalBroadcaster {
79 sender: broadcast::Sender<UIUpdateMessage>,
80}
81
82pub static APP_DATA_DIR: OnceLock<PathBuf> = OnceLock::new();
83
84impl GlobalBroadcaster {
85 fn new(capacity: usize) -> Self {
86 let (sender, _) = broadcast::channel(capacity);
87 Self { sender }
88 }
89
90 fn broadcast(
91 &self,
92 message: UIUpdateMessage,
93 ) -> Result<usize, broadcast::error::SendError<UIUpdateMessage>> {
94 self.sender.send(message)
95 }
96
97 fn subscribe(&self) -> broadcast::Receiver<UIUpdateMessage> {
98 self.sender.subscribe()
99 }
100}
101
102pub fn init_broadcaster(capacity: usize) -> Result<(), &'static str> {
104 GLOBAL_BROADCASTER
105 .set(GlobalBroadcaster::new(capacity))
106 .map_err(|_| "Broadcaster already initialized")
107}
108
109pub fn broadcast_event(message: UIUpdateMessage) {
111 let broadcaster = GLOBAL_BROADCASTER
112 .get()
113 .expect("Broadcaster not initialized. Call init_broadcaster() first.");
114
115 let _ = broadcaster.broadcast(message);
116}
117
118pub fn subscribe_to_events() -> Result<broadcast::Receiver<UIUpdateMessage>, &'static str> {
120 let broadcaster = GLOBAL_BROADCASTER
121 .get()
122 .ok_or("Broadcaster not initialized. Call init_broadcaster() first.")?;
123
124 Ok(broadcaster.subscribe())
125}
126
127pub static EVENT_BRIDGE: OnceLock<EventBridge> = OnceLock::new();
130
131pub fn get_event_bridge<'a>() -> anyhow::Result<&'a EventBridge> {
132 let bridge = EVENT_BRIDGE
133 .get()
134 .ok_or(anyhow!("The event bridge is not yet set"))?;
135 Ok(bridge)
136}
137
138pub static VERIFICATION_RESPONSE_RECEIVER: OnceLock<
141 tokio::sync::Mutex<Receiver<MatrixVerificationResponse>>,
142> = OnceLock::new();
143
144pub async fn get_verification_response_receiver_lock<'a>()
145-> anyhow::Result<tokio::sync::MutexGuard<'a, Receiver<MatrixVerificationResponse>>> {
146 let recv = VERIFICATION_RESPONSE_RECEIVER
147 .get()
148 .ok_or(anyhow!("The verification response receiver is not yet set"))?;
149 Ok(recv.lock().await)
150}
151
152pub(crate) struct ExpiryMap {
157 items: Arc<Mutex<HashMap<OwnedRoomId, Instant>>>,
159}
160
161impl ExpiryMap {
162 fn new() -> Self {
163 let items: Arc<Mutex<HashMap<OwnedRoomId, Instant>>> = Arc::new(Mutex::new(HashMap::new()));
164 let items_clone = Arc::clone(&items);
165
166 tokio::spawn(async move {
167 let mut ticker = interval(Duration::from_millis(500));
168
169 loop {
170 ticker.tick().await;
171
172 let mut expired_ids = Vec::new();
173 let now = Instant::now();
174
175 {
176 let mut map = items_clone.lock().unwrap();
177
178 map.retain(|id, &mut expiry| {
181 if now >= expiry {
182 expired_ids.push(id.to_owned());
183 false
184 } else {
185 true
186 }
187 });
188 }
189
190 for id in expired_ids {
191 submit_async_request(MatrixRequest::GetRoomMembers {
192 timeline_kind: TimelineKind::MainRoom { room_id: id },
193 memberships: RoomMemberships::JOIN,
194 local_only: true,
197 });
198 }
199 }
200 });
201
202 ExpiryMap { items }
203 }
204
205 pub(crate) fn insert(&self, id: OwnedRoomId, duration: Duration) {
206 let expiry = Instant::now() + duration;
207 self.items.lock().unwrap().insert(id, expiry);
208 }
209}
210
211pub(crate) static MEMBERSHIP_UPDATES_EXPIRY_MAP: LazyLock<ExpiryMap> =
212 LazyLock::new(ExpiryMap::new);