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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion apps/test-suite/tests/CalendarNext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -498,7 +498,10 @@ export async function test(t) {
t.it('creates an event via UI', async () => {
const eventData = createEventData();
await alertAndWaitForResponse('Please confirm the event creation dialog.');
const result = await Calendar.createEventInCalendarAsync(eventData, dontStartNewTask);
const result = await calendar.addEventWithForm({
...eventData,
...dontStartNewTask,
});
if (Platform.OS === 'ios') {
t.expect(result.action).toBe('saved');
t.expect(typeof result.id).toBe('string');
Expand Down
2 changes: 1 addition & 1 deletion packages/expo-brownfield/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@
"chalk": "^4.1.2",
"commander": "^14.0.3",
"diff": "^5.2.0",
"expo-build-properties": "workspace:~56.0.10",
"expo-build-properties": "workspace:~56.0.11",
"expo-manifests": "workspace:~56.0.4",
"ora": "^5.4.1",
"prompts": "^2.4.2"
Expand Down
4 changes: 4 additions & 0 deletions packages/expo-build-properties/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@

### 💡 Others

## 56.0.11 — 2026-05-20

_This version does not introduce any user-facing changes._

## 56.0.10 — 2026-05-19

_This version does not introduce any user-facing changes._
Expand Down
2 changes: 1 addition & 1 deletion packages/expo-build-properties/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "expo-build-properties",
"version": "56.0.10",
"version": "56.0.11",
"description": "Config plugin to customize native build properties on prebuild",
"main": "build/withBuildProperties.js",
"types": "build/withBuildProperties.d.ts",
Expand Down
2 changes: 2 additions & 0 deletions packages/expo-calendar/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@

### 🎉 New features

- [Android][next] Add `calendar.addEventWithForm()` ([#46004](https://github.com/expo/expo/pull/46004) by [@Wenszel](https://github.com/Wenszel))

### 🐛 Bug fixes

- [next] Remove legacy exports ([#45739](https://github.com/expo/expo/pull/45739) by [@Wenszel](https://github.com/Wenszel))
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package expo.modules.calendar.next

import android.content.Context
import android.content.Intent
import android.content.Intent.ACTION_INSERT
import android.provider.CalendarContract
import android.provider.CalendarContract.EXTRA_EVENT_ALL_DAY
import android.provider.CalendarContract.EXTRA_EVENT_BEGIN_TIME
import android.provider.CalendarContract.EXTRA_EVENT_END_TIME
import expo.modules.calendar.CalendarUtils
import expo.modules.calendar.dialogs.CreateEventIntentResult
import expo.modules.calendar.next.mappers.EventMapper
import expo.modules.calendar.next.records.AddEventWithFormOptionsRecord
import expo.modules.kotlin.activityresult.AppContextActivityResultContract
import java.io.Serializable

internal class AddEventWithFormContract(private val eventMapper: EventMapper) :
AppContextActivityResultContract<AddEventWithFormContract.Input, CreateEventIntentResult> {
data class Input(
val calendarId: String,
val options: AddEventWithFormOptionsRecord
) : Serializable

override fun createIntent(context: Context, input: Input): Intent =
Intent(ACTION_INSERT)
.setData(CalendarContract.Events.CONTENT_URI)
.apply {
input.calendarId.toLongOrNull()?.let {
putExtra(CalendarContract.Events.CALENDAR_ID, it)
}
input.options.title?.let { putExtra(CalendarContract.Events.TITLE, it) }
input.options.allDay?.let { putExtra(EXTRA_EVENT_ALL_DAY, it) }
input.options.notes?.let { putExtra(CalendarContract.Events.DESCRIPTION, it) }
input.options.location?.let { putExtra(CalendarContract.Events.EVENT_LOCATION, it) }
input.options.startDate?.let {
putExtra(EXTRA_EVENT_BEGIN_TIME, getTimestamp(it))
}
input.options.endDate?.let {
putExtra(EXTRA_EVENT_END_TIME, getTimestamp(it))
}
input.options.timeZone?.let {
putExtra(CalendarContract.Events.EVENT_TIMEZONE, it)
}
input.options.availability?.let {
putExtra(CalendarContract.Events.AVAILABILITY, with(eventMapper) { it.toDomain().value })
}
input.options.recurrenceRule?.let {
with(eventMapper) {
it.toDomain().toRuleString()
}
}?.let {
putExtra(CalendarContract.Events.RRULE, it)
}
input.options.startNewActivityTask.takeIf { it }?.let {
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
}
}

private fun getTimestamp(value: String): Long {
val maybeTimestamp = CalendarUtils.sdf.parse(value)?.time
return maybeTimestamp ?: throw IllegalArgumentException("Invalid date format")
}

override fun parseResult(input: Input, resultCode: Int, intent: Intent?): CreateEventIntentResult =
CreateEventIntentResult()
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
package expo.modules.calendar.next

import android.Manifest
import expo.modules.calendar.dialogs.CreateEventContract
import expo.modules.calendar.dialogs.CreateEventIntentResult
import expo.modules.calendar.dialogs.CreatedEventOptions
import expo.modules.calendar.dialogs.ViewEventContract
import expo.modules.calendar.dialogs.ViewEventIntentResult
import expo.modules.calendar.dialogs.ViewedEventOptions
Expand All @@ -13,8 +11,10 @@ import expo.modules.calendar.next.domain.repositories.instance.InstanceRepositor
import expo.modules.calendar.next.domain.repositories.attendee.AttendeeRepository
import expo.modules.calendar.next.domain.repositories.reminder.ReminderRepository
import expo.modules.calendar.next.domain.repositories.calendar.CalendarRepository
import expo.modules.calendar.next.exceptions.CalendarNotFoundException
import expo.modules.calendar.next.mappers.AttendeeMapper
import expo.modules.calendar.next.records.AttendeeRecord
import expo.modules.calendar.next.records.AddEventWithFormOptionsRecord
import expo.modules.calendar.next.mappers.CalendarMapper
import expo.modules.calendar.next.mappers.EventMapper
import expo.modules.calendar.next.mappers.ReminderMapper
Expand All @@ -34,7 +34,8 @@ import expo.modules.kotlin.modules.Module
import expo.modules.kotlin.modules.ModuleDefinition

class CalendarNextModule : Module() {
private lateinit var createEventLauncher: AppContextActivityResultLauncher<CreatedEventOptions, CreateEventIntentResult>
private lateinit var addEventWithFormLauncher:
AppContextActivityResultLauncher<AddEventWithFormContract.Input, CreateEventIntentResult>
private lateinit var viewEventLauncher: AppContextActivityResultLauncher<ViewedEventOptions, ViewEventIntentResult>

private val context
Expand Down Expand Up @@ -99,8 +100,8 @@ class CalendarNextModule : Module() {
Name("CalendarNext")

RegisterActivityContracts {
createEventLauncher = registerForActivityResult(
CreateEventContract()
addEventWithFormLauncher = registerForActivityResult(
AddEventWithFormContract(eventMapper)
)
viewEventLauncher = registerForActivityResult(
ViewEventContract()
Expand Down Expand Up @@ -220,6 +221,16 @@ class CalendarNextModule : Module() {
expoCalendar.createEvent(record)
}

AsyncFunction("addEventWithForm") Coroutine { expoCalendar: ExpoCalendar, options: AddEventWithFormOptionsRecord? ->
val result = addEventWithFormLauncher.launch(
AddEventWithFormContract.Input(
calendarId = expoCalendar.id ?: throw CalendarNotFoundException("Calendar ID is null"),
options = options ?: AddEventWithFormOptionsRecord()
)
)
return@Coroutine result
}

AsyncFunction("update") Coroutine { expoCalendar: ExpoCalendar, updateCalendarInput: CalendarUpdateRecord ->
permissionsDelegate.requireSystemPermissions(true)
expoCalendar.update(updateCalendarInput)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,20 +52,20 @@ class EventMapper {
rrule = eventRecord.recurrenceRule?.toDomain()
)

private fun EventAvailability.toDomain() = when (this) {
fun EventAvailability.toDomain() = when (this) {
EventAvailability.BUSY -> Availability.BUSY
EventAvailability.FREE -> Availability.FREE
EventAvailability.TENTATIVE -> Availability.TENTATIVE
}

private fun EventAccessLevel.toDomain() = when (this) {
fun EventAccessLevel.toDomain() = when (this) {
EventAccessLevel.PUBLIC -> AccessLevel.PUBLIC
EventAccessLevel.PRIVATE -> AccessLevel.PRIVATE
EventAccessLevel.CONFIDENTIAL -> AccessLevel.CONFIDENTIAL
EventAccessLevel.DEFAULT -> AccessLevel.DEFAULT
}

private fun RecurrenceRuleRecord.toDomain() = RecurrenceRule(
fun RecurrenceRuleRecord.toDomain() = RecurrenceRule(
frequency = frequency
?: throw IllegalArgumentException("Frequency is required for recurrence rule"),
interval = interval,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package expo.modules.calendar.next.records

import expo.modules.kotlin.records.Field
import expo.modules.kotlin.records.Record
import expo.modules.kotlin.types.OptimizedRecord
import java.io.Serializable

@OptimizedRecord
data class AddEventWithFormOptionsRecord(
@Field val title: String? = null,
@Field val location: String? = null,
@Field val notes: String? = null,
@Field val timeZone: String? = null,
@Field val availability: EventAvailability? = null,
@Field val allDay: Boolean? = null,
@Field val startDate: String? = null,
@Field val endDate: String? = null,
@Field val recurrenceRule: RecurrenceRuleRecord? = null,
@Field val startNewActivityTask: Boolean = true
) : Record, Serializable
3 changes: 1 addition & 2 deletions packages/expo-calendar/build/next/ExpoCalendar.types.d.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading