diff --git a/CalendarComponents/EventDetail/src/main/kotlin/com/infomaniak/calendar/components/eventdetail/component/Recurrence.kt b/CalendarComponents/EventDetail/src/main/kotlin/com/infomaniak/calendar/components/eventdetail/component/Recurrence.kt new file mode 100644 index 000000000..406b4785c --- /dev/null +++ b/CalendarComponents/EventDetail/src/main/kotlin/com/infomaniak/calendar/components/eventdetail/component/Recurrence.kt @@ -0,0 +1,53 @@ +/* + * Infomaniak Calendar - Android + * Copyright (C) 2026 Infomaniak Network SA + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.infomaniak.calendar.components.eventdetail.component + +import androidx.compose.material3.ListItem +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Surface +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.produceState +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.tooling.preview.Preview +import com.infomaniak.calendar.components.resources.R + +@Composable +internal fun Recurrence(text: suspend () -> String?, modifier: Modifier = Modifier) { + val recurrenceText by produceState(null, text) { + value = text() + } + + ListItem( + modifier = modifier, + leadingContent = { ItemIcon(painterResource(R.drawable.ic_circular_arrows_clockwise)) }, + content = { recurrenceText?.let { Text(text = it) } }, + ) +} + +@Preview +@Composable +private fun Preview() { + MaterialTheme { + Surface { + Recurrence({ "Every day" }) + } + } +} diff --git a/CalendarComponents/EventDetail/src/main/kotlin/com/infomaniak/calendar/components/eventdetail/detail/EventDetail.kt b/CalendarComponents/EventDetail/src/main/kotlin/com/infomaniak/calendar/components/eventdetail/detail/EventDetail.kt index 7dc8e304a..0eb7fa566 100644 --- a/CalendarComponents/EventDetail/src/main/kotlin/com/infomaniak/calendar/components/eventdetail/detail/EventDetail.kt +++ b/CalendarComponents/EventDetail/src/main/kotlin/com/infomaniak/calendar/components/eventdetail/detail/EventDetail.kt @@ -36,6 +36,7 @@ import androidx.compose.ui.tooling.preview.datasource.LoremIpsum import com.infomaniak.calendar.components.eventdetail.component.AttachmentFiles import com.infomaniak.calendar.components.eventdetail.component.AttendeesButton import com.infomaniak.calendar.components.eventdetail.component.KMeetButton +import com.infomaniak.calendar.components.eventdetail.component.Recurrence import com.infomaniak.calendar.components.eventdetail.component.Section import com.infomaniak.calendar.components.eventdetail.component.Title import com.infomaniak.calendar.components.eventdetail.detail.component.Calendar @@ -79,8 +80,13 @@ fun EventDetail( Column(modifier = modifier.padding(contentPadding)) { with(eventDetail) { Title(dotColor = eventColor, title = title, modifier = Modifier.eventSharedElement(EventSharedElement.Title)) + DateAndTime(start, end, isAllDay) + eventDetail.getRecurrenceText?.let { + Recurrence(it) + } + Section { if (attendees.all.isNotEmpty()) { AttendeesButton( @@ -143,6 +149,7 @@ private fun PreviewEventDetail() { calendarColor = Color.Blue, calendarName = "Vacation", title = "Event Title", + getRecurrenceText = { "Every week on Monday" }, start = EventDetailTiming.Precise(Instant.parse("2026-05-20T08:00:00Z"), TimeZone.of("Europe/Paris")), end = EventDetailTiming.Precise(Instant.parse("2026-05-20T09:00:00Z"), TimeZone.of("Europe/Paris")), isAllDay = false, diff --git a/CalendarComponents/EventDetail/src/main/kotlin/com/infomaniak/calendar/components/eventdetail/models/EventDetailUi.kt b/CalendarComponents/EventDetail/src/main/kotlin/com/infomaniak/calendar/components/eventdetail/models/EventDetailUi.kt index 602916600..17434d4c2 100644 --- a/CalendarComponents/EventDetail/src/main/kotlin/com/infomaniak/calendar/components/eventdetail/models/EventDetailUi.kt +++ b/CalendarComponents/EventDetail/src/main/kotlin/com/infomaniak/calendar/components/eventdetail/models/EventDetailUi.kt @@ -34,6 +34,7 @@ data class EventDetailUi( val start: EventDetailTiming, val end: EventDetailTiming, val isAllDay: Boolean, + val getRecurrenceText: (suspend () -> String?)?, val attendees: Attendees, val kMeetUrl: String?, val location: String?, diff --git a/app/src/main/java/com/infomaniak/calendar/di/AppGraph.kt b/app/src/main/java/com/infomaniak/calendar/di/AppGraph.kt index a3a2fcd83..462339ad9 100644 --- a/app/src/main/java/com/infomaniak/calendar/di/AppGraph.kt +++ b/app/src/main/java/com/infomaniak/calendar/di/AppGraph.kt @@ -31,7 +31,6 @@ import com.infomaniak.core.login.InfomaniakLogin import com.infomaniak.core.network.LOGIN_ENDPOINT_URL import com.infomaniak.multiplatform_calendar.core.crashreporting.CrashReport import com.infomaniak.multiplatform_calendar.core.managers.AccountManager -import com.infomaniak.multiplatform_core.contacts.ContactsSettings import dev.zacsweers.metro.AppScope import dev.zacsweers.metro.DependencyGraph import dev.zacsweers.metro.Provides @@ -81,11 +80,6 @@ interface AppGraph : AndroidComponentProvider, ViewModelGraph { @SingleIn(AppScope::class) fun provideCrashReportInterface(): CrashReport = AndroidCrashReport - @Provides - @SingleIn(AppScope::class) - fun provideContactsSettings(appContext: Context): ContactsSettings = - ContactsSettings(databasePath = appContext.getDatabasePath("contacts.db").absolutePath) - @DependencyGraph.Factory fun interface Factory { fun create(@Provides appContext: Context): AppGraph diff --git a/app/src/main/java/com/infomaniak/calendar/ui/screen/eventDetail/detail/EventDetailScreen.kt b/app/src/main/java/com/infomaniak/calendar/ui/screen/eventDetail/detail/EventDetailScreen.kt index 7b2b74306..8e5746ccf 100644 --- a/app/src/main/java/com/infomaniak/calendar/ui/screen/eventDetail/detail/EventDetailScreen.kt +++ b/app/src/main/java/com/infomaniak/calendar/ui/screen/eventDetail/detail/EventDetailScreen.kt @@ -178,6 +178,7 @@ private fun Preview() { start = EventDetailTiming.Precise(Instant.parse("2026-05-20T08:00:00Z"), TimeZone.of("Europe/Paris")), end = EventDetailTiming.Precise(Instant.parse("2026-05-20T09:00:00Z"), TimeZone.of("Europe/Paris")), isAllDay = false, + getRecurrenceText = { "Every week on Monday" }, attendees = Attendees(all = emptyList(), me = null), kMeetUrl = null, location = "Salle Tokyo", diff --git a/app/src/main/java/com/infomaniak/calendar/utils/EventExt.kt b/app/src/main/java/com/infomaniak/calendar/utils/EventExt.kt index 52c981b12..7675eea8a 100644 --- a/app/src/main/java/com/infomaniak/calendar/utils/EventExt.kt +++ b/app/src/main/java/com/infomaniak/calendar/utils/EventExt.kt @@ -30,6 +30,7 @@ import com.infomaniak.multiplatform_calendar.core.domain.model.event.TimeBlockin import com.infomaniak.multiplatform_calendar.core.domain.model.event.alarm.AlarmAction import com.infomaniak.multiplatform_calendar.core.domain.model.event.alarm.AlarmTrigger import com.infomaniak.multiplatform_calendar.core.domain.model.event.alarm.EventAlarm +import com.infomaniak.multiplatform_calendar.core.domain.model.event.toLocalizedRecurrenceString import com.infomaniak.multiplatform_core.account.domain.model.AccountId import kotlinx.datetime.LocalDateTime import kotlinx.datetime.LocalTime @@ -46,6 +47,7 @@ fun Event.toEventDetailUi(calendar: Calendar, emailsByUserId: Map