diff --git a/deltachat-ffi/deltachat.h b/deltachat-ffi/deltachat.h index 005905e74..f1332246f 100644 --- a/deltachat-ffi/deltachat.h +++ b/deltachat-ffi/deltachat.h @@ -5121,6 +5121,23 @@ int64_t dc_contact_get_last_seen (const dc_contact_t* contact); int dc_contact_was_seen_recently (const dc_contact_t* contact); +/** + * Check if the contact was not seen a long time ago. + * + * The UI shall highlight these contacts, + * draw a orange green dot on the avatars of the user, + * and show a hint in the contact's profile. + * + * DC_CONTACT_ID_SELF and other special contact IDs are defined as never been stale (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_is_stale (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 d13335977..adc5d81dd 100644 --- a/deltachat-ffi/src/lib.rs +++ b/deltachat-ffi/src/lib.rs @@ -4055,6 +4055,16 @@ pub unsafe extern "C" fn dc_contact_was_seen_recently(contact: *mut dc_contact_t ffi_contact.contact.was_seen_recently() as libc::c_int } +#[unsafe(no_mangle)] +pub unsafe extern "C" fn dc_contact_is_stale(contact: *mut dc_contact_t) -> libc::c_int { + if contact.is_null() { + eprintln!("ignoring careless call to dc_contact_is_stale()"); + return 0; + } + let ffi_contact = unsafe { &*contact }; + ffi_contact.contact.is_stale() as libc::c_int +} + #[unsafe(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/src/contact.rs b/src/contact.rs index 9acd67be5..3e0b0a95a 100644 --- a/src/contact.rs +++ b/src/contact.rs @@ -43,6 +43,9 @@ use crate::{chat, chatlist_events, ensure_and_debug_assert, stock_str}; /// Time during which a contact is considered as seen recently. const SEEN_RECENTLY_SECONDS: i64 = 600; +/// If "seen recently" is older, the contact is marked as potentially stale. +const CONTACT_STALE_SECONDS: i64 = 90 * 24 * 60 * 60; + /// Contact ID, including reserved IDs. /// /// Some contact IDs are reserved to identify special contacts. This @@ -731,6 +734,12 @@ impl Contact { time() - self.last_seen <= SEEN_RECENTLY_SECONDS } + /// Returns `true` if this contact was not seen for a long time. + #[expect(clippy::arithmetic_side_effects)] + pub fn is_stale(&self) -> bool { + time() - self.last_seen > CONTACT_STALE_SECONDS + } + /// Check if a contact is blocked. pub async fn is_blocked_load(context: &Context, id: ContactId) -> Result { let blocked = context