From cf5b5bcd54244c8bf7bda39515a36c04e7221192 Mon Sep 17 00:00:00 2001 From: Joel Becker Date: Fri, 27 Sep 2024 11:07:30 +0200 Subject: [PATCH 1/8] Added support for recurring events + Added new API method to delete recurring or normal events with specified span (needs to be tagged with @since in JsDoc) Info: The new API endpoint needs to be added to the example app --- README.md | 65 ++--- .../capacitor/calendar/CapacitorCalendar.kt | 176 ++++++------ .../calendar/CapacitorCalendarPlugin.kt | 153 +++++++---- .../capacitor/calendar/model/CalendarEvent.kt | 79 ++++++ .../capacitor/calendar/model/EventReminder.kt | 36 +++ .../calendar/model/RecurrenceFrequency.kt | 14 + .../calendar/model/RecurrenceRule.kt | 55 ++++ docs/api-reference.md | 252 +++++++++++------- example/package-lock.json | 216 ++++++++++----- example/package.json | 4 +- ios/Plugin/CapacitorCalendar.swift | 84 +++++- ios/Plugin/CapacitorCalendarPlugin.m | 1 + ios/Plugin/CapacitorCalendarPlugin.swift | 112 +++++++- ios/Plugin/EventCreationParameters.swift | 1 + ios/Plugin/RecurrenceParameters.swift | 2 +- package-lock.json | 202 ++++++++++---- package.json | 5 +- src/definitions.ts | 39 ++- src/index.ts | 8 +- ...e-frequency.ts => recurrence-frequency.ts} | 4 +- ...-recurrence-rule.ts => recurrence-rule.ts} | 6 +- src/schemas/interfaces/reminder.ts | 6 +- src/web.ts | 12 +- 23 files changed, 1104 insertions(+), 428 deletions(-) create mode 100644 android/src/main/java/dev/barooni/capacitor/calendar/model/CalendarEvent.kt create mode 100644 android/src/main/java/dev/barooni/capacitor/calendar/model/EventReminder.kt create mode 100644 android/src/main/java/dev/barooni/capacitor/calendar/model/RecurrenceFrequency.kt create mode 100644 android/src/main/java/dev/barooni/capacitor/calendar/model/RecurrenceRule.kt rename src/schemas/enums/{reminder-recurrence-frequency.ts => recurrence-frequency.ts} (81%) rename src/schemas/interfaces/{reminder-recurrence-rule.ts => recurrence-rule.ts} (84%) diff --git a/README.md b/README.md index 3915206..1848252 100644 --- a/README.md +++ b/README.md @@ -117,38 +117,39 @@ usage descriptions can be found below: -- [`checkPermission(...)`](#checkpermission) -- [`checkAllPermissions()`](#checkallpermissions) -- [`requestPermission(...)`](#requestpermission) -- [`requestAllPermissions()`](#requestallpermissions) -- [`createEventWithPrompt(...)`](#createeventwithprompt) -- [`selectCalendarsWithPrompt(...)`](#selectcalendarswithprompt) -- [`listCalendars()`](#listcalendars) -- [`getDefaultCalendar()`](#getdefaultcalendar) -- [`createEvent(...)`](#createevent) -- [`getDefaultRemindersList()`](#getdefaultreminderslist) -- [`getRemindersLists()`](#getreminderslists) -- [`createReminder(...)`](#createreminder) -- [`openCalendar(...)`](#opencalendar) -- [`openReminders()`](#openreminders) -- [`listEventsInRange(...)`](#listeventsinrange) -- [`deleteEventsById(...)`](#deleteeventsbyid) -- [`createCalendar(...)`](#createcalendar) -- [`deleteCalendar(...)`](#deletecalendar) -- [`getRemindersFromLists(...)`](#getremindersfromlists) -- [`deleteRemindersById(...)`](#deleteremindersbyid) -- [`requestWriteOnlyCalendarAccess()`](#requestwriteonlycalendaraccess) -- [`requestReadOnlyCalendarAccess()`](#requestreadonlycalendaraccess) -- [`requestFullCalendarAccess()`](#requestfullcalendaraccess) -- [`requestFullRemindersAccess()`](#requestfullremindersaccess) -- [`modifyEventWithPrompt(...)`](#modifyeventwithprompt) -- [`modifyEvent(...)`](#modifyevent) -- [`fetchAllCalendarSources()`](#fetchallcalendarsources) -- [`fetchAllRemindersSources()`](#fetchallreminderssources) -- [`modifyReminder(...)`](#modifyreminder) -- [Interfaces](#interfaces) -- [Type Aliases](#type-aliases) -- [Enums](#enums) +* [`checkPermission(...)`](#checkpermission) +* [`checkAllPermissions()`](#checkallpermissions) +* [`requestPermission(...)`](#requestpermission) +* [`requestAllPermissions()`](#requestallpermissions) +* [`createEventWithPrompt(...)`](#createeventwithprompt) +* [`selectCalendarsWithPrompt(...)`](#selectcalendarswithprompt) +* [`listCalendars()`](#listcalendars) +* [`getDefaultCalendar()`](#getdefaultcalendar) +* [`createEvent(...)`](#createevent) +* [`getDefaultRemindersList()`](#getdefaultreminderslist) +* [`getRemindersLists()`](#getreminderslists) +* [`createReminder(...)`](#createreminder) +* [`openCalendar(...)`](#opencalendar) +* [`openReminders()`](#openreminders) +* [`listEventsInRange(...)`](#listeventsinrange) +* [`deleteEventsById(...)`](#deleteeventsbyid) +* [`deleteEventById(...)`](#deleteeventbyid) +* [`createCalendar(...)`](#createcalendar) +* [`deleteCalendar(...)`](#deletecalendar) +* [`getRemindersFromLists(...)`](#getremindersfromlists) +* [`deleteRemindersById(...)`](#deleteremindersbyid) +* [`requestWriteOnlyCalendarAccess()`](#requestwriteonlycalendaraccess) +* [`requestReadOnlyCalendarAccess()`](#requestreadonlycalendaraccess) +* [`requestFullCalendarAccess()`](#requestfullcalendaraccess) +* [`requestFullRemindersAccess()`](#requestfullremindersaccess) +* [`modifyEventWithPrompt(...)`](#modifyeventwithprompt) +* [`modifyEvent(...)`](#modifyevent) +* [`fetchAllCalendarSources()`](#fetchallcalendarsources) +* [`fetchAllRemindersSources()`](#fetchallreminderssources) +* [`modifyReminder(...)`](#modifyreminder) +* [Interfaces](#interfaces) +* [Type Aliases](#type-aliases) +* [Enums](#enums) diff --git a/android/src/main/java/dev/barooni/capacitor/calendar/CapacitorCalendar.kt b/android/src/main/java/dev/barooni/capacitor/calendar/CapacitorCalendar.kt index 253b152..0a1bc43 100644 --- a/android/src/main/java/dev/barooni/capacitor/calendar/CapacitorCalendar.kt +++ b/android/src/main/java/dev/barooni/capacitor/calendar/CapacitorCalendar.kt @@ -6,9 +6,10 @@ import android.content.Context import android.content.Intent import android.net.Uri import android.provider.CalendarContract -import android.util.Log import com.getcapacitor.JSArray import com.getcapacitor.JSObject +import dev.barooni.capacitor.calendar.model.CalendarEvent +import dev.barooni.capacitor.calendar.model.EventReminder import java.util.Calendar import java.util.TimeZone @@ -54,8 +55,10 @@ class CapacitorCalendar { null, )?.use { cursor -> val idColumnIndex = cursor.getColumnIndex(CalendarContract.Calendars._ID) - val nameColumnIndex = cursor.getColumnIndex(CalendarContract.Calendars.CALENDAR_DISPLAY_NAME) - val calendarColorColumnIndex = cursor.getColumnIndex(CalendarContract.Calendars.CALENDAR_COLOR) + val nameColumnIndex = + cursor.getColumnIndex(CalendarContract.Calendars.CALENDAR_DISPLAY_NAME) + val calendarColorColumnIndex = + cursor.getColumnIndex(CalendarContract.Calendars.CALENDAR_COLOR) while (cursor.moveToNext()) { val id = cursor.getLong(idColumnIndex) @@ -94,9 +97,10 @@ class CapacitorCalendar { selectionArgs, null, )?.use { cursor -> - if (cursor.moveToFirst()) { + return if (cursor.moveToFirst()) { val idColumnIndex = cursor.getColumnIndex(CalendarContract.Calendars._ID) - val nameColumnIndex = cursor.getColumnIndex(CalendarContract.Calendars.CALENDAR_DISPLAY_NAME) + val nameColumnIndex = + cursor.getColumnIndex(CalendarContract.Calendars.CALENDAR_DISPLAY_NAME) val id = cursor.getLong(idColumnIndex) val title = cursor.getString(nameColumnIndex) @@ -105,9 +109,9 @@ class CapacitorCalendar { put("id", id.toString()) put("title", title) } - return calendarObject + calendarObject } else { - return null + null } } throw Exception("No primary calendar found") @@ -116,56 +120,19 @@ class CapacitorCalendar { @Throws(Exception::class) fun createEvent( context: Context, - title: String, - calendarId: String?, - location: String?, - startDate: Long?, - endDate: Long?, - isAllDay: Boolean?, - alertOffsetInMinutesSingle: Float?, - alertOffsetInMinutesMultiple: JSArray?, - url: String?, - notes: String?, + event: CalendarEvent, + reminder: List, ): Uri { - val startMillis = startDate ?: Calendar.getInstance().timeInMillis - val endMillis = endDate ?: (startMillis + 3600 * 1000) - - val eventValues = - ContentValues().apply { - put(CalendarContract.Events.DTSTART, startMillis) - put(CalendarContract.Events.DTEND, endMillis) - put(CalendarContract.Events.TITLE, title) - location?.let { put(CalendarContract.Events.EVENT_LOCATION, it) } - put(CalendarContract.Events.CALENDAR_ID, calendarId ?: getDefaultCalendar(context)?.getString("id")) - put(CalendarContract.Events.EVENT_TIMEZONE, TimeZone.getDefault().id) - isAllDay?.let { put(CalendarContract.Events.ALL_DAY, if (it) 1 else 0) } - put(CalendarContract.Events.DESCRIPTION, listOfNotNull(notes, url?.let { "URL: $it" }).joinToString("\n")) - } + val eventValues = from(context, event) - val eventUri = context.contentResolver.insert(CalendarContract.Events.CONTENT_URI, eventValues) - val eventId = eventUri?.lastPathSegment?.toLong() ?: throw IllegalArgumentException("Failed to convert event id to long") + val eventUri = + context.contentResolver.insert(CalendarContract.Events.CONTENT_URI, eventValues) + val eventId = eventUri?.lastPathSegment?.toLong() + ?: throw IllegalArgumentException("Failed to convert event id to long") - when { - alertOffsetInMinutesSingle != null && alertOffsetInMinutesSingle > -1 -> { - val alertValues = createAlertValues(eventId, alertOffsetInMinutesSingle) - context.contentResolver.insert(CalendarContract.Reminders.CONTENT_URI, alertValues) - } - alertOffsetInMinutesMultiple != null -> { - alertOffsetInMinutesMultiple - .toList() - .mapNotNull { alert -> - try { - val alertFloat = alert.toString().toFloat() - if (alertFloat > -1) alertFloat else null - } catch (e: NumberFormatException) { - Log.e("Error", "Failed to convert alert to float: $alert", e) - null - } - }.forEach { alertFloat -> - val alertValues = createAlertValues(eventId, alertFloat) - context.contentResolver.insert(CalendarContract.Reminders.CONTENT_URI, alertValues) - } - } + reminder.forEach { + val alertValues = createAlertValues(eventId, it.offset) + context.contentResolver.insert(CalendarContract.Reminders.CONTENT_URI, alertValues) } return eventUri @@ -175,36 +142,14 @@ class CapacitorCalendar { fun modifyEvent( context: Context, id: Long, - update: JSObject, + update: CalendarEvent, ): Boolean { - val title = update.getString("title") - val calendarId = update.getString("calendarId") - val location = update.getString("location") - val startDate = update.getLong("startDate") - val endDate = update.getLong("endDate") - val isAllDay = update.getBoolean("isAllDay") - val url = update.getString("url") - val notes = update.getString("notes") - val eventUri: Uri = ContentUris.withAppendedId(CalendarContract.Events.CONTENT_URI, id) - val values = - ContentValues().apply { - if (title != null) put(CalendarContract.Events.TITLE, title) - if (calendarId != null) put(CalendarContract.Events.CALENDAR_ID, calendarId) - if (location != null) put(CalendarContract.Events.EVENT_LOCATION, location) - if (startDate != null) put(CalendarContract.Events.DTSTART, startDate) - if (endDate != null) put(CalendarContract.Events.DTEND, endDate) - if (isAllDay != null) put(CalendarContract.Events.ALL_DAY, if (isAllDay) 1 else 0) - if (notes != - null - ) { - put(CalendarContract.Events.DESCRIPTION, listOfNotNull(notes, url?.let { "URL: $it" }).joinToString("\n")) - } - } + val contentValues = from(context, update) - val rows: Int = context.contentResolver.update(eventUri, values, null, null) - return rows > 0 + val updatedRows = context.contentResolver.update(eventUri, contentValues, null, null) + return updatedRows > 0 } private fun createAlertValues( @@ -245,7 +190,8 @@ class CapacitorCalendar { CalendarContract.Events.ALL_DAY, CalendarContract.Events.CALENDAR_ID, ) - val selection = "(${CalendarContract.Events.DTSTART} >= ?) AND (${CalendarContract.Events.DTEND} <= ?)" + val selection = + "(${CalendarContract.Events.DTSTART} >= ?) AND (${CalendarContract.Events.DTEND} <= ?)" val selectionArgs = arrayOf(startDate.toString(), endDate.toString()) val events = JSArray() @@ -260,17 +206,23 @@ class CapacitorCalendar { )?.use { cursor -> val idColumnIndex = cursor.getColumnIndex(CalendarContract.Events._ID) val nameColumnIndex = cursor.getColumnIndex(CalendarContract.Events.TITLE) - val locationColumnIndex = cursor.getColumnIndex(CalendarContract.Events.EVENT_LOCATION) - val calendarColorColumnIndex = cursor.getColumnIndex(CalendarContract.Events.CALENDAR_COLOR) + val locationColumnIndex = + cursor.getColumnIndex(CalendarContract.Events.EVENT_LOCATION) + val calendarColorColumnIndex = + cursor.getColumnIndex(CalendarContract.Events.CALENDAR_COLOR) val organizerColumnIndex = cursor.getColumnIndex(CalendarContract.Events.ORGANIZER) - val descriptionColumnIndex = cursor.getColumnIndex(CalendarContract.Events.DESCRIPTION) + val descriptionColumnIndex = + cursor.getColumnIndex(CalendarContract.Events.DESCRIPTION) val dtStartColumnIndex = cursor.getColumnIndex(CalendarContract.Events.DTSTART) val dtEndColumnIndex = cursor.getColumnIndex(CalendarContract.Events.DTEND) - val eventTimezoneColumnIndex = cursor.getColumnIndex(CalendarContract.Events.EVENT_TIMEZONE) - val eventEndTimezoneColumnIndex = cursor.getColumnIndex(CalendarContract.Events.EVENT_END_TIMEZONE) + val eventTimezoneColumnIndex = + cursor.getColumnIndex(CalendarContract.Events.EVENT_TIMEZONE) + val eventEndTimezoneColumnIndex = + cursor.getColumnIndex(CalendarContract.Events.EVENT_END_TIMEZONE) val durationColumnIndex = cursor.getColumnIndex(CalendarContract.Events.DURATION) val isAllDayColumnIndex = cursor.getColumnIndex(CalendarContract.Events.ALL_DAY) - val calendarIdColumnIndex = cursor.getColumnIndex(CalendarContract.Events.CALENDAR_ID) + val calendarIdColumnIndex = + cursor.getColumnIndex(CalendarContract.Events.CALENDAR_ID) while (cursor.moveToNext()) { val id = cursor.getLong(idColumnIndex) @@ -292,7 +244,8 @@ class CapacitorCalendar { put("id", id.toString()) title?.takeIf { it.isNotEmpty() }?.let { put("title", it) } location?.takeIf { it.isNotEmpty() }?.let { put("location", it) } - calendarColor.takeIf { it != 0 }?.let { put("eventColor", String.format("#%06X", 0xFFFFFF and it)) } + calendarColor.takeIf { it != 0 } + ?.let { put("eventColor", String.format("#%06X", 0xFFFFFF and it)) } organizer?.takeIf { it.isNotEmpty() }?.let { put("organizer", it) } desc?.takeIf { it.isNotEmpty() }?.let { put("description", it) } dtStart.takeIf { it != 0.toLong() }?.let { put("startDate", it) } @@ -313,7 +266,8 @@ class CapacitorCalendar { } duration?.takeIf { it.isNotEmpty() }?.let { put("duration", it) } put("isAllDay", allDay) - calendarId.takeIf { it != 0.toLong() }?.let { put("calendarId", it.toString()) } + calendarId.takeIf { it != 0.toLong() } + ?.let { put("calendarId", it.toString()) } } events.put(event) } @@ -328,16 +282,17 @@ class CapacitorCalendar { ): JSObject { val deletedEvents = JSArray() val failedToDeleteEvents = JSArray() - val contentResolver = context.contentResolver ids.toList().forEach { id -> try { - val uri = ContentUris.withAppendedId(CalendarContract.Events.CONTENT_URI, id.toLong()) - val rowsDeleted = contentResolver.delete(uri, null, null) - if (rowsDeleted > 0) { - deletedEvents.put(id) - } else { - failedToDeleteEvents.put(id) + when { + deleteEventById(context, id) -> { + deletedEvents.put(id) + } + + else -> { + failedToDeleteEvents.put(id) + } } } catch (error: Exception) { failedToDeleteEvents.put(id) @@ -350,9 +305,36 @@ class CapacitorCalendar { return ret } - fun getTimeZoneAbbreviation(timeZoneId: String): String { + @Throws(Exception::class) + fun deleteEventById(context: Context, id: String): Boolean { + val deleteUri = ContentUris.withAppendedId(CalendarContract.Events.CONTENT_URI, id.toLong()) + val deletedRows = context.contentResolver.delete(deleteUri, null, null) + return deletedRows > 0 + } + + private fun getTimeZoneAbbreviation(timeZoneId: String): String { val timeZone = TimeZone.getTimeZone(timeZoneId) val now = Calendar.getInstance(timeZone) return timeZone.getDisplayName(timeZone.inDaylightTime(now.time), TimeZone.SHORT) } + + private fun from(context: Context, event: CalendarEvent): ContentValues { + return ContentValues().apply { + put(CalendarContract.Events.DTSTART, event.startMillis) + put(CalendarContract.Events.DTEND, event.endMillis) + put(CalendarContract.Events.TITLE, event.title) + event.location?.let { put(CalendarContract.Events.EVENT_LOCATION, it) } + put( + CalendarContract.Events.CALENDAR_ID, + event.calendarId ?: getDefaultCalendar(context)?.getString("id") + ) + put(CalendarContract.Events.EVENT_TIMEZONE, TimeZone.getDefault().id) + event.isAllDay?.let { put(CalendarContract.Events.ALL_DAY, if (it) 1 else 0) } + put( + CalendarContract.Events.DESCRIPTION, + listOfNotNull(event.notes, event.url?.let { "URL: $it" }).joinToString("\n") + ) + event.recurrenceRule?.let { put(CalendarContract.Events.RRULE, it.toString()) } + } + } } diff --git a/android/src/main/java/dev/barooni/capacitor/calendar/CapacitorCalendarPlugin.kt b/android/src/main/java/dev/barooni/capacitor/calendar/CapacitorCalendarPlugin.kt index 2c1e715..a4d2d25 100644 --- a/android/src/main/java/dev/barooni/capacitor/calendar/CapacitorCalendarPlugin.kt +++ b/android/src/main/java/dev/barooni/capacitor/calendar/CapacitorCalendarPlugin.kt @@ -15,6 +15,8 @@ import com.getcapacitor.annotation.ActivityCallback import com.getcapacitor.annotation.CapacitorPlugin import com.getcapacitor.annotation.Permission import com.getcapacitor.annotation.PermissionCallback +import dev.barooni.capacitor.calendar.model.CalendarEvent +import dev.barooni.capacitor.calendar.model.EventReminder @CapacitorPlugin( name = "CapacitorCalendar", @@ -47,27 +49,13 @@ class CapacitorCalendarPlugin : Plugin() { @PluginMethod(returnType = PluginMethod.RETURN_PROMISE) fun createEventWithPrompt(call: PluginCall) { try { - val title = call.getString("title", "") - val calendarId = call.getString("calendarId") - val location = call.getString("location") - val startDate = call.getLong("startDate") - val endDate = call.getLong("endDate") - val isAllDay = call.getBoolean("isAllDay", false) - val url = call.getString("url") - val notes = call.getString("notes") + val calendarEvent = CalendarEvent.fromPluginCall(call) eventIdOptional = call.getBoolean("eventIdOptional", false) ?: false - if (!eventIdOptional) implementation.eventIdsArray = implementation.fetchCalendarEventIDs(context) + if (!eventIdOptional) implementation.eventIdsArray = + implementation.fetchCalendarEventIDs(context) - val intent = Intent(Intent.ACTION_INSERT).setData(CalendarContract.Events.CONTENT_URI) - - intent.putExtra(CalendarContract.Events.TITLE, title) - calendarId?.let { intent.putExtra(CalendarContract.Events.CALENDAR_ID, it) } - location?.let { intent.putExtra(CalendarContract.Events.EVENT_LOCATION, it) } - startDate?.let { intent.putExtra(CalendarContract.EXTRA_EVENT_BEGIN_TIME, it) } - endDate?.let { intent.putExtra(CalendarContract.EXTRA_EVENT_END_TIME, it) } - isAllDay?.let { intent.putExtra(CalendarContract.EXTRA_EVENT_ALL_DAY, it) } - intent.putExtra(CalendarContract.Events.DESCRIPTION, listOfNotNull(notes, url?.let { "URL: $it" }).joinToString("\n")) + val intent = createEventIntent(calendarEvent) return startActivityForResult( call, @@ -75,11 +63,60 @@ class CapacitorCalendarPlugin : Plugin() { "openCalendarIntentActivityCallback", ) } catch (error: Exception) { - call.reject("", "[CapacitorCalendar.${::openCalendarIntentActivityCallback.name}] Could not create the event") + call.reject( + "", + "[CapacitorCalendar.${::openCalendarIntentActivityCallback.name}] Could not create the event", + error + ) return } } + private fun createEventIntent(calendarEvent: CalendarEvent): Intent { + val intent = Intent(Intent.ACTION_INSERT).setData(CalendarContract.Events.CONTENT_URI) + + intent.putExtra(CalendarContract.Events.TITLE, calendarEvent.title) + + calendarEvent.calendarId?.let { + intent.putExtra( + CalendarContract.Events.CALENDAR_ID, it + ) + } + calendarEvent.location?.let { + intent.putExtra( + CalendarContract.Events.EVENT_LOCATION, it + ) + } + calendarEvent.startMillis.let { + intent.putExtra( + CalendarContract.EXTRA_EVENT_BEGIN_TIME, it + ) + } + calendarEvent.endMillis.let { + intent.putExtra( + CalendarContract.EXTRA_EVENT_END_TIME, it + ) + } + calendarEvent.isAllDay?.let { + intent.putExtra( + CalendarContract.EXTRA_EVENT_ALL_DAY, it + ) + } + calendarEvent.recurrenceRule?.let { + intent.putExtra( + CalendarContract.Events.RRULE, it.toString() + ) + } + + intent.putExtra( + CalendarContract.Events.DESCRIPTION, + listOfNotNull( + calendarEvent.notes, + calendarEvent.url?.let { "URL: $it" }).joinToString("\n") + ) + return intent + } + @ActivityCallback private fun openCalendarIntentActivityCallback( call: PluginCall?, @@ -102,13 +139,13 @@ class CapacitorCalendarPlugin : Plugin() { @PluginMethod(returnType = PluginMethod.RETURN_PROMISE) fun modifyEventWithPrompt(call: PluginCall) { try { - val stringId = - call.getString("id") ?: throw Exception("[CapacitorCalendar.${::modifyEventWithPrompt.name}] Event ID not defined") + val stringId = call.getString("id") ?: throw Exception("[CapacitorCalendar.${::modifyEventWithPrompt.name}] Event ID not defined") val update = call.getObject("update") + + val calendarEvent = CalendarEvent.fromJSObject(update) + val uri: Uri = ContentUris.withAppendedId(CalendarContract.Events.CONTENT_URI, stringId.toLong()) - val intent = - Intent(Intent.ACTION_EDIT) - .setData(uri) + val intent = Intent(Intent.ACTION_EDIT).setData(uri) if (update != null) { val title = update.getString("title") @@ -158,7 +195,10 @@ class CapacitorCalendarPlugin : Plugin() { try { val stringId = call.getString("id") ?: throw Exception("[CapacitorCalendar.${::modifyEvent.name}] Event ID not defined") val update = call.getObject("update") ?: throw Exception("[CapacitorCalendar.${::modifyEvent.name}] Update not provided") - val updated = implementation.modifyEvent(context, stringId.toLong(), update) + + val newEvent = CalendarEvent.fromJSObject(update) + + val updated = implementation.modifyEvent(context, stringId.toLong(), newEvent) if (updated) { call.resolve() } else { @@ -350,40 +390,25 @@ class CapacitorCalendarPlugin : Plugin() { @PluginMethod(returnType = PluginMethod.RETURN_PROMISE) fun createEvent(call: PluginCall) { try { - val title = - call.getString("title") - ?: throw Exception("[CapacitorCalendar.${::createEvent.name}] A title for the event was not provided") - val calendarId = call.getString("calendarId") - val location = call.getString("location") - val startDate = call.getLong("startDate") - val endDate = call.getLong("endDate") - val isAllDay = call.getBoolean("isAllDay", false) - val alertOffsetInMinutesSingle = call.getFloat("alertOffsetInMinutes") - val alertOffsetInMinutesMultiple = call.getArray("alertOffsetInMinutes") - val url = call.getString("url") - val notes = call.getString("notes") - - val eventUri = - implementation.createEvent( - context, - title, - calendarId, - location, - startDate, - endDate, - isAllDay, - alertOffsetInMinutesSingle, - alertOffsetInMinutesMultiple, - url, - notes, - ) - - val id = eventUri?.lastPathSegment ?: throw IllegalArgumentException("Failed to insert event into calendar") + val alertOffsetInMinutes: Any? = + call.getFloat("alertOffsetInMinutes") ?: call.getArray("alertOffsetInMinutes") + + val eventUri = implementation.createEvent( + context, + CalendarEvent.fromPluginCall(call), + EventReminder.from(alertOffsetInMinutes) + ) + + val id = eventUri.lastPathSegment ?: throw IllegalArgumentException("Failed to insert event into calendar") val ret = JSObject() ret.put("result", id) call.resolve(ret) } catch (error: Exception) { - call.reject("", "[CapacitorCalendar.${::createEvent.name}] Unable to create event") + call.reject( + "", + "[CapacitorCalendar.${::createEvent.name}] Unable to create event", + error + ) return } } @@ -449,6 +474,24 @@ class CapacitorCalendarPlugin : Plugin() { return } } + + @PluginMethod(returnType = PluginMethod.RETURN_PROMISE) + fun deleteEventById(call: PluginCall) { + try { + val id = call.getString("id") + ?: throw Exception("[CapacitorCalendar.${::deleteEventById.name}] Event ids were not provided") + + if (implementation.deleteEventById(context, id)) { + call.resolve(JSObject().apply { + put("result", id) + }) + } + + throw Exception("Event was not deleted") + } catch (error: Exception) { + call.reject("", "[CapacitorCalendar.${::deleteEventById.name}] Could not delete event", error) + } + } @PluginMethod(returnType = PluginMethod.RETURN_PROMISE) fun createCalendar(call: PluginCall) { diff --git a/android/src/main/java/dev/barooni/capacitor/calendar/model/CalendarEvent.kt b/android/src/main/java/dev/barooni/capacitor/calendar/model/CalendarEvent.kt new file mode 100644 index 0000000..5618f13 --- /dev/null +++ b/android/src/main/java/dev/barooni/capacitor/calendar/model/CalendarEvent.kt @@ -0,0 +1,79 @@ +package dev.barooni.capacitor.calendar.model + +import com.getcapacitor.JSObject +import com.getcapacitor.PluginCall +import java.util.Calendar + +data class CalendarEvent( + val title: String, + val calendarId: String?, + val location: String?, + val startMillis: Long, + val endMillis: Long, + val isAllDay: Boolean?, + val url: String?, + val notes: String?, + val recurrenceRule: RecurrenceRule?, +) { + init { + require(title.isNotBlank()) + } + + companion object { + fun fromJSObject(jsObject: JSObject): CalendarEvent { + val title = jsObject.getString("title") + ?: throw Exception("[CapacitorCalendar.${::fromJSObject.name}] A title for the event was not provided") + val calendarId = jsObject.getString("calendarId") + val location = jsObject.getString("location") + val startDate = jsObject.getString("startDate") + val endDate = jsObject.getString("endDate") + val isAllDay = jsObject.getBoolean("isAllDay", false) + val url = jsObject.getString("url") + val notes = jsObject.getString("notes") + val recurrenceRule = jsObject.getJSObject("recurrence")?.let { RecurrenceRule(it) } + + val startMillis = startDate?.toLong() ?: Calendar.getInstance().timeInMillis + val endMillis = endDate?.toLong() ?: (startMillis + 3600 * 1000) + + return CalendarEvent( + title, + calendarId, + location, + startMillis, + endMillis, + isAllDay, + url, + notes, + recurrenceRule + ) + } + + fun fromPluginCall(call: PluginCall): CalendarEvent { + val title = call.getString("title") + ?: throw Exception("[CapacitorCalendar.${::fromPluginCall.name}] A title for the event was not provided") + val calendarId = call.getString("calendarId") + val location = call.getString("location") + val startDate = call.getLong("startDate") + val endDate = call.getLong("endDate") + val isAllDay = call.getBoolean("isAllDay", false) + val url = call.getString("url") + val notes = call.getString("notes") + val recurrenceRule = call.getObject("recurrence")?.let { RecurrenceRule(it) } + + val startMillis = startDate ?: Calendar.getInstance().timeInMillis + val endMillis = endDate ?: (startMillis + 3600 * 1000) + + return CalendarEvent( + title, + calendarId, + location, + startMillis, + endMillis, + isAllDay, + url, + notes, + recurrenceRule + ) + } + } +} diff --git a/android/src/main/java/dev/barooni/capacitor/calendar/model/EventReminder.kt b/android/src/main/java/dev/barooni/capacitor/calendar/model/EventReminder.kt new file mode 100644 index 0000000..d184cfd --- /dev/null +++ b/android/src/main/java/dev/barooni/capacitor/calendar/model/EventReminder.kt @@ -0,0 +1,36 @@ +package dev.barooni.capacitor.calendar.model + +import android.util.Log +import com.getcapacitor.JSArray + +data class EventReminder( + val offset: Float +) { + companion object { + fun from(offset: Any?): List { + return when (offset) { + is Float -> from(offset) + is JSArray -> from(offset) + else -> emptyList() + } + } + + private fun from(offset: Float): List { + return listOf(EventReminder(offset)) + } + + private fun from(jsArray: JSArray): List { + return jsArray.toList().mapNotNull { + try { + val offset = it.toString().toFloat() + if (offset > -1) offset else null + } catch (e: NumberFormatException) { + Log.e("Error", "Failed to convert alert to float: $it", e) + null + } + }.map { + EventReminder(it) + } + } + } +} \ No newline at end of file diff --git a/android/src/main/java/dev/barooni/capacitor/calendar/model/RecurrenceFrequency.kt b/android/src/main/java/dev/barooni/capacitor/calendar/model/RecurrenceFrequency.kt new file mode 100644 index 0000000..98d799b --- /dev/null +++ b/android/src/main/java/dev/barooni/capacitor/calendar/model/RecurrenceFrequency.kt @@ -0,0 +1,14 @@ +package dev.barooni.capacitor.calendar.model + +/** + * RFC 5545 compliant frequency + */ +enum class RecurrenceFrequency { + SECONDLY, + MINUTELY, + HOURLY, + DAILY, + WEEKLY, + MONTHLY, + YEARLY, +} \ No newline at end of file diff --git a/android/src/main/java/dev/barooni/capacitor/calendar/model/RecurrenceRule.kt b/android/src/main/java/dev/barooni/capacitor/calendar/model/RecurrenceRule.kt new file mode 100644 index 0000000..5f18e56 --- /dev/null +++ b/android/src/main/java/dev/barooni/capacitor/calendar/model/RecurrenceRule.kt @@ -0,0 +1,55 @@ +package dev.barooni.capacitor.calendar.model + +import com.getcapacitor.JSObject +import java.time.Instant +import java.time.ZoneId +import java.time.format.DateTimeFormatter + +data class RecurrenceRule( + val frequency: RecurrenceFrequency, + val interval: Int, + val end: Long?, +) { + + init { + require(interval > 0) + } + + constructor(rule: JSObject) : this( + rule.getString("frequency")?.let { + when (it.toInt()) { + 0 -> RecurrenceFrequency.DAILY + 1 -> RecurrenceFrequency.WEEKLY + 2 -> RecurrenceFrequency.MONTHLY + 3 -> RecurrenceFrequency.YEARLY + else -> throw IllegalArgumentException("frequency is not a valid ${RecurrenceRule::class.qualifiedName}") + } + } + ?: throw IllegalArgumentException("'frequency' is either not defined or not a valid integer"), + rule.getInteger("interval") + ?: throw IllegalArgumentException("'interval' is either not defined or not a valid integer"), + rule.getString("end")?.toLong(), + ) + + /** + * @return a RFC 5545 compliant recurrence rule + */ + override fun toString(): String { + val stringBuilder = StringBuilder() + + stringBuilder.append("FREQ=${frequency};") + stringBuilder.append("INTERVAL=${interval};") + + if (end != null) { + val instant = Instant.ofEpochMilli(end).atZone(ZoneId.of("UTC")) + stringBuilder.append( + "UNTIL=${ + DateTimeFormatter.ofPattern("yyyyMMdd'T'HHmmss'Z'").format(instant) + };" + ) + } + + return stringBuilder.removeSuffix(";").toString() + } + +} \ No newline at end of file diff --git a/docs/api-reference.md b/docs/api-reference.md index 76cf003..cd48d63 100644 --- a/docs/api-reference.md +++ b/docs/api-reference.md @@ -2,38 +2,39 @@ -- [`checkPermission(...)`](#checkpermission) -- [`checkAllPermissions()`](#checkallpermissions) -- [`requestPermission(...)`](#requestpermission) -- [`requestAllPermissions()`](#requestallpermissions) -- [`createEventWithPrompt(...)`](#createeventwithprompt) -- [`selectCalendarsWithPrompt(...)`](#selectcalendarswithprompt) -- [`listCalendars()`](#listcalendars) -- [`getDefaultCalendar()`](#getdefaultcalendar) -- [`createEvent(...)`](#createevent) -- [`getDefaultRemindersList()`](#getdefaultreminderslist) -- [`getRemindersLists()`](#getreminderslists) -- [`createReminder(...)`](#createreminder) -- [`openCalendar(...)`](#opencalendar) -- [`openReminders()`](#openreminders) -- [`listEventsInRange(...)`](#listeventsinrange) -- [`deleteEventsById(...)`](#deleteeventsbyid) -- [`createCalendar(...)`](#createcalendar) -- [`deleteCalendar(...)`](#deletecalendar) -- [`getRemindersFromLists(...)`](#getremindersfromlists) -- [`deleteRemindersById(...)`](#deleteremindersbyid) -- [`requestWriteOnlyCalendarAccess()`](#requestwriteonlycalendaraccess) -- [`requestReadOnlyCalendarAccess()`](#requestreadonlycalendaraccess) -- [`requestFullCalendarAccess()`](#requestfullcalendaraccess) -- [`requestFullRemindersAccess()`](#requestfullremindersaccess) -- [`modifyEventWithPrompt(...)`](#modifyeventwithprompt) -- [`modifyEvent(...)`](#modifyevent) -- [`fetchAllCalendarSources()`](#fetchallcalendarsources) -- [`fetchAllRemindersSources()`](#fetchallreminderssources) -- [`modifyReminder(...)`](#modifyreminder) -- [Interfaces](#interfaces) -- [Type Aliases](#type-aliases) -- [Enums](#enums) +* [`checkPermission(...)`](#checkpermission) +* [`checkAllPermissions()`](#checkallpermissions) +* [`requestPermission(...)`](#requestpermission) +* [`requestAllPermissions()`](#requestallpermissions) +* [`createEventWithPrompt(...)`](#createeventwithprompt) +* [`selectCalendarsWithPrompt(...)`](#selectcalendarswithprompt) +* [`listCalendars()`](#listcalendars) +* [`getDefaultCalendar()`](#getdefaultcalendar) +* [`createEvent(...)`](#createevent) +* [`getDefaultRemindersList()`](#getdefaultreminderslist) +* [`getRemindersLists()`](#getreminderslists) +* [`createReminder(...)`](#createreminder) +* [`openCalendar(...)`](#opencalendar) +* [`openReminders()`](#openreminders) +* [`listEventsInRange(...)`](#listeventsinrange) +* [`deleteEventsById(...)`](#deleteeventsbyid) +* [`deleteEventById(...)`](#deleteeventbyid) +* [`createCalendar(...)`](#createcalendar) +* [`deleteCalendar(...)`](#deletecalendar) +* [`getRemindersFromLists(...)`](#getremindersfromlists) +* [`deleteRemindersById(...)`](#deleteremindersbyid) +* [`requestWriteOnlyCalendarAccess()`](#requestwriteonlycalendaraccess) +* [`requestReadOnlyCalendarAccess()`](#requestreadonlycalendaraccess) +* [`requestFullCalendarAccess()`](#requestfullcalendaraccess) +* [`requestFullRemindersAccess()`](#requestfullremindersaccess) +* [`modifyEventWithPrompt(...)`](#modifyeventwithprompt) +* [`modifyEvent(...)`](#modifyevent) +* [`fetchAllCalendarSources()`](#fetchallcalendarsources) +* [`fetchAllRemindersSources()`](#fetchallreminderssources) +* [`modifyReminder(...)`](#modifyreminder) +* [Interfaces](#interfaces) +* [Type Aliases](#type-aliases) +* [Enums](#enums) @@ -56,7 +57,8 @@ Checks the current authorization status of a specific permission. **Since:** 0.1.0 ---- +-------------------- + ### checkAllPermissions() @@ -70,7 +72,8 @@ Checks the current authorization status of all the required permissions for the **Since:** 0.1.0 ---- +-------------------- + ### requestPermission(...) @@ -89,7 +92,8 @@ If the permission is already granted, it will directly return the status. **Since:** 0.1.0 ---- +-------------------- + ### requestAllPermissions() @@ -103,26 +107,28 @@ Requests authorization to all the required permissions for the plugin, if they h **Since:** 0.1.0 ---- +-------------------- + ### createEventWithPrompt(...) ```typescript -createEventWithPrompt(options: { title: string; calendarId?: string; location?: string; startDate?: number; endDate?: number; isAllDay?: boolean; alertOffsetInMinutes?: number | number[]; url?: string; notes?: string; eventIdOptional?: boolean; }) => Promise<{ result: string[]; }> +createEventWithPrompt(options: { title: string; calendarId?: string; location?: string; startDate?: number; endDate?: number; isAllDay?: boolean; alertOffsetInMinutes?: number | number[]; url?: string; notes?: string; eventIdOptional?: boolean; recurrence?: ReminderRecurrenceRule; }) => Promise<{ result: string[]; }> ``` Creates an event in the calendar by using the native calendar. On iOS opens a native sheet and on Android opens an intent. -| Param | Type | Description | -| ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------- | -| **`options`** | { title: string; calendarId?: string; location?: string; startDate?: number; endDate?: number; isAllDay?: boolean; alertOffsetInMinutes?: number \| number[]; url?: string; notes?: string; eventIdOptional?: boolean; } | Options for creating the event. | +| Param | Type | Description | +| ------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------- | +| **`options`** | { title: string; calendarId?: string; location?: string; startDate?: number; endDate?: number; isAllDay?: boolean; alertOffsetInMinutes?: number \| number[]; url?: string; notes?: string; eventIdOptional?: boolean; recurrence?: ReminderRecurrenceRule; } | Options for creating the event. | **Returns:** Promise<{ result: string[]; }> **Since:** 0.1.0 ---- +-------------------- + ### selectCalendarsWithPrompt(...) @@ -140,7 +146,8 @@ Presents a prompt to the user to select calendars. This method is available only **Since:** 0.2.0 ---- +-------------------- + ### listCalendars() @@ -152,7 +159,8 @@ Retrieves a list of calendars available on the device. **Returns:** Promise<{ result: Calendar[]; }> ---- +-------------------- + ### getDefaultCalendar() @@ -166,25 +174,27 @@ Retrieves the default calendar set on the device. **Since:** 0.3.0 ---- +-------------------- + ### createEvent(...) ```typescript -createEvent(options: { title: string; calendarId?: string; location?: string; startDate?: number; endDate?: number; isAllDay?: boolean; alertOffsetInMinutes?: number | number[]; url?: string; notes?: string; }) => Promise<{ result: string; }> +createEvent(options: { title: string; calendarId?: string; location?: string; startDate?: number; endDate?: number; isAllDay?: boolean; alertOffsetInMinutes?: number | number[]; url?: string; notes?: string; recurrence?: ReminderRecurrenceRule; }) => Promise<{ result: string; }> ``` Creates an event with the provided options. -| Param | Type | Description | -| ------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------- | -| **`options`** | { title: string; calendarId?: string; location?: string; startDate?: number; endDate?: number; isAllDay?: boolean; alertOffsetInMinutes?: number \| number[]; url?: string; notes?: string; } | Options for creating the event. | +| Param | Type | Description | +| ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------- | +| **`options`** | { title: string; calendarId?: string; location?: string; startDate?: number; endDate?: number; isAllDay?: boolean; alertOffsetInMinutes?: number \| number[]; url?: string; notes?: string; recurrence?: ReminderRecurrenceRule; } | Options for creating the event. | **Returns:** Promise<{ result: string; }> **Since:** 0.4.0 ---- +-------------------- + ### getDefaultRemindersList() @@ -196,7 +206,8 @@ Retrieves the default reminders list set on the device. **Returns:** Promise<{ result: RemindersList; }> ---- +-------------------- + ### getRemindersLists() @@ -208,7 +219,8 @@ Retrieves all available reminders lists on the device. **Returns:** Promise<{ result: RemindersList[]; }> ---- +-------------------- + ### createReminder(...) @@ -226,7 +238,8 @@ Creates a reminder with the provided options. **Since:** 0.5.0 ---- +-------------------- + ### openCalendar(...) @@ -241,7 +254,8 @@ It will open the calendar on today's date if no date is provided. | ------------- | ------------------------------- | ----------------------------------- | | **`options`** | { date?: number; } | - Options for opening the calendar. | ---- +-------------------- + ### openReminders() @@ -251,7 +265,8 @@ openReminders() => Promise Opens the reminders app. Since the user leaves your app, use this method with caution. ---- +-------------------- + ### listEventsInRange(...) @@ -269,7 +284,8 @@ Retrieves the list of calendar events present in the given date range. **Since:** 0.10.0 ---- +-------------------- + ### deleteEventsById(...) @@ -278,6 +294,8 @@ deleteEventsById(options: { ids: string[]; }) => Promise<{ result: { deleted: st ``` Deletes events from the calendar given their IDs. +If the event is recurring it will automatically delete this and future events. +To modify this behaviour consider using the method "deleteEventById". | Param | Type | Description | | ------------- | ------------------------------- | ------------------------------- | @@ -287,7 +305,27 @@ Deletes events from the calendar given their IDs. **Since:** 0.11.0 ---- +-------------------- + + +### deleteEventById(...) + +```typescript +deleteEventById(options: { id: string; span?: EventSpan; }) => Promise<{ result: string; }> +``` + +Deletes an even from the calendar by their ID. + +| Param | Type | Description | +| ------------- | ----------------------------------------------------------------------- | --------------------------------------- | +| **`options`** | { id: string; span?: EventSpan; } | Options for defining event ID and span. | + +**Returns:** Promise<{ result: string; }> + +**Since:** TODO: Add version number + +-------------------- + ### createCalendar(...) @@ -305,7 +343,8 @@ Creates a calendar **Since:** 5.2.0 ---- +-------------------- + ### deleteCalendar(...) @@ -321,7 +360,8 @@ Deletes a calendar by id **Since:** 5.2.0 ---- +-------------------- + ### getRemindersFromLists(...) @@ -339,7 +379,8 @@ Retrieves the list of reminders present in the given date range. **Since:** 5.3.0 ---- +-------------------- + ### deleteRemindersById(...) @@ -357,7 +398,8 @@ Deletes reminders given their IDs. **Since:** 5.3.0 ---- +-------------------- + ### requestWriteOnlyCalendarAccess() @@ -371,7 +413,8 @@ Requests write access for the calendar. If its already granted, it will directly **Since:** 5.4.0 ---- +-------------------- + ### requestReadOnlyCalendarAccess() @@ -385,7 +428,8 @@ Requests read access for the calendar. If its already granted, it will directly **Since:** 5.4.0 ---- +-------------------- + ### requestFullCalendarAccess() @@ -399,7 +443,8 @@ Requests read and write access for the calendar. If its already granted, it will **Since:** 5.4.0 ---- +-------------------- + ### requestFullRemindersAccess() @@ -413,41 +458,44 @@ Requests read and write access for the reminders. If its already granted, it wil **Since:** 5.4.0 ---- +-------------------- + ### modifyEventWithPrompt(...) ```typescript -modifyEventWithPrompt(options: { id: string; update?: { title?: string; calendarId?: string; location?: string; startDate?: number; endDate?: number; isAllDay?: boolean; alertOffsetInMinutes?: number | number[]; url?: string; notes?: string; }; }) => Promise<{ result: string[]; }> +modifyEventWithPrompt(options: { id: string; update?: { title?: string; calendarId?: string; location?: string; startDate?: number; endDate?: number; isAllDay?: boolean; alertOffsetInMinutes?: number | number[]; url?: string; notes?: string; recurrence?: ReminderRecurrenceRule; }; }) => Promise<{ result: string[]; }> ``` Opens a native prompt to modify an event given its id. -| Param | Type | Description | -| ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------- | -| **`options`** | { id: string; update?: { title?: string; calendarId?: string; location?: string; startDate?: number; endDate?: number; isAllDay?: boolean; alertOffsetInMinutes?: number \| number[]; url?: string; notes?: string; }; } | The options for modifying an event. | +| Param | Type | Description | +| ------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------- | +| **`options`** | { id: string; update?: { title?: string; calendarId?: string; location?: string; startDate?: number; endDate?: number; isAllDay?: boolean; alertOffsetInMinutes?: number \| number[]; url?: string; notes?: string; recurrence?: ReminderRecurrenceRule; }; } | The options for modifying an event. | **Returns:** Promise<{ result: string[]; }> **Since:** 6.6.0 ---- +-------------------- + ### modifyEvent(...) ```typescript -modifyEvent(options: { id: string; span?: EventSpan; update: { title?: string; calendarId?: string; location?: string; startDate?: number; endDate?: number; isAllDay?: boolean; alertOffsetInMinutes?: number | number[]; url?: string; notes?: string; }; }) => Promise +modifyEvent(options: { id: string; span?: EventSpan; update: { title?: string; calendarId?: string; location?: string; startDate?: number; endDate?: number; isAllDay?: boolean; alertOffsetInMinutes?: number | number[]; url?: string; notes?: string; recurrence?: ReminderRecurrenceRule; }; }) => Promise ``` Modifies an event given its id and update details. -| Param | Type | Description | -| ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------- | -| **`options`** | { id: string; span?: EventSpan; update: { title?: string; calendarId?: string; location?: string; startDate?: number; endDate?: number; isAllDay?: boolean; alertOffsetInMinutes?: number \| number[]; url?: string; notes?: string; }; } | The options for updating an event. | +| Param | Type | Description | +| ------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------- | +| **`options`** | { id: string; span?: EventSpan; update: { title?: string; calendarId?: string; location?: string; startDate?: number; endDate?: number; isAllDay?: boolean; alertOffsetInMinutes?: number \| number[]; url?: string; notes?: string; recurrence?: ReminderRecurrenceRule; }; } | The options for updating an event. | **Since:** 6.6.0 ---- +-------------------- + ### fetchAllCalendarSources() @@ -461,7 +509,8 @@ Retrieves a list of calendar sources. **Since:** 6.6.0 ---- +-------------------- + ### fetchAllRemindersSources() @@ -475,7 +524,8 @@ Retrieves a list of reminders sources. **Since:** 6.6.0 ---- +-------------------- + ### modifyReminder(...) @@ -491,12 +541,24 @@ Modifies a reminder given its id and update details. **Since:** 6.7.0 ---- +-------------------- + ### Interfaces + #### PluginPermissionsMap + +#### ReminderRecurrenceRule + +| Prop | Type | Description | +| --------------- | ----------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------- | +| **`frequency`** | ReminderRecurrenceFrequency | How frequent should the reminder repeat. | +| **`interval`** | number | The interval should be a number greater than 0. For values lower than 1 the method will throw an error. | +| **`end`** | number | When provided, the reminder will stop repeating at the given time. | + + #### Calendar Represents a calendar object. @@ -512,6 +574,7 @@ Represents a calendar object. | **`isSubscribed`** | boolean | | **`source`** | CalendarSource | + #### CalendarSource Represents the account a calendar belongs to @@ -522,15 +585,9 @@ Represents the account a calendar belongs to | **`id`** | string | | **`title`** | string | -#### RemindersList -#### ReminderRecurrenceRule +#### RemindersList -| Prop | Type | Description | -| --------------- | ----------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------- | -| **`frequency`** | ReminderRecurrenceFrequency | How frequent should the reminder repeat. | -| **`interval`** | number | The interval should be a number greater than 0. For values lower than 1 the method will throw an error. | -| **`end`** | number | When provided, the reminder will stop repeating at the given time. | #### CalendarEvent @@ -553,6 +610,7 @@ Represents an event in the calendar. | **`calendarId`** | string | | **`url`** | string | + #### Reminder Represents a reminder in a reminders list. @@ -572,14 +630,18 @@ Represents a reminder in a reminders list. | **`completionDate`** | number | | **`recurrence`** | ReminderRecurrenceRule[] | + ### Type Aliases + #### PermissionState 'prompt' | 'prompt-with-rationale' | 'granted' | 'denied' + ### Enums + #### PluginPermission | Members | Value | Description | @@ -589,6 +651,17 @@ Represents a reminder in a reminders list. | **`READ_REMINDERS`** | 'readReminders' | Represents the permission state for reading reminders. | | **`WRITE_REMINDERS`** | 'writeReminders' | Represents the permission state for writing reminders. | + +#### ReminderRecurrenceFrequency + +| Members | Description | +| ------------- | --------------------------------------- | +| **`DAILY`** | The reminder repeats on a daily basis | +| **`WEEKLY`** | The reminder repeats on a weekly basis | +| **`MONTHLY`** | The reminder repeats on a monthly basis | +| **`YEARLY`** | The reminder repeats on a yearly basis | + + #### CalendarType | Members | Description | @@ -599,6 +672,7 @@ Represents a reminder in a reminders list. | **`SUBSCRIPTION`** | This is a locally subscribed calendar. | | **`BIRTHDAY`** | This is the built-in birthday calendar. | + #### CalendarSourceType | Members | Description | @@ -610,6 +684,7 @@ Represents a reminder in a reminders list. | **`SUBSCRIBED`** | Calendars that the user has subscribed to. These are read-only calendars that can be added by subscribing to a calendar URL. | | **`BIRTHDAYS`** | The built-in Birthdays calendar, which shows birthdays of contacts from the user's address book. This calendar is typically read-only and is managed by the system. | + #### CalendarChooserDisplayStyle | Members | Description | @@ -617,6 +692,7 @@ Represents a reminder in a reminders list. | **`ALL_CALENDARS`** | Display all calendars available for selection. | | **`WRITABLE_CALENDARS_ONLY`** | Display only writable calendars available for selection. | + #### CalendarChooserSelectionStyle | Members | Description | @@ -624,14 +700,6 @@ Represents a reminder in a reminders list. | **`SINGLE`** | Allows only a single selection in the calendar chooser. | | **`MULTIPLE`** | Allows multiple selections in the calendar chooser. | -#### ReminderRecurrenceFrequency - -| Members | Description | -| ------------- | --------------------------------------- | -| **`DAILY`** | The reminder repeats on a daily basis | -| **`WEEKLY`** | The reminder repeats on a weekly basis | -| **`MONTHLY`** | The reminder repeats on a monthly basis | -| **`YEARLY`** | The reminder repeats on a yearly basis | #### EventSpan diff --git a/example/package-lock.json b/example/package-lock.json index 6aa30c8..80d9328 100644 --- a/example/package-lock.json +++ b/example/package-lock.json @@ -27,6 +27,7 @@ "@ionic/core": "^8.2.5", "@ngrx/component": "^17.1.0", "@types/jest": "^29.5.12", + "rimraf": "^6.0.1", "rxjs": "~7.8.0", "tslib": "^2.3.0", "zone.js": "~0.14.3" @@ -44,8 +45,11 @@ }, "..": { "name": "@ebarooni/capacitor-calendar", - "version": "6.5.0", + "version": "6.7.1", "license": "MIT", + "dependencies": { + "rimraf": "^6.0.1" + }, "devDependencies": { "@angular/cli": "^17.1.2", "@capacitor/android": "^6.0.0", @@ -53,7 +57,7 @@ "@capacitor/core": "^6.0.0", "@capacitor/docgen": "^0.0.18", "@capacitor/ios": "^6.0.0", - "@ebarooni/eslint-config": "^1.0.0", + "@ebarooni/eslint-config": "^1.0.1", "@ebarooni/prettier-config": "^1.1.0", "@ebarooni/stylelint-config": "^1.1.0", "@ebarooni/swiftlint-config": "^1.1.0", @@ -70,8 +74,9 @@ "stylelint-config-standard-scss": "^13.1.0", "stylelint-prettier": "^5.0.0", "swiftlint": "^1.0.2", + "typedoc": "^0.26.5", "typescript": "~5.4.2", - "typescript-eslint": "^8.0.0-alpha.20" + "typescript-eslint": "^8.0.1" }, "engines": { "node": ">=18.19.0" @@ -2404,6 +2409,75 @@ "node": ">=18.0.0" } }, + "node_modules/@capacitor/cli/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@capacitor/cli/node_modules/glob": { + "version": "9.3.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-9.3.5.tgz", + "integrity": "sha512-e1LleDykUz2Iu+MTYdkSsuWX8lvAjAcs0Xef0lNIu0S2wOAzuTxCJtcd9S3cijlwYF18EsU3rzb8jPVobxDh9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "minimatch": "^8.0.2", + "minipass": "^4.2.4", + "path-scurry": "^1.6.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@capacitor/cli/node_modules/minimatch": { + "version": "8.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-8.0.4.tgz", + "integrity": "sha512-W0Wvr9HyFXZRGIDgCicunpQ299OKXs9RgZfaukz4qAW/pJhcpUfupc9c+OObPOFueNy8VSrZgEmDtk6Kh4WzDA==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@capacitor/cli/node_modules/minipass": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-4.2.8.tgz", + "integrity": "sha512-fNzuVyifolSLFL4NzpF+wEF4qrgqaaKX0haXPQEdQ7NKAN+WecoKMHV09YcuL/DHxrUsYQOK3MiuDf7Ip2OXfQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@capacitor/cli/node_modules/rimraf": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-4.4.1.tgz", + "integrity": "sha512-Gk8NlF062+T9CqNGn6h4tls3k6T1+/nXdOcSZVikNVtlRdYpA7wRJJMoXmuvOnLW844rPjdQ7JgXCYM6PPC/og==", + "dev": true, + "dependencies": { + "glob": "^9.2.0" + }, + "bin": { + "rimraf": "dist/cjs/src/bin.js" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/@capacitor/core": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/@capacitor/core/-/core-6.0.0.tgz", @@ -3033,7 +3107,6 @@ "version": "8.0.2", "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", - "dev": true, "dependencies": { "string-width": "^5.1.2", "string-width-cjs": "npm:string-width@^4.2.0", @@ -3050,7 +3123,6 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", - "dev": true, "engines": { "node": ">=12" }, @@ -3062,7 +3134,6 @@ "version": "6.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", - "dev": true, "engines": { "node": ">=12" }, @@ -3073,14 +3144,12 @@ "node_modules/@isaacs/cliui/node_modules/emoji-regex": { "version": "9.2.2", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "dev": true + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==" }, "node_modules/@isaacs/cliui/node_modules/string-width": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", - "dev": true, "dependencies": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", @@ -3097,7 +3166,6 @@ "version": "7.1.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "dev": true, "dependencies": { "ansi-regex": "^6.0.1" }, @@ -3112,7 +3180,6 @@ "version": "8.1.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", - "dev": true, "dependencies": { "ansi-styles": "^6.1.0", "string-width": "^5.0.1", @@ -5768,8 +5835,7 @@ "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, "node_modules/base64-js": { "version": "1.5.1", @@ -6792,7 +6858,6 @@ "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", @@ -7156,8 +7221,7 @@ "node_modules/eastasianwidth": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", - "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", - "dev": true + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==" }, "node_modules/ee-first": { "version": "1.1.1", @@ -7198,8 +7262,7 @@ "node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" }, "node_modules/emojis-list": { "version": "3.0.0", @@ -7836,7 +7899,6 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz", "integrity": "sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==", - "dev": true, "dependencies": { "cross-spawn": "^7.0.0", "signal-exit": "^4.0.1" @@ -7852,7 +7914,6 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "dev": true, "engines": { "node": ">=14" }, @@ -8789,7 +8850,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, "engines": { "node": ">=8" } @@ -8917,8 +8977,7 @@ "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" }, "node_modules/isobject": { "version": "3.0.1", @@ -11334,10 +11393,9 @@ } }, "node_modules/minipass": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.4.tgz", - "integrity": "sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==", - "dev": true, + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", "engines": { "node": ">=16 || 14 >=14.17" } @@ -12259,6 +12317,11 @@ "node": ">=6" } }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==" + }, "node_modules/pacote": { "version": "17.0.6", "resolved": "https://registry.npmjs.org/pacote/-/pacote-17.0.6.tgz", @@ -12405,7 +12468,6 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, "engines": { "node": ">=8" } @@ -13322,18 +13384,18 @@ } }, "node_modules/rimraf": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-4.4.1.tgz", - "integrity": "sha512-Gk8NlF062+T9CqNGn6h4tls3k6T1+/nXdOcSZVikNVtlRdYpA7wRJJMoXmuvOnLW844rPjdQ7JgXCYM6PPC/og==", - "dev": true, + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-6.0.1.tgz", + "integrity": "sha512-9dkvaxAsk/xNXSJzMgFqqMCuFgt2+KsOFek3TMLfo8NCPfWpBmqwyNn5Y+NX56QUYfCtsyhF3ayiboEoUmJk/A==", "dependencies": { - "glob": "^9.2.0" + "glob": "^11.0.0", + "package-json-from-dist": "^1.0.0" }, "bin": { - "rimraf": "dist/cjs/src/bin.js" + "rimraf": "dist/esm/bin.mjs" }, "engines": { - "node": ">=14" + "node": "20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" @@ -13343,51 +13405,81 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, "dependencies": { "balanced-match": "^1.0.0" } }, "node_modules/rimraf/node_modules/glob": { - "version": "9.3.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-9.3.5.tgz", - "integrity": "sha512-e1LleDykUz2Iu+MTYdkSsuWX8lvAjAcs0Xef0lNIu0S2wOAzuTxCJtcd9S3cijlwYF18EsU3rzb8jPVobxDh9Q==", - "dev": true, + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-11.0.0.tgz", + "integrity": "sha512-9UiX/Bl6J2yaBbxKoEBRm4Cipxgok8kQYcOPEhScPwebu2I0HoQOuYdIO6S3hLuWoZgpDpwQZMzTFxgpkyT76g==", "dependencies": { - "fs.realpath": "^1.0.0", - "minimatch": "^8.0.2", - "minipass": "^4.2.4", - "path-scurry": "^1.6.1" + "foreground-child": "^3.1.0", + "jackspeak": "^4.0.1", + "minimatch": "^10.0.0", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^2.0.0" + }, + "bin": { + "glob": "dist/esm/bin.mjs" }, "engines": { - "node": ">=16 || 14 >=14.17" + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/node_modules/jackspeak": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.0.2.tgz", + "integrity": "sha512-bZsjR/iRjl1Nk1UkjGpAzLNfQtzuijhn2g+pbZb98HQ1Gk8vM9hfbxeMBP+M2/UUdwj0RqGG3mlvk2MsAqwvEw==", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "engines": { + "node": "20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/rimraf/node_modules/lru-cache": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.0.1.tgz", + "integrity": "sha512-CgeuL5uom6j/ZVrg7G/+1IXqRY8JXX4Hghfy5YE0EhoYQWvndP1kufu58cmZLNIDKnRhZrXfdS9urVWx98AipQ==", + "engines": { + "node": "20 || >=22" + } + }, "node_modules/rimraf/node_modules/minimatch": { - "version": "8.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-8.0.4.tgz", - "integrity": "sha512-W0Wvr9HyFXZRGIDgCicunpQ299OKXs9RgZfaukz4qAW/pJhcpUfupc9c+OObPOFueNy8VSrZgEmDtk6Kh4WzDA==", - "dev": true, + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.1.tgz", + "integrity": "sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ==", "dependencies": { "brace-expansion": "^2.0.1" }, "engines": { - "node": ">=16 || 14 >=14.17" + "node": "20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/rimraf/node_modules/minipass": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-4.2.8.tgz", - "integrity": "sha512-fNzuVyifolSLFL4NzpF+wEF4qrgqaaKX0haXPQEdQ7NKAN+WecoKMHV09YcuL/DHxrUsYQOK3MiuDf7Ip2OXfQ==", - "dev": true, + "node_modules/rimraf/node_modules/path-scurry": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.0.tgz", + "integrity": "sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg==", + "dependencies": { + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" + }, "engines": { - "node": ">=8" + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/rollup": { @@ -13822,7 +13914,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, "dependencies": { "shebang-regex": "^3.0.0" }, @@ -13834,7 +13925,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, "engines": { "node": ">=8" } @@ -14215,7 +14305,6 @@ "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -14230,7 +14319,6 @@ "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -14256,7 +14344,6 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, "dependencies": { "ansi-regex": "^5.0.1" }, @@ -15827,7 +15914,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, "dependencies": { "isexe": "^2.0.0" }, @@ -15866,7 +15952,6 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -15883,7 +15968,6 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, "dependencies": { "color-convert": "^2.0.1" }, @@ -15898,7 +15982,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, "dependencies": { "color-name": "~1.1.4" }, @@ -15909,8 +15992,7 @@ "node_modules/wrap-ansi-cjs/node_modules/color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, "node_modules/wrap-ansi/node_modules/ansi-styles": { "version": "4.3.0", diff --git a/example/package.json b/example/package.json index a874929..4ca4c2e 100644 --- a/example/package.json +++ b/example/package.json @@ -6,7 +6,8 @@ "ng": "ng", "start": "ng serve", "preinstall:capacitor-calendar": "cd .. && npm ci && npm run build", - "build": "rm -rf .angular && npm run preinstall:capacitor-calendar && npm ci && ng build && npx cap sync", + "clean": "npx rimraf .angular", + "build": "npm run clean && npm run preinstall:capacitor-calendar && npm ci && ng build && npx cap sync", "open:ios": "npx cap open ios", "open:android": "npx cap open android", "watch": "ng build --watch --configuration development", @@ -33,6 +34,7 @@ "@ionic/core": "^8.2.5", "@ngrx/component": "^17.1.0", "@types/jest": "^29.5.12", + "rimraf": "^6.0.1", "rxjs": "~7.8.0", "tslib": "^2.3.0", "zone.js": "~0.14.3" diff --git a/ios/Plugin/CapacitorCalendar.swift b/ios/Plugin/CapacitorCalendar.swift index 85e422a..3e9b347 100644 --- a/ios/Plugin/CapacitorCalendar.swift +++ b/ios/Plugin/CapacitorCalendar.swift @@ -8,6 +8,13 @@ public class CapacitorCalendar: NSObject, EKEventEditViewDelegate, EKCalendarCho private var currentCreateEventContinuation: CheckedContinuation<[String], any Error>? private var currentSelectCalendarsContinuation: CheckedContinuation<[[String: Any]], any Error>? + private let recurrenceFrequencyMapping: [Int: EKRecurrenceFrequency] = [ + 0: .daily, + 1: .weekly, + 2: .monthly, + 3: .yearly + ] + init(bridge: (any CAPBridgeProtocol)?, eventStore: EKEventStore) { self.bridge = bridge self.eventStore = eventStore @@ -184,6 +191,8 @@ public class CapacitorCalendar: NSObject, EKEventEditViewDelegate, EKCalendarCho } } + setEventFrequency(event: event, recurrence: update.recurrence) + do { try eventStore.save(event, span: span) } catch { @@ -293,6 +302,8 @@ public class CapacitorCalendar: NSObject, EKEventEditViewDelegate, EKCalendarCho newEvent.url = url } + setEventFrequency(event: newEvent, recurrence: parameters.recurrence) + do { try eventStore.save(newEvent, span: .thisEvent) return newEvent.eventIdentifier @@ -301,6 +312,21 @@ public class CapacitorCalendar: NSObject, EKEventEditViewDelegate, EKCalendarCho } } + private func setEventFrequency(event: EKEvent, recurrence: RecurrenceParameters?) { + guard let frequency = recurrence?.frequency, let interval = recurrence?.interval else { return } + var endDate: EKRecurrenceEnd? + if let end = recurrence?.end { + endDate = EKRecurrenceEnd(end: Date(timeIntervalSince1970: end / 1000)) + } + if let recurrenceFrequency = recurrenceFrequencyMapping[frequency] { + event.recurrenceRules = [EKRecurrenceRule( + recurrenceWith: recurrenceFrequency, + interval: interval, + end: endDate + )] + } + } + public func checkAllPermissions() async throws -> [String: String] { return try await withCheckedThrowingContinuation { continuation in var permissionsState: [String: String] @@ -454,7 +480,7 @@ public class CapacitorCalendar: NSObject, EKEventEditViewDelegate, EKCalendarCho } do { - try eventStore.remove(event, span: .thisEvent, commit: false) + try eventStore.remove(event, span: .futureEvents, commit: false) deletedEvents.append("\(id)") } catch { failedToDeleteEvents.append("\(id)") @@ -472,6 +498,38 @@ public class CapacitorCalendar: NSObject, EKEventEditViewDelegate, EKCalendarCho } } + public func deleteEventById(id: String, span: EKSpan) async throws -> String? + { + await withCheckedContinuation { continuation in + + var deletedEvent: String? + + guard let event = eventStore.event(withIdentifier: "\(id)") else { + return + } + + do + { + try eventStore.remove(event, span: .futureEvents, commit: false) + deletedEvent = id + } + catch + { + } + + do + { + try eventStore.commit() + } + catch + { + deletedEvent = nil + } + + continuation.resume(returning: deletedEvent) + } + } + public func createCalendar(title: String, color: String?, sourceId: String?) throws -> String { let newCalendar = EKCalendar(for: .event, eventStore: eventStore) newCalendar.title = title @@ -615,10 +673,34 @@ public class CapacitorCalendar: NSObject, EKEventEditViewDelegate, EKCalendarCho } dict["isAllDay"] = event.isAllDay dict["calendarId"] = event.calendar.calendarIdentifier + + if let recurrenceRules = event.recurrenceRules { + let recurrence = extractEventRecurrenceRules(rules: recurrenceRules) + + if !recurrence.isEmpty { + dict["recurrence"] = recurrence + } + } return dict } } + + private func extractEventRecurrenceRules(rules: [EKRecurrenceRule]) -> [[String: Any]] { + return rules.map { rule in + var obj = [String: Any]() + + obj["frequency"] = rule.frequency.rawValue + obj["interval"] = rule.interval + + if let endDate = rule.recurrenceEnd?.endDate { + obj["end"] = endDate.timeIntervalSince1970 * 1000 + } + + return obj + } + } + private func hexStringFromColor(color: CGColor) -> String { guard let components = color.components, components.count >= 3 else { return "#000000" diff --git a/ios/Plugin/CapacitorCalendarPlugin.m b/ios/Plugin/CapacitorCalendarPlugin.m index a16aef8..e15457e 100644 --- a/ios/Plugin/CapacitorCalendarPlugin.m +++ b/ios/Plugin/CapacitorCalendarPlugin.m @@ -24,6 +24,7 @@ CAP_PLUGIN_METHOD(deleteCalendar, CAPPluginReturnNone); CAP_PLUGIN_METHOD(getRemindersFromLists, CAPPluginReturnPromise); CAP_PLUGIN_METHOD(deleteRemindersById, CAPPluginReturnPromise); + CAP_PLUGIN_METHOD(deleteReminderById, CAPPluginReturnPromise); CAP_PLUGIN_METHOD(requestWriteOnlyCalendarAccess, CAPPluginReturnPromise); CAP_PLUGIN_METHOD(requestFullCalendarAccess, CAPPluginReturnPromise); CAP_PLUGIN_METHOD(requestFullRemindersAccess, CAPPluginReturnPromise); diff --git a/ios/Plugin/CapacitorCalendarPlugin.swift b/ios/Plugin/CapacitorCalendarPlugin.swift index acfc99e..31a54cb 100644 --- a/ios/Plugin/CapacitorCalendarPlugin.swift +++ b/ios/Plugin/CapacitorCalendarPlugin.swift @@ -133,6 +133,23 @@ public class CapacitorCalendarPlugin: CAPPlugin { let notes = call.getString("notes") let url = call.getString("url") + var recurrence: RecurrenceParameters? + if let recurrenceData = call.getObject("recurrence") { + guard let frequency = recurrenceData["frequency"] as? Int else { + call.reject("[CapacitorCalendar.\(#function)] Frequency must be provided when using recurrence") + return + } + + guard let interval = recurrenceData["interval"] as? Int, interval > 0 else { + call.reject("[CapacitorCalendar.\(#function)] Interval must be greater than 0 when using recurrence") + return + } + + let end = recurrenceData["end"] as? Double + + recurrence = RecurrenceParameters(frequency: frequency, interval: interval, end: end) + } + Task { var eventParameters = EventCreationParameters( title: title, @@ -142,7 +159,8 @@ public class CapacitorCalendarPlugin: CAPPlugin { endDate: endDate, isAllDay: isAllDay, notes: notes, - url: url + url: url, + recurrence: recurrence ) if let alertOffsetInMinutesSingle = call.getDouble("alertOffsetInMinutes") as Double? { @@ -184,6 +202,23 @@ public class CapacitorCalendarPlugin: CAPPlugin { let alertOffsetInMinutesSingle = update["alertOffsetInMinutes"] as? Double let alertOffsetInMinutesMultiple = update["alertOffsetInMinutes"] as? [Double] + var recurrence: RecurrenceParameters? + if let recurrenceData = update["recurrence"] as? JSObject { + guard let frequency = recurrenceData["frequency"] as? Int else { + call.reject("[CapacitorCalendar.\(#function)] Frequency must be provided when using recurrence") + return + } + + guard let interval = recurrenceData["interval"] as? Int, interval > 0 else { + call.reject("[CapacitorCalendar.\(#function)] Interval must be greater than 0 when using recurrence") + return + } + + let end = recurrenceData["end"] as? Double + + recurrence = RecurrenceParameters(frequency: frequency, interval: interval, end: end) + } + var eventParameters = EventCreationParameters( title: title, calendarId: calendarId, @@ -194,7 +229,8 @@ public class CapacitorCalendarPlugin: CAPPlugin { alertOffsetInMinutesSingle: alertOffsetInMinutesSingle, alertOffsetInMinutesMultiple: alertOffsetInMinutesMultiple, notes: notes, - url: url + url: url, + recurrence: recurrence ) result = try await calendar.modifyEventWithPrompt(id: id, update: eventParameters) @@ -257,6 +293,23 @@ public class CapacitorCalendarPlugin: CAPPlugin { let notes = call.getString("notes") let url = call.getString("url") + var recurrence: RecurrenceParameters? + if let recurrenceData = call.getObject("recurrence") { + guard let frequency = recurrenceData["frequency"] as? Int else { + call.reject("[CapacitorCalendar.\(#function)] Frequency must be provided when using recurrence") + return + } + + guard let interval = recurrenceData["interval"] as? Int, interval > 0 else { + call.reject("[CapacitorCalendar.\(#function)] Interval must be greater than 0 when using recurrence") + return + } + + let end = recurrenceData["end"] as? Double + + recurrence = RecurrenceParameters(frequency: frequency, interval: interval, end: end) + } + var eventParameters = EventCreationParameters( title: title, calendarId: calendarId, @@ -265,7 +318,8 @@ public class CapacitorCalendarPlugin: CAPPlugin { endDate: endDate, isAllDay: isAllDay, notes: notes, - url: url + url: url, + recurrence: recurrence ) if let alertOffsetInMinutesSingle = call.getDouble("alertOffsetInMinutes") as Double? { @@ -304,6 +358,23 @@ public class CapacitorCalendarPlugin: CAPPlugin { let alertOffsetInMinutesSingle = update["alertOffsetInMinutes"] as? Double let alertOffsetInMinutesMultiple = update["alertOffsetInMinutes"] as? [Double] + var recurrence: RecurrenceParameters? + if let recurrenceData = update["recurrence"] as? JSObject { + guard let frequency = recurrenceData["frequency"] as? Int else { + call.reject("[CapacitorCalendar.\(#function)] Frequency must be provided when using recurrence") + return + } + + guard let interval = recurrenceData["interval"] as? Int, interval > 0 else { + call.reject("[CapacitorCalendar.\(#function)] Interval must be greater than 0 when using recurrence") + return + } + + let end = recurrenceData["end"] as? Double + + recurrence = RecurrenceParameters(frequency: frequency, interval: interval, end: end) + } + do { var eventParameters = EventCreationParameters( title: title, @@ -315,7 +386,8 @@ public class CapacitorCalendarPlugin: CAPPlugin { alertOffsetInMinutesSingle: alertOffsetInMinutesSingle, alertOffsetInMinutesMultiple: alertOffsetInMinutesMultiple, notes: notes, - url: url + url: url, + recurrence: recurrence ) try calendar.modifyEvent(id: eventId, span: EKSpan(rawValue: span) ?? .thisEvent, update: eventParameters) call.resolve() @@ -440,6 +512,38 @@ public class CapacitorCalendarPlugin: CAPPlugin { } } + @objc public func deleteEventById(_ call: CAPPluginCall) + { + guard let eventId = call.getString("id") else { + call.reject("[CapacitorCalendar.\(#function)] Event id were not provided") + return + } + + let span = call.getInt("span", 0) + + Task { + do { + let deleteResult = try await calendar.deleteEventById(id: eventId, + span: span == 0 ? .thisEvent : .futureEvents) + + if deleteResult != nil + { + call.resolve([ + "result": deleteResult! + ]) + } + else + { + call.reject("[CapacitorCalendar.\(#function)] Could not delete events") + } + } catch + { + call.reject("[CapacitorCalendar.\(#function)] Could not delete events") + return + } + } + } + @objc public func deleteEventsById(_ call: CAPPluginCall) { guard let eventIds = call.getArray("ids") else { call.reject("[CapacitorCalendar.\(#function)] Event ids were not provided") diff --git a/ios/Plugin/EventCreationParameters.swift b/ios/Plugin/EventCreationParameters.swift index 4d068ba..29f7f05 100644 --- a/ios/Plugin/EventCreationParameters.swift +++ b/ios/Plugin/EventCreationParameters.swift @@ -19,4 +19,5 @@ public struct EventCreationParameters { public var alertOffsetInMinutesMultiple: [Double]? public var notes: String? public var url: String? + public var recurrence: RecurrenceParameters? } diff --git a/ios/Plugin/RecurrenceParameters.swift b/ios/Plugin/RecurrenceParameters.swift index 2550a6c..796e393 100644 --- a/ios/Plugin/RecurrenceParameters.swift +++ b/ios/Plugin/RecurrenceParameters.swift @@ -8,7 +8,7 @@ import Foundation -struct RecurrenceParameters { +public struct RecurrenceParameters { let frequency: Int let interval: Int let end: Double? diff --git a/package-lock.json b/package-lock.json index 9db4c02..9fb6412 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,6 +8,9 @@ "name": "@ebarooni/capacitor-calendar", "version": "6.7.1", "license": "MIT", + "dependencies": { + "rimraf": "^6.0.1" + }, "devDependencies": { "@angular/cli": "^17.1.2", "@capacitor/android": "^6.0.0", @@ -443,6 +446,75 @@ "node": ">=10.3.0" } }, + "node_modules/@capacitor/cli/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@capacitor/cli/node_modules/glob": { + "version": "9.3.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-9.3.5.tgz", + "integrity": "sha512-e1LleDykUz2Iu+MTYdkSsuWX8lvAjAcs0Xef0lNIu0S2wOAzuTxCJtcd9S3cijlwYF18EsU3rzb8jPVobxDh9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "minimatch": "^8.0.2", + "minipass": "^4.2.4", + "path-scurry": "^1.6.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@capacitor/cli/node_modules/minimatch": { + "version": "8.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-8.0.4.tgz", + "integrity": "sha512-W0Wvr9HyFXZRGIDgCicunpQ299OKXs9RgZfaukz4qAW/pJhcpUfupc9c+OObPOFueNy8VSrZgEmDtk6Kh4WzDA==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@capacitor/cli/node_modules/minipass": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-4.2.8.tgz", + "integrity": "sha512-fNzuVyifolSLFL4NzpF+wEF4qrgqaaKX0haXPQEdQ7NKAN+WecoKMHV09YcuL/DHxrUsYQOK3MiuDf7Ip2OXfQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@capacitor/cli/node_modules/rimraf": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-4.4.1.tgz", + "integrity": "sha512-Gk8NlF062+T9CqNGn6h4tls3k6T1+/nXdOcSZVikNVtlRdYpA7wRJJMoXmuvOnLW844rPjdQ7JgXCYM6PPC/og==", + "dev": true, + "dependencies": { + "glob": "^9.2.0" + }, + "bin": { + "rimraf": "dist/cjs/src/bin.js" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/@capacitor/core": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/@capacitor/core/-/core-6.0.0.tgz", @@ -868,7 +940,6 @@ }, "node_modules/@isaacs/cliui": { "version": "8.0.2", - "dev": true, "license": "ISC", "dependencies": { "string-width": "^5.1.2", @@ -884,7 +955,6 @@ }, "node_modules/@isaacs/cliui/node_modules/ansi-regex": { "version": "6.0.1", - "dev": true, "license": "MIT", "engines": { "node": ">=12" @@ -895,7 +965,6 @@ }, "node_modules/@isaacs/cliui/node_modules/ansi-styles": { "version": "6.2.1", - "dev": true, "license": "MIT", "engines": { "node": ">=12" @@ -906,12 +975,10 @@ }, "node_modules/@isaacs/cliui/node_modules/emoji-regex": { "version": "9.2.2", - "dev": true, "license": "MIT" }, "node_modules/@isaacs/cliui/node_modules/string-width": { "version": "5.1.2", - "dev": true, "license": "MIT", "dependencies": { "eastasianwidth": "^0.2.0", @@ -927,7 +994,6 @@ }, "node_modules/@isaacs/cliui/node_modules/strip-ansi": { "version": "7.1.0", - "dev": true, "license": "MIT", "dependencies": { "ansi-regex": "^6.0.1" @@ -941,7 +1007,6 @@ }, "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { "version": "8.1.0", - "dev": true, "license": "MIT", "dependencies": { "ansi-styles": "^6.1.0", @@ -2542,7 +2607,6 @@ }, "node_modules/ansi-regex": { "version": "5.0.1", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -2550,7 +2614,6 @@ }, "node_modules/ansi-styles": { "version": "4.3.0", - "dev": true, "license": "MIT", "dependencies": { "color-convert": "^2.0.1" @@ -2634,7 +2697,6 @@ }, "node_modules/balanced-match": { "version": "1.0.2", - "dev": true, "license": "MIT" }, "node_modules/base64-js": { @@ -2905,7 +2967,6 @@ }, "node_modules/color-convert": { "version": "2.0.1", - "dev": true, "license": "MIT", "dependencies": { "color-name": "~1.1.4" @@ -2916,7 +2977,6 @@ }, "node_modules/color-name": { "version": "1.1.4", - "dev": true, "license": "MIT" }, "node_modules/colord": { @@ -2973,7 +3033,6 @@ }, "node_modules/cross-spawn": { "version": "7.0.3", - "dev": true, "license": "MIT", "dependencies": { "path-key": "^3.1.0", @@ -3139,7 +3198,6 @@ }, "node_modules/eastasianwidth": { "version": "0.2.0", - "dev": true, "license": "MIT" }, "node_modules/ejs": { @@ -3170,7 +3228,6 @@ }, "node_modules/emoji-regex": { "version": "8.0.0", - "dev": true, "license": "MIT" }, "node_modules/encoding": { @@ -3699,7 +3756,6 @@ }, "node_modules/foreground-child": { "version": "3.1.1", - "dev": true, "license": "ISC", "dependencies": { "cross-spawn": "^7.0.0", @@ -3714,7 +3770,6 @@ }, "node_modules/foreground-child/node_modules/signal-exit": { "version": "4.1.0", - "dev": true, "license": "ISC", "engines": { "node": ">=14" @@ -3812,8 +3867,9 @@ }, "node_modules/fs.realpath": { "version": "1.0.0", - "dev": true, - "license": "ISC" + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true }, "node_modules/fsevents": { "version": "2.3.3", @@ -4340,7 +4396,6 @@ }, "node_modules/is-fullwidth-code-point": { "version": "3.0.0", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -4421,7 +4476,6 @@ }, "node_modules/isexe": { "version": "2.0.0", - "dev": true, "license": "ISC" }, "node_modules/jackspeak": { @@ -5611,6 +5665,11 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==" + }, "node_modules/pacote": { "version": "17.0.5", "dev": true, @@ -5700,7 +5759,6 @@ }, "node_modules/path-key": { "version": "3.1.1", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -6167,18 +6225,18 @@ } }, "node_modules/rimraf": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-4.4.1.tgz", - "integrity": "sha512-Gk8NlF062+T9CqNGn6h4tls3k6T1+/nXdOcSZVikNVtlRdYpA7wRJJMoXmuvOnLW844rPjdQ7JgXCYM6PPC/og==", - "dev": true, + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-6.0.1.tgz", + "integrity": "sha512-9dkvaxAsk/xNXSJzMgFqqMCuFgt2+KsOFek3TMLfo8NCPfWpBmqwyNn5Y+NX56QUYfCtsyhF3ayiboEoUmJk/A==", "dependencies": { - "glob": "^9.2.0" + "glob": "^11.0.0", + "package-json-from-dist": "^1.0.0" }, "bin": { - "rimraf": "dist/cjs/src/bin.js" + "rimraf": "dist/esm/bin.mjs" }, "engines": { - "node": ">=14" + "node": "20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" @@ -6188,51 +6246,89 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, "dependencies": { "balanced-match": "^1.0.0" } }, "node_modules/rimraf/node_modules/glob": { - "version": "9.3.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-9.3.5.tgz", - "integrity": "sha512-e1LleDykUz2Iu+MTYdkSsuWX8lvAjAcs0Xef0lNIu0S2wOAzuTxCJtcd9S3cijlwYF18EsU3rzb8jPVobxDh9Q==", - "dev": true, + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-11.0.0.tgz", + "integrity": "sha512-9UiX/Bl6J2yaBbxKoEBRm4Cipxgok8kQYcOPEhScPwebu2I0HoQOuYdIO6S3hLuWoZgpDpwQZMzTFxgpkyT76g==", "dependencies": { - "fs.realpath": "^1.0.0", - "minimatch": "^8.0.2", - "minipass": "^4.2.4", - "path-scurry": "^1.6.1" + "foreground-child": "^3.1.0", + "jackspeak": "^4.0.1", + "minimatch": "^10.0.0", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^2.0.0" + }, + "bin": { + "glob": "dist/esm/bin.mjs" }, "engines": { - "node": ">=16 || 14 >=14.17" + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/node_modules/jackspeak": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.0.2.tgz", + "integrity": "sha512-bZsjR/iRjl1Nk1UkjGpAzLNfQtzuijhn2g+pbZb98HQ1Gk8vM9hfbxeMBP+M2/UUdwj0RqGG3mlvk2MsAqwvEw==", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "engines": { + "node": "20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/rimraf/node_modules/lru-cache": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.0.1.tgz", + "integrity": "sha512-CgeuL5uom6j/ZVrg7G/+1IXqRY8JXX4Hghfy5YE0EhoYQWvndP1kufu58cmZLNIDKnRhZrXfdS9urVWx98AipQ==", + "engines": { + "node": "20 || >=22" + } + }, "node_modules/rimraf/node_modules/minimatch": { - "version": "8.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-8.0.4.tgz", - "integrity": "sha512-W0Wvr9HyFXZRGIDgCicunpQ299OKXs9RgZfaukz4qAW/pJhcpUfupc9c+OObPOFueNy8VSrZgEmDtk6Kh4WzDA==", - "dev": true, + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.1.tgz", + "integrity": "sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ==", "dependencies": { "brace-expansion": "^2.0.1" }, "engines": { - "node": ">=16 || 14 >=14.17" + "node": "20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, "node_modules/rimraf/node_modules/minipass": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-4.2.8.tgz", - "integrity": "sha512-fNzuVyifolSLFL4NzpF+wEF4qrgqaaKX0haXPQEdQ7NKAN+WecoKMHV09YcuL/DHxrUsYQOK3MiuDf7Ip2OXfQ==", - "dev": true, + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", "engines": { - "node": ">=8" + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/rimraf/node_modules/path-scurry": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.0.tgz", + "integrity": "sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg==", + "dependencies": { + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/rollup": { @@ -6368,7 +6464,6 @@ }, "node_modules/shebang-command": { "version": "2.0.0", - "dev": true, "license": "MIT", "dependencies": { "shebang-regex": "^3.0.0" @@ -6379,7 +6474,6 @@ }, "node_modules/shebang-regex": { "version": "3.0.0", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -6568,7 +6662,6 @@ }, "node_modules/string-width": { "version": "4.2.3", - "dev": true, "license": "MIT", "dependencies": { "emoji-regex": "^8.0.0", @@ -6582,7 +6675,6 @@ "node_modules/string-width-cjs": { "name": "string-width", "version": "4.2.3", - "dev": true, "license": "MIT", "dependencies": { "emoji-regex": "^8.0.0", @@ -6595,7 +6687,6 @@ }, "node_modules/strip-ansi": { "version": "6.0.1", - "dev": true, "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1" @@ -6607,7 +6698,6 @@ "node_modules/strip-ansi-cjs": { "name": "strip-ansi", "version": "6.0.1", - "dev": true, "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1" @@ -7458,7 +7548,6 @@ }, "node_modules/which": { "version": "2.0.2", - "dev": true, "license": "ISC", "dependencies": { "isexe": "^2.0.0" @@ -7498,7 +7587,6 @@ "node_modules/wrap-ansi-cjs": { "name": "wrap-ansi", "version": "7.0.0", - "dev": true, "license": "MIT", "dependencies": { "ansi-styles": "^4.0.0", diff --git a/package.json b/package.json index 07c95de..1015db1 100644 --- a/package.json +++ b/package.json @@ -62,7 +62,7 @@ "docgen:api-reference": "docgen --api CapacitorCalendarPlugin --output-readme docs/api-reference.md --output-json dist/docs.json", "docgen:readme": "docgen --api CapacitorCalendarPlugin --output-readme README.md", "typedoc:index": "npx typedoc", - "clean": "rm -rf ./dist", + "clean": "npx rimraf ./dist", "build": "npm run clean && npm run docgen:api-reference && npm run docgen:readme && tsc && rollup -c rollup.config.mjs", "prepublishOnly": "npm run build", "serve": "cd example && npx ng serve", @@ -112,5 +112,8 @@ "prettier": "@ebarooni/prettier-config", "publishConfig": { "access": "public" + }, + "dependencies": { + "rimraf": "^6.0.1" } } diff --git a/src/definitions.ts b/src/definitions.ts index 60bed3d..75eb94f 100644 --- a/src/definitions.ts +++ b/src/definitions.ts @@ -6,7 +6,7 @@ import type { PermissionState } from "@capacitor/core"; import type { Calendar } from "./schemas/interfaces/calendar"; import type { RemindersList } from "./schemas/interfaces/reminders-list"; import type { PluginPermissionsMap } from "./schemas/interfaces/plugin-permissions-map"; -import type { ReminderRecurrenceRule } from "./schemas/interfaces/reminder-recurrence-rule"; +import type { RecurrenceRule } from "./schemas/interfaces/recurrence-rule"; import type { CalendarEvent } from "./schemas/interfaces/calendar-event"; import type { Reminder } from "./schemas/interfaces/reminder"; import type { CalendarSource } from "./schemas/interfaces/calendar-source"; @@ -126,6 +126,7 @@ export interface CapacitorCalendarPlugin { url?: string; notes?: string; eventIdOptional?: boolean; + recurrence?: RecurrenceRule; }): Promise<{ result: string[] }>; /** @@ -238,6 +239,7 @@ export interface CapacitorCalendarPlugin { alertOffsetInMinutes?: number | number[]; url?: string; notes?: string; + recurrence?: RecurrenceRule; }): Promise<{ result: string }>; /** @@ -324,7 +326,7 @@ export interface CapacitorCalendarPlugin { notes?: string; url?: string; location?: string; - recurrence?: ReminderRecurrenceRule; + recurrence?: RecurrenceRule; }): Promise<{ result: string }>; /** @@ -381,6 +383,8 @@ export interface CapacitorCalendarPlugin { /** * Deletes events from the calendar given their IDs. + * If the event is recurring it will automatically delete this and future events. + * To modify this behaviour consider using the method "deleteEventById". * * @async * @since 0.11.0 @@ -404,9 +408,32 @@ export interface CapacitorCalendarPlugin { * console.log(result.failed) // ['ID_DOES_NOT_EXIST'] */ deleteEventsById(options: { - ids: string[]; + ids: string[] }): Promise<{ result: { deleted: string[]; failed: string[] } }>; + /** + * Deletes an even from the calendar by their ID. + * + * @async + * @since TODO: Add version number + * @platform iOS, Android + * @permissions + *

Runtime Permissions:

+ *
    + *
  • iOS: writeCalendar
  • + *
  • Android: writeCalendar
  • + *
+ * @param {object} options Options for defining event ID and span. + * @param {number} options.ids The ID of the event that should be deleted. + * @returns {Promise<{ result: string }>} + * A promise that resolves to an object with one property: + * - result: string - The ID of the deleted event. + * @example + * const { result } = await CapacitorCalendar.deleteEventsById("ID_1") + * console.log(result.deleted) // ['ID_1'] + */ + deleteEventById(options: {id: string, span?: EventSpan}): Promise<{ result: string }>; + /** * Creates a calendar * @@ -603,6 +630,7 @@ export interface CapacitorCalendarPlugin { alertOffsetInMinutes?: number | number[]; url?: string; notes?: string; + recurrence?: RecurrenceRule; }; }): Promise<{ result: string[] }>; @@ -621,7 +649,7 @@ export interface CapacitorCalendarPlugin { * @param {Object} options The options for updating an event. * @param {string} options.id The id of the event to be modified. * @param {EventSpan} options.span The scope of the modifications. - * Only supported on iOS. (Optional) + * Only supported on iOS. If not supplied it will only affect this event. (Optional) * @param {Object} options.update The set of event properties to be modified. * If a property is not supported, it will be ignored. Modifying the reminder of an * event is currently not supported on Android. @@ -650,6 +678,7 @@ export interface CapacitorCalendarPlugin { alertOffsetInMinutes?: number | number[]; url?: string; notes?: string; + recurrence?: RecurrenceRule; }; }): Promise; @@ -729,7 +758,7 @@ export interface CapacitorCalendarPlugin { notes?: string; url?: string; location?: string; - recurrence?: ReminderRecurrenceRule; + recurrence?: RecurrenceRule; }; }): Promise; } diff --git a/src/index.ts b/src/index.ts index a767f34..372767f 100644 --- a/src/index.ts +++ b/src/index.ts @@ -2,7 +2,7 @@ import { registerPlugin } from "@capacitor/core"; import { CalendarChooserDisplayStyle } from "./schemas/enums/calendar-chooser-display-style"; import { CalendarChooserSelectionStyle } from "./schemas/enums/calendar-chooser-selection-style"; import { PluginPermission } from "./schemas/enums/plugin-permission"; -import { ReminderRecurrenceFrequency } from "./schemas/enums/reminder-recurrence-frequency"; +import { RecurrenceFrequency } from "./schemas/enums/recurrence-frequency"; import { EventSpan } from "./schemas/enums/event-span"; import { CalendarSourceType } from "./schemas/enums/calendar-source-type"; import { CalendarType } from "./schemas/enums/calendar-type"; @@ -10,7 +10,7 @@ import type { CapacitorCalendarPlugin } from "./definitions"; import type { Calendar } from "./schemas/interfaces/calendar"; import type { RemindersList } from "./schemas/interfaces/reminders-list"; import type { PluginPermissionsMap } from "./schemas/interfaces/plugin-permissions-map"; -import type { ReminderRecurrenceRule } from "./schemas/interfaces/reminder-recurrence-rule"; +import type { RecurrenceRule } from "./schemas/interfaces/recurrence-rule"; import type { CalendarEvent } from "./schemas/interfaces/calendar-event"; import type { Reminder } from "./schemas/interfaces/reminder"; import type { CalendarSource } from "./schemas/interfaces/calendar-source"; @@ -23,13 +23,13 @@ const CapacitorCalendar = registerPlugin( ); export * from "./definitions"; -export type { Calendar, RemindersList, ReminderRecurrenceRule, CalendarSource }; +export type { Calendar, RemindersList, RecurrenceRule, CalendarSource }; export { CalendarChooserSelectionStyle, CalendarChooserDisplayStyle, PluginPermission, PluginPermissionsMap, - ReminderRecurrenceFrequency, + RecurrenceFrequency, CalendarEvent, Reminder, EventSpan, diff --git a/src/schemas/enums/reminder-recurrence-frequency.ts b/src/schemas/enums/recurrence-frequency.ts similarity index 81% rename from src/schemas/enums/reminder-recurrence-frequency.ts rename to src/schemas/enums/recurrence-frequency.ts index b08d9ca..a36d632 100644 --- a/src/schemas/enums/reminder-recurrence-frequency.ts +++ b/src/schemas/enums/recurrence-frequency.ts @@ -1,10 +1,10 @@ /** * Enum representing the repeating frequency of the reminder. * - * @enum ReminderRecurrenceFrequency + * @enum RecurrenceFrequency * @platform iOS */ -export enum ReminderRecurrenceFrequency { +export enum RecurrenceFrequency { /** * The reminder repeats on a daily basis */ diff --git a/src/schemas/interfaces/reminder-recurrence-rule.ts b/src/schemas/interfaces/recurrence-rule.ts similarity index 84% rename from src/schemas/interfaces/reminder-recurrence-rule.ts rename to src/schemas/interfaces/recurrence-rule.ts index 58d3f0f..e5db43a 100644 --- a/src/schemas/interfaces/reminder-recurrence-rule.ts +++ b/src/schemas/interfaces/recurrence-rule.ts @@ -1,6 +1,6 @@ -import { ReminderRecurrenceFrequency } from '../enums/reminder-recurrence-frequency'; +import { RecurrenceFrequency } from '../enums/recurrence-frequency'; -export interface ReminderRecurrenceRule { +export interface RecurrenceRule { /** * How frequent should the reminder repeat. * @@ -11,7 +11,7 @@ export interface ReminderRecurrenceRule { * interval: 1, * } */ - frequency: ReminderRecurrenceFrequency; + frequency: RecurrenceFrequency; /** * The interval should be a number greater than 0. For values lower than 1 the method will throw an error. diff --git a/src/schemas/interfaces/reminder.ts b/src/schemas/interfaces/reminder.ts index 003ed83..3e938da 100644 --- a/src/schemas/interfaces/reminder.ts +++ b/src/schemas/interfaces/reminder.ts @@ -1,4 +1,4 @@ -import { ReminderRecurrenceRule } from './reminder-recurrence-rule'; +import { RecurrenceRule } from './recurrence-rule'; /** * Represents a reminder in a reminders list. @@ -14,7 +14,7 @@ import { ReminderRecurrenceRule } from './reminder-recurrence-rule'; * @property {number} startDate The start date of the reminder. (Optional) * @property {number} dueDate The due date of the reminder. (Optional) * @property {number} completionDate The completion date of the reminder. (Optional) - * @property {ReminderRecurrenceRule[]} recurrence The recurrence rules of the reminder. (Optional) + * @property {RecurrenceRule[]} recurrence The recurrence rules of the reminder. (Optional) */ export interface Reminder { /** @@ -75,5 +75,5 @@ export interface Reminder { /** * @platform iOS */ - recurrence?: ReminderRecurrenceRule[]; + recurrence?: RecurrenceRule[]; } diff --git a/src/web.ts b/src/web.ts index 99ae6e1..43364e0 100644 --- a/src/web.ts +++ b/src/web.ts @@ -1,7 +1,7 @@ import { PermissionState, WebPlugin } from "@capacitor/core"; import { CapacitorCalendarPlugin } from "./definitions"; import { PluginPermission } from "./schemas/enums/plugin-permission"; -import { ReminderRecurrenceRule } from "./schemas/interfaces/reminder-recurrence-rule"; +import { RecurrenceRule } from "./schemas/interfaces/recurrence-rule"; import { EventSpan } from "./schemas/enums/event-span"; import type { Calendar } from "./schemas/interfaces/calendar"; import type { RemindersList } from "./schemas/interfaces/reminders-list"; @@ -106,7 +106,7 @@ export class CapacitorCalendarWeb notes?: string; url?: string; location?: string; - recurrence?: ReminderRecurrenceRule; + recurrence?: RecurrenceRule; }): Promise<{ result: string }> { throw this.unimplemented( `${this.createReminder.name} is not implemented on the web`, @@ -154,6 +154,12 @@ export class CapacitorCalendarWeb ); } + public deleteEventById(_options: { id: string, span?: EventSpan }): Promise<{ result: string }> { + throw this.unimplemented( + `${this.deleteEventById.name} is not implemented on the web`, + ); + } + public createCalendar(_options: { title: string; color?: string; @@ -276,7 +282,7 @@ export class CapacitorCalendarWeb notes?: string; url?: string; location?: string; - recurrence?: ReminderRecurrenceRule; + recurrence?: RecurrenceRule; }; }): Promise { throw this.unimplemented( From d8d0d0ff0066d5820a1cea0f0cce072cd11d7e64 Mon Sep 17 00:00:00 2001 From: Joel Becker Date: Sun, 29 Sep 2024 13:25:55 +0200 Subject: [PATCH 2/8] Moved `rimraf` to `devDependencies` --- example/package.json | 4 ++-- package.json | 6 ++---- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/example/package.json b/example/package.json index 4ca4c2e..413bcb8 100644 --- a/example/package.json +++ b/example/package.json @@ -34,7 +34,6 @@ "@ionic/core": "^8.2.5", "@ngrx/component": "^17.1.0", "@types/jest": "^29.5.12", - "rimraf": "^6.0.1", "rxjs": "~7.8.0", "tslib": "^2.3.0", "zone.js": "~0.14.3" @@ -47,6 +46,7 @@ "@ionic/angular-toolkit": "^11.0.1", "jest": "^29.7.0", "jest-environment-jsdom": "^29.7.0", - "typescript": "~5.3.3" + "typescript": "~5.3.3", + "rimraf": "^6.0.1" } } diff --git a/package.json b/package.json index 1015db1..eb1e4ba 100644 --- a/package.json +++ b/package.json @@ -96,7 +96,8 @@ "swiftlint": "^1.0.2", "typedoc": "^0.26.5", "typescript": "~5.4.2", - "typescript-eslint": "^8.0.1" + "typescript-eslint": "^8.0.1", + "rimraf": "^6.0.1" }, "peerDependencies": { "@capacitor/core": "^6.0.0" @@ -112,8 +113,5 @@ "prettier": "@ebarooni/prettier-config", "publishConfig": { "access": "public" - }, - "dependencies": { - "rimraf": "^6.0.1" } } From bd7971054d4b95788ab51b1e4bfe2f0c5b626e1f Mon Sep 17 00:00:00 2001 From: Joel Becker Date: Sun, 29 Sep 2024 13:29:56 +0200 Subject: [PATCH 3/8] Added missing `recurrence` to `create` and `modify` event methods --- src/web.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/web.ts b/src/web.ts index 43364e0..e13e4d6 100644 --- a/src/web.ts +++ b/src/web.ts @@ -53,6 +53,7 @@ export class CapacitorCalendarWeb url?: string; notes?: string; eventIdOptional?: boolean; + recurrence?: RecurrenceRule; }): Promise<{ result: string[] }> { throw this.unimplemented( `${this.createEventWithPrompt.name} is not implemented on the web`, @@ -87,6 +88,7 @@ export class CapacitorCalendarWeb alertOffsetInMinutes?: number | number[]; url?: string; notes?: string; + recurrence?: RecurrenceRule; }): Promise<{ result: string; }> { @@ -230,6 +232,7 @@ export class CapacitorCalendarWeb alertOffsetInMinutes?: number | number[]; url?: string; notes?: string; + recurrence?: RecurrenceRule; }; }): Promise<{ result: string[] }> { throw this.unimplemented( @@ -250,6 +253,7 @@ export class CapacitorCalendarWeb alertOffsetInMinutes?: number | number[]; url?: string; notes?: string; + recurrence?: RecurrenceRule; }; }): Promise { throw this.unimplemented( From 93eaea1c69adf4c0596d03f7409b9504ef056e16 Mon Sep 17 00:00:00 2001 From: Joel Becker Date: Sun, 29 Sep 2024 13:30:44 +0200 Subject: [PATCH 4/8] Reformatted code with `npm run fmt` --- README.md | 66 ++++++++-------- docs/api-reference.md | 173 +++++++++++++++--------------------------- 2 files changed, 96 insertions(+), 143 deletions(-) diff --git a/README.md b/README.md index 1848252..3e54dab 100644 --- a/README.md +++ b/README.md @@ -117,39 +117,39 @@ usage descriptions can be found below: -* [`checkPermission(...)`](#checkpermission) -* [`checkAllPermissions()`](#checkallpermissions) -* [`requestPermission(...)`](#requestpermission) -* [`requestAllPermissions()`](#requestallpermissions) -* [`createEventWithPrompt(...)`](#createeventwithprompt) -* [`selectCalendarsWithPrompt(...)`](#selectcalendarswithprompt) -* [`listCalendars()`](#listcalendars) -* [`getDefaultCalendar()`](#getdefaultcalendar) -* [`createEvent(...)`](#createevent) -* [`getDefaultRemindersList()`](#getdefaultreminderslist) -* [`getRemindersLists()`](#getreminderslists) -* [`createReminder(...)`](#createreminder) -* [`openCalendar(...)`](#opencalendar) -* [`openReminders()`](#openreminders) -* [`listEventsInRange(...)`](#listeventsinrange) -* [`deleteEventsById(...)`](#deleteeventsbyid) -* [`deleteEventById(...)`](#deleteeventbyid) -* [`createCalendar(...)`](#createcalendar) -* [`deleteCalendar(...)`](#deletecalendar) -* [`getRemindersFromLists(...)`](#getremindersfromlists) -* [`deleteRemindersById(...)`](#deleteremindersbyid) -* [`requestWriteOnlyCalendarAccess()`](#requestwriteonlycalendaraccess) -* [`requestReadOnlyCalendarAccess()`](#requestreadonlycalendaraccess) -* [`requestFullCalendarAccess()`](#requestfullcalendaraccess) -* [`requestFullRemindersAccess()`](#requestfullremindersaccess) -* [`modifyEventWithPrompt(...)`](#modifyeventwithprompt) -* [`modifyEvent(...)`](#modifyevent) -* [`fetchAllCalendarSources()`](#fetchallcalendarsources) -* [`fetchAllRemindersSources()`](#fetchallreminderssources) -* [`modifyReminder(...)`](#modifyreminder) -* [Interfaces](#interfaces) -* [Type Aliases](#type-aliases) -* [Enums](#enums) +- [`checkPermission(...)`](#checkpermission) +- [`checkAllPermissions()`](#checkallpermissions) +- [`requestPermission(...)`](#requestpermission) +- [`requestAllPermissions()`](#requestallpermissions) +- [`createEventWithPrompt(...)`](#createeventwithprompt) +- [`selectCalendarsWithPrompt(...)`](#selectcalendarswithprompt) +- [`listCalendars()`](#listcalendars) +- [`getDefaultCalendar()`](#getdefaultcalendar) +- [`createEvent(...)`](#createevent) +- [`getDefaultRemindersList()`](#getdefaultreminderslist) +- [`getRemindersLists()`](#getreminderslists) +- [`createReminder(...)`](#createreminder) +- [`openCalendar(...)`](#opencalendar) +- [`openReminders()`](#openreminders) +- [`listEventsInRange(...)`](#listeventsinrange) +- [`deleteEventsById(...)`](#deleteeventsbyid) +- [`deleteEventById(...)`](#deleteeventbyid) +- [`createCalendar(...)`](#createcalendar) +- [`deleteCalendar(...)`](#deletecalendar) +- [`getRemindersFromLists(...)`](#getremindersfromlists) +- [`deleteRemindersById(...)`](#deleteremindersbyid) +- [`requestWriteOnlyCalendarAccess()`](#requestwriteonlycalendaraccess) +- [`requestReadOnlyCalendarAccess()`](#requestreadonlycalendaraccess) +- [`requestFullCalendarAccess()`](#requestfullcalendaraccess) +- [`requestFullRemindersAccess()`](#requestfullremindersaccess) +- [`modifyEventWithPrompt(...)`](#modifyeventwithprompt) +- [`modifyEvent(...)`](#modifyevent) +- [`fetchAllCalendarSources()`](#fetchallcalendarsources) +- [`fetchAllRemindersSources()`](#fetchallreminderssources) +- [`modifyReminder(...)`](#modifyreminder) +- [Interfaces](#interfaces) +- [Type Aliases](#type-aliases) +- [Enums](#enums) diff --git a/docs/api-reference.md b/docs/api-reference.md index cd48d63..26bbf7b 100644 --- a/docs/api-reference.md +++ b/docs/api-reference.md @@ -2,39 +2,39 @@ -* [`checkPermission(...)`](#checkpermission) -* [`checkAllPermissions()`](#checkallpermissions) -* [`requestPermission(...)`](#requestpermission) -* [`requestAllPermissions()`](#requestallpermissions) -* [`createEventWithPrompt(...)`](#createeventwithprompt) -* [`selectCalendarsWithPrompt(...)`](#selectcalendarswithprompt) -* [`listCalendars()`](#listcalendars) -* [`getDefaultCalendar()`](#getdefaultcalendar) -* [`createEvent(...)`](#createevent) -* [`getDefaultRemindersList()`](#getdefaultreminderslist) -* [`getRemindersLists()`](#getreminderslists) -* [`createReminder(...)`](#createreminder) -* [`openCalendar(...)`](#opencalendar) -* [`openReminders()`](#openreminders) -* [`listEventsInRange(...)`](#listeventsinrange) -* [`deleteEventsById(...)`](#deleteeventsbyid) -* [`deleteEventById(...)`](#deleteeventbyid) -* [`createCalendar(...)`](#createcalendar) -* [`deleteCalendar(...)`](#deletecalendar) -* [`getRemindersFromLists(...)`](#getremindersfromlists) -* [`deleteRemindersById(...)`](#deleteremindersbyid) -* [`requestWriteOnlyCalendarAccess()`](#requestwriteonlycalendaraccess) -* [`requestReadOnlyCalendarAccess()`](#requestreadonlycalendaraccess) -* [`requestFullCalendarAccess()`](#requestfullcalendaraccess) -* [`requestFullRemindersAccess()`](#requestfullremindersaccess) -* [`modifyEventWithPrompt(...)`](#modifyeventwithprompt) -* [`modifyEvent(...)`](#modifyevent) -* [`fetchAllCalendarSources()`](#fetchallcalendarsources) -* [`fetchAllRemindersSources()`](#fetchallreminderssources) -* [`modifyReminder(...)`](#modifyreminder) -* [Interfaces](#interfaces) -* [Type Aliases](#type-aliases) -* [Enums](#enums) +- [`checkPermission(...)`](#checkpermission) +- [`checkAllPermissions()`](#checkallpermissions) +- [`requestPermission(...)`](#requestpermission) +- [`requestAllPermissions()`](#requestallpermissions) +- [`createEventWithPrompt(...)`](#createeventwithprompt) +- [`selectCalendarsWithPrompt(...)`](#selectcalendarswithprompt) +- [`listCalendars()`](#listcalendars) +- [`getDefaultCalendar()`](#getdefaultcalendar) +- [`createEvent(...)`](#createevent) +- [`getDefaultRemindersList()`](#getdefaultreminderslist) +- [`getRemindersLists()`](#getreminderslists) +- [`createReminder(...)`](#createreminder) +- [`openCalendar(...)`](#opencalendar) +- [`openReminders()`](#openreminders) +- [`listEventsInRange(...)`](#listeventsinrange) +- [`deleteEventsById(...)`](#deleteeventsbyid) +- [`deleteEventById(...)`](#deleteeventbyid) +- [`createCalendar(...)`](#createcalendar) +- [`deleteCalendar(...)`](#deletecalendar) +- [`getRemindersFromLists(...)`](#getremindersfromlists) +- [`deleteRemindersById(...)`](#deleteremindersbyid) +- [`requestWriteOnlyCalendarAccess()`](#requestwriteonlycalendaraccess) +- [`requestReadOnlyCalendarAccess()`](#requestreadonlycalendaraccess) +- [`requestFullCalendarAccess()`](#requestfullcalendaraccess) +- [`requestFullRemindersAccess()`](#requestfullremindersaccess) +- [`modifyEventWithPrompt(...)`](#modifyeventwithprompt) +- [`modifyEvent(...)`](#modifyevent) +- [`fetchAllCalendarSources()`](#fetchallcalendarsources) +- [`fetchAllRemindersSources()`](#fetchallreminderssources) +- [`modifyReminder(...)`](#modifyreminder) +- [Interfaces](#interfaces) +- [Type Aliases](#type-aliases) +- [Enums](#enums) @@ -57,8 +57,7 @@ Checks the current authorization status of a specific permission. **Since:** 0.1.0 --------------------- - +--- ### checkAllPermissions() @@ -72,8 +71,7 @@ Checks the current authorization status of all the required permissions for the **Since:** 0.1.0 --------------------- - +--- ### requestPermission(...) @@ -92,8 +90,7 @@ If the permission is already granted, it will directly return the status. **Since:** 0.1.0 --------------------- - +--- ### requestAllPermissions() @@ -107,8 +104,7 @@ Requests authorization to all the required permissions for the plugin, if they h **Since:** 0.1.0 --------------------- - +--- ### createEventWithPrompt(...) @@ -127,8 +123,7 @@ On iOS opens a native sheet and on Android opens an intent. **Since:** 0.1.0 --------------------- - +--- ### selectCalendarsWithPrompt(...) @@ -146,8 +141,7 @@ Presents a prompt to the user to select calendars. This method is available only **Since:** 0.2.0 --------------------- - +--- ### listCalendars() @@ -159,8 +153,7 @@ Retrieves a list of calendars available on the device. **Returns:** Promise<{ result: Calendar[]; }> --------------------- - +--- ### getDefaultCalendar() @@ -174,8 +167,7 @@ Retrieves the default calendar set on the device. **Since:** 0.3.0 --------------------- - +--- ### createEvent(...) @@ -193,8 +185,7 @@ Creates an event with the provided options. **Since:** 0.4.0 --------------------- - +--- ### getDefaultRemindersList() @@ -206,8 +197,7 @@ Retrieves the default reminders list set on the device. **Returns:** Promise<{ result: RemindersList; }> --------------------- - +--- ### getRemindersLists() @@ -219,8 +209,7 @@ Retrieves all available reminders lists on the device. **Returns:** Promise<{ result: RemindersList[]; }> --------------------- - +--- ### createReminder(...) @@ -238,8 +227,7 @@ Creates a reminder with the provided options. **Since:** 0.5.0 --------------------- - +--- ### openCalendar(...) @@ -254,8 +242,7 @@ It will open the calendar on today's date if no date is provided. | ------------- | ------------------------------- | ----------------------------------- | | **`options`** | { date?: number; } | - Options for opening the calendar. | --------------------- - +--- ### openReminders() @@ -265,8 +252,7 @@ openReminders() => Promise Opens the reminders app. Since the user leaves your app, use this method with caution. --------------------- - +--- ### listEventsInRange(...) @@ -284,8 +270,7 @@ Retrieves the list of calendar events present in the given date range. **Since:** 0.10.0 --------------------- - +--- ### deleteEventsById(...) @@ -305,8 +290,7 @@ To modify this behaviour consider using the method "deleteEventById". **Since:** 0.11.0 --------------------- - +--- ### deleteEventById(...) @@ -324,8 +308,7 @@ Deletes an even from the calendar by their ID. **Since:** TODO: Add version number --------------------- - +--- ### createCalendar(...) @@ -343,8 +326,7 @@ Creates a calendar **Since:** 5.2.0 --------------------- - +--- ### deleteCalendar(...) @@ -360,8 +342,7 @@ Deletes a calendar by id **Since:** 5.2.0 --------------------- - +--- ### getRemindersFromLists(...) @@ -379,8 +360,7 @@ Retrieves the list of reminders present in the given date range. **Since:** 5.3.0 --------------------- - +--- ### deleteRemindersById(...) @@ -398,8 +378,7 @@ Deletes reminders given their IDs. **Since:** 5.3.0 --------------------- - +--- ### requestWriteOnlyCalendarAccess() @@ -413,8 +392,7 @@ Requests write access for the calendar. If its already granted, it will directly **Since:** 5.4.0 --------------------- - +--- ### requestReadOnlyCalendarAccess() @@ -428,8 +406,7 @@ Requests read access for the calendar. If its already granted, it will directly **Since:** 5.4.0 --------------------- - +--- ### requestFullCalendarAccess() @@ -443,8 +420,7 @@ Requests read and write access for the calendar. If its already granted, it will **Since:** 5.4.0 --------------------- - +--- ### requestFullRemindersAccess() @@ -458,8 +434,7 @@ Requests read and write access for the reminders. If its already granted, it wil **Since:** 5.4.0 --------------------- - +--- ### modifyEventWithPrompt(...) @@ -477,8 +452,7 @@ Opens a native prompt to modify an event given its id. **Since:** 6.6.0 --------------------- - +--- ### modifyEvent(...) @@ -494,8 +468,7 @@ Modifies an event given its id and update details. **Since:** 6.6.0 --------------------- - +--- ### fetchAllCalendarSources() @@ -509,8 +482,7 @@ Retrieves a list of calendar sources. **Since:** 6.6.0 --------------------- - +--- ### fetchAllRemindersSources() @@ -524,8 +496,7 @@ Retrieves a list of reminders sources. **Since:** 6.6.0 --------------------- - +--- ### modifyReminder(...) @@ -541,15 +512,12 @@ Modifies a reminder given its id and update details. **Since:** 6.7.0 --------------------- - +--- ### Interfaces - #### PluginPermissionsMap - #### ReminderRecurrenceRule | Prop | Type | Description | @@ -558,7 +526,6 @@ Modifies a reminder given its id and update details. | **`interval`** | number | The interval should be a number greater than 0. For values lower than 1 the method will throw an error. | | **`end`** | number | When provided, the reminder will stop repeating at the given time. | - #### Calendar Represents a calendar object. @@ -574,7 +541,6 @@ Represents a calendar object. | **`isSubscribed`** | boolean | | **`source`** | CalendarSource | - #### CalendarSource Represents the account a calendar belongs to @@ -585,10 +551,8 @@ Represents the account a calendar belongs to | **`id`** | string | | **`title`** | string | - #### RemindersList - #### CalendarEvent Represents an event in the calendar. @@ -610,7 +574,6 @@ Represents an event in the calendar. | **`calendarId`** | string | | **`url`** | string | - #### Reminder Represents a reminder in a reminders list. @@ -630,18 +593,14 @@ Represents a reminder in a reminders list. | **`completionDate`** | number | | **`recurrence`** | ReminderRecurrenceRule[] | - ### Type Aliases - #### PermissionState 'prompt' | 'prompt-with-rationale' | 'granted' | 'denied' - ### Enums - #### PluginPermission | Members | Value | Description | @@ -651,7 +610,6 @@ Represents a reminder in a reminders list. | **`READ_REMINDERS`** | 'readReminders' | Represents the permission state for reading reminders. | | **`WRITE_REMINDERS`** | 'writeReminders' | Represents the permission state for writing reminders. | - #### ReminderRecurrenceFrequency | Members | Description | @@ -661,7 +619,6 @@ Represents a reminder in a reminders list. | **`MONTHLY`** | The reminder repeats on a monthly basis | | **`YEARLY`** | The reminder repeats on a yearly basis | - #### CalendarType | Members | Description | @@ -672,7 +629,6 @@ Represents a reminder in a reminders list. | **`SUBSCRIPTION`** | This is a locally subscribed calendar. | | **`BIRTHDAY`** | This is the built-in birthday calendar. | - #### CalendarSourceType | Members | Description | @@ -684,7 +640,6 @@ Represents a reminder in a reminders list. | **`SUBSCRIBED`** | Calendars that the user has subscribed to. These are read-only calendars that can be added by subscribing to a calendar URL. | | **`BIRTHDAYS`** | The built-in Birthdays calendar, which shows birthdays of contacts from the user's address book. This calendar is typically read-only and is managed by the system. | - #### CalendarChooserDisplayStyle | Members | Description | @@ -692,7 +647,6 @@ Represents a reminder in a reminders list. | **`ALL_CALENDARS`** | Display all calendars available for selection. | | **`WRITABLE_CALENDARS_ONLY`** | Display only writable calendars available for selection. | - #### CalendarChooserSelectionStyle | Members | Description | @@ -700,7 +654,6 @@ Represents a reminder in a reminders list. | **`SINGLE`** | Allows only a single selection in the calendar chooser. | | **`MULTIPLE`** | Allows multiple selections in the calendar chooser. | - #### EventSpan | Members | Description | From a7d5908ca86b1ac5e1aca08466329360c7c46954 Mon Sep 17 00:00:00 2001 From: Joel Becker Date: Sun, 29 Sep 2024 13:37:50 +0200 Subject: [PATCH 5/8] Removed id from `deleteEventById` as the id is already known by the caller. --- .../barooni/capacitor/calendar/CapacitorCalendarPlugin.kt | 8 +++----- src/definitions.ts | 2 +- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/android/src/main/java/dev/barooni/capacitor/calendar/CapacitorCalendarPlugin.kt b/android/src/main/java/dev/barooni/capacitor/calendar/CapacitorCalendarPlugin.kt index a4d2d25..95e92ea 100644 --- a/android/src/main/java/dev/barooni/capacitor/calendar/CapacitorCalendarPlugin.kt +++ b/android/src/main/java/dev/barooni/capacitor/calendar/CapacitorCalendarPlugin.kt @@ -481,13 +481,11 @@ class CapacitorCalendarPlugin : Plugin() { val id = call.getString("id") ?: throw Exception("[CapacitorCalendar.${::deleteEventById.name}] Event ids were not provided") - if (implementation.deleteEventById(context, id)) { - call.resolve(JSObject().apply { - put("result", id) - }) + if (!implementation.deleteEventById(context, id)) { + throw Exception("Event was not deleted") } - throw Exception("Event was not deleted") + call.resolve() } catch (error: Exception) { call.reject("", "[CapacitorCalendar.${::deleteEventById.name}] Could not delete event", error) } diff --git a/src/definitions.ts b/src/definitions.ts index 75eb94f..f74ebd3 100644 --- a/src/definitions.ts +++ b/src/definitions.ts @@ -432,7 +432,7 @@ export interface CapacitorCalendarPlugin { * const { result } = await CapacitorCalendar.deleteEventsById("ID_1") * console.log(result.deleted) // ['ID_1'] */ - deleteEventById(options: {id: string, span?: EventSpan}): Promise<{ result: string }>; + deleteEventById(options: {id: string, span?: EventSpan}): Promise; /** * Creates a calendar From 14ff31f2e45cc77aff7fbc7a07a35ca6b3a12591 Mon Sep 17 00:00:00 2001 From: Joel Becker Date: Sun, 29 Sep 2024 13:40:22 +0200 Subject: [PATCH 6/8] Registered `deleteEventById` in `.m` --- ios/Plugin/CapacitorCalendarPlugin.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ios/Plugin/CapacitorCalendarPlugin.m b/ios/Plugin/CapacitorCalendarPlugin.m index e15457e..796a256 100644 --- a/ios/Plugin/CapacitorCalendarPlugin.m +++ b/ios/Plugin/CapacitorCalendarPlugin.m @@ -20,11 +20,11 @@ CAP_PLUGIN_METHOD(openReminders, CAPPluginReturnNone); CAP_PLUGIN_METHOD(listEventsInRange, CAPPluginReturnPromise); CAP_PLUGIN_METHOD(deleteEventsById, CAPPluginReturnPromise); + CAP_PLUGIN_METHOD(deleteEventById, CAPPluginReturnPromise); CAP_PLUGIN_METHOD(createCalendar, CAPPluginReturnPromise); CAP_PLUGIN_METHOD(deleteCalendar, CAPPluginReturnNone); CAP_PLUGIN_METHOD(getRemindersFromLists, CAPPluginReturnPromise); CAP_PLUGIN_METHOD(deleteRemindersById, CAPPluginReturnPromise); - CAP_PLUGIN_METHOD(deleteReminderById, CAPPluginReturnPromise); CAP_PLUGIN_METHOD(requestWriteOnlyCalendarAccess, CAPPluginReturnPromise); CAP_PLUGIN_METHOD(requestFullCalendarAccess, CAPPluginReturnPromise); CAP_PLUGIN_METHOD(requestFullRemindersAccess, CAPPluginReturnPromise); From 0b10025d201b7469266d3a1c264d2ddb12a33076 Mon Sep 17 00:00:00 2001 From: Joel Becker Date: Sun, 29 Sep 2024 13:41:19 +0200 Subject: [PATCH 7/8] Take span into account when deleting event by id --- ios/Plugin/CapacitorCalendar.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ios/Plugin/CapacitorCalendar.swift b/ios/Plugin/CapacitorCalendar.swift index 3e9b347..7fa443a 100644 --- a/ios/Plugin/CapacitorCalendar.swift +++ b/ios/Plugin/CapacitorCalendar.swift @@ -510,7 +510,7 @@ public class CapacitorCalendar: NSObject, EKEventEditViewDelegate, EKCalendarCho do { - try eventStore.remove(event, span: .futureEvents, commit: false) + try eventStore.remove(event, span, commit: false) deletedEvent = id } catch From cdb6566413d461c2062786883987ebbb2bb57853 Mon Sep 17 00:00:00 2001 From: Joel Becker Date: Wed, 2 Oct 2024 08:03:10 +0200 Subject: [PATCH 8/8] Removed `result` from `deleteEventById` --- ios/Plugin/CapacitorCalendarPlugin.swift | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/ios/Plugin/CapacitorCalendarPlugin.swift b/ios/Plugin/CapacitorCalendarPlugin.swift index 31a54cb..dc7bddf 100644 --- a/ios/Plugin/CapacitorCalendarPlugin.swift +++ b/ios/Plugin/CapacitorCalendarPlugin.swift @@ -528,9 +528,7 @@ public class CapacitorCalendarPlugin: CAPPlugin { if deleteResult != nil { - call.resolve([ - "result": deleteResult! - ]) + call.resolve() } else {