use chrono::{DateTime, Utc};
use saorsa_gossip_types::PeerId;
use std::collections::HashMap;
use std::sync::Arc;
use tokio::sync::RwLock;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PresenceStatus {
Online,
Offline,
Unknown,
}
#[derive(Debug, Clone)]
pub struct PresenceInfo {
pub peer_id: PeerId,
pub status: PresenceStatus,
pub last_seen: Option<DateTime<Utc>>,
pub shared_groups: Vec<String>, }
pub struct PresenceWrapper {
#[allow(dead_code)] presence_service: Arc<RwLock<saorsa_gossip_presence::PresenceManager>>,
cache: Arc<RwLock<HashMap<PeerId, PresenceInfo>>>,
}
impl PresenceWrapper {
pub fn new(presence_service: Arc<RwLock<saorsa_gossip_presence::PresenceManager>>) -> Self {
Self {
presence_service,
cache: Arc::new(RwLock::new(HashMap::new())),
}
}
pub async fn get_status(&self, peer_id: PeerId) -> PresenceStatus {
let cache = self.cache.read().await;
cache
.get(&peer_id)
.map(|info| info.status)
.unwrap_or(PresenceStatus::Unknown)
}
pub async fn get_info(&self, peer_id: PeerId) -> Option<PresenceInfo> {
let cache = self.cache.read().await;
cache.get(&peer_id).cloned()
}
pub async fn get_online_in_group(&self, group_id: &str) -> Vec<PeerId> {
let cache = self.cache.read().await;
cache
.values()
.filter(|info| {
info.status == PresenceStatus::Online
&& info.shared_groups.contains(&group_id.to_string())
})
.map(|info| info.peer_id)
.collect()
}
pub async fn find(&self, _four_words: &str) -> Option<PeerId> {
None
}
#[allow(dead_code)] async fn update_from_beacon(&self, peer_id: PeerId, group_id: String) {
let mut cache = self.cache.write().await;
cache
.entry(peer_id)
.and_modify(|info| {
info.status = PresenceStatus::Online;
info.last_seen = Some(Utc::now());
if !info.shared_groups.contains(&group_id) {
info.shared_groups.push(group_id.clone());
}
})
.or_insert(PresenceInfo {
peer_id,
status: PresenceStatus::Online,
last_seen: Some(Utc::now()),
shared_groups: vec![group_id],
});
}
pub async fn cleanup_expired(&self) {
const TTL_SECONDS: i64 = 15 * 60;
let mut cache = self.cache.write().await;
let now = Utc::now();
for info in cache.values_mut() {
if let Some(last_seen) = info.last_seen {
let age = (now - last_seen).num_seconds();
if age > TTL_SECONDS {
info.status = PresenceStatus::Offline;
}
}
}
}
}
#[cfg(test)]
mod tests {
#[tokio::test]
async fn test_presence_ttl() {
}
}