diff --git a/apps/desktop/src/services/apple-calendar/fetch/incoming.ts b/apps/desktop/src/services/apple-calendar/fetch/incoming.ts index 658f19b4a3..8ba1b54e8d 100644 --- a/apps/desktop/src/services/apple-calendar/fetch/incoming.ts +++ b/apps/desktop/src/services/apple-calendar/fetch/incoming.ts @@ -112,8 +112,39 @@ function normalizeParticipant( ): EventParticipant { return { name: participant.name ?? undefined, - email: participant.email ?? undefined, + email: resolveParticipantEmail(participant), is_organizer: isOrganizer, is_current_user: participant.is_current_user, }; } + +function resolveParticipantEmail(participant: Participant): string | undefined { + if (participant.email) { + return participant.email; + } + + if (participant.contact?.email_addresses?.length) { + return participant.contact.email_addresses[0]; + } + + if (participant.url) { + const lower = participant.url.toLowerCase(); + if (lower.startsWith("mailto:")) { + const email = participant.url.slice(7); + if (email) { + return email; + } + } + } + + if ( + participant.name && + participant.name.includes("@") && + participant.name.includes(".") && + !participant.name.includes(" ") + ) { + return participant.name; + } + + return undefined; +} diff --git a/plugins/apple-calendar/src/apple/contacts.rs b/plugins/apple-calendar/src/apple/contacts.rs index a068d1a8e4..f0bb8b7317 100644 --- a/plugins/apple-calendar/src/apple/contacts.rs +++ b/plugins/apple-calendar/src/apple/contacts.rs @@ -8,13 +8,18 @@ use crate::types::{ParticipantContact, ParticipantScheduleStatus}; pub fn resolve_participant_contact( participant: &objc2_event_kit::EKParticipant, url: Option<&str>, + name: Option<&str>, ) -> (Option, Option) { if let Some(contact) = try_fetch_contact(participant) { let email = contact.email_addresses.first().cloned(); + if email.is_some() { + return (email, Some(contact)); + } + let email = parse_email_from_url(url).or_else(|| parse_email_from_name(name)); return (email, Some(contact)); } - let email = parse_email_from_url(url); + let email = parse_email_from_url(url).or_else(|| parse_email_from_name(name)); (email, None) } @@ -31,8 +36,20 @@ fn try_fetch_contact(participant: &objc2_event_kit::EKParticipant) -> Option) -> Option { let url = url?; - if url.starts_with("mailto:") { - Some(url.trim_start_matches("mailto:").to_string()) + let lower = url.to_lowercase(); + if lower.starts_with("mailto:") { + let email = url[7..].to_string(); + if !email.is_empty() { + return Some(email); + } + } + None +} + +fn parse_email_from_name(name: Option<&str>) -> Option { + let name = name?.trim(); + if name.contains('@') && name.contains('.') && !name.contains(' ') { + Some(name.to_string()) } else { None } diff --git a/plugins/apple-calendar/src/apple/transforms/participant.rs b/plugins/apple-calendar/src/apple/transforms/participant.rs index 3123c42ee6..e47d5b502e 100644 --- a/plugins/apple-calendar/src/apple/transforms/participant.rs +++ b/plugins/apple-calendar/src/apple/transforms/participant.rs @@ -23,7 +23,8 @@ pub fn transform_participant(participant: &EKParticipant) -> Participant { url_obj.and_then(|u| u.absoluteString().map(|s| s.to_string())) }; - let (email, contact) = contacts::resolve_participant_contact(participant, url.as_deref()); + let (email, contact) = + contacts::resolve_participant_contact(participant, url.as_deref(), name.as_deref()); Participant { name,