diff --git a/Cargo.lock b/Cargo.lock index 1950c2217..77fbd65f9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1257,6 +1257,7 @@ dependencies = [ "axum", "base64 0.22.1", "deltachat", + "deltachat-contact-tools", "env_logger 0.11.3", "futures", "log", diff --git a/deltachat-contact-tools/src/lib.rs b/deltachat-contact-tools/src/lib.rs index 325ec748e..3f6bf0294 100644 --- a/deltachat-contact-tools/src/lib.rs +++ b/deltachat-contact-tools/src/lib.rs @@ -44,7 +44,8 @@ use regex::Regex; pub struct VcardContact { /// The email address, vcard property `email` pub addr: String, - /// The contact's display name, vcard property `fn` + /// The contact's display name, vcard property `fn`. Can be empty, one should use + /// `display_name()` to obtain the actual value. pub display_name: String, /// The contact's public PGP key in Base64, vcard property `key` pub key: Option, @@ -54,6 +55,16 @@ pub struct VcardContact { pub timestamp: Result, } +impl VcardContact { + /// Returns the contact's display name. + pub fn display_name(&self) -> &str { + match self.display_name.is_empty() { + false => &self.display_name, + true => &self.addr, + } + } +} + /// Returns a vCard containing given contacts. /// /// Calling [`parse_vcard()`] on the returned result is a reverse operation. @@ -68,10 +79,7 @@ pub fn make_vcard(contacts: &[VcardContact]) -> String { let mut res = "".to_string(); for c in contacts { let addr = &c.addr; - let display_name = match c.display_name.is_empty() { - false => &c.display_name, - true => &c.addr, - }; + let display_name = c.display_name(); res += &format!( "BEGIN:VCARD\n\ VERSION:4.0\n\ diff --git a/deltachat-jsonrpc/Cargo.toml b/deltachat-jsonrpc/Cargo.toml index 23c1d7129..6d8e7b7d1 100644 --- a/deltachat-jsonrpc/Cargo.toml +++ b/deltachat-jsonrpc/Cargo.toml @@ -15,6 +15,7 @@ required-features = ["webserver"] [dependencies] anyhow = "1" deltachat = { path = ".." } +deltachat-contact-tools = { path = "../deltachat-contact-tools" } num-traits = "0.2" schemars = "0.8.19" serde = { version = "1.0", features = ["derive"] } diff --git a/deltachat-jsonrpc/src/api.rs b/deltachat-jsonrpc/src/api.rs index c0ebfb52d..d022a6235 100644 --- a/deltachat-jsonrpc/src/api.rs +++ b/deltachat-jsonrpc/src/api.rs @@ -1,5 +1,6 @@ use std::collections::BTreeMap; use std::path::Path; +use std::str; use std::sync::Arc; use std::time::Duration; use std::{collections::HashMap, str::FromStr}; @@ -42,7 +43,7 @@ pub mod types; use num_traits::FromPrimitive; use types::account::Account; use types::chat::FullChat; -use types::contact::ContactObject; +use types::contact::{ContactObject, VcardContact}; use types::events::Event; use types::http::HttpResponse; use types::message::{MessageData, MessageObject, MessageReadReceipt}; @@ -1426,6 +1427,16 @@ impl CommandApi { Ok(contact_id.map(|id| id.to_u32())) } + /// Parses a vCard file located at the given path. Returns contacts in their original order. + async fn parse_vcard(&self, path: String) -> Result> { + let vcard = tokio::fs::read(Path::new(&path)).await?; + let vcard = str::from_utf8(&vcard)?; + Ok(deltachat_contact_tools::parse_vcard(vcard)? + .into_iter() + .map(|c| c.into()) + .collect()) + } + // --------------------------------------------- // chat // --------------------------------------------- diff --git a/deltachat-jsonrpc/src/api/types/contact.rs b/deltachat-jsonrpc/src/api/types/contact.rs index f6d9096bd..c3f25c038 100644 --- a/deltachat-jsonrpc/src/api/types/contact.rs +++ b/deltachat-jsonrpc/src/api/types/contact.rs @@ -1,4 +1,5 @@ use anyhow::Result; +use deltachat::color; use deltachat::context::Context; use serde::Serialize; use typescript_type_def::TypeDef; @@ -87,3 +88,34 @@ impl ContactObject { }) } } + +#[derive(Serialize, TypeDef, schemars::JsonSchema)] +#[serde(rename_all = "camelCase")] +pub struct VcardContact { + /// Email address. + addr: String, + display_name: String, + /// Public PGP key in Base64. + key: Option, + /// Profile image in Base64. + profile_image: Option, + /// Contact color as hex string. + color: String, + /// Last update timestamp. + timestamp: Option, +} + +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()); + Self { + addr: vc.addr, + display_name, + key: vc.key, + profile_image: vc.profile_image, + color: color_int_to_hex_string(color), + timestamp: vc.timestamp.ok(), + } + } +} diff --git a/src/color.rs b/src/color.rs index 495cf5401..146970b0f 100644 --- a/src/color.rs +++ b/src/color.rs @@ -31,10 +31,11 @@ fn rgb_to_u32((r, g, b): (f64, f64, f64)) -> u32 { /// /// Saturation is set to maximum (100.0) to make colors distinguishable, and lightness is set to /// half (50.0) to make colors suitable both for light and dark theme. -pub(crate) fn str_to_color(s: &str) -> u32 { +pub fn str_to_color(s: &str) -> u32 { rgb_to_u32(hsluv_to_rgb((str_to_angle(s), 100.0, 50.0))) } +/// Returns color as a "#RRGGBB" `String` where R, G, B are hex digits. pub fn color_int_to_hex_string(color: u32) -> String { format!("{color:#08x}").replace("0x", "#") } diff --git a/src/lib.rs b/src/lib.rs index ed3f6860d..55d56c27f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -94,7 +94,7 @@ pub mod webxdc; #[macro_use] mod dehtml; mod authres; -mod color; +pub mod color; pub mod html; pub mod net; pub mod plaintext;