summaryrefslogtreecommitdiffstats
path: root/src/user_presences.rs
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/user_presences.rs173
1 files changed, 173 insertions, 0 deletions
diff --git a/src/user_presences.rs b/src/user_presences.rs
new file mode 100644
index 0000000..87f9bdc
--- /dev/null
+++ b/src/user_presences.rs
@@ -0,0 +1,173 @@
+// SPDX-FileCopyrightText: 2025 cel <cel@bunny.garden>
+//
+// SPDX-License-Identifier: AGPL-3.0-or-later
+
+use std::collections::HashMap;
+
+use chrono::Utc;
+use filamento::presence::{Offline, Presence, PresenceType, Show};
+use indexmap::IndexMap;
+use jid::BareJID;
+use leptos::prelude::*;
+use reactive_stores::Store;
+
+#[derive(Store)]
+pub struct UserPresences {
+ #[store(key: BareJID = |(jid, _)| jid.clone())]
+ pub user_presences: HashMap<BareJID, ArcRwSignal<Presences>>,
+}
+
+impl UserPresences {
+ pub fn clear(&mut self) {
+ for (_user, presences) in &mut self.user_presences {
+ presences.set(Presences::new())
+ }
+ }
+
+ // TODO: should be a bare jid
+ pub fn get_user_presences(&mut self, user: &BareJID) -> ArcRwSignal<Presences> {
+ if let Some(presences) = self.user_presences.get(user) {
+ presences.clone()
+ } else {
+ let presences = Presences::new();
+ let signal = ArcRwSignal::new(presences);
+ self.user_presences.insert(user.clone(), signal.clone());
+ signal
+ }
+ }
+}
+
+impl UserPresences {
+ pub fn new() -> Self {
+ Self {
+ user_presences: HashMap::new(),
+ }
+ }
+}
+
+pub struct Presences {
+ /// presences are sorted by time, first by type, then by last activity.
+ presences: IndexMap<String, Presence>,
+}
+
+impl Presences {
+ pub fn new() -> Self {
+ Self {
+ presences: IndexMap::new(),
+ }
+ }
+
+ /// gets the highest priority presence
+ pub fn presence(&self) -> Option<(String, Presence)> {
+ if let Some((resource, presence)) = self
+ .presences
+ .iter()
+ .filter(|(_resource, presence)| {
+ if let PresenceType::Online(online) = &presence.presence {
+ online.show == Some(Show::DoNotDisturb)
+ } else {
+ false
+ }
+ })
+ .next()
+ {
+ return Some((resource.clone(), presence.clone()));
+ }
+ if let Some((resource, presence)) = self
+ .presences
+ .iter()
+ .filter(|(_resource, presence)| {
+ if let PresenceType::Online(online) = &presence.presence {
+ online.show == Some(Show::Chat)
+ } else {
+ false
+ }
+ })
+ .next()
+ {
+ return Some((resource.clone(), presence.clone()));
+ }
+ if let Some((resource, presence)) = self
+ .presences
+ .iter()
+ .filter(|(_resource, presence)| {
+ if let PresenceType::Online(online) = &presence.presence {
+ online.show == None
+ } else {
+ false
+ }
+ })
+ .next()
+ {
+ return Some((resource.clone(), presence.clone()));
+ }
+ if let Some((resource, presence)) = self
+ .presences
+ .iter()
+ .filter(|(_resource, presence)| {
+ if let PresenceType::Online(online) = &presence.presence {
+ online.show == Some(Show::Away)
+ } else {
+ false
+ }
+ })
+ .next()
+ {
+ return Some((resource.clone(), presence.clone()));
+ }
+ if let Some((resource, presence)) = self
+ .presences
+ .iter()
+ .filter(|(_resource, presence)| {
+ if let PresenceType::Online(online) = &presence.presence {
+ online.show == Some(Show::ExtendedAway)
+ } else {
+ false
+ }
+ })
+ .next()
+ {
+ return Some((resource.clone(), presence.clone()));
+ }
+ if let Some((resource, presence)) = self
+ .presences
+ .iter()
+ .filter(|(_resource, presence)| {
+ if let PresenceType::Offline(_offline) = &presence.presence {
+ true
+ } else {
+ false
+ }
+ })
+ .next()
+ {
+ return Some((resource.clone(), presence.clone()));
+ } else {
+ None
+ }
+ }
+
+ pub fn update_presence(&mut self, resource: String, presence: Presence) {
+ let index = match self.presences.binary_search_by(|_, existing_presence| {
+ presence.timestamp.cmp(&existing_presence.timestamp)
+ }) {
+ Ok(i) => i,
+ Err(i) => i,
+ };
+ self.presences.insert_before(
+ // TODO: check if this logic is correct
+ index, resource, presence,
+ );
+ }
+
+ pub fn resource_presence(&mut self, resource: String) -> Presence {
+ if let Some(presence) = self.presences.get(&resource) {
+ presence.clone()
+ } else {
+ Presence {
+ timestamp: Utc::now(),
+ presence: PresenceType::Offline(Offline::default()),
+ }
+ }
+ }
+}