diff --git a/deltachat-jsonrpc/src/api/types/contact.rs b/deltachat-jsonrpc/src/api/types/contact.rs index 9a98a75f9..bafdfb5cc 100644 --- a/deltachat-jsonrpc/src/api/types/contact.rs +++ b/deltachat-jsonrpc/src/api/types/contact.rs @@ -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 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, diff --git a/src/contact.rs b/src/contact.rs index ad556bc1f..bf1bbe517 100644 --- a/src/contact.rs +++ b/src/contact.rs @@ -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) -> 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. diff --git a/src/key.rs b/src/key.rs index 06f25b8eb..cde859d00 100644 --- a/src/key.rs +++ b/src/key.rs @@ -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 { let res = ::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; } diff --git a/src/tools.rs b/src/tools.rs index 59cca8d15..e7dc108cf 100644 --- a/src/tools.rs +++ b/src/tools.rs @@ -753,6 +753,14 @@ pub(crate) fn buf_decompress(buf: &[u8]) -> Result> { 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: &mut T,