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