From e3ddcb4e50bc4464b87d52f6a31f1e36aa2b1f70 Mon Sep 17 00:00:00 2001 From: iequidoo Date: Fri, 7 Nov 2025 22:21:10 -0300 Subject: [PATCH] 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. --- deltachat-ffi/src/lib.rs | 12 +++++++++++- deltachat-jsonrpc/src/api/types/account.rs | 5 ++++- src/contact.rs | 15 ++++++++++++++- src/contact/contact_tests.rs | 16 ++++++++++------ 4 files changed, 39 insertions(+), 9 deletions(-) diff --git a/deltachat-ffi/src/lib.rs b/deltachat-ffi/src/lib.rs index c99e1c574b..c6eae57a7c 100644 --- a/deltachat-ffi/src/lib.rs +++ b/deltachat-ffi/src/lib.rs @@ -4240,7 +4240,17 @@ pub unsafe extern "C" fn dc_contact_get_color(contact: *mut dc_contact_t) -> u32 return 0; } 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] diff --git a/deltachat-jsonrpc/src/api/types/account.rs b/deltachat-jsonrpc/src/api/types/account.rs index a5f14240ca..eb5931a325 100644 --- a/deltachat-jsonrpc/src/api/types/account.rs +++ b/deltachat-jsonrpc/src/api/types/account.rs @@ -32,7 +32,10 @@ impl Account { let addr = ctx.get_config(Config::Addr).await?; let profile_image = ctx.get_config(Config::Selfavatar).await?; 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?; Ok(Account::Configured { diff --git a/src/contact.rs b/src/contact.rs index f15790260e..17285d4fe4 100644 --- a/src/contact.rs +++ b/src/contact.rs @@ -1574,11 +1574,24 @@ impl 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 { 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 { + 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. /// /// Status is the last signature received in a message from this contact. diff --git a/src/contact/contact_tests.rs b/src/contact/contact_tests.rs index b2f78630c9..9969fe608c 100644 --- a/src/contact/contact_tests.rs +++ b/src/contact/contact_tests.rs @@ -4,7 +4,6 @@ use super::*; use crate::chat::{Chat, get_chat_contacts, send_text_msg}; use crate::chatlist::Chatlist; use crate::receive_imf::receive_imf; -use crate::securejoin::get_securejoin_qr; use crate::test_utils::{self, TestContext, TestContextManager, TimeShiftFalsePositiveNote}; #[test] @@ -775,16 +774,21 @@ async fn test_contact_get_color() -> Result<()> { } #[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 t = &tcm.unconfigured().await; t.configure_addr("alice@example.org").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); - get_securejoin_qr(t, None).await?; - let color1 = Contact::get_by_id(t, ContactId::SELF).await?.get_color(); - assert_ne!(color1, color); + let color = self_contact.get_or_gen_color(t).await?; + assert_ne!(color, 0x808080); + 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(()) }