feat(ui): brouillon enregistrement carnet local prioritaire
EBO <eric.bouhana@softalys.com> committé le 2026-07-22 15:51
7c82759e6b2655759517b59f6a7e44ca93ceb569
1 parent(s)
5 fichiers modifiés
+52
-41
M
android/app/src/main/java/fr/ebii/card2vcf/ui/ContactDraftScreen.kt
+27
-38
@@ -5,7 +5,6 @@ import androidx.compose.foundation.border
| 5 | 5 | import androidx.compose.foundation.layout.Arrangement |
| 6 | 6 | import androidx.compose.foundation.layout.Box |
| 7 | 7 | import androidx.compose.foundation.layout.Column |
| 8 | -import androidx.compose.foundation.layout.Row | |
| 9 | 8 | import androidx.compose.foundation.layout.fillMaxSize |
| 10 | 9 | import androidx.compose.foundation.layout.fillMaxWidth |
| 11 | 10 | import androidx.compose.foundation.layout.padding |
@@ -39,14 +38,13 @@ private val Square = RoundedCornerShape(0.dp)
| 39 | 38 | fun ContactDraftScreen( |
| 40 | 39 | draft: ContactDraftUi, |
| 41 | 40 | onCardChange: (fr.ebii.card2vcf.contact.ContactCard) -> Unit, |
| 41 | + onSaveToCarnet: () -> Unit, | |
| 42 | 42 | onCreateContact: () -> Unit, |
| 43 | 43 | onExportVcf: () -> Unit, |
| 44 | 44 | onRetry: () -> Unit, |
| 45 | 45 | modifier: Modifier = Modifier, |
| 46 | - /** Mode rescan : bouton primary « Mettre à jour ». */ | |
| 47 | - onUpdateContact: (() -> Unit)? = null, | |
| 46 | + isEditing: Boolean = false, | |
| 48 | 47 | ) { |
| 49 | - val isUpdate = onUpdateContact != null | |
| 50 | 48 | Column( |
| 51 | 49 | modifier.fillMaxSize().background(Fond).padding(horizontal = 18.dp, vertical = 16.dp), |
| 52 | 50 | verticalArrangement = Arrangement.spacedBy(14.dp), |
@@ -66,40 +64,31 @@ fun ContactDraftScreen(
| 66 | 64 | Text(draft.rawOcrText, fontFamily = FontFamily.Monospace, color = TexteFaible) |
| 67 | 65 | } |
| 68 | 66 | } |
| 69 | - if (isUpdate) { | |
| 70 | - Button( | |
| 71 | - onClick = onUpdateContact, | |
| 72 | - modifier = Modifier.fillMaxWidth(), | |
| 73 | - shape = Square, | |
| 74 | - colors = ButtonDefaults.buttonColors(containerColor = Ink, contentColor = OnPrimary), | |
| 75 | - ) { | |
| 76 | - Text(stringResource(R.string.edit_mettre_a_jour)) | |
| 77 | - } | |
| 78 | - OutlinedButton( | |
| 79 | - onClick = onExportVcf, | |
| 80 | - modifier = Modifier.fillMaxWidth(), | |
| 81 | - shape = Square, | |
| 82 | - ) { | |
| 83 | - Text(stringResource(R.string.scan_exporter_vcf)) | |
| 84 | - } | |
| 85 | - } else { | |
| 86 | - Row(Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.spacedBy(10.dp)) { | |
| 87 | - Button( | |
| 88 | - onClick = onCreateContact, | |
| 89 | - modifier = Modifier.weight(1f), | |
| 90 | - shape = Square, | |
| 91 | - colors = ButtonDefaults.buttonColors(containerColor = Ink, contentColor = OnPrimary), | |
| 92 | - ) { | |
| 93 | - Text(stringResource(R.string.scan_creer_contact)) | |
| 94 | - } | |
| 95 | - OutlinedButton( | |
| 96 | - onClick = onExportVcf, | |
| 97 | - modifier = Modifier.weight(1f), | |
| 98 | - shape = Square, | |
| 99 | - ) { | |
| 100 | - Text(stringResource(R.string.scan_exporter_vcf)) | |
| 101 | - } | |
| 102 | - } | |
| 67 | + Button( | |
| 68 | + onClick = onSaveToCarnet, | |
| 69 | + modifier = Modifier.fillMaxWidth(), | |
| 70 | + shape = Square, | |
| 71 | + colors = ButtonDefaults.buttonColors(containerColor = Ink, contentColor = OnPrimary), | |
| 72 | + ) { | |
| 73 | + Text( | |
| 74 | + stringResource( | |
| 75 | + if (isEditing) R.string.edit_mettre_a_jour else R.string.scan_enregistrer_carnet, | |
| 76 | + ), | |
| 77 | + ) | |
| 78 | + } | |
| 79 | + OutlinedButton( | |
| 80 | + onClick = onCreateContact, | |
| 81 | + modifier = Modifier.fillMaxWidth(), | |
| 82 | + shape = Square, | |
| 83 | + ) { | |
| 84 | + Text(stringResource(R.string.scan_creer_contact)) | |
| 85 | + } | |
| 86 | + OutlinedButton( | |
| 87 | + onClick = onExportVcf, | |
| 88 | + modifier = Modifier.fillMaxWidth(), | |
| 89 | + shape = Square, | |
| 90 | + ) { | |
| 91 | + Text(stringResource(R.string.scan_exporter_vcf)) | |
| 103 | 92 | } |
| 104 | 93 | OutlinedButton( |
| 105 | 94 | onClick = onRetry, |
M
android/app/src/main/java/fr/ebii/card2vcf/ui/ScanCarteScreen.kt
+2
-1
@@ -70,10 +70,11 @@ fun ScanCarteScreen(
| 70 | 70 | is ScanUiState.DraftReady -> ContactDraftScreen( |
| 71 | 71 | draft = s.draft, |
| 72 | 72 | onCardChange = viewModel::updateDraft, |
| 73 | + onSaveToCarnet = viewModel::saveToCarnet, | |
| 73 | 74 | onCreateContact = { onCreateContact(s.draft.card) }, |
| 74 | 75 | onExportVcf = { onExportVcf(s.draft.card) }, |
| 75 | 76 | onRetry = viewModel::reset, |
| 76 | - onUpdateContact = if (viewModel.isEditing) viewModel::updateFromDraft else null, | |
| 77 | + isEditing = viewModel.isEditing, | |
| 77 | 78 | modifier = modifier, |
| 78 | 79 | ) |
| 79 | 80 | ScanUiState.Scanning -> ScanProgress(R.string.scan_en_cours) |
M
android/app/src/main/java/fr/ebii/card2vcf/ui/ScanCarteViewModel.kt
+19
-0
@@ -101,6 +101,25 @@ class ScanCarteViewModel(
| 101 | 101 | } |
| 102 | 102 | } |
| 103 | 103 | |
| 104 | + /** Enregistre le brouillon dans le carnet local (nouveau contact). Retourne l'id via [saveDoneId]. */ | |
| 105 | + fun saveToCarnet() { | |
| 106 | + if (editingContactId != null) { | |
| 107 | + updateFromDraft() | |
| 108 | + return | |
| 109 | + } | |
| 110 | + val draft = (_state.value as? ScanUiState.DraftReady)?.draft ?: return | |
| 111 | + viewModelScope.launch { | |
| 112 | + val id = withContext(Dispatchers.IO) { | |
| 113 | + repository.insertFromCard( | |
| 114 | + card = draft.card, | |
| 115 | + cardBitmap = draft.preview, | |
| 116 | + profileBitmap = null, | |
| 117 | + ) | |
| 118 | + } | |
| 119 | + _saveDoneId.value = id | |
| 120 | + } | |
| 121 | + } | |
| 122 | + | |
| 104 | 123 | /** Met à jour le contact existant (mode rescan) à partir du brouillon courant. */ |
| 105 | 124 | fun updateFromDraft() { |
| 106 | 125 | val editId = editingContactId ?: return |
M
android/app/src/main/res/values-en/strings.xml
+2
-1
@@ -10,7 +10,8 @@
| 10 | 10 | <string name="scan_ocr_en_cours">Recognizing text…</string> |
| 11 | 11 | <string name="scan_structuration">Analyzing contact…</string> |
| 12 | 12 | <string name="scan_brouillon_titre">Review contact</string> |
| 13 | - <string name="scan_brouillon_sous_titre">Edit fields before creating the contact or exporting VCF.</string> | |
| 13 | + <string name="scan_brouillon_sous_titre">Edit fields, save to the address book, or export to Contacts / VCF.</string> | |
| 14 | + <string name="scan_enregistrer_carnet">Save</string> | |
| 14 | 15 | <string name="scan_ocr_brut">Raw OCR text</string> |
| 15 | 16 | <string name="scan_creer_contact">Create contact</string> |
| 16 | 17 | <string name="scan_exporter_vcf">Export VCF</string> |
M
android/app/src/main/res/values/strings.xml
+2
-1
@@ -10,7 +10,8 @@
| 10 | 10 | <string name="scan_ocr_en_cours">Reconnaissance du texte…</string> |
| 11 | 11 | <string name="scan_structuration">Analyse du contact…</string> |
| 12 | 12 | <string name="scan_brouillon_titre">Vérifier le contact</string> |
| 13 | - <string name="scan_brouillon_sous_titre">Corrigez les champs avant de créer le contact ou d\'exporter le VCF.</string> | |
| 13 | + <string name="scan_brouillon_sous_titre">Corrigez les champs, enregistrez dans le carnet, ou exportez vers Contacts / VCF.</string> | |
| 14 | + <string name="scan_enregistrer_carnet">Enregistrer</string> | |
| 14 | 15 | <string name="scan_ocr_brut">Texte OCR brut</string> |
| 15 | 16 | <string name="scan_creer_contact">Créer le contact</string> |
| 16 | 17 | <string name="scan_exporter_vcf">Exporter VCF</string> |
GitRust