Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion docs/api-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -510,6 +510,13 @@ interface Event {
availability: string;
/** External identifier for the event, useful for external sync services */
externalIdentifier: string | null;
/** Organizer info when available from EventKit */
organizer: {
name: string | null;
email: string | null;
} | null;
/** JSON-encoded attendee list with name/email/status/role/type/isCurrentUser */
attendeesJson: string;
}
```

Expand Down Expand Up @@ -674,4 +681,4 @@ if (success) {
} else {
console.log('Reminder not found or could not be removed');
}
```
```
9 changes: 8 additions & 1 deletion index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,13 @@ export interface Event {
availability: 'free' | 'busy' | 'tentative' | 'unavailable' | 'unknown';
/** External identifier for the event */
externalIdentifier: string | null;
/** Organizer info when available from EventKit */
organizer: {
name: string | null;
email: string | null;
} | null;
/** JSON-encoded attendee list with name/email/status/role/type/isCurrentUser */
attendeesJson: string;
}

/**
Expand Down Expand Up @@ -502,4 +509,4 @@ export function getCalendarItemsWithExternalIdentifier(externalIdentifier: strin
* @returns The current authorization status
* @see https://developer.apple.com/documentation/eventkit/ekauthorizationstatus
*/
export function getAuthorizationStatus(entityType: EntityType): AuthorizationStatus;
export function getAuthorizationStatus(entityType: EntityType): AuthorizationStatus;
9 changes: 8 additions & 1 deletion index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,13 @@ export interface Event {
availability: 'free' | 'busy' | 'tentative' | 'unavailable' | 'unknown';
/** External identifier for the event, useful for external sync services */
externalIdentifier: string | null;
/** Organizer info when available from EventKit */
organizer: {
name: string | null;
email: string | null;
} | null;
/** JSON-encoded attendee list with name/email/status/role/type/isCurrentUser */
attendeesJson: string;
}

/**
Expand Down Expand Up @@ -713,4 +720,4 @@ export function saveReminder(reminderData: ReminderData, commit: boolean = true)
*/
export function getAuthorizationStatus(entityType: EntityType): AuthorizationStatus {
return nativeModule.getAuthorizationStatus(entityType);
}
}
70 changes: 69 additions & 1 deletion node-addon/EventKitBridge.swift
Original file line number Diff line number Diff line change
Expand Up @@ -383,6 +383,9 @@ import Foundation
@objc public let hasAlarms: Bool
@objc public let availability: String
@objc public let externalIdentifier: String?
@objc public let organizerName: String?
@objc public let organizerEmail: String?
@objc public let attendeesJson: String

init(from ekEvent: EKEvent) {
self.id = ekEvent.eventIdentifier
Expand All @@ -397,6 +400,71 @@ import Foundation
self.url = ekEvent.url?.absoluteString
self.hasAlarms = ekEvent.hasAlarms
self.externalIdentifier = ekEvent.calendarItemExternalIdentifier
if let organizer = ekEvent.organizer {
self.organizerName = organizer.name
let raw = organizer.url.absoluteString
if raw.lowercased().hasPrefix("mailto:") {
self.organizerEmail = String(raw.dropFirst("mailto:".count))
} else {
self.organizerEmail = nil
}
} else {
self.organizerName = nil
self.organizerEmail = nil
}

let attendeesArray: [[String: Any]] = (ekEvent.attendees ?? []).map { attendee in
let raw = attendee.url.absoluteString
let email: String? = raw.lowercased().hasPrefix("mailto:")
? String(raw.dropFirst("mailto:".count))
: nil

let status: String
switch attendee.participantStatus {
case .accepted: status = "accepted"
case .declined: status = "declined"
case .tentative: status = "tentative"
case .pending: status = "pending"
case .delegated: status = "delegated"
case .completed: status = "completed"
case .inProcess: status = "inProcess"
default: status = "unknown"
}

let role: String
switch attendee.participantRole {
case .required: role = "required"
case .optional: role = "optional"
case .chair: role = "chair"
case .nonParticipant: role = "nonParticipant"
default: role = "unknown"
}

let type: String
switch attendee.participantType {
case .person: type = "person"
case .room: type = "room"
case .resource: type = "resource"
case .group: type = "group"
default: type = "unknown"
}

return [
"name": attendee.name ?? "",
"email": email ?? "",
"url": raw,
"status": status,
"role": role,
"type": type,
"isCurrentUser": attendee.isCurrentUser
]
}
if let data = try? JSONSerialization.data(withJSONObject: attendeesArray, options: []),
let json = String(data: data, encoding: .utf8) {
self.attendeesJson = json
} else {
self.attendeesJson = "[]"
}

// Convert availability to string
switch ekEvent.availability {
Expand Down Expand Up @@ -850,4 +918,4 @@ import Foundation
return ["success": false, "error": error.localizedDescription]
}
}
}
}
18 changes: 17 additions & 1 deletion node-addon/eventkit_binding.mm
Original file line number Diff line number Diff line change
Expand Up @@ -709,7 +709,6 @@ void SetResult(bool success, const std::string& calendarIdOrError) {
return promise;
}

// Helper to convert Event object to JS object
Napi::Object EventToJSObject(const Napi::CallbackInfo& info, Event *event) {
Napi::Env env = info.Env();
Napi::Object jsObject = Napi::Object::New(env);
Expand Down Expand Up @@ -753,6 +752,23 @@ void SetResult(bool success, const std::string& calendarIdOrError) {
} else {
jsObject.Set("externalIdentifier", env.Null());
}

if (event.organizerName || event.organizerEmail) {
Napi::Object organizer = Napi::Object::New(env);
if (event.organizerName) organizer.Set("name", Napi::String::New(env, [event.organizerName UTF8String]));
else organizer.Set("name", env.Null());
if (event.organizerEmail) organizer.Set("email", Napi::String::New(env, [event.organizerEmail UTF8String]));
else organizer.Set("email", env.Null());
jsObject.Set("organizer", organizer);
} else {
jsObject.Set("organizer", env.Null());
}

if (event.attendeesJson) {
jsObject.Set("attendeesJson", Napi::String::New(env, [event.attendeesJson UTF8String]));
} else {
jsObject.Set("attendeesJson", Napi::String::New(env, "[]"));
}

return jsObject;
}
Expand Down