From 37b734fb3f166a35906087091029de51531f6616 Mon Sep 17 00:00:00 2001 From: = Date: Wed, 14 Feb 2024 11:57:11 +0100 Subject: [PATCH 01/11] update: [ANDROAPP-4826] design system version updated --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index baa59ad61f..6cc27e7f6c 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -10,7 +10,7 @@ kotlin = '1.9.21' hilt = '2.47' hiltCompiler = '1.0.0' jacoco = '0.8.10' -designSystem = "0.2-20240208.105715-18" +designSystem = "0.2-20240213.152311-21" dhis2sdk = "1.10.0-20240207.110936-11" ruleEngine = "2.1.9" appcompat = "1.6.1" From 56a52c4caee03d8a25861841b3a848e1c668855e Mon Sep 17 00:00:00 2001 From: = Date: Wed, 14 Feb 2024 11:57:49 +0100 Subject: [PATCH 02/11] add: [ANDROAPP-4826] String added --- form/src/main/res/values/strings.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/form/src/main/res/values/strings.xml b/form/src/main/res/values/strings.xml index 2ecc71fb38..99f959679e 100644 --- a/form/src/main/res/values/strings.xml +++ b/form/src/main/res/values/strings.xml @@ -103,4 +103,5 @@ QR code Bar code Add location + This form has no fields configured \ No newline at end of file From 0e0ac71941fa1b77a1a1fdf752d1491fe5f4f329 Mon Sep 17 00:00:00 2001 From: = Date: Wed, 14 Feb 2024 11:59:13 +0100 Subject: [PATCH 03/11] add: [ANDROAPP-4826] Infobar added, warning message field added --- form/src/main/java/org/dhis2/form/ui/Form.kt | 117 +++++++++++++------ 1 file changed, 79 insertions(+), 38 deletions(-) diff --git a/form/src/main/java/org/dhis2/form/ui/Form.kt b/form/src/main/java/org/dhis2/form/ui/Form.kt index 81fa048149..c4b7584871 100644 --- a/form/src/main/java/org/dhis2/form/ui/Form.kt +++ b/form/src/main/java/org/dhis2/form/ui/Form.kt @@ -6,13 +6,19 @@ import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.interaction.MutableInteractionSource +import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.foundation.lazy.rememberLazyListState +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.Icon +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.outlined.ErrorOutline import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.MutableState @@ -21,6 +27,7 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip import androidx.compose.ui.focus.FocusDirection import androidx.compose.ui.focus.FocusManager import androidx.compose.ui.graphics.Color @@ -29,13 +36,19 @@ import androidx.compose.ui.unit.dp import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch import org.dhis2.commons.resources.ResourceManager +import org.dhis2.form.R import org.dhis2.form.model.FieldUiModel import org.dhis2.form.model.FormSection import org.dhis2.form.ui.event.RecyclerViewUiEvents import org.dhis2.form.ui.intent.FormIntent import org.dhis2.form.ui.provider.inputfield.FieldProvider +import org.hisp.dhis.mobile.ui.designsystem.component.InfoBar +import org.hisp.dhis.mobile.ui.designsystem.component.InfoBarData import org.hisp.dhis.mobile.ui.designsystem.component.Section import org.hisp.dhis.mobile.ui.designsystem.component.SectionState +import org.hisp.dhis.mobile.ui.designsystem.theme.Radius +import org.hisp.dhis.mobile.ui.designsystem.theme.Spacing +import org.hisp.dhis.mobile.ui.designsystem.theme.SurfaceColor @OptIn(ExperimentalFoundationApi::class) @Composable @@ -59,20 +72,20 @@ fun Form( } } } - val focusNext = remember { mutableStateOf(false) } - LazyColumn( - modifier = Modifier - .fillMaxSize() - .background(Color.White) - .clickable( - interactionSource = MutableInteractionSource(), - indication = null, - onClick = { focusManager.clearFocus() }, - ), - contentPadding = PaddingValues(horizontal = 16.dp, vertical = 16.dp), - state = scrollState, - ) { - if (sections.isNotEmpty()) { + if (sections.isNotEmpty()) { + val focusNext = remember { mutableStateOf(false) } + LazyColumn( + modifier = Modifier + .fillMaxSize() + .background(Color.White) + .clickable( + interactionSource = MutableInteractionSource(), + indication = null, + onClick = { focusManager.clearFocus() }, + ), + contentPadding = PaddingValues(horizontal = 16.dp, vertical = 16.dp), + state = scrollState, + ) { this.itemsIndexed( items = sections, key = { _, fieldUiModel -> fieldUiModel.uid }, @@ -100,48 +113,76 @@ fun Form( Section( title = section.title, isLastSection = getNextSection(section, sections) == null, - description = section.description, + description = if (sections.size >= 2 && section.fields.isNotEmpty()) section.description else null, completedFields = section.completedFields(), totalFields = section.fields.size, state = section.state, errorCount = section.errorCount(), warningCount = section.warningCount(), + warningMessage = resources.getString(R.string.form_without_fields), onNextSection = onNextSection, onSectionClick = { intentHandler.invoke(FormIntent.OnSection(section.uid)) }, content = { - section.fields.forEachIndexed { index, fieldUiModel -> - fieldUiModel.setCallback(callback) - FieldProvider( - modifier = Modifier.animateItemPlacement( - animationSpec = tween( - durationMillis = 500, - easing = LinearOutSlowInEasing, + if (sections.size >= 2 && section.fields.isNotEmpty()) { + section.fields.forEachIndexed { index, fieldUiModel -> + fieldUiModel.setCallback(callback) + FieldProvider( + modifier = Modifier.animateItemPlacement( + animationSpec = tween( + durationMillis = 500, + easing = LinearOutSlowInEasing, + ), ), - ), - fieldUiModel = fieldUiModel, - uiEventHandler = uiEventHandler, - intentHandler = intentHandler, - resources = resources, - focusManager = focusManager, - onNextClicked = { - if (index == section.fields.size - 1) { - onNextSection() - focusNext.value = true - } else { - focusManager.moveFocus(FocusDirection.Down) - } - }, - ) + fieldUiModel = fieldUiModel, + uiEventHandler = uiEventHandler, + intentHandler = intentHandler, + resources = resources, + focusManager = focusManager, + onNextClicked = { + if (index == section.fields.size - 1) { + onNextSection() + focusNext.value = true + } else { + focusManager.moveFocus(FocusDirection.Down) + } + }, + ) + } } }, ) } item(sections.size - 1) { - Spacer(modifier = Modifier.height(120.dp)) + Spacer(modifier = Modifier.height(Spacing.Spacing120)) } } + } else { + Column( + modifier = Modifier + .padding(Spacing.Spacing16), + ) { + InfoBar( + infoBarData = InfoBarData( + text = resources.getString(R.string.form_without_fields), + icon = { + Icon( + imageVector = Icons.Outlined.ErrorOutline, + contentDescription = "no fields", + tint = SurfaceColor.Warning, + ) + }, + color = SurfaceColor.Warning, + backgroundColor = SurfaceColor.WarningContainer, + actionText = null, + onClick = null, + ), + modifier = Modifier + .clip(shape = RoundedCornerShape(Radius.Full)) + .background(SurfaceColor.WarningContainer), + ) + } } } From 66a6b7276281eabdb06ad525a59e6696392d8454 Mon Sep 17 00:00:00 2001 From: = Date: Thu, 15 Feb 2024 10:45:15 +0100 Subject: [PATCH 04/11] update: [ANDROAPP-4826] visual changes when there is no fields in the section --- form/src/main/java/org/dhis2/form/ui/Form.kt | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/form/src/main/java/org/dhis2/form/ui/Form.kt b/form/src/main/java/org/dhis2/form/ui/Form.kt index c4b7584871..b330ea52cb 100644 --- a/form/src/main/java/org/dhis2/form/ui/Form.kt +++ b/form/src/main/java/org/dhis2/form/ui/Form.kt @@ -109,6 +109,10 @@ fun Form( focusManager.clearFocus() } } + + val sectionMessage = if (section.fields.isEmpty()) resources.getString(R.string.form_without_fields) else null + + val sectionState = if (section.fields.isEmpty()) SectionState.FIXED else section.state Section( title = section.title, @@ -116,16 +120,16 @@ fun Form( description = if (sections.size >= 2 && section.fields.isNotEmpty()) section.description else null, completedFields = section.completedFields(), totalFields = section.fields.size, - state = section.state, + state = sectionState, errorCount = section.errorCount(), warningCount = section.warningCount(), - warningMessage = resources.getString(R.string.form_without_fields), + warningMessage = sectionMessage, onNextSection = onNextSection, onSectionClick = { intentHandler.invoke(FormIntent.OnSection(section.uid)) }, content = { - if (sections.size >= 2 && section.fields.isNotEmpty()) { + if (section.fields.isNotEmpty()) { section.fields.forEachIndexed { index, fieldUiModel -> fieldUiModel.setCallback(callback) FieldProvider( From 99569bdc96dc6bda6ab7a08f936e3f853d0ed94d Mon Sep 17 00:00:00 2001 From: = Date: Thu, 15 Feb 2024 11:03:54 +0100 Subject: [PATCH 05/11] update: [ANDROAPP-4826] code formatted, visual changes to description --- form/src/main/java/org/dhis2/form/ui/Form.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/form/src/main/java/org/dhis2/form/ui/Form.kt b/form/src/main/java/org/dhis2/form/ui/Form.kt index b330ea52cb..a805f15dab 100644 --- a/form/src/main/java/org/dhis2/form/ui/Form.kt +++ b/form/src/main/java/org/dhis2/form/ui/Form.kt @@ -109,7 +109,7 @@ fun Form( focusManager.clearFocus() } } - + val sectionMessage = if (section.fields.isEmpty()) resources.getString(R.string.form_without_fields) else null val sectionState = if (section.fields.isEmpty()) SectionState.FIXED else section.state @@ -117,7 +117,7 @@ fun Form( Section( title = section.title, isLastSection = getNextSection(section, sections) == null, - description = if (sections.size >= 2 && section.fields.isNotEmpty()) section.description else null, + description = if (section.fields.isNotEmpty()) section.description else null, completedFields = section.completedFields(), totalFields = section.fields.size, state = sectionState, From 0ae891cb8bb23ffabbd14389d5acb86dbe27cd3d Mon Sep 17 00:00:00 2001 From: = Date: Mon, 19 Feb 2024 12:14:11 +0100 Subject: [PATCH 06/11] delete: [ANDROAPP-4826] Dummy section deleted in FormSectionMapper --- .../org/dhis2/form/ui/mapper/FormSectionMapper.kt | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/form/src/main/java/org/dhis2/form/ui/mapper/FormSectionMapper.kt b/form/src/main/java/org/dhis2/form/ui/mapper/FormSectionMapper.kt index 06f12753f1..ed94e851a6 100644 --- a/form/src/main/java/org/dhis2/form/ui/mapper/FormSectionMapper.kt +++ b/form/src/main/java/org/dhis2/form/ui/mapper/FormSectionMapper.kt @@ -29,18 +29,7 @@ class FormSectionMapper { ) } } - } else { - sections.add( - FormSection( - uid = "DUMMY", - title = "TITLE", - description = null, - state = SectionState.NO_HEADER, - fields = items.filterIsInstance(), - ), - ) } - return sections } From 09b24a497082ceca85cd894edcb9d68fe45e5654 Mon Sep 17 00:00:00 2001 From: = Date: Mon, 19 Feb 2024 13:48:31 +0100 Subject: [PATCH 07/11] update: [ANDROAPP-4826] the InfoBar doesn't appears when the form is loading --- form/src/main/java/org/dhis2/form/ui/FormView.kt | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/form/src/main/java/org/dhis2/form/ui/FormView.kt b/form/src/main/java/org/dhis2/form/ui/FormView.kt index dbb727030c..223cc1010c 100644 --- a/form/src/main/java/org/dhis2/form/ui/FormView.kt +++ b/form/src/main/java/org/dhis2/form/ui/FormView.kt @@ -365,12 +365,14 @@ class FormView : Fragment() { val sections = items?.let { formSectionMapper.mapFromFieldUiModelList(it) } ?: emptyList() - Form( - sections = sections, - intentHandler = ::intentHandler, - uiEventHandler = ::uiEventHandler, - resources = Injector.provideResourcesManager(context), - ) + if (viewModel.loading.value == false) { + Form( + sections = sections, + intentHandler = ::intentHandler, + uiEventHandler = ::uiEventHandler, + resources = Injector.provideResourcesManager(context), + ) + } } } } else { From b312dfe5d9ccecae73ce71e17ee0dc1490514e5e Mon Sep 17 00:00:00 2001 From: = Date: Tue, 20 Feb 2024 12:30:55 +0100 Subject: [PATCH 08/11] update: [ANDROAPP-4826] libs updated --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 6cc27e7f6c..69f79b77c4 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -102,7 +102,7 @@ hiltPlugin = { group = "com.google.dagger", name = "hilt-android-gradle-plugin", jacoco = { group = "org.jacoco", name = "org.jacoco.core", version.ref = "jacoco" } dhis2-android-sdk = { group = "org.hisp.dhis", name = "android-core", version.ref = "dhis2sdk" } dhis2-ruleengine = { group = "org.hisp.dhis.rules", name = "rule-engine", version.ref = "ruleEngine" } -dhis2-mobile-designsystem = { group = "org.hisp.dhis.mobile", name = "designsystem", version.ref = "designSystem" } +dhis2-mobile-designsystem = { group = "org.hisp.dhis.mobile", name = "designsystem-android", version.ref = "designSystem" } desugar = { group = "com.android.tools", name = "desugar_jdk_libs", version.ref = "desugar_jdk_libs" } androidx-activityKtx = { group = "androidx.activity", name = "activity-ktx", version.ref = "activityCompose" } androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" } From d1f126a250502ec0ee622997c21119a80a6d80e4 Mon Sep 17 00:00:00 2001 From: andresmr Date: Wed, 21 Feb 2024 11:31:18 +0100 Subject: [PATCH 09/11] [ANDROAPP-4826] Show no fields warnings if one section and is NO_HEADER --- form/src/main/java/org/dhis2/form/ui/Form.kt | 220 +++++++++--------- .../main/java/org/dhis2/form/ui/FormView.kt | 14 +- .../dhis2/form/ui/mapper/FormSectionMapper.kt | 10 + 3 files changed, 132 insertions(+), 112 deletions(-) diff --git a/form/src/main/java/org/dhis2/form/ui/Form.kt b/form/src/main/java/org/dhis2/form/ui/Form.kt index a805f15dab..2984d23041 100644 --- a/form/src/main/java/org/dhis2/form/ui/Form.kt +++ b/form/src/main/java/org/dhis2/form/ui/Form.kt @@ -72,121 +72,133 @@ fun Form( } } } - if (sections.isNotEmpty()) { - val focusNext = remember { mutableStateOf(false) } - LazyColumn( - modifier = Modifier - .fillMaxSize() - .background(Color.White) - .clickable( - interactionSource = MutableInteractionSource(), - indication = null, - onClick = { focusManager.clearFocus() }, - ), - contentPadding = PaddingValues(horizontal = 16.dp, vertical = 16.dp), - state = scrollState, - ) { - this.itemsIndexed( - items = sections, - key = { _, fieldUiModel -> fieldUiModel.uid }, - ) { _, section -> - val isSectionOpen = remember(section.state) { - derivedStateOf { section.state == SectionState.OPEN } - } + val focusNext = remember { mutableStateOf(false) } + LazyColumn( + modifier = Modifier + .fillMaxSize() + .background(Color.White) + .clickable( + interactionSource = MutableInteractionSource(), + indication = null, + onClick = { focusManager.clearFocus() }, + ), + contentPadding = PaddingValues(horizontal = 16.dp, vertical = 16.dp), + state = scrollState, + ) { + this.itemsIndexed( + items = sections, + key = { _, fieldUiModel -> fieldUiModel.uid }, + ) { _, section -> + val isSectionOpen = remember(section.state) { + derivedStateOf { section.state == SectionState.OPEN } + } - LaunchIfTrue(isSectionOpen.value) { - scrollState.animateScrollToItem(sections.indexOf(section)) - focusManager.moveFocusNext(focusNext) - } + LaunchIfTrue(isSectionOpen.value) { + scrollState.animateScrollToItem(sections.indexOf(section)) + focusManager.moveFocusNext(focusNext) + } - val onNextSection: () -> Unit = { - getNextSection(section, sections)?.let { - intentHandler.invoke(FormIntent.OnSection(it.uid)) - scope.launch { - scrollState.animateScrollToItem(sections.indexOf(it)) - } - } ?: run { - focusManager.clearFocus() + val onNextSection: () -> Unit = { + getNextSection(section, sections)?.let { + intentHandler.invoke(FormIntent.OnSection(it.uid)) + scope.launch { + scrollState.animateScrollToItem(sections.indexOf(it)) } + } ?: run { + focusManager.clearFocus() } + } - val sectionMessage = if (section.fields.isEmpty()) resources.getString(R.string.form_without_fields) else null - - val sectionState = if (section.fields.isEmpty()) SectionState.FIXED else section.state + val sectionMessage = + if (section.fields.isEmpty()) resources.getString(R.string.form_without_fields) else null - Section( - title = section.title, - isLastSection = getNextSection(section, sections) == null, - description = if (section.fields.isNotEmpty()) section.description else null, - completedFields = section.completedFields(), - totalFields = section.fields.size, - state = sectionState, - errorCount = section.errorCount(), - warningCount = section.warningCount(), - warningMessage = sectionMessage, - onNextSection = onNextSection, - onSectionClick = { - intentHandler.invoke(FormIntent.OnSection(section.uid)) - }, - content = { - if (section.fields.isNotEmpty()) { - section.fields.forEachIndexed { index, fieldUiModel -> - fieldUiModel.setCallback(callback) - FieldProvider( - modifier = Modifier.animateItemPlacement( - animationSpec = tween( - durationMillis = 500, - easing = LinearOutSlowInEasing, - ), + Section( + title = section.title, + isLastSection = getNextSection(section, sections) == null, + description = if (section.fields.isNotEmpty()) section.description else null, + completedFields = section.completedFields(), + totalFields = section.fields.size, + state = section.state, + errorCount = section.errorCount(), + warningCount = section.warningCount(), + warningMessage = sectionMessage, + onNextSection = onNextSection, + onSectionClick = { + intentHandler.invoke(FormIntent.OnSection(section.uid)) + }, + content = { + if (section.fields.isNotEmpty()) { + section.fields.forEachIndexed { index, fieldUiModel -> + fieldUiModel.setCallback(callback) + FieldProvider( + modifier = Modifier.animateItemPlacement( + animationSpec = tween( + durationMillis = 500, + easing = LinearOutSlowInEasing, ), - fieldUiModel = fieldUiModel, - uiEventHandler = uiEventHandler, - intentHandler = intentHandler, - resources = resources, - focusManager = focusManager, - onNextClicked = { - if (index == section.fields.size - 1) { - onNextSection() - focusNext.value = true - } else { - focusManager.moveFocus(FocusDirection.Down) - } - }, - ) - } + ), + fieldUiModel = fieldUiModel, + uiEventHandler = uiEventHandler, + intentHandler = intentHandler, + resources = resources, + focusManager = focusManager, + onNextClicked = { + if (index == section.fields.size - 1) { + onNextSection() + focusNext.value = true + } else { + focusManager.moveFocus(FocusDirection.Down) + } + }, + ) } - }, - ) - } - item(sections.size - 1) { - Spacer(modifier = Modifier.height(Spacing.Spacing120)) - } + } + }, + ) } + item(sections.size - 1) { + Spacer(modifier = Modifier.height(Spacing.Spacing120)) + } + } + if (shouldDisplayNoFieldsWarning(sections)) { + NoFieldsWarning(resources) + } +} + +fun shouldDisplayNoFieldsWarning(sections: List): Boolean { + return if (sections.size == 1) { + val section = sections.first() + section.state == SectionState.NO_HEADER && section.fields.isEmpty() } else { - Column( + false + } +} + +@Composable +fun NoFieldsWarning(resources: ResourceManager) { + Column( + modifier = Modifier + .padding(Spacing.Spacing16), + ) { + InfoBar( + infoBarData = InfoBarData( + text = resources.getString(R.string.form_without_fields), + icon = { + Icon( + imageVector = Icons.Outlined.ErrorOutline, + contentDescription = "no fields", + tint = SurfaceColor.Warning, + ) + }, + color = SurfaceColor.Warning, + backgroundColor = SurfaceColor.WarningContainer, + actionText = null, + onClick = null, + ), modifier = Modifier - .padding(Spacing.Spacing16), - ) { - InfoBar( - infoBarData = InfoBarData( - text = resources.getString(R.string.form_without_fields), - icon = { - Icon( - imageVector = Icons.Outlined.ErrorOutline, - contentDescription = "no fields", - tint = SurfaceColor.Warning, - ) - }, - color = SurfaceColor.Warning, - backgroundColor = SurfaceColor.WarningContainer, - actionText = null, - onClick = null, - ), - modifier = Modifier - .clip(shape = RoundedCornerShape(Radius.Full)) - .background(SurfaceColor.WarningContainer), - ) - } + .clip(shape = RoundedCornerShape(Radius.Full)) + .background(SurfaceColor.WarningContainer), + ) } } diff --git a/form/src/main/java/org/dhis2/form/ui/FormView.kt b/form/src/main/java/org/dhis2/form/ui/FormView.kt index 223cc1010c..dbb727030c 100644 --- a/form/src/main/java/org/dhis2/form/ui/FormView.kt +++ b/form/src/main/java/org/dhis2/form/ui/FormView.kt @@ -365,14 +365,12 @@ class FormView : Fragment() { val sections = items?.let { formSectionMapper.mapFromFieldUiModelList(it) } ?: emptyList() - if (viewModel.loading.value == false) { - Form( - sections = sections, - intentHandler = ::intentHandler, - uiEventHandler = ::uiEventHandler, - resources = Injector.provideResourcesManager(context), - ) - } + Form( + sections = sections, + intentHandler = ::intentHandler, + uiEventHandler = ::uiEventHandler, + resources = Injector.provideResourcesManager(context), + ) } } } else { diff --git a/form/src/main/java/org/dhis2/form/ui/mapper/FormSectionMapper.kt b/form/src/main/java/org/dhis2/form/ui/mapper/FormSectionMapper.kt index ed94e851a6..17cfe0e794 100644 --- a/form/src/main/java/org/dhis2/form/ui/mapper/FormSectionMapper.kt +++ b/form/src/main/java/org/dhis2/form/ui/mapper/FormSectionMapper.kt @@ -29,6 +29,16 @@ class FormSectionMapper { ) } } + } else { + sections.add( + FormSection( + uid = "DUMMY", + title = "TITLE", + description = null, + state = SectionState.NO_HEADER, + fields = items.filterIsInstance(), + ), + ) } return sections } From f57365d5605ad7f233dc2f2889bb451ab7e5b192 Mon Sep 17 00:00:00 2001 From: andresmr Date: Wed, 21 Feb 2024 11:38:54 +0100 Subject: [PATCH 10/11] [ANDROAPP-4826] Refactor section warning generation to mapper --- .../src/main/java/org/dhis2/form/model/FormSection.kt | 1 + form/src/main/java/org/dhis2/form/ui/Form.kt | 5 +---- .../org/dhis2/form/ui/mapper/FormSectionMapper.kt | 11 +++++++++-- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/form/src/main/java/org/dhis2/form/model/FormSection.kt b/form/src/main/java/org/dhis2/form/model/FormSection.kt index 302c3b3b4b..83325b54a4 100644 --- a/form/src/main/java/org/dhis2/form/model/FormSection.kt +++ b/form/src/main/java/org/dhis2/form/model/FormSection.kt @@ -8,6 +8,7 @@ data class FormSection( val description: String? = null, val state: SectionState, val fields: List, + var warningMessage: Int? = null, ) { fun completedFields() = fields.count { it.value != null } fun errorCount() = fields.count { it.error != null } diff --git a/form/src/main/java/org/dhis2/form/ui/Form.kt b/form/src/main/java/org/dhis2/form/ui/Form.kt index 2984d23041..d9974702ee 100644 --- a/form/src/main/java/org/dhis2/form/ui/Form.kt +++ b/form/src/main/java/org/dhis2/form/ui/Form.kt @@ -109,9 +109,6 @@ fun Form( } } - val sectionMessage = - if (section.fields.isEmpty()) resources.getString(R.string.form_without_fields) else null - Section( title = section.title, isLastSection = getNextSection(section, sections) == null, @@ -121,7 +118,7 @@ fun Form( state = section.state, errorCount = section.errorCount(), warningCount = section.warningCount(), - warningMessage = sectionMessage, + warningMessage = section.warningMessage?.let { resources.getString(it) }, onNextSection = onNextSection, onSectionClick = { intentHandler.invoke(FormIntent.OnSection(section.uid)) diff --git a/form/src/main/java/org/dhis2/form/ui/mapper/FormSectionMapper.kt b/form/src/main/java/org/dhis2/form/ui/mapper/FormSectionMapper.kt index 17cfe0e794..118e47ae2a 100644 --- a/form/src/main/java/org/dhis2/form/ui/mapper/FormSectionMapper.kt +++ b/form/src/main/java/org/dhis2/form/ui/mapper/FormSectionMapper.kt @@ -1,5 +1,6 @@ package org.dhis2.form.ui.mapper +import org.dhis2.form.R import org.dhis2.form.model.FieldUiModel import org.dhis2.form.model.FieldUiModelImpl import org.dhis2.form.model.FormSection @@ -13,6 +14,8 @@ class FormSectionMapper { if (hasSections(items)) { items.forEach { item -> if (item is SectionUiModelImpl) { + val fields = items.filterIsInstance() + .filter { it.programStageSection == item.uid } sections.add( FormSection( uid = item.uid, @@ -23,8 +26,12 @@ class FormSectionMapper { false -> SectionState.CLOSE null -> SectionState.FIXED }, - fields = items.filterIsInstance() - .filter { it.programStageSection == item.uid }, + fields = fields, + warningMessage = if (fields.isEmpty()) { + R.string.form_without_fields + } else { + null + }, ), ) } From 57dfa82fad433a2f78ffa72a7eaee2501ae8ee79 Mon Sep 17 00:00:00 2001 From: andresmr Date: Wed, 21 Feb 2024 11:42:07 +0100 Subject: [PATCH 11/11] [ANDROAPP-4826] Refactor section warning generation to mapper --- form/src/main/java/org/dhis2/form/ui/Form.kt | 128 ++++++++++--------- 1 file changed, 65 insertions(+), 63 deletions(-) diff --git a/form/src/main/java/org/dhis2/form/ui/Form.kt b/form/src/main/java/org/dhis2/form/ui/Form.kt index d9974702ee..96f0459d26 100644 --- a/form/src/main/java/org/dhis2/form/ui/Form.kt +++ b/form/src/main/java/org/dhis2/form/ui/Form.kt @@ -85,76 +85,78 @@ fun Form( contentPadding = PaddingValues(horizontal = 16.dp, vertical = 16.dp), state = scrollState, ) { - this.itemsIndexed( - items = sections, - key = { _, fieldUiModel -> fieldUiModel.uid }, - ) { _, section -> - val isSectionOpen = remember(section.state) { - derivedStateOf { section.state == SectionState.OPEN } - } + if (sections.isNotEmpty()) { + this.itemsIndexed( + items = sections, + key = { _, fieldUiModel -> fieldUiModel.uid }, + ) { _, section -> + val isSectionOpen = remember(section.state) { + derivedStateOf { section.state == SectionState.OPEN } + } - LaunchIfTrue(isSectionOpen.value) { - scrollState.animateScrollToItem(sections.indexOf(section)) - focusManager.moveFocusNext(focusNext) - } + LaunchIfTrue(isSectionOpen.value) { + scrollState.animateScrollToItem(sections.indexOf(section)) + focusManager.moveFocusNext(focusNext) + } - val onNextSection: () -> Unit = { - getNextSection(section, sections)?.let { - intentHandler.invoke(FormIntent.OnSection(it.uid)) - scope.launch { - scrollState.animateScrollToItem(sections.indexOf(it)) + val onNextSection: () -> Unit = { + getNextSection(section, sections)?.let { + intentHandler.invoke(FormIntent.OnSection(it.uid)) + scope.launch { + scrollState.animateScrollToItem(sections.indexOf(it)) + } + } ?: run { + focusManager.clearFocus() } - } ?: run { - focusManager.clearFocus() } - } - Section( - title = section.title, - isLastSection = getNextSection(section, sections) == null, - description = if (section.fields.isNotEmpty()) section.description else null, - completedFields = section.completedFields(), - totalFields = section.fields.size, - state = section.state, - errorCount = section.errorCount(), - warningCount = section.warningCount(), - warningMessage = section.warningMessage?.let { resources.getString(it) }, - onNextSection = onNextSection, - onSectionClick = { - intentHandler.invoke(FormIntent.OnSection(section.uid)) - }, - content = { - if (section.fields.isNotEmpty()) { - section.fields.forEachIndexed { index, fieldUiModel -> - fieldUiModel.setCallback(callback) - FieldProvider( - modifier = Modifier.animateItemPlacement( - animationSpec = tween( - durationMillis = 500, - easing = LinearOutSlowInEasing, + Section( + title = section.title, + isLastSection = getNextSection(section, sections) == null, + description = if (section.fields.isNotEmpty()) section.description else null, + completedFields = section.completedFields(), + totalFields = section.fields.size, + state = section.state, + errorCount = section.errorCount(), + warningCount = section.warningCount(), + warningMessage = section.warningMessage?.let { resources.getString(it) }, + onNextSection = onNextSection, + onSectionClick = { + intentHandler.invoke(FormIntent.OnSection(section.uid)) + }, + content = { + if (section.fields.isNotEmpty()) { + section.fields.forEachIndexed { index, fieldUiModel -> + fieldUiModel.setCallback(callback) + FieldProvider( + modifier = Modifier.animateItemPlacement( + animationSpec = tween( + durationMillis = 500, + easing = LinearOutSlowInEasing, + ), ), - ), - fieldUiModel = fieldUiModel, - uiEventHandler = uiEventHandler, - intentHandler = intentHandler, - resources = resources, - focusManager = focusManager, - onNextClicked = { - if (index == section.fields.size - 1) { - onNextSection() - focusNext.value = true - } else { - focusManager.moveFocus(FocusDirection.Down) - } - }, - ) + fieldUiModel = fieldUiModel, + uiEventHandler = uiEventHandler, + intentHandler = intentHandler, + resources = resources, + focusManager = focusManager, + onNextClicked = { + if (index == section.fields.size - 1) { + onNextSection() + focusNext.value = true + } else { + focusManager.moveFocus(FocusDirection.Down) + } + }, + ) + } } - } - }, - ) - } - item(sections.size - 1) { - Spacer(modifier = Modifier.height(Spacing.Spacing120)) + }, + ) + } + item(sections.size - 1) { + Spacer(modifier = Modifier.height(Spacing.Spacing120)) + } } } if (shouldDisplayNoFieldsWarning(sections)) {