feat: Add Contact::get_or_gen_color. Use it in CFFI and JSON-RPC to avoid gray self-color (#7374)

`Contact::get_color()` returns gray if own keypair doesn't exist yet and we don't want any UIs
displaying it. Keypair generation can't be done in `get_color()` or `get_by_id_optional()` to avoid
breaking Core tests on key import. Also this makes the API clearer, pure getters shouldn't modify
any visible state.
This commit is contained in:
iequidoo
2025-11-07 22:21:10 -03:00
committed by iequidoo
parent e70307af1f
commit c499dabbe1
4 changed files with 39 additions and 9 deletions

View File

@@ -4240,7 +4240,17 @@ pub unsafe extern "C" fn dc_contact_get_color(contact: *mut dc_contact_t) -> u32
return 0; return 0;
} }
let ffi_contact = &*contact; let ffi_contact = &*contact;
ffi_contact.contact.get_color() let ctx = &*ffi_contact.context;
block_on(async move {
ffi_contact
.contact
// We don't want any UIs displaying gray self-color.
.get_or_gen_color(ctx)
.await
.context("Contact::get_color()")
.log_err(ctx)
.unwrap_or(0)
})
} }
#[no_mangle] #[no_mangle]

View File

@@ -32,7 +32,10 @@ impl Account {
let addr = ctx.get_config(Config::Addr).await?; let addr = ctx.get_config(Config::Addr).await?;
let profile_image = ctx.get_config(Config::Selfavatar).await?; let profile_image = ctx.get_config(Config::Selfavatar).await?;
let color = color_int_to_hex_string( let color = color_int_to_hex_string(
Contact::get_by_id(ctx, ContactId::SELF).await?.get_color(), Contact::get_by_id(ctx, ContactId::SELF)
.await?
.get_or_gen_color(ctx)
.await?,
); );
let private_tag = ctx.get_config(Config::PrivateTag).await?; let private_tag = ctx.get_config(Config::PrivateTag).await?;
Ok(Account::Configured { Ok(Account::Configured {

View File

@@ -1573,11 +1573,24 @@ impl Contact {
} }
/// Returns a color for the contact. /// Returns a color for the contact.
/// See [`self::get_color`]. /// For self-contact this returns gray if own keypair doesn't exist yet.
/// See also [`self::get_color`].
pub fn get_color(&self) -> u32 { pub fn get_color(&self) -> u32 {
get_color(self.id == ContactId::SELF, &self.addr, &self.fingerprint()) get_color(self.id == ContactId::SELF, &self.addr, &self.fingerprint())
} }
/// Returns a color for the contact.
/// Ensures that the color isn't gray. For self-contact this generates own keypair if it doesn't
/// exist yet.
/// See also [`self::get_color`].
pub async fn get_or_gen_color(&self, context: &Context) -> Result<u32> {
let mut fpr = self.fingerprint();
if fpr.is_none() && self.id == ContactId::SELF {
fpr = Some(load_self_public_key(context).await?.dc_fingerprint());
}
Ok(get_color(self.id == ContactId::SELF, &self.addr, &fpr))
}
/// Gets the contact's status. /// Gets the contact's status.
/// ///
/// Status is the last signature received in a message from this contact. /// Status is the last signature received in a message from this contact.

View File

@@ -4,7 +4,6 @@ use super::*;
use crate::chat::{Chat, get_chat_contacts, send_text_msg}; use crate::chat::{Chat, get_chat_contacts, send_text_msg};
use crate::chatlist::Chatlist; use crate::chatlist::Chatlist;
use crate::receive_imf::receive_imf; use crate::receive_imf::receive_imf;
use crate::securejoin::get_securejoin_qr;
use crate::test_utils::{self, TestContext, TestContextManager, TimeShiftFalsePositiveNote}; use crate::test_utils::{self, TestContext, TestContextManager, TimeShiftFalsePositiveNote};
#[test] #[test]
@@ -775,16 +774,21 @@ async fn test_contact_get_color() -> Result<()> {
} }
#[tokio::test(flavor = "multi_thread", worker_threads = 2)] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_self_color_vs_key() -> Result<()> { async fn test_self_color() -> Result<()> {
let mut tcm = TestContextManager::new(); let mut tcm = TestContextManager::new();
let t = &tcm.unconfigured().await; let t = &tcm.unconfigured().await;
t.configure_addr("alice@example.org").await; t.configure_addr("alice@example.org").await;
assert!(t.is_configured().await?); assert!(t.is_configured().await?);
let color = Contact::get_by_id(t, ContactId::SELF).await?.get_color(); let self_contact = Contact::get_by_id(t, ContactId::SELF).await?;
let color = self_contact.get_color();
assert_eq!(color, 0x808080); assert_eq!(color, 0x808080);
get_securejoin_qr(t, None).await?; let color = self_contact.get_or_gen_color(t).await?;
let color1 = Contact::get_by_id(t, ContactId::SELF).await?.get_color(); assert_ne!(color, 0x808080);
assert_ne!(color1, color); let color1 = self_contact.get_or_gen_color(t).await?;
assert_eq!(color1, color);
let bob = &tcm.bob().await;
assert_eq!(bob.add_or_lookup_contact(t).await.get_color(), color);
Ok(()) Ok(())
} }