api: Contact::get_all(): Support listing address-contacts

Also test-cover `DC_GCL_ADD_SELF`.
This commit is contained in:
iequidoo
2025-06-29 17:24:06 -03:00
committed by iequidoo
parent 0a73c2b7ab
commit 7e4d4cf680
6 changed files with 62 additions and 16 deletions

View File

@@ -2137,6 +2137,7 @@ uint32_t dc_create_contact (dc_context_t* context, const char*
#define DC_GCL_DEPRECATED_VERIFIED_ONLY 0x01 #define DC_GCL_DEPRECATED_VERIFIED_ONLY 0x01
#define DC_GCL_ADD_SELF 0x02 #define DC_GCL_ADD_SELF 0x02
#define DC_GCL_ADDRESS 0x04
/** /**
@@ -2192,11 +2193,13 @@ dc_array_t* dc_import_vcard (dc_context_t* context, const char*
* Returns known and unblocked contacts. * Returns known and unblocked contacts.
* *
* To get information about a single contact, see dc_get_contact(). * To get information about a single contact, see dc_get_contact().
* By default, key-contacts are listed.
* *
* @memberof dc_context_t * @memberof dc_context_t
* @param context The context object. * @param context The context object.
* @param flags A combination of flags: * @param flags A combination of flags:
* - if the flag DC_GCL_ADD_SELF is set, SELF is added to the list unless filtered by other parameters * - DC_GCL_ADD_SELF: SELF is added to the list unless filtered by other parameters
* - DC_GCL_ADDRESS: List address-contacts instead of key-contacts.
* @param query A string to filter the list. Typically used to implement an * @param query A string to filter the list. Typically used to implement an
* incremental search. NULL for no filtering. * incremental search. NULL for no filtering.
* @return An array containing all contact IDs. Must be dc_array_unref()'d * @return An array containing all contact IDs. Must be dc_array_unref()'d

View File

@@ -1505,6 +1505,14 @@ impl CommandApi {
Ok(contacts) Ok(contacts)
} }
/// Returns ids of known and unblocked contacts.
///
/// By default, key-contacts are listed.
///
/// * `list_flags` - A combination of flags:
/// - `DC_GCL_ADD_SELF` - Add SELF unless filtered by other parameters.
/// - `DC_GCL_ADDRESS` - List address-contacts instead of key-contacts.
/// * `query` - A string to filter the list.
async fn get_contact_ids( async fn get_contact_ids(
&self, &self,
account_id: u32, account_id: u32,
@@ -1516,8 +1524,10 @@ impl CommandApi {
Ok(contacts.into_iter().map(|c| c.to_u32()).collect()) Ok(contacts.into_iter().map(|c| c.to_u32()).collect())
} }
/// Get a list of contacts. /// Returns known and unblocked contacts.
/// (formerly called getContacts2 in desktop) ///
/// Formerly called `getContacts2` in Desktop.
/// See [`Self::get_contact_ids`] for parameters and more info.
async fn get_contacts( async fn get_contacts(
&self, &self,
account_id: u32, account_id: u32,

View File

@@ -9,6 +9,7 @@ class ContactFlag(IntEnum):
"""Bit flags for get_contacts() method.""" """Bit flags for get_contacts() method."""
ADD_SELF = 0x02 ADD_SELF = 0x02
ADDRESS = 0x04
class ChatlistFlag(IntEnum): class ChatlistFlag(IntEnum):

View File

@@ -89,6 +89,7 @@ pub const DC_GCL_ADD_ALLDONE_HINT: usize = 0x04;
pub const DC_GCL_FOR_FORWARDING: usize = 0x08; pub const DC_GCL_FOR_FORWARDING: usize = 0x08;
pub const DC_GCL_ADD_SELF: u32 = 0x02; pub const DC_GCL_ADD_SELF: u32 = 0x02;
pub const DC_GCL_ADDRESS: u32 = 0x04;
// unchanged user avatars are resent to the recipients every some days // unchanged user avatars are resent to the recipients every some days
pub(crate) const DC_RESEND_USER_AVATAR_DAYS: i64 = 14; pub(crate) const DC_RESEND_USER_AVATAR_DAYS: i64 = 14;

View File

@@ -24,7 +24,7 @@ use crate::blob::BlobObject;
use crate::chat::{ChatId, ChatIdBlocked, ProtectionStatus}; use crate::chat::{ChatId, ChatIdBlocked, ProtectionStatus};
use crate::color::str_to_color; use crate::color::str_to_color;
use crate::config::Config; use crate::config::Config;
use crate::constants::{Blocked, Chattype, DC_GCL_ADD_SELF}; use crate::constants::{self, Blocked, Chattype};
use crate::context::Context; use crate::context::Context;
use crate::events::EventType; use crate::events::EventType;
use crate::key::{ use crate::key::{
@@ -1063,11 +1063,12 @@ impl Contact {
/// Returns known and unblocked contacts. /// Returns known and unblocked contacts.
/// ///
/// To get information about a single contact, see get_contact(). /// To get information about a single contact, see get_contact().
/// By default, key-contacts are listed.
/// ///
/// `listflags` is a combination of flags: /// * `listflags` - A combination of flags:
/// - if the flag DC_GCL_ADD_SELF is set, SELF is added to the list unless filtered by other parameters /// - `DC_GCL_ADD_SELF` - Add SELF unless filtered by other parameters.
/// /// - `DC_GCL_ADDRESS` - List address-contacts instead of key-contacts.
/// `query` is a string to filter the list. /// * `query` - A string to filter the list.
pub async fn get_all( pub async fn get_all(
context: &Context, context: &Context,
listflags: u32, listflags: u32,
@@ -1080,7 +1081,8 @@ impl Contact {
.collect::<HashSet<_>>(); .collect::<HashSet<_>>();
let mut add_self = false; let mut add_self = false;
let mut ret = Vec::new(); let mut ret = Vec::new();
let flag_add_self = (listflags & DC_GCL_ADD_SELF) != 0; let flag_add_self = (listflags & constants::DC_GCL_ADD_SELF) != 0;
let flag_address = (listflags & constants::DC_GCL_ADDRESS) != 0;
let minimal_origin = if context.get_config_bool(Config::Bot).await? { let minimal_origin = if context.get_config_bool(Config::Bot).await? {
Origin::Unknown Origin::Unknown
} else { } else {
@@ -1093,13 +1095,14 @@ impl Contact {
.query_map( .query_map(
"SELECT c.id, c.addr FROM contacts c "SELECT c.id, c.addr FROM contacts c
WHERE c.id>? WHERE c.id>?
AND c.fingerprint!='' \ AND (c.fingerprint='')=?
AND c.origin>=? \ AND c.origin>=? \
AND c.blocked=0 \ AND c.blocked=0 \
AND (iif(c.name='',c.authname,c.name) LIKE ? OR c.addr LIKE ?) \ AND (iif(c.name='',c.authname,c.name) LIKE ? OR c.addr LIKE ?) \
ORDER BY c.last_seen DESC, c.id DESC;", ORDER BY c.last_seen DESC, c.id DESC;",
( (
ContactId::LAST_SPECIAL, ContactId::LAST_SPECIAL,
flag_address,
minimal_origin, minimal_origin,
&s3str_like_cmd, &s3str_like_cmd,
&s3str_like_cmd, &s3str_like_cmd,
@@ -1149,11 +1152,11 @@ impl Contact {
.query_map( .query_map(
"SELECT id, addr FROM contacts "SELECT id, addr FROM contacts
WHERE id>? WHERE id>?
AND fingerprint!='' AND (fingerprint='')=?
AND origin>=? AND origin>=?
AND blocked=0 AND blocked=0
ORDER BY last_seen DESC, id DESC;", ORDER BY last_seen DESC, id DESC;",
(ContactId::LAST_SPECIAL, minimal_origin), (ContactId::LAST_SPECIAL, flag_address, minimal_origin),
|row| { |row| {
let id: ContactId = row.get(0)?; let id: ContactId = row.get(0)?;
let addr: String = row.get(1)?; let addr: String = row.get(1)?;

View File

@@ -69,6 +69,9 @@ async fn test_get_contacts() -> Result<()> {
let contacts = Contact::get_all(&context.ctx, 0, Some("MyName")).await?; let contacts = Contact::get_all(&context.ctx, 0, Some("MyName")).await?;
assert_eq!(contacts.len(), 0); assert_eq!(contacts.len(), 0);
let claire_id = Contact::create(&context, "someone", "claire@example.org").await?;
let dave_id = Contact::create(&context, "", "dave@example.org").await?;
let id = context.add_or_lookup_contact_id(&alice).await; let id = context.add_or_lookup_contact_id(&alice).await;
assert_ne!(id, ContactId::UNDEFINED); assert_ne!(id, ContactId::UNDEFINED);
@@ -101,10 +104,35 @@ async fn test_get_contacts() -> Result<()> {
let contacts = Contact::get_all(&context, 0, Some("MyName")).await?; let contacts = Contact::get_all(&context, 0, Some("MyName")).await?;
assert_eq!(contacts.len(), 0); assert_eq!(contacts.len(), 0);
// Search by display name (same as manually set name). for add_self in [0, constants::DC_GCL_ADD_SELF] {
let contacts = Contact::get_all(&context.ctx, 0, Some("someone")).await?; info!(&context, "add_self={add_self}");
assert_eq!(contacts.len(), 1);
assert_eq!(contacts.first(), Some(&id)); // Search key-contacts by display name (same as manually set name).
let contacts = Contact::get_all(&context.ctx, add_self, Some("someone")).await?;
assert_eq!(contacts, vec![id]);
// Get all key-contacts.
let contacts = Contact::get_all(&context, add_self, None).await?;
match add_self {
0 => assert_eq!(contacts, vec![id]),
_ => assert_eq!(contacts, vec![id, ContactId::SELF]),
}
}
// Search address-contacts by display name.
let contacts = Contact::get_all(&context, constants::DC_GCL_ADDRESS, Some("someone")).await?;
assert_eq!(contacts, vec![claire_id]);
// Get all address-contacts. Newer contacts go first.
let contacts = Contact::get_all(&context, constants::DC_GCL_ADDRESS, None).await?;
assert_eq!(contacts, vec![dave_id, claire_id]);
let contacts = Contact::get_all(
&context,
constants::DC_GCL_ADDRESS | constants::DC_GCL_ADD_SELF,
None,
)
.await?;
assert_eq!(contacts, vec![dave_id, claire_id, ContactId::SELF]);
Ok(()) Ok(())
} }