diff --git a/src/api/client/profile.rs b/src/api/client/profile.rs index 651baad9..ca37644a 100644 --- a/src/api/client/profile.rs +++ b/src/api/client/profile.rs @@ -49,7 +49,7 @@ pub(crate) async fn set_displayname_route( // Presence update services .presence - .maybe_ping_presence(&body.user_id, &PresenceState::Online) + .maybe_ping_presence(&body.user_id, body.sender_device.as_deref(), &PresenceState::Online) .await?; Ok(set_display_name::v3::Response {}) @@ -149,7 +149,7 @@ pub(crate) async fn set_avatar_url_route( // Presence update services .presence - .maybe_ping_presence(&body.user_id, &PresenceState::Online) + .maybe_ping_presence(&body.user_id, body.sender_device.as_deref(), &PresenceState::Online) .await .ok(); diff --git a/src/api/client/read_marker.rs b/src/api/client/read_marker.rs index 0f7ccba7..d1ae34b4 100644 --- a/src/api/client/read_marker.rs +++ b/src/api/client/read_marker.rs @@ -93,7 +93,11 @@ pub(crate) async fn set_read_marker_route( services .presence - .maybe_ping_presence(sender_user, &ruma::presence::PresenceState::Online) + .maybe_ping_presence( + sender_user, + body.sender_device.as_deref(), + &ruma::presence::PresenceState::Online, + ) .await .ok(); } @@ -165,7 +169,11 @@ pub(crate) async fn create_receipt_route( services .presence - .maybe_ping_presence(sender_user, &ruma::presence::PresenceState::Online) + .maybe_ping_presence( + sender_user, + body.sender_device.as_deref(), + &ruma::presence::PresenceState::Online, + ) .await .ok(); }, diff --git a/src/api/client/sync/v3.rs b/src/api/client/sync/v3.rs index c748a550..a1b700c8 100644 --- a/src/api/client/sync/v3.rs +++ b/src/api/client/sync/v3.rs @@ -130,7 +130,7 @@ pub(crate) async fn sync_events_route( let ping_presence = services .presence - .maybe_ping_presence(sender_user, &body.body.set_presence) + .maybe_ping_presence(sender_user, Some(sender_device), &body.body.set_presence) .inspect_err(inspect_log) .ok(); diff --git a/src/api/client/sync/v5.rs b/src/api/client/sync/v5.rs index 723a16ec..ac0cd6ff 100644 --- a/src/api/client/sync/v5.rs +++ b/src/api/client/sync/v5.rs @@ -108,7 +108,7 @@ pub(crate) async fn sync_events_v5_route( let conn = conn_val.lock(); let ping_presence = services .presence - .maybe_ping_presence(sender_user, &request.set_presence) + .maybe_ping_presence(sender_user, Some(sender_device), &request.set_presence) .inspect_err(inspect_log) .ok(); diff --git a/src/api/client/typing.rs b/src/api/client/typing.rs index ee12f817..52cf2639 100644 --- a/src/api/client/typing.rs +++ b/src/api/client/typing.rs @@ -66,7 +66,11 @@ pub(crate) async fn create_typing_event_route( // ping presence services .presence - .maybe_ping_presence(&body.user_id, &ruma::presence::PresenceState::Online) + .maybe_ping_presence( + &body.user_id, + body.sender_device.as_deref(), + &ruma::presence::PresenceState::Online, + ) .await?; Ok(create_typing_event::v3::Response {}) diff --git a/src/api/client/unstable.rs b/src/api/client/unstable.rs index 79c593c6..dc311689 100644 --- a/src/api/client/unstable.rs +++ b/src/api/client/unstable.rs @@ -76,7 +76,7 @@ pub(crate) async fn delete_timezone_key_route( // Presence update services .presence - .maybe_ping_presence(&body.user_id, &PresenceState::Online) + .maybe_ping_presence(&body.user_id, body.sender_device.as_deref(), &PresenceState::Online) .await?; Ok(delete_timezone_key::unstable::Response {}) @@ -104,7 +104,7 @@ pub(crate) async fn set_timezone_key_route( // Presence update services .presence - .maybe_ping_presence(&body.user_id, &PresenceState::Online) + .maybe_ping_presence(&body.user_id, body.sender_device.as_deref(), &PresenceState::Online) .await?; Ok(set_timezone_key::unstable::Response {}) @@ -170,7 +170,7 @@ pub(crate) async fn set_profile_field_route( // Presence update services .presence - .maybe_ping_presence(&body.user_id, &PresenceState::Online) + .maybe_ping_presence(&body.user_id, body.sender_device.as_deref(), &PresenceState::Online) .await?; Ok(set_profile_field::v3::Response {}) @@ -224,7 +224,7 @@ pub(crate) async fn delete_profile_field_route( // Presence update services .presence - .maybe_ping_presence(&body.user_id, &PresenceState::Online) + .maybe_ping_presence(&body.user_id, body.sender_device.as_deref(), &PresenceState::Online) .await?; Ok(delete_profile_field::v3::Response {}) diff --git a/src/service/presence/mod.rs b/src/service/presence/mod.rs index efe4dc3c..49a7db30 100644 --- a/src/service/presence/mod.rs +++ b/src/service/presence/mod.rs @@ -4,11 +4,20 @@ mod presence; use std::{collections::HashMap, sync::Arc, time::Duration}; use async_trait::async_trait; -use futures::{Stream, StreamExt, TryFutureExt, stream::FuturesUnordered}; +use futures::{ + Stream, StreamExt, TryFutureExt, + future::{OptionFuture, try_join}, + stream::FuturesUnordered, +}; use loole::{Receiver, Sender}; -use ruma::{OwnedUserId, UInt, UserId, events::presence::PresenceEvent, presence::PresenceState}; +use ruma::{ + DeviceId, OwnedUserId, UInt, UserId, events::presence::PresenceEvent, presence::PresenceState, +}; use tokio::{sync::RwLock, time::sleep}; -use tuwunel_core::{Error, Result, checked, debug, debug_warn, error, result::LogErr, trace}; +use tuwunel_core::{ + Error, Result, checked, debug, debug_warn, error, result::LogErr, trace, + utils::future::OptionExt, +}; use self::{data::Data, presence::Presence}; @@ -46,7 +55,7 @@ impl crate::Service for Service { // online self.unset_all_presence().await; _ = self - .maybe_ping_presence(&self.services.globals.server_user, &PresenceState::Online) + .maybe_ping_presence(&self.services.globals.server_user, None, &PresenceState::Online) .await; let receiver = self.timer_channel.1.clone(); @@ -73,7 +82,11 @@ impl crate::Service for Service { async fn interrupt(&self) { // set the server user as offline _ = self - .maybe_ping_presence(&self.services.globals.server_user, &PresenceState::Offline) + .maybe_ping_presence( + &self.services.globals.server_user, + None, + &PresenceState::Offline, + ) .await; let (timer_sender, _) = &self.timer_channel; @@ -118,11 +131,12 @@ impl Service { .await } - /// Pings the presence of the given user in the given room, setting the - /// specified state. + /// Pings the presence of the given user, setting the specified state. When + /// device_id is supplied. pub async fn maybe_ping_presence( &self, user_id: &UserId, + device_id: Option<&DeviceId>, new_state: &PresenceState, ) -> Result { const REFRESH_TIMEOUT: u64 = 60 * 1000; @@ -150,6 +164,14 @@ impl Service { return Ok(()); } + let update_device_seen: OptionFuture<_> = device_id + .map(|device_id| { + self.services + .users + .update_device_last_seen(user_id, device_id, None) + }) + .into(); + let status_msg = match last_presence { | Ok((_, ref presence)) => presence.content.status_msg.clone(), | Err(_) => Some(String::new()), @@ -157,7 +179,16 @@ impl Service { let last_active_ago = UInt::new(0); let currently_active = *new_state == PresenceState::Online; - self.set_presence(user_id, new_state, Some(currently_active), last_active_ago, status_msg) + let set_presence = self.set_presence( + user_id, + new_state, + Some(currently_active), + last_active_ago, + status_msg, + ); + + try_join(set_presence, update_device_seen.unwrap_or(Ok(()))) + .map_ok(|_| ()) .await } diff --git a/src/service/users/device.rs b/src/service/users/device.rs index ce02af3b..3ab34d87 100644 --- a/src/service/users/device.rs +++ b/src/service/users/device.rs @@ -362,6 +362,26 @@ pub async fn remove_to_device_events( .await; } +#[implement(super::Service)] +pub async fn update_device_last_seen( + &self, + user_id: &UserId, + device_id: &DeviceId, + last_seen: Option, +) -> Result { + let mut device = self + .get_device_metadata(user_id, device_id) + .await?; + + device + .last_seen_ts + .replace(last_seen.unwrap_or_else(MilliSecondsSinceUnixEpoch::now)); + + self.put_device_metadata(user_id, false, &device); + + Ok(()) +} + #[implement(super::Service)] pub fn put_device_metadata(&self, user_id: &UserId, notify: bool, device: &Device) { let key = (user_id, &device.device_id);