Skip to content

Commit

Permalink
feat: Display vCard contact name in the message summary
Browse files Browse the repository at this point in the history
  • Loading branch information
iequidoo committed May 26, 2024
1 parent d29538b commit 0ce4037
Show file tree
Hide file tree
Showing 8 changed files with 71 additions and 34 deletions.
3 changes: 0 additions & 3 deletions deltachat-ffi/deltachat.h
Original file line number Diff line number Diff line change
Expand Up @@ -7363,9 +7363,6 @@ void dc_event_unref(dc_event_t* event);
/// Used as info message.
#define DC_STR_SECUREJOIN_WAIT_TIMEOUT 191

/// "Contact"
#define DC_STR_CONTACT 200

/**
* @}
*/
Expand Down
3 changes: 3 additions & 0 deletions src/chat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -887,6 +887,7 @@ impl ChatId {
bail!("No text and no quote in draft");
}
}
Viewtype::Vcard => msg.try_set_vcard(context).await?,
_ => {
let blob = msg
.param
Expand Down Expand Up @@ -2643,6 +2644,8 @@ async fn prepare_msg_blob(context: &Context, msg: &mut Message) -> Result<()> {
context
.ensure_sendable_webxdc_file(&blob.to_abs_path())
.await?;
} else if msg.viewtype == Viewtype::Vcard {
msg.try_set_vcard(context).await?;
}

let mut maybe_sticker = msg.viewtype == Viewtype::Sticker;
Expand Down
24 changes: 22 additions & 2 deletions src/message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ use crate::ephemeral::{start_ephemeral_timers_msgids, Timer as EphemeralTimer};
use crate::events::EventType;
use crate::imap::markseen_on_imap_table;
use crate::location::delete_poi_location;
use crate::mimeparser::{parse_message_id, SystemMessage};
use crate::mimeparser::{get_vcard_summary, parse_message_id, SystemMessage};
use crate::param::{Param, Params};
use crate::pgp::split_armored_data;
use crate::reaction::get_msg_reactions;
Expand Down Expand Up @@ -1057,7 +1057,9 @@ impl Message {
/// the file will only be used when the message is prepared
/// for sending.
pub fn set_file(&mut self, file: impl ToString, filemime: Option<&str>) {
if let Some(name) = Path::new(&file.to_string()).file_name() {
let file = file.to_string();
let path = Path::new(&file);
if let Some(name) = path.file_name() {
if let Some(name) = name.to_str() {
self.param.set(Param::Filename, name);
}
Expand All @@ -1081,6 +1083,24 @@ impl Message {
Ok(())
}

/// Updates message state from the vCard attachment.
pub(crate) async fn try_set_vcard(&mut self, context: &Context) -> Result<()> {
let blob = self
.param
.get_blob(Param::File, context, !self.is_increation())
.await?
.context("Attachment missing")?;
let path = blob.to_abs_path();
let vcard = fs::read(path).await.context("Could not read {path}")?;
if let Some(summary) = get_vcard_summary(&vcard) {
self.param.set(Param::Summary1, summary);
} else {
warn!(context, "try_set_vcard: Not a valid DeltaChat vCard.");
self.viewtype = Viewtype::File;
}
Ok(())
}

/// Set different sender name for a message.
/// This overrides the name set by the `set_config()`-option `displayname`.
pub fn set_override_sender_name(&mut self, name: Option<String>) {
Expand Down
37 changes: 24 additions & 13 deletions src/mimeparser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1233,6 +1233,7 @@ impl MimeMessage {
return Ok(());
}
}
let mut param = Params::new();
let msg_type = if context
.is_webxdc_file(filename, decoded_data)
.await
Expand Down Expand Up @@ -1276,6 +1277,13 @@ impl MimeMessage {
.unwrap_or_default();
self.webxdc_status_update = Some(serialized);
return Ok(());
} else if msg_type == Viewtype::Vcard {
if let Some(summary) = get_vcard_summary(decoded_data) {
param.set(Param::Summary1, summary);
msg_type
} else {
Viewtype::File
}
} else {
msg_type
};
Expand All @@ -1299,18 +1307,19 @@ impl MimeMessage {
let mut part = Part::default();
if mime_type.type_() == mime::IMAGE {
if let Ok((width, height)) = get_filemeta(decoded_data) {
part.param.set_int(Param::Width, width as i32);
part.param.set_int(Param::Height, height as i32);
param.set_int(Param::Width, width as i32);
param.set_int(Param::Height, height as i32);
}
}

part.typ = msg_type;
part.org_filename = Some(filename.to_string());
part.mimetype = Some(mime_type);
part.bytes = decoded_data.len();
part.param.set(Param::File, blob.as_name());
part.param.set(Param::Filename, filename);
part.param.set(Param::MimeType, raw_mime);
param.set(Param::File, blob.as_name());
param.set(Param::Filename, filename);
param.set(Param::MimeType, raw_mime);
part.param = param;
part.is_related = is_related;

self.do_add_single_part(part);
Expand Down Expand Up @@ -1937,7 +1946,7 @@ fn get_mime_type(

let viewtype = match mimetype.type_() {
mime::TEXT => match mimetype.subtype() {
mime::VCARD if is_valid_deltachat_vcard(mail) => Viewtype::Vcard,
mime::VCARD => Viewtype::Vcard,
mime::PLAIN | mime::HTML if !is_attachment_disposition(mail) => Viewtype::Text,
_ => Viewtype::File,
},
Expand Down Expand Up @@ -1988,15 +1997,17 @@ fn is_attachment_disposition(mail: &mailparse::ParsedMail<'_>) -> bool {
.any(|(key, _value)| key.starts_with("filename"))
}

fn is_valid_deltachat_vcard(mail: &mailparse::ParsedMail) -> bool {
let Ok(body) = &mail.get_body() else {
return false;
/// Returns the 1st part of summary text (i.e. before the dash if any) for a valid DeltaChat vCard.
pub(crate) fn get_vcard_summary(vcard: &[u8]) -> Option<String> {
let vcard = str::from_utf8(vcard).ok()?;
let contacts = deltachat_contact_tools::parse_vcard(vcard);
let [c] = &contacts[..] else {
return None;
};
let contacts = deltachat_contact_tools::parse_vcard(body);
if let [c] = &contacts[..] {
return deltachat_contact_tools::may_be_valid_addr(&c.addr);
if !deltachat_contact_tools::may_be_valid_addr(&c.addr) {
return None;
}
false
Some(c.display_name().to_string())
}

/// Tries to get attachment filename.
Expand Down
3 changes: 3 additions & 0 deletions src/param.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,9 @@ pub enum Param {
/// For Messages: quoted text.
Quote = b'q',

/// For Messages: the 1st part of summary text (i.e. before the dash if any).
Summary1 = b'4',

/// For Messages
Cmd = b'S',

Expand Down
1 change: 1 addition & 0 deletions src/receive_imf/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4551,6 +4551,7 @@ async fn test_receive_vcard() -> Result<()> {

if vcard_contains_address {
assert_eq!(rcvd.viewtype, Viewtype::Vcard);
assert_eq!(rcvd.get_summary_text(&bob).await, "👤 Claire");
} else {
// VCards without an email address are not "deltachat contacts",
// so they are shown as files
Expand Down
8 changes: 0 additions & 8 deletions src/stock_str.rs
Original file line number Diff line number Diff line change
Expand Up @@ -443,9 +443,6 @@ pub enum StockMessage {
fallback = "Could not yet establish guaranteed end-to-end encryption, but you may already send a message."
))]
SecurejoinWaitTimeout = 191,

#[strum(props(fallback = "Contact"))]
Contact = 200,
}

impl StockMessage {
Expand Down Expand Up @@ -1101,11 +1098,6 @@ pub(crate) async fn videochat_invite_msg_body(context: &Context, url: &str) -> S
.replace1(url)
}

/// Stock string: `Contact`.
pub(crate) async fn contact(context: &Context) -> String {
translated(context, StockMessage::Contact).await
}

/// Stock string: `Error:\n\n“%1$s”`.
pub(crate) async fn configuration_failed(context: &Context, details: &str) -> String {
translated(context, StockMessage::ConfigurationFailed)
Expand Down
26 changes: 18 additions & 8 deletions src/summary.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use crate::contact::{Contact, ContactId};
use crate::context::Context;
use crate::message::{Message, MessageState, Viewtype};
use crate::mimeparser::SystemMessage;
use crate::param::Param;
use crate::stock_str;
use crate::stock_str::msg_reacted;
use crate::tools::truncate;
Expand Down Expand Up @@ -149,7 +150,7 @@ impl Summary {

impl Message {
/// Returns a summary text.
async fn get_summary_text(&self, context: &Context) -> String {
pub(crate) async fn get_summary_text(&self, context: &Context) -> String {
let summary = self.get_summary_text_without_prefix(context).await;

if self.is_forwarded() {
Expand Down Expand Up @@ -231,8 +232,8 @@ impl Message {
}
Viewtype::Vcard => {
emoji = Some("👤");
type_name = Some(stock_str::contact(context).await);
type_file = None;
type_name = None;
type_file = self.param.get(Param::Summary1).map(|s| s.to_string());
append_text = true;
}
Viewtype::Text | Viewtype::Unknown => {
Expand Down Expand Up @@ -284,6 +285,7 @@ impl Message {
#[cfg(test)]
mod tests {
use super::*;
use crate::chat::ChatId;
use crate::param::Param;
use crate::test_utils as test;

Expand All @@ -296,7 +298,9 @@ mod tests {
async fn test_get_summary_text() {
let d = test::TestContext::new().await;
let ctx = &d.ctx;

let chat_id = ChatId::create_for_contact(ctx, ContactId::SELF)
.await
.unwrap();
let some_text = " bla \t\n\tbla\n\t".to_string();

let mut msg = Message::new(Viewtype::Text);
Expand Down Expand Up @@ -367,10 +371,15 @@ mod tests {
assert_summary_texts(&msg, ctx, "Video chat invitation").await; // text is not added for videochat invitations

let mut msg = Message::new(Viewtype::Vcard);
msg.set_file("foo.vcf", None);
assert_summary_texts(&msg, ctx, "👤 Contact").await;
msg.set_file_from_bytes(ctx, "foo.vcf", b"", None)
.await
.unwrap();
chat_id.set_draft(ctx, Some(&mut msg)).await.unwrap();
// If a vCard can't be parsed, the message becomes `Viewtype::File`.
assert_eq!(msg.viewtype, Viewtype::File);
assert_summary_texts(&msg, ctx, "📎 foo.vcf").await;
msg.set_text(some_text.clone());
assert_summary_texts(&msg, ctx, "👤 bla bla").await;
assert_summary_texts(&msg, ctx, "📎 foo.vcf \u{2013} bla bla").await;

let mut msg = Message::new(Viewtype::Vcard);
msg.set_file_from_bytes(
Expand All @@ -385,7 +394,8 @@ mod tests {
)
.await
.unwrap();
assert_summary_texts(&msg, ctx, "👤 Contact").await;
chat_id.set_draft(ctx, Some(&mut msg)).await.unwrap();
assert_summary_texts(&msg, ctx, "👤 Alice Wonderland").await;

// Forwarded
let mut msg = Message::new(Viewtype::Text);
Expand Down

0 comments on commit 0ce4037

Please sign in to comment.