From 682ec563c855e65bdfa2acc4ff63175eba143496 Mon Sep 17 00:00:00 2001 From: bjoern Date: Fri, 26 Aug 2022 19:21:44 +0200 Subject: [PATCH] add was_seen_recently() (#3560) * add dc_contact_was_seen_recently() * add wasSeenRecently() to node --- CHANGELOG.md | 1 + deltachat-ffi/deltachat.h | 16 ++++++++++++++++ deltachat-ffi/src/lib.rs | 10 ++++++++++ node/lib/contact.ts | 4 ++++ node/src/module.c | 7 +++++++ src/contact.rs | 36 ++++++++++++++++++++++++++++++++++-- 6 files changed, 72 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 88e44e988..8dfc4df42 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ## Unreleased ### API-Changes +- add `dc_contact_was_seen_recently()` #3560 ### Changes diff --git a/deltachat-ffi/deltachat.h b/deltachat-ffi/deltachat.h index f4d022166..303373e70 100644 --- a/deltachat-ffi/deltachat.h +++ b/deltachat-ffi/deltachat.h @@ -4638,6 +4638,22 @@ char* dc_contact_get_status (const dc_contact_t* contact); */ int64_t dc_contact_get_last_seen (const dc_contact_t* contact); + +/** + * Check if the contact was seen recently. + * + * The UI may highlight these contacts, + * eg. draw a little green dot on the avatars of the users recently seen. + * DC_CONTACT_ID_SELF and other special contact IDs are defined as never seen recently (they should not get a dot). + * To get the time a contact was seen, use dc_contact_get_last_seen(). + * + * @memberof dc_contact_t + * @param contact The contact object. + * @return 1=contact seen recently, 0=contact not seen recently. + */ +int dc_contact_was_seen_recently (const dc_contact_t* contact); + + /** * Check if a contact is blocked. * diff --git a/deltachat-ffi/src/lib.rs b/deltachat-ffi/src/lib.rs index 704e3bb18..017c3817f 100644 --- a/deltachat-ffi/src/lib.rs +++ b/deltachat-ffi/src/lib.rs @@ -3859,6 +3859,16 @@ pub unsafe extern "C" fn dc_contact_get_last_seen(contact: *mut dc_contact_t) -> ffi_contact.contact.last_seen() } +#[no_mangle] +pub unsafe extern "C" fn dc_contact_was_seen_recently(contact: *mut dc_contact_t) -> libc::c_int { + if contact.is_null() { + eprintln!("ignoring careless call to dc_contact_was_seen_recently()"); + return 0; + } + let ffi_contact = &*contact; + ffi_contact.contact.was_seen_recently() as libc::c_int +} + #[no_mangle] pub unsafe extern "C" fn dc_contact_is_blocked(contact: *mut dc_contact_t) -> libc::c_int { if contact.is_null() { diff --git a/node/lib/contact.ts b/node/lib/contact.ts index de5d1f379..41e3e270c 100644 --- a/node/lib/contact.ts +++ b/node/lib/contact.ts @@ -72,6 +72,10 @@ export class Contact { return binding.dcn_contact_get_last_seen(this.dc_contact) } + wasSeenRecently() { + return Boolean(binding.dcn_contact_was_seen_recently(this.dc_contact)) + } + getName(): string { return binding.dcn_contact_get_name(this.dc_contact) } diff --git a/node/src/module.c b/node/src/module.c index 61ae675b9..0e0369e02 100644 --- a/node/src/module.c +++ b/node/src/module.c @@ -1930,6 +1930,13 @@ NAPI_METHOD(dcn_contact_get_last_seen) { NAPI_RETURN_INT64(timestamp); } +NAPI_METHOD(dcn_contact_was_seen_recently) { + NAPI_ARGV(1); + NAPI_DC_CONTACT(); + int seen_recently = dc_contact_was_seen_recently(dc_contact); + NAPI_RETURN_UINT32(seen_recently); +} + NAPI_METHOD(dcn_contact_is_blocked) { NAPI_ARGV(1); NAPI_DC_CONTACT(); diff --git a/src/contact.rs b/src/contact.rs index 9aea65318..ade5ee721 100644 --- a/src/contact.rs +++ b/src/contact.rs @@ -24,9 +24,12 @@ use crate::mimeparser::AvatarAction; use crate::param::{Param, Params}; use crate::peerstate::{Peerstate, PeerstateVerifiedStatus}; use crate::sql::{self, params_iter}; -use crate::tools::{get_abs_path, improve_single_line_input, EmailAddress}; +use crate::tools::{get_abs_path, improve_single_line_input, time, EmailAddress}; use crate::{chat, stock_str}; +/// Time during which a contact is considered as seen recently. +const SEEN_RECENTLY_SECONDS: i64 = 600; + /// Contact ID, including reserved IDs. /// /// Some contact IDs are reserved to identify special contacts. This @@ -323,6 +326,11 @@ impl Contact { self.last_seen } + /// Returns `true` if this contact was seen recently. + pub fn was_seen_recently(&self) -> bool { + time() - self.last_seen <= SEEN_RECENTLY_SECONDS + } + /// Check if a contact is blocked. pub async fn is_blocked_load(context: &Context, id: ContactId) -> Result { let blocked = Self::load_from_db(context, id).await?.blocked; @@ -1444,7 +1452,7 @@ mod tests { use crate::chatlist::Chatlist; use crate::message::Message; use crate::receive_imf::receive_imf; - use crate::test_utils::{self, TestContext}; + use crate::test_utils::{self, TestContext, TestContextManager}; #[test] fn test_contact_id_values() { @@ -2270,4 +2278,28 @@ Hi."#; Ok(()) } + + #[tokio::test(flavor = "multi_thread", worker_threads = 2)] + async fn test_was_seen_recently() -> Result<()> { + let mut tcm = TestContextManager::new().await; + let alice = tcm.alice().await; + let bob = tcm.bob().await; + + let chat = alice.create_chat(&bob).await; + let sent_msg = alice.send_text(chat.id, "moin").await; + + let chat = bob.create_chat(&alice).await; + let contacts = chat::get_chat_contacts(&bob, chat.id).await?; + let contact = Contact::get_by_id(&bob, *contacts.first().unwrap()).await?; + assert!(!contact.was_seen_recently()); + + bob.recv_msg(&sent_msg).await; + let contact = Contact::get_by_id(&bob, *contacts.first().unwrap()).await?; + assert!(contact.was_seen_recently()); + + let self_contact = Contact::get_by_id(&bob, ContactId::SELF).await?; + assert!(!self_contact.was_seen_recently()); + + Ok(()) + } }