fix(jsonrpc): Use Core's logic for computing VcardContact.color (#7294)

Before, the color was computed from the address, but as we've switched to fingerprint-based contact
colors, this logic became stale. Now `deltachat::contact::get_color()` is used. A test would be nice
to have, but as now all the logic is in Core, this isn't critical as there are Core tests at least.
This commit is contained in:
iequidoo
2025-10-13 01:26:39 -03:00
committed by iequidoo
parent caf5f1f619
commit 377f57f1c3
4 changed files with 39 additions and 16 deletions

View File

@@ -1,6 +1,6 @@
use anyhow::Result;
use deltachat::color;
use deltachat::context::Context;
use deltachat::key::{DcKey, SignedPublicKey};
use serde::Serialize;
use typescript_type_def::TypeDef;
@@ -130,7 +130,13 @@ pub struct VcardContact {
impl From<deltachat_contact_tools::VcardContact> for VcardContact {
fn from(vc: deltachat_contact_tools::VcardContact) -> Self {
let display_name = vc.display_name().to_string();
let color = color::str_to_color(&vc.addr.to_lowercase());
let is_self = false;
let fpr = vc.key.as_deref().and_then(|k| {
SignedPublicKey::from_base64(k)
.ok()
.map(|k| k.dc_fingerprint())
});
let color = deltachat::contact::get_color(is_self, &vc.addr, &fpr);
Self {
addr: vc.addr,
display_name,

View File

@@ -36,7 +36,7 @@ use crate::message::MessageState;
use crate::mimeparser::AvatarAction;
use crate::param::{Param, Params};
use crate::sync::{self, Sync::*};
use crate::tools::{SystemTime, duration_to_str, get_abs_path, time};
use crate::tools::{SystemTime, duration_to_str, get_abs_path, time, to_lowercase};
use crate::{chat, chatlist_events, ensure_and_debug_assert_ne, stock_str};
/// Time during which a contact is considered as seen recently.
@@ -1574,19 +1574,10 @@ impl Contact {
Ok(None)
}
/// Get a color for the contact.
/// The color is calculated from the contact's fingerprint (for key-contacts)
/// or email address (for address-contacts) and can be used
/// for an fallback avatar with white initials
/// as well as for headlines in bubbles of group chats.
/// Returns a color for the contact.
/// See [`self::get_color`].
pub fn get_color(&self) -> u32 {
if let Some(fingerprint) = self.fingerprint() {
str_to_color(&fingerprint.hex())
} else if self.id == ContactId::SELF {
0x808080
} else {
str_to_color(&self.addr.to_lowercase())
}
get_color(self.id == ContactId::SELF, &self.addr, &self.fingerprint())
}
/// Gets the contact's status.
@@ -1682,6 +1673,21 @@ impl Contact {
}
}
/// Returns a color for a contact having given attributes.
///
/// The color is calculated from contact's fingerprint (for key-contacts) or email address (for
/// address-contacts; should be lowercased to avoid allocation) and can be used for an fallback
/// avatar with white initials as well as for headlines in bubbles of group chats.
pub fn get_color(is_self: bool, addr: &str, fingerprint: &Option<Fingerprint>) -> u32 {
if let Some(fingerprint) = fingerprint {
str_to_color(&fingerprint.hex())
} else if is_self {
0x808080
} else {
str_to_color(&to_lowercase(addr))
}
}
// Updates the names of the chats which use the contact name.
//
// This is one of the few duplicated data, however, getting the chat list is easier this way.

View File

@@ -25,7 +25,7 @@ use crate::tools::{self, time_elapsed};
/// This trait is implemented for rPGP's [SignedPublicKey] and
/// [SignedSecretKey] types and makes working with them a little
/// easier in the deltachat world.
pub(crate) trait DcKey: Serialize + Deserializable + Clone {
pub trait DcKey: Serialize + Deserializable + Clone {
/// Create a key from some bytes.
fn from_slice(bytes: &[u8]) -> Result<Self> {
let res = <Self as Deserializable>::from_bytes(Cursor::new(bytes));
@@ -112,7 +112,10 @@ pub(crate) trait DcKey: Serialize + Deserializable + Clone {
/// The fingerprint for the key.
fn dc_fingerprint(&self) -> Fingerprint;
/// Whether the key is private (or public).
fn is_private() -> bool;
/// Returns the OpenPGP Key ID.
fn key_id(&self) -> KeyId;
}

View File

@@ -753,6 +753,14 @@ pub(crate) fn buf_decompress(buf: &[u8]) -> Result<Vec<u8>> {
Ok(mem::take(decompressor.get_mut()))
}
/// Returns the given `&str` if already lowercased to avoid allocation, otherwise lowercases it.
pub(crate) fn to_lowercase(s: &str) -> Cow<'_, str> {
match s.chars().all(char::is_lowercase) {
true => Cow::Borrowed(s),
false => Cow::Owned(s.to_lowercase()),
}
}
/// Increments `*t` and checks that it equals to `expected` after that.
pub(crate) fn inc_and_check<T: PrimInt + AddAssign + std::fmt::Debug>(
t: &mut T,