From 1f117b0b45de34ce05bee0736a6c3d6ae467a4c7 Mon Sep 17 00:00:00 2001 From: Elouan BOITEUX Date: Thu, 23 Jul 2026 18:12:33 +0200 Subject: [PATCH 01/12] feat: Add ContactCard constants, defaults and top bar state --- .../contactcard/ContactCardDefaults.kt | 44 +++++++++++++++++++ .../contactcard/ContactCardTopBarState.kt | 23 ++++++++++ .../component/ContactCardConstants.kt | 21 +++++++++ 3 files changed, 88 insertions(+) create mode 100644 Ui/Compose/ContactCard/src/main/kotlin/com/infomaniak/core/ui/compose/contactcard/ContactCardDefaults.kt create mode 100644 Ui/Compose/ContactCard/src/main/kotlin/com/infomaniak/core/ui/compose/contactcard/ContactCardTopBarState.kt create mode 100644 Ui/Compose/ContactCard/src/main/kotlin/com/infomaniak/core/ui/compose/contactcard/component/ContactCardConstants.kt diff --git a/Ui/Compose/ContactCard/src/main/kotlin/com/infomaniak/core/ui/compose/contactcard/ContactCardDefaults.kt b/Ui/Compose/ContactCard/src/main/kotlin/com/infomaniak/core/ui/compose/contactcard/ContactCardDefaults.kt new file mode 100644 index 000000000..e2f973ad9 --- /dev/null +++ b/Ui/Compose/ContactCard/src/main/kotlin/com/infomaniak/core/ui/compose/contactcard/ContactCardDefaults.kt @@ -0,0 +1,44 @@ +/* + * Infomaniak Core - 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 (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.core.ui.compose.contactcard + +import androidx.compose.material3.MaterialTheme +import androidx.compose.runtime.Composable +import androidx.compose.runtime.Immutable +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.takeOrElse + +@Immutable +data class ContactCardColors( + val waveBackground: Color, + val background: Color, +) + +object ContactCardDefaults { + @Composable + fun colors( + waveBackground: Color = Color.Unspecified, + background: Color = Color.Unspecified, + ): ContactCardColors { + val defaultWave = MaterialTheme.colorScheme.surfaceContainerLowest + val defaultBackground = MaterialTheme.colorScheme.primaryContainer + return ContactCardColors( + waveBackground = waveBackground.takeOrElse { defaultWave }, + background = background.takeOrElse { defaultBackground }, + ) + } +} diff --git a/Ui/Compose/ContactCard/src/main/kotlin/com/infomaniak/core/ui/compose/contactcard/ContactCardTopBarState.kt b/Ui/Compose/ContactCard/src/main/kotlin/com/infomaniak/core/ui/compose/contactcard/ContactCardTopBarState.kt new file mode 100644 index 000000000..526b3b58d --- /dev/null +++ b/Ui/Compose/ContactCard/src/main/kotlin/com/infomaniak/core/ui/compose/contactcard/ContactCardTopBarState.kt @@ -0,0 +1,23 @@ +/* + * Infomaniak Core - 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 (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.core.ui.compose.contactcard + +sealed interface ContactCardTopBarState { + data class Editor(val onCancel: () -> Unit, val onSave: () -> Unit) : ContactCardTopBarState + data class Preview(val onClose: () -> Unit, val onMore: () -> Unit) : ContactCardTopBarState + data class Default(val onBack: () -> Unit) : ContactCardTopBarState +} diff --git a/Ui/Compose/ContactCard/src/main/kotlin/com/infomaniak/core/ui/compose/contactcard/component/ContactCardConstants.kt b/Ui/Compose/ContactCard/src/main/kotlin/com/infomaniak/core/ui/compose/contactcard/component/ContactCardConstants.kt new file mode 100644 index 000000000..a6bc9d667 --- /dev/null +++ b/Ui/Compose/ContactCard/src/main/kotlin/com/infomaniak/core/ui/compose/contactcard/component/ContactCardConstants.kt @@ -0,0 +1,21 @@ +/* + * Infomaniak Core - 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 (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.core.ui.compose.contactcard.component + +import androidx.compose.ui.unit.dp + +internal val CardCornerRadius = 12.dp From a939453345d938736667b284e803865f543069c4 Mon Sep 17 00:00:00 2001 From: Elouan BOITEUX Date: Thu, 23 Jul 2026 18:12:41 +0200 Subject: [PATCH 02/12] feat: Add ContactCard preview data helpers --- .../contactcard/component/PreviewData.kt | 58 +++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 Ui/Compose/ContactCard/src/main/kotlin/com/infomaniak/core/ui/compose/contactcard/component/PreviewData.kt diff --git a/Ui/Compose/ContactCard/src/main/kotlin/com/infomaniak/core/ui/compose/contactcard/component/PreviewData.kt b/Ui/Compose/ContactCard/src/main/kotlin/com/infomaniak/core/ui/compose/contactcard/component/PreviewData.kt new file mode 100644 index 000000000..982540cee --- /dev/null +++ b/Ui/Compose/ContactCard/src/main/kotlin/com/infomaniak/core/ui/compose/contactcard/component/PreviewData.kt @@ -0,0 +1,58 @@ +/* + * Infomaniak Core - 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 (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.core.ui.compose.contactcard.component + +import com.infomaniak.core.auth.models.user.Card +import com.infomaniak.core.auth.models.user.CardLink +import com.infomaniak.core.auth.models.user.CardLinkType +import com.infomaniak.core.auth.models.user.User +import com.infomaniak.core.auth.models.user.preferences.OrganizationPreference +import com.infomaniak.core.auth.models.user.preferences.Preferences + +internal fun previewUser(): User { + val orgPreference = OrganizationPreference(currentOrganizationId = 0) + val preferences = Preferences( + security = null, + organizationPreference = orgPreference, + ) + return User( + id = 42, + displayName = "Alice Doe", + firstname = "Alice", + lastname = "Doe", + email = "alice.doe@example.com", + avatar = "https://example.com/avatar.png", + card = null, + login = "alice.doe", + isStaff = false, + preferences = preferences, + ) +} + +internal fun previewCard(): Card = Card( + firstName = "Alice", + lastName = "Doe", + email = "alice.doe@example.com", + phone = "+41 79 123 45 67", + company = "Infomaniak", + avatarUrl = "https://example.com/avatar.png", + links = listOf( + CardLink(CardLinkType.LinkedIn, "https://linkedin.com/in/alicedoe"), + CardLink(CardLinkType.Website, "https://example.com"), + CardLink(CardLinkType.Other, "https://blog.example.com"), + ), +) From e83240707d78ff69f191e7c38b66b48d9e9217f4 Mon Sep 17 00:00:00 2001 From: Elouan BOITEUX Date: Thu, 23 Jul 2026 18:12:42 +0200 Subject: [PATCH 03/12] feat: Add ContactCard layout primitives (divider, loading, section card) --- .../contactcard/component/FieldDivider.kt | 44 ++++++++++++ .../contactcard/component/LoadingContent.kt | 44 ++++++++++++ .../contactcard/component/SectionCard.kt | 69 +++++++++++++++++++ 3 files changed, 157 insertions(+) create mode 100644 Ui/Compose/ContactCard/src/main/kotlin/com/infomaniak/core/ui/compose/contactcard/component/FieldDivider.kt create mode 100644 Ui/Compose/ContactCard/src/main/kotlin/com/infomaniak/core/ui/compose/contactcard/component/LoadingContent.kt create mode 100644 Ui/Compose/ContactCard/src/main/kotlin/com/infomaniak/core/ui/compose/contactcard/component/SectionCard.kt diff --git a/Ui/Compose/ContactCard/src/main/kotlin/com/infomaniak/core/ui/compose/contactcard/component/FieldDivider.kt b/Ui/Compose/ContactCard/src/main/kotlin/com/infomaniak/core/ui/compose/contactcard/component/FieldDivider.kt new file mode 100644 index 000000000..20b31cfc6 --- /dev/null +++ b/Ui/Compose/ContactCard/src/main/kotlin/com/infomaniak/core/ui/compose/contactcard/component/FieldDivider.kt @@ -0,0 +1,44 @@ +/* + * Infomaniak Core - 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 (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.core.ui.compose.contactcard.component + +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.HorizontalDivider +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Surface +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.tooling.preview.Preview +import com.infomaniak.core.ui.compose.margin.Margin + +@Composable +internal fun FieldDivider() { + HorizontalDivider( + modifier = Modifier.padding(horizontal = Margin.Medium), + color = MaterialTheme.colorScheme.outlineVariant, + ) +} + +@Preview(name = "FieldDivider") +@Composable +private fun FieldDividerPreview() { + MaterialTheme { + Surface { + FieldDivider() + } + } +} diff --git a/Ui/Compose/ContactCard/src/main/kotlin/com/infomaniak/core/ui/compose/contactcard/component/LoadingContent.kt b/Ui/Compose/ContactCard/src/main/kotlin/com/infomaniak/core/ui/compose/contactcard/component/LoadingContent.kt new file mode 100644 index 000000000..2a8f7d1ad --- /dev/null +++ b/Ui/Compose/ContactCard/src/main/kotlin/com/infomaniak/core/ui/compose/contactcard/component/LoadingContent.kt @@ -0,0 +1,44 @@ +/* + * Infomaniak Core - 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 (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.core.ui.compose.contactcard.component + +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.material3.CircularProgressIndicator +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Surface +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.tooling.preview.Preview + +@Composable +internal fun LoadingContent() { + Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) { + CircularProgressIndicator() + } +} + +@Preview(name = "LoadingContent") +@Composable +private fun LoadingContentPreview() { + MaterialTheme { + Surface { + LoadingContent() + } + } +} diff --git a/Ui/Compose/ContactCard/src/main/kotlin/com/infomaniak/core/ui/compose/contactcard/component/SectionCard.kt b/Ui/Compose/ContactCard/src/main/kotlin/com/infomaniak/core/ui/compose/contactcard/component/SectionCard.kt new file mode 100644 index 000000000..46fb5322a --- /dev/null +++ b/Ui/Compose/ContactCard/src/main/kotlin/com/infomaniak/core/ui/compose/contactcard/component/SectionCard.kt @@ -0,0 +1,69 @@ +/* + * Infomaniak Core - 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 (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.core.ui.compose.contactcard.component + +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Surface +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.tooling.preview.Preview +import com.infomaniak.core.ui.compose.margin.Margin + +@Composable +internal fun SectionCard( + title: String, + content: @Composable () -> Unit, +) { + Column(modifier = Modifier.fillMaxWidth()) { + Text( + text = title, + style = MaterialTheme.typography.bodySmall, + color = MaterialTheme.colorScheme.onSurfaceVariant, + modifier = Modifier.padding( + start = Margin.Medium + Margin.Mini, + end = Margin.Medium, + bottom = Margin.Mini, + ), + ) + Surface( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = Margin.Medium), + color = MaterialTheme.colorScheme.surface, + shape = RoundedCornerShape(CardCornerRadius), + ) { + Column(modifier = Modifier.fillMaxWidth()) { content() } + } + } +} + +@Preview(name = "SectionCard") +@Composable +private fun SectionCardPreview() { + MaterialTheme { + Surface { + SectionCard(title = "Section Title") { + Text("Content") + } + } + } +} From d751d3764b6aa357aea7706fe5fbe50e4532a178 Mon Sep 17 00:00:00 2001 From: Elouan BOITEUX Date: Thu, 23 Jul 2026 18:15:13 +0200 Subject: [PATCH 04/12] feat: Add ContactCard top bars --- .../component/ContactCardTopBar.kt | 54 ++++++++++++++ .../contactcard/component/PreviewTopBar.kt | 70 +++++++++++++++++++ 2 files changed, 124 insertions(+) create mode 100644 Ui/Compose/ContactCard/src/main/kotlin/com/infomaniak/core/ui/compose/contactcard/component/ContactCardTopBar.kt create mode 100644 Ui/Compose/ContactCard/src/main/kotlin/com/infomaniak/core/ui/compose/contactcard/component/PreviewTopBar.kt diff --git a/Ui/Compose/ContactCard/src/main/kotlin/com/infomaniak/core/ui/compose/contactcard/component/ContactCardTopBar.kt b/Ui/Compose/ContactCard/src/main/kotlin/com/infomaniak/core/ui/compose/contactcard/component/ContactCardTopBar.kt new file mode 100644 index 000000000..1efc7e028 --- /dev/null +++ b/Ui/Compose/ContactCard/src/main/kotlin/com/infomaniak/core/ui/compose/contactcard/component/ContactCardTopBar.kt @@ -0,0 +1,54 @@ +/* + * Infomaniak Core - 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 (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.core.ui.compose.contactcard.component + +import androidx.compose.foundation.layout.RowScope +import androidx.compose.material3.CenterAlignedTopAppBar +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.material3.TopAppBarDefaults +import androidx.compose.runtime.Composable +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.res.stringResource +import com.infomaniak.core.ui.compose.contactcard.R + +@Composable +@OptIn(ExperimentalMaterial3Api::class) +internal fun ContactCardTopBar( + navigationIcon: @Composable () -> Unit = {}, + actions: @Composable RowScope.() -> Unit = {}, + containerColor: Color = Color.Transparent, +) { + CenterAlignedTopAppBar( + title = { + Text( + text = stringResource(R.string.contactCardTitle), + style = MaterialTheme.typography.titleMedium, + color = MaterialTheme.colorScheme.onSurface, + ) + }, + navigationIcon = navigationIcon, + actions = actions, + colors = TopAppBarDefaults.centerAlignedTopAppBarColors( + containerColor = containerColor, + titleContentColor = MaterialTheme.colorScheme.onSurface, + navigationIconContentColor = MaterialTheme.colorScheme.primary, + actionIconContentColor = MaterialTheme.colorScheme.primary, + ), + ) +} diff --git a/Ui/Compose/ContactCard/src/main/kotlin/com/infomaniak/core/ui/compose/contactcard/component/PreviewTopBar.kt b/Ui/Compose/ContactCard/src/main/kotlin/com/infomaniak/core/ui/compose/contactcard/component/PreviewTopBar.kt new file mode 100644 index 000000000..f0758b224 --- /dev/null +++ b/Ui/Compose/ContactCard/src/main/kotlin/com/infomaniak/core/ui/compose/contactcard/component/PreviewTopBar.kt @@ -0,0 +1,70 @@ +/* + * Infomaniak Core - 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 (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.core.ui.compose.contactcard.component + +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Close +import androidx.compose.material.icons.filled.MoreVert +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Surface +import androidx.compose.runtime.Composable +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.tooling.preview.Preview +import com.infomaniak.core.ui.compose.contactcard.R +import com.infomaniak.core.common.R as RCore + +@Composable +internal fun PreviewTopBar( + onClose: () -> Unit, + onMore: () -> Unit, +) { + ContactCardTopBar( + navigationIcon = { + IconButton(onClick = onClose) { + Icon( + imageVector = Icons.Filled.Close, + contentDescription = stringResource(RCore.string.buttonCancel), + tint = MaterialTheme.colorScheme.primary, + ) + } + }, + actions = { + IconButton(onClick = onMore) { + Icon( + imageVector = Icons.Filled.MoreVert, + contentDescription = stringResource(R.string.buttonMore), + tint = MaterialTheme.colorScheme.primary, + ) + } + }, + ) +} + +@Preview(name = "PreviewTopBar") +@Composable +private fun PreviewTopBarPreview() { + MaterialTheme { + Surface { + PreviewTopBar( + onClose = {}, + onMore = {}, + ) + } + } +} From 658696bb0a5b4a5c64c214cbc96d0b14bfcb420d Mon Sep 17 00:00:00 2001 From: Elouan BOITEUX Date: Thu, 23 Jul 2026 18:12:48 +0200 Subject: [PATCH 05/12] feat: Add ContactCard editor field and top bar --- .../contactcard/component/EditorField.kt | 74 +++++++++++++++++++ .../contactcard/component/EditorTopBar.kt | 64 ++++++++++++++++ 2 files changed, 138 insertions(+) create mode 100644 Ui/Compose/ContactCard/src/main/kotlin/com/infomaniak/core/ui/compose/contactcard/component/EditorField.kt create mode 100644 Ui/Compose/ContactCard/src/main/kotlin/com/infomaniak/core/ui/compose/contactcard/component/EditorTopBar.kt diff --git a/Ui/Compose/ContactCard/src/main/kotlin/com/infomaniak/core/ui/compose/contactcard/component/EditorField.kt b/Ui/Compose/ContactCard/src/main/kotlin/com/infomaniak/core/ui/compose/contactcard/component/EditorField.kt new file mode 100644 index 000000000..064501db5 --- /dev/null +++ b/Ui/Compose/ContactCard/src/main/kotlin/com/infomaniak/core/ui/compose/contactcard/component/EditorField.kt @@ -0,0 +1,74 @@ +/* + * Infomaniak Core - 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 (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.core.ui.compose.contactcard.component + +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.text.KeyboardOptions +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Surface +import androidx.compose.material3.Text +import androidx.compose.material3.TextField +import androidx.compose.material3.TextFieldDefaults +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.input.KeyboardType +import androidx.compose.ui.tooling.preview.Preview + +@Composable +internal fun EditorField( + value: String, + placeholder: String, + onValueChange: (String) -> Unit, + modifier: Modifier = Modifier.fillMaxWidth(), + keyboardType: KeyboardType = KeyboardType.Text, +) { + TextField( + value = value, + onValueChange = onValueChange, + modifier = modifier, + placeholder = { + Text(text = placeholder) + }, + singleLine = true, + keyboardOptions = KeyboardOptions(keyboardType = keyboardType), + colors = TextFieldDefaults.colors( + focusedContainerColor = Color.Transparent, + unfocusedContainerColor = Color.Transparent, + disabledContainerColor = Color.Transparent, + errorContainerColor = Color.Transparent, + focusedIndicatorColor = Color.Transparent, + unfocusedIndicatorColor = Color.Transparent, + disabledIndicatorColor = Color.Transparent, + errorIndicatorColor = Color.Transparent, + ), + ) +} + +@Preview(name = "EditorField") +@Composable +private fun EditorFieldPreview() { + MaterialTheme { + Surface { + EditorField( + value = "", + placeholder = "Placeholder", + onValueChange = {}, + ) + } + } +} diff --git a/Ui/Compose/ContactCard/src/main/kotlin/com/infomaniak/core/ui/compose/contactcard/component/EditorTopBar.kt b/Ui/Compose/ContactCard/src/main/kotlin/com/infomaniak/core/ui/compose/contactcard/component/EditorTopBar.kt new file mode 100644 index 000000000..5689bf7d5 --- /dev/null +++ b/Ui/Compose/ContactCard/src/main/kotlin/com/infomaniak/core/ui/compose/contactcard/component/EditorTopBar.kt @@ -0,0 +1,64 @@ +/* + * Infomaniak Core - 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 (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.core.ui.compose.contactcard.component + +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Surface +import androidx.compose.material3.Text +import androidx.compose.material3.TextButton +import androidx.compose.runtime.Composable +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.tooling.preview.Preview +import com.infomaniak.core.common.R as RCore + +@Composable +internal fun EditorTopBar( + onCancel: () -> Unit, + onSave: () -> Unit, +) { + ContactCardTopBar( + navigationIcon = { + TextButton(onClick = onCancel) { + Text( + text = stringResource(RCore.string.buttonCancel), + color = MaterialTheme.colorScheme.primary, + ) + } + }, + actions = { + TextButton(onClick = onSave) { + Text( + text = stringResource(RCore.string.buttonSave), + color = MaterialTheme.colorScheme.primary, + ) + } + }, + ) +} + +@Preview(name = "EditorTopBar") +@Composable +private fun EditorTopBarPreview() { + MaterialTheme { + Surface { + EditorTopBar( + onCancel = {}, + onSave = {}, + ) + } + } +} From e53248c501e627a7639dd51440dc2a7bba483cef Mon Sep 17 00:00:00 2001 From: Elouan BOITEUX Date: Thu, 23 Jul 2026 18:12:49 +0200 Subject: [PATCH 06/12] feat: Add ContactCard bottom sheet action composable --- .../component/BottomSheetAction.kt | 92 +++++++++++++++++++ 1 file changed, 92 insertions(+) create mode 100644 Ui/Compose/ContactCard/src/main/kotlin/com/infomaniak/core/ui/compose/contactcard/component/BottomSheetAction.kt diff --git a/Ui/Compose/ContactCard/src/main/kotlin/com/infomaniak/core/ui/compose/contactcard/component/BottomSheetAction.kt b/Ui/Compose/ContactCard/src/main/kotlin/com/infomaniak/core/ui/compose/contactcard/component/BottomSheetAction.kt new file mode 100644 index 000000000..b8b235469 --- /dev/null +++ b/Ui/Compose/ContactCard/src/main/kotlin/com/infomaniak/core/ui/compose/contactcard/component/BottomSheetAction.kt @@ -0,0 +1,92 @@ +/* + * Infomaniak Core - 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 (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.core.ui.compose.contactcard.component + +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Edit +import androidx.compose.material3.ButtonDefaults +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Surface +import androidx.compose.material3.Text +import androidx.compose.material3.TextButton +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.RectangleShape +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import com.infomaniak.core.ui.compose.margin.Margin + +@Composable +internal fun BottomSheetAction( + icon: ImageVector, + label: String, + onClick: () -> Unit, +) { + TextButton( + onClick = onClick, + shape = RectangleShape, + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = Margin.Mini), + contentPadding = PaddingValues(Margin.Small), + colors = ButtonDefaults.textButtonColors( + contentColor = MaterialTheme.colorScheme.onSurface, + ) + ) { + Row( + modifier = Modifier.fillMaxWidth(), + verticalAlignment = Alignment.CenterVertically, + ) { + Icon( + imageVector = icon, + contentDescription = null, + tint = MaterialTheme.colorScheme.primary, + modifier = Modifier.size(24.dp), + ) + Spacer(Modifier.width(Margin.Medium)) + Text( + text = label, + style = MaterialTheme.typography.bodyLarge, + ) + } + } +} + +@Preview(name = "BottomSheetAction") +@Composable +private fun BottomSheetActionPreview() { + MaterialTheme { + Surface { + BottomSheetAction( + icon = Icons.Default.Edit, + label = "Edit", + onClick = {}, + ) + } + } +} From 696f532d048fbb52beab069489c0c62852d7f655 Mon Sep 17 00:00:00 2001 From: Elouan BOITEUX Date: Thu, 23 Jul 2026 18:12:49 +0200 Subject: [PATCH 07/12] feat: Add ContactCard info rows --- .../contactcard/component/ContactInfoRows.kt | 87 +++++++++++++++++++ 1 file changed, 87 insertions(+) create mode 100644 Ui/Compose/ContactCard/src/main/kotlin/com/infomaniak/core/ui/compose/contactcard/component/ContactInfoRows.kt diff --git a/Ui/Compose/ContactCard/src/main/kotlin/com/infomaniak/core/ui/compose/contactcard/component/ContactInfoRows.kt b/Ui/Compose/ContactCard/src/main/kotlin/com/infomaniak/core/ui/compose/contactcard/component/ContactInfoRows.kt new file mode 100644 index 000000000..2d9934435 --- /dev/null +++ b/Ui/Compose/ContactCard/src/main/kotlin/com/infomaniak/core/ui/compose/contactcard/component/ContactInfoRows.kt @@ -0,0 +1,87 @@ +/* + * Infomaniak Core - 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 (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.core.ui.compose.contactcard.component + +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.HorizontalDivider +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Surface +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import com.infomaniak.core.auth.models.user.Card +import com.infomaniak.core.auth.models.user.CardLinkType +import com.infomaniak.core.ui.compose.contactcard.R +import com.infomaniak.core.ui.compose.margin.Margin + +@Composable +internal fun ContactInfoRows(card: Card, modifier: Modifier = Modifier) { + val rows = buildList { + card.company?.takeIf { it.isNotBlank() }?.let { add(stringResource(R.string.company) to it) } + card.phone.takeIf { it.isNotBlank() }?.let { add(stringResource(R.string.phone) to it) } + card.links.orEmpty().firstOrNull { it.type == CardLinkType.Website }?.let { + add(stringResource(R.string.webSite) to it.url) + } + } + Column(modifier = modifier) { + rows.forEachIndexed { index, (label, value) -> + if (index > 0) { + HorizontalDivider( + thickness = 0.5.dp, + color = MaterialTheme.colorScheme.outlineVariant, + ) + } + Row( + modifier = Modifier + .fillMaxWidth() + .padding(vertical = Margin.Small), + horizontalArrangement = androidx.compose.foundation.layout.Arrangement.SpaceBetween, + verticalAlignment = Alignment.CenterVertically, + ) { + Text( + text = label, + color = MaterialTheme.colorScheme.onSurfaceVariant, + style = MaterialTheme.typography.bodyMedium, + ) + Text( + text = value, + color = MaterialTheme.colorScheme.primary, + style = MaterialTheme.typography.bodyMedium, + fontWeight = FontWeight.SemiBold, + ) + } + } + } +} + +@Preview(name = "ContactInfoRows") +@Composable +private fun ContactInfoRowsPreview() { + MaterialTheme { + Surface { + ContactInfoRows(card = previewCard()) + } + } +} From 341d87a82248a98e860826b809b96b66cd416cbd Mon Sep 17 00:00:00 2001 From: Elouan BOITEUX Date: Thu, 23 Jul 2026 18:12:49 +0200 Subject: [PATCH 08/12] feat: Add ContactCard default dialogs --- .../DefaultDeleteConfirmationDialog.kt | 62 +++++++++++++++++++ .../component/DefaultValidationErrorDialog.kt | 57 +++++++++++++++++ 2 files changed, 119 insertions(+) create mode 100644 Ui/Compose/ContactCard/src/main/kotlin/com/infomaniak/core/ui/compose/contactcard/component/DefaultDeleteConfirmationDialog.kt create mode 100644 Ui/Compose/ContactCard/src/main/kotlin/com/infomaniak/core/ui/compose/contactcard/component/DefaultValidationErrorDialog.kt diff --git a/Ui/Compose/ContactCard/src/main/kotlin/com/infomaniak/core/ui/compose/contactcard/component/DefaultDeleteConfirmationDialog.kt b/Ui/Compose/ContactCard/src/main/kotlin/com/infomaniak/core/ui/compose/contactcard/component/DefaultDeleteConfirmationDialog.kt new file mode 100644 index 000000000..43c0c8051 --- /dev/null +++ b/Ui/Compose/ContactCard/src/main/kotlin/com/infomaniak/core/ui/compose/contactcard/component/DefaultDeleteConfirmationDialog.kt @@ -0,0 +1,62 @@ +/* + * Infomaniak Core - 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 (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.core.ui.compose.contactcard.component + +import androidx.compose.material3.AlertDialog +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Surface +import androidx.compose.material3.Text +import androidx.compose.material3.TextButton +import androidx.compose.runtime.Composable +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.tooling.preview.Preview +import com.infomaniak.core.ui.compose.contactcard.R + +@Composable +internal fun DefaultDeleteConfirmationDialog( + onDismiss: () -> Unit, + onConfirm: () -> Unit, +) { + AlertDialog( + onDismissRequest = onDismiss, + confirmButton = { + TextButton(onClick = onConfirm) { + Text(text = stringResource(android.R.string.ok)) + } + }, + dismissButton = { + TextButton(onClick = onDismiss) { + Text(text = stringResource(android.R.string.cancel)) + } + }, + title = { Text(text = stringResource(R.string.deleteAlertTitle)) }, + text = { Text(text = stringResource(R.string.deleteAlertDescription)) }, + ) +} + +@Preview(name = "DefaultDeleteConfirmationDialog") +@Composable +private fun DefaultDeleteConfirmationDialogPreview() { + MaterialTheme { + Surface { + DefaultDeleteConfirmationDialog( + onDismiss = {}, + onConfirm = {}, + ) + } + } +} diff --git a/Ui/Compose/ContactCard/src/main/kotlin/com/infomaniak/core/ui/compose/contactcard/component/DefaultValidationErrorDialog.kt b/Ui/Compose/ContactCard/src/main/kotlin/com/infomaniak/core/ui/compose/contactcard/component/DefaultValidationErrorDialog.kt new file mode 100644 index 000000000..649bbdd30 --- /dev/null +++ b/Ui/Compose/ContactCard/src/main/kotlin/com/infomaniak/core/ui/compose/contactcard/component/DefaultValidationErrorDialog.kt @@ -0,0 +1,57 @@ +/* + * Infomaniak Core - 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 (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.core.ui.compose.contactcard.component + +import androidx.compose.material3.AlertDialog +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Surface +import androidx.compose.material3.Text +import androidx.compose.material3.TextButton +import androidx.compose.runtime.Composable +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.tooling.preview.Preview +import com.infomaniak.core.ui.compose.contactcard.R + +@Composable +internal fun DefaultValidationErrorDialog( + onDismiss: () -> Unit, + onConfirm: () -> Unit, +) { + AlertDialog( + onDismissRequest = onDismiss, + confirmButton = { + TextButton(onClick = onConfirm) { + Text(text = stringResource(android.R.string.ok)) + } + }, + title = { Text(text = stringResource(R.string.alertTitle)) }, + text = { Text(text = stringResource(R.string.alertDescription)) }, + ) +} + +@Preview(name = "DefaultValidationErrorDialog") +@Composable +private fun DefaultValidationErrorDialogPreview() { + MaterialTheme { + Surface { + DefaultValidationErrorDialog( + onDismiss = {}, + onConfirm = {}, + ) + } + } +} From 50c223a954f42757a83186c8bc72195ea19bb75d Mon Sep 17 00:00:00 2001 From: Elouan BOITEUX Date: Mon, 3 Aug 2026 13:49:34 +0200 Subject: [PATCH 09/12] fix: Update strings --- .../contactcard/component/DefaultDeleteConfirmationDialog.kt | 5 +++-- .../core/ui/compose/contactcard/component/PreviewTopBar.kt | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/Ui/Compose/ContactCard/src/main/kotlin/com/infomaniak/core/ui/compose/contactcard/component/DefaultDeleteConfirmationDialog.kt b/Ui/Compose/ContactCard/src/main/kotlin/com/infomaniak/core/ui/compose/contactcard/component/DefaultDeleteConfirmationDialog.kt index 43c0c8051..fb277c2ac 100644 --- a/Ui/Compose/ContactCard/src/main/kotlin/com/infomaniak/core/ui/compose/contactcard/component/DefaultDeleteConfirmationDialog.kt +++ b/Ui/Compose/ContactCard/src/main/kotlin/com/infomaniak/core/ui/compose/contactcard/component/DefaultDeleteConfirmationDialog.kt @@ -25,6 +25,7 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import com.infomaniak.core.ui.compose.contactcard.R +import com.infomaniak.core.common.R as RCore @Composable internal fun DefaultDeleteConfirmationDialog( @@ -35,12 +36,12 @@ internal fun DefaultDeleteConfirmationDialog( onDismissRequest = onDismiss, confirmButton = { TextButton(onClick = onConfirm) { - Text(text = stringResource(android.R.string.ok)) + Text(text = stringResource(R.string.deleteButton)) } }, dismissButton = { TextButton(onClick = onDismiss) { - Text(text = stringResource(android.R.string.cancel)) + Text(text = stringResource(RCore.string.buttonCancel)) } }, title = { Text(text = stringResource(R.string.deleteAlertTitle)) }, diff --git a/Ui/Compose/ContactCard/src/main/kotlin/com/infomaniak/core/ui/compose/contactcard/component/PreviewTopBar.kt b/Ui/Compose/ContactCard/src/main/kotlin/com/infomaniak/core/ui/compose/contactcard/component/PreviewTopBar.kt index f0758b224..f04b24fd4 100644 --- a/Ui/Compose/ContactCard/src/main/kotlin/com/infomaniak/core/ui/compose/contactcard/component/PreviewTopBar.kt +++ b/Ui/Compose/ContactCard/src/main/kotlin/com/infomaniak/core/ui/compose/contactcard/component/PreviewTopBar.kt @@ -39,7 +39,7 @@ internal fun PreviewTopBar( IconButton(onClick = onClose) { Icon( imageVector = Icons.Filled.Close, - contentDescription = stringResource(RCore.string.buttonCancel), + contentDescription = stringResource(RCore.string.buttonClose), tint = MaterialTheme.colorScheme.primary, ) } From d129813f553b21c613037bac0863e12dfefe5b72 Mon Sep 17 00:00:00 2001 From: Elouan BOITEUX Date: Mon, 3 Aug 2026 14:05:20 +0200 Subject: [PATCH 10/12] fix: Display link only if it is not blank --- .../core/ui/compose/contactcard/component/ContactInfoRows.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Ui/Compose/ContactCard/src/main/kotlin/com/infomaniak/core/ui/compose/contactcard/component/ContactInfoRows.kt b/Ui/Compose/ContactCard/src/main/kotlin/com/infomaniak/core/ui/compose/contactcard/component/ContactInfoRows.kt index 2d9934435..b5b1d04f0 100644 --- a/Ui/Compose/ContactCard/src/main/kotlin/com/infomaniak/core/ui/compose/contactcard/component/ContactInfoRows.kt +++ b/Ui/Compose/ContactCard/src/main/kotlin/com/infomaniak/core/ui/compose/contactcard/component/ContactInfoRows.kt @@ -41,7 +41,7 @@ internal fun ContactInfoRows(card: Card, modifier: Modifier = Modifier) { val rows = buildList { card.company?.takeIf { it.isNotBlank() }?.let { add(stringResource(R.string.company) to it) } card.phone.takeIf { it.isNotBlank() }?.let { add(stringResource(R.string.phone) to it) } - card.links.orEmpty().firstOrNull { it.type == CardLinkType.Website }?.let { + card.links.orEmpty().firstOrNull { it.type == CardLinkType.Website && it.url.isNotBlank() }?.let { add(stringResource(R.string.webSite) to it.url) } } From 8c52fc582b58f0ba9a20f4196e37b3a24750c58b Mon Sep 17 00:00:00 2001 From: Elouan BOITEUX Date: Fri, 14 Aug 2026 09:17:52 +0200 Subject: [PATCH 11/12] refactor: Create a reusable component for a dialog --- ...nDialog.kt => DefaultContactCardDialog.kt} | 61 ++++++++++++------- .../component/DefaultValidationErrorDialog.kt | 57 ----------------- 2 files changed, 40 insertions(+), 78 deletions(-) rename Ui/Compose/ContactCard/src/main/kotlin/com/infomaniak/core/ui/compose/contactcard/component/{DefaultDeleteConfirmationDialog.kt => DefaultContactCardDialog.kt} (53%) delete mode 100644 Ui/Compose/ContactCard/src/main/kotlin/com/infomaniak/core/ui/compose/contactcard/component/DefaultValidationErrorDialog.kt diff --git a/Ui/Compose/ContactCard/src/main/kotlin/com/infomaniak/core/ui/compose/contactcard/component/DefaultDeleteConfirmationDialog.kt b/Ui/Compose/ContactCard/src/main/kotlin/com/infomaniak/core/ui/compose/contactcard/component/DefaultContactCardDialog.kt similarity index 53% rename from Ui/Compose/ContactCard/src/main/kotlin/com/infomaniak/core/ui/compose/contactcard/component/DefaultDeleteConfirmationDialog.kt rename to Ui/Compose/ContactCard/src/main/kotlin/com/infomaniak/core/ui/compose/contactcard/component/DefaultContactCardDialog.kt index fb277c2ac..60c482b54 100644 --- a/Ui/Compose/ContactCard/src/main/kotlin/com/infomaniak/core/ui/compose/contactcard/component/DefaultDeleteConfirmationDialog.kt +++ b/Ui/Compose/ContactCard/src/main/kotlin/com/infomaniak/core/ui/compose/contactcard/component/DefaultContactCardDialog.kt @@ -17,47 +17,66 @@ package com.infomaniak.core.ui.compose.contactcard.component import androidx.compose.material3.AlertDialog -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Surface import androidx.compose.material3.Text import androidx.compose.material3.TextButton import androidx.compose.runtime.Composable import androidx.compose.ui.res.stringResource -import androidx.compose.ui.tooling.preview.Preview import com.infomaniak.core.ui.compose.contactcard.R import com.infomaniak.core.common.R as RCore @Composable -internal fun DefaultDeleteConfirmationDialog( - onDismiss: () -> Unit, +internal fun DefaultContactCardDialog( + titleRes: Int, + descriptionRes: Int, + confirmButtonRes: Int, onConfirm: () -> Unit, + onDismiss: () -> Unit, + dismissButtonRes: Int? = null, ) { AlertDialog( onDismissRequest = onDismiss, confirmButton = { TextButton(onClick = onConfirm) { - Text(text = stringResource(R.string.deleteButton)) + Text(text = stringResource(confirmButtonRes)) } }, - dismissButton = { - TextButton(onClick = onDismiss) { - Text(text = stringResource(RCore.string.buttonCancel)) + dismissButton = dismissButtonRes?.let { + { + TextButton(onClick = onDismiss) { + Text(text = stringResource(it)) + } } }, - title = { Text(text = stringResource(R.string.deleteAlertTitle)) }, - text = { Text(text = stringResource(R.string.deleteAlertDescription)) }, + title = { Text(text = stringResource(titleRes)) }, + text = { Text(text = stringResource(descriptionRes)) }, ) } -@Preview(name = "DefaultDeleteConfirmationDialog") @Composable -private fun DefaultDeleteConfirmationDialogPreview() { - MaterialTheme { - Surface { - DefaultDeleteConfirmationDialog( - onDismiss = {}, - onConfirm = {}, - ) - } - } +internal fun DefaultValidationErrorDialog( + onDismiss: () -> Unit, + onConfirm: () -> Unit, +) { + DefaultContactCardDialog( + titleRes = R.string.alertTitle, + descriptionRes = R.string.alertDescription, + confirmButtonRes = android.R.string.ok, + onConfirm = onConfirm, + onDismiss = onDismiss, + ) +} + +@Composable +internal fun DefaultDeleteConfirmationDialog( + onDismiss: () -> Unit, + onConfirm: () -> Unit, +) { + DefaultContactCardDialog( + titleRes = R.string.deleteAlertTitle, + descriptionRes = R.string.deleteAlertDescription, + confirmButtonRes = R.string.deleteButton, + onConfirm = onConfirm, + onDismiss = onDismiss, + dismissButtonRes = RCore.string.buttonCancel, + ) } diff --git a/Ui/Compose/ContactCard/src/main/kotlin/com/infomaniak/core/ui/compose/contactcard/component/DefaultValidationErrorDialog.kt b/Ui/Compose/ContactCard/src/main/kotlin/com/infomaniak/core/ui/compose/contactcard/component/DefaultValidationErrorDialog.kt deleted file mode 100644 index 649bbdd30..000000000 --- a/Ui/Compose/ContactCard/src/main/kotlin/com/infomaniak/core/ui/compose/contactcard/component/DefaultValidationErrorDialog.kt +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Infomaniak Core - 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 (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.core.ui.compose.contactcard.component - -import androidx.compose.material3.AlertDialog -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Surface -import androidx.compose.material3.Text -import androidx.compose.material3.TextButton -import androidx.compose.runtime.Composable -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.tooling.preview.Preview -import com.infomaniak.core.ui.compose.contactcard.R - -@Composable -internal fun DefaultValidationErrorDialog( - onDismiss: () -> Unit, - onConfirm: () -> Unit, -) { - AlertDialog( - onDismissRequest = onDismiss, - confirmButton = { - TextButton(onClick = onConfirm) { - Text(text = stringResource(android.R.string.ok)) - } - }, - title = { Text(text = stringResource(R.string.alertTitle)) }, - text = { Text(text = stringResource(R.string.alertDescription)) }, - ) -} - -@Preview(name = "DefaultValidationErrorDialog") -@Composable -private fun DefaultValidationErrorDialogPreview() { - MaterialTheme { - Surface { - DefaultValidationErrorDialog( - onDismiss = {}, - onConfirm = {}, - ) - } - } -} From 24eae6913fd70ee8773b3677974a1ff301140985 Mon Sep 17 00:00:00 2001 From: Elouan BOITEUX Date: Fri, 14 Aug 2026 10:04:32 +0200 Subject: [PATCH 12/12] fix: Use PreviewParamaterProvider --- .../contactcard/component/ContactInfoRows.kt | 7 +- .../contactcard/component/PreviewData.kt | 69 ++++++++++--------- 2 files changed, 43 insertions(+), 33 deletions(-) diff --git a/Ui/Compose/ContactCard/src/main/kotlin/com/infomaniak/core/ui/compose/contactcard/component/ContactInfoRows.kt b/Ui/Compose/ContactCard/src/main/kotlin/com/infomaniak/core/ui/compose/contactcard/component/ContactInfoRows.kt index b5b1d04f0..534f3f937 100644 --- a/Ui/Compose/ContactCard/src/main/kotlin/com/infomaniak/core/ui/compose/contactcard/component/ContactInfoRows.kt +++ b/Ui/Compose/ContactCard/src/main/kotlin/com/infomaniak/core/ui/compose/contactcard/component/ContactInfoRows.kt @@ -30,6 +30,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp import com.infomaniak.core.auth.models.user.Card import com.infomaniak.core.auth.models.user.CardLinkType @@ -78,10 +79,12 @@ internal fun ContactInfoRows(card: Card, modifier: Modifier = Modifier) { @Preview(name = "ContactInfoRows") @Composable -private fun ContactInfoRowsPreview() { +private fun ContactInfoRowsPreview( + @PreviewParameter(ContactPreviewProvider::class) contactData: PreviewContactData, +) { MaterialTheme { Surface { - ContactInfoRows(card = previewCard()) + ContactInfoRows(card = contactData.card) } } } diff --git a/Ui/Compose/ContactCard/src/main/kotlin/com/infomaniak/core/ui/compose/contactcard/component/PreviewData.kt b/Ui/Compose/ContactCard/src/main/kotlin/com/infomaniak/core/ui/compose/contactcard/component/PreviewData.kt index 982540cee..bfac309b8 100644 --- a/Ui/Compose/ContactCard/src/main/kotlin/com/infomaniak/core/ui/compose/contactcard/component/PreviewData.kt +++ b/Ui/Compose/ContactCard/src/main/kotlin/com/infomaniak/core/ui/compose/contactcard/component/PreviewData.kt @@ -16,6 +16,7 @@ */ package com.infomaniak.core.ui.compose.contactcard.component +import androidx.compose.ui.tooling.preview.PreviewParameterProvider import com.infomaniak.core.auth.models.user.Card import com.infomaniak.core.auth.models.user.CardLink import com.infomaniak.core.auth.models.user.CardLinkType @@ -23,36 +24,42 @@ import com.infomaniak.core.auth.models.user.User import com.infomaniak.core.auth.models.user.preferences.OrganizationPreference import com.infomaniak.core.auth.models.user.preferences.Preferences -internal fun previewUser(): User { - val orgPreference = OrganizationPreference(currentOrganizationId = 0) - val preferences = Preferences( - security = null, - organizationPreference = orgPreference, - ) - return User( - id = 42, - displayName = "Alice Doe", - firstname = "Alice", - lastname = "Doe", - email = "alice.doe@example.com", - avatar = "https://example.com/avatar.png", - card = null, - login = "alice.doe", - isStaff = false, - preferences = preferences, +public data class PreviewContactData( + val user: User, + val card: Card, +) + +public class ContactPreviewProvider : PreviewParameterProvider { + override val values: Sequence = sequenceOf( + PreviewContactData( + user = User( + id = 42, + displayName = "Alice Doe", + firstname = "Alice", + lastname = "Doe", + email = "alice.doe@example.com", + avatar = "https://example.com/avatar.png", + card = null, + login = "alice.doe", + isStaff = false, + preferences = Preferences( + security = null, + organizationPreference = OrganizationPreference(currentOrganizationId = 0), + ), + ), + card = Card( + firstName = "Alice", + lastName = "Doe", + email = "alice.doe@example.com", + phone = "+41 79 123 45 67", + company = "Infomaniak", + avatarUrl = "https://example.com/avatar.png", + links = listOf( + CardLink(CardLinkType.LinkedIn, "https://linkedin.com/in/alicedoe"), + CardLink(CardLinkType.Website, "https://example.com"), + CardLink(CardLinkType.Other, "https://blog.example.com"), + ), + ), + ) ) } - -internal fun previewCard(): Card = Card( - firstName = "Alice", - lastName = "Doe", - email = "alice.doe@example.com", - phone = "+41 79 123 45 67", - company = "Infomaniak", - avatarUrl = "https://example.com/avatar.png", - links = listOf( - CardLink(CardLinkType.LinkedIn, "https://linkedin.com/in/alicedoe"), - CardLink(CardLinkType.Website, "https://example.com"), - CardLink(CardLinkType.Other, "https://blog.example.com"), - ), -)