use std::collections::HashMap;
use anyhow::Result;
use dioxus::prelude::*;
use matrix_sdk::Client;
use crate::notifications::push_rules::notification_level_from_user_defined_mode;
use crate::state::app_state::{AppState, SyncStatus};
use crate::state::room_state::{RoomMembership, RoomSummary};
use crate::state::user_state::UserProfile;
pub async fn start_sync_loop(client: Client, mut state: Signal<AppState>) {
tracing::info!("Starting sync loop...");
state.write().sync_status = SyncStatus::Syncing;
if let Err(e) = load_user_profile(&client, &mut state).await {
tracing::error!("Failed to load user profile: {e}");
}
let sync_settings = matrix_sdk::config::SyncSettings::default()
.timeout(std::time::Duration::from_secs(30));
loop {
match client.sync_once(sync_settings.clone()).await {
Ok(_response) => {
{
let mut w = state.write();
w.sync_status = SyncStatus::Synced;
w.sync_generation += 1;
}
refresh_room_list(&client, &mut state).await;
}
Err(e) => {
tracing::error!("Sync error: {e}");
state.write().sync_status = SyncStatus::Error(e.to_string());
tokio::time::sleep(std::time::Duration::from_secs(5)).await;
}
}
}
}
async fn load_user_profile(client: &Client, state: &mut Signal<AppState>) -> Result<()> {
let user_id = client
.user_id()
.ok_or_else(|| anyhow::anyhow!("No user ID"))?;
let account = client.account();
let display_name = account.get_display_name().await?.unwrap_or_default();
let avatar_url = account.get_avatar_url().await?.map(|u| u.to_string());
state.write().user_profile = Some(UserProfile {
user_id: Some(user_id.to_owned()),
display_name: Some(display_name),
avatar_url,
email: None,
status_message: None,
});
Ok(())
}
async fn refresh_room_list(client: &Client, state: &mut Signal<AppState>) {
let mut rooms = HashMap::new();
let notification_settings = client.notification_settings().await;
for room in client.joined_rooms() {
let room_id = room.room_id().to_owned();
let display_name = match room.display_name().await {
Ok(name) => name.to_string(),
Err(_) => room_id.to_string(),
};
let avatar_url = room.avatar_url().map(|u| u.to_string());
let topic = room.topic();
let is_direct = room.is_direct().await.unwrap_or(false);
let unread = room.unread_notification_counts();
let member_count = room.joined_members_count();
let notification_level = notification_level_from_user_defined_mode(
notification_settings
.get_user_defined_room_notification_mode(room.room_id())
.await,
);
let summary = RoomSummary {
room_id: room_id.clone(),
display_name,
avatar_url,
topic,
is_direct,
is_favorite: false,
is_encrypted: false,
is_tombstoned: false,
tombstone_successor: None,
tombstone_body: None,
unread_count: unread.notification_count.into(),
highlight_count: unread.highlight_count.into(),
last_activity_ts: None,
last_event_preview: None,
last_event_sender: None,
member_count,
typing_members: Vec::new(),
notification_level,
parent_spaces: Vec::new(),
membership: RoomMembership::Joined,
};
rooms.insert(room_id, summary);
}
let total_unread: u64 = rooms.values().map(|r| r.unread_count).sum();
let total_highlights: u64 = rooms.values().map(|r| r.highlight_count).sum();
{
let prev_unread = state.read().notifications.total_unread;
if total_unread > prev_unread {
let new_msgs: Vec<_> = rooms.values()
.filter(|r| {
let prev = state.read().rooms.get(&r.room_id).map(|p| p.unread_count).unwrap_or(0);
r.unread_count > prev
})
.collect();
for room in new_msgs.iter().take(3) {
let room_name = room.display_name.clone();
let sender = room.last_event_sender.clone().unwrap_or_default();
let body = room.last_event_preview.clone().unwrap_or_else(|| "New message".to_string());
let rid = room.room_id.to_string();
spawn(async move {
crate::notifications::desktop::show_desktop_notification(
&room_name, &sender, &body, &rid,
).await;
});
}
}
}
let mut state_write = state.write();
state_write.rooms = rooms;
state_write.notifications.total_unread = total_unread;
state_write.notifications.total_highlights = total_highlights;
state_write.update_sorted_rooms();
}