From f3f516b9239bf33313d2c8f9a2473c774044f581 Mon Sep 17 00:00:00 2001 From: Paul Kraft Date: Mon, 11 Nov 2024 13:43:22 -0800 Subject: [PATCH] Finish up SpeziViews --- .../design/personalInfo/UserProfileTest.kt | 3 +- .../composables/NameFieldsTestComposable.kt | 4 +- .../composables/UserProfileTestComposable.kt | 14 ++-- .../simulators/UserProfileTestSimulator.kt | 8 ++ .../{views => validation}/ValidationTest.kt | 6 +- .../composables/DefaultValidationRules.kt | 38 +++++++++ .../composables/FocusValidationRules.kt | 16 ++-- .../FocusValidationRulesSimulator.kt | 2 +- .../spezi/core/design/views/MarkdownTest.kt | 42 ++++++++++ .../core/design/views/SuspendButtonTest.kt | 39 +++++++++ .../composables/DefaultValidationRules.kt | 38 --------- .../composables/MarkdownTestComposable.kt | 24 ++++++ .../SuspendButtonTestComposable.kt | 46 +++++++++++ .../views/simulators/MarkdownTestSimulator.kt | 25 ++++++ .../simulators/SuspendButtonTestSimulator.kt | 70 ++++++++++++++++ .../component/markdown/MarkdownElement.kt | 10 +-- .../personalInfo/PersonNameComponents.kt | 2 +- .../personalInfo/UserProfileComposable.kt | 29 +++++-- .../personalInfo/fields/NameFieldRow.kt | 6 +- .../personalInfo/fields/NameTextField.kt | 4 +- .../validation/CascadingValidationEffect.kt | 2 +- .../validation/ValidationEngine.kt | 6 +- .../validation/ValidationModifier.kt | 35 ++++---- .../validation/ValidationRule.kt | 4 +- .../validation/ValidationRuleDefaults.kt | 2 +- .../ValidationDebounceDuration.kt | 2 +- .../configuration/ValidationEngine.kt | 6 ++ .../ValidationEngineConfiguration.kt | 9 ++ .../state/CapturedValidationState.kt | 4 +- .../state/CapturedValidationStateEntries.kt | 2 +- .../state/FailedValidationResult.kt | 4 +- .../validation/state/ReceiveValidation.kt | 2 +- .../validation/state/ValidationContext.kt | 2 +- .../views/ValidationResultsComposable.kt | 4 +- .../validation/views/VerifiableTextField.kt | 4 +- .../ProcessingDebounceDuration.kt | 2 +- .../views/layout/DescriptionGridRow.kt | 2 +- .../views/model/OperationState.kt | 2 +- .../views/model/ViewState.kt | 2 +- .../viewState/OperationStateAlert.kt | 4 +- .../viewModifier/viewState/ViewStateAlert.kt | 7 +- .../viewModifier/viewState/ViewStateMapper.kt | 6 +- .../views/views/button/ProcessingOverlay.kt | 17 +++- .../views/views/button/SuspendButton.kt | 25 ++++-- .../validation/views/views/text/Markdown.kt | 82 +++++++++++++++++++ .../configuration/ValidationEngine.kt | 6 -- .../ValidationEngineConfiguration.kt | 9 -- .../spezi/core/design/SpeziValidationTest.kt | 6 +- .../simulator/ContactComposableSimulator.kt | 2 +- 49 files changed, 533 insertions(+), 153 deletions(-) rename core/design/src/androidTest/kotlin/edu/stanford/spezi/core/design/{views => validation}/ValidationTest.kt (80%) create mode 100644 core/design/src/androidTest/kotlin/edu/stanford/spezi/core/design/validation/composables/DefaultValidationRules.kt rename core/design/src/androidTest/kotlin/edu/stanford/spezi/core/design/{views => validation}/composables/FocusValidationRules.kt (77%) rename core/design/src/androidTest/kotlin/edu/stanford/spezi/core/design/{views => validation}/simulators/FocusValidationRulesSimulator.kt (96%) create mode 100644 core/design/src/androidTest/kotlin/edu/stanford/spezi/core/design/views/MarkdownTest.kt create mode 100644 core/design/src/androidTest/kotlin/edu/stanford/spezi/core/design/views/SuspendButtonTest.kt delete mode 100644 core/design/src/androidTest/kotlin/edu/stanford/spezi/core/design/views/composables/DefaultValidationRules.kt create mode 100644 core/design/src/androidTest/kotlin/edu/stanford/spezi/core/design/views/composables/MarkdownTestComposable.kt create mode 100644 core/design/src/androidTest/kotlin/edu/stanford/spezi/core/design/views/composables/SuspendButtonTestComposable.kt create mode 100644 core/design/src/androidTest/kotlin/edu/stanford/spezi/core/design/views/simulators/MarkdownTestSimulator.kt create mode 100644 core/design/src/androidTest/kotlin/edu/stanford/spezi/core/design/views/simulators/SuspendButtonTestSimulator.kt rename core/design/src/main/kotlin/edu/stanford/spezi/core/design/{views => validation}/personalInfo/PersonNameComponents.kt (94%) rename core/design/src/main/kotlin/edu/stanford/spezi/core/design/{views => validation}/personalInfo/UserProfileComposable.kt (67%) rename core/design/src/main/kotlin/edu/stanford/spezi/core/design/{views => validation}/personalInfo/fields/NameFieldRow.kt (89%) rename core/design/src/main/kotlin/edu/stanford/spezi/core/design/{views => validation}/personalInfo/fields/NameTextField.kt (92%) rename core/design/src/main/kotlin/edu/stanford/spezi/core/design/{views => validation}/validation/CascadingValidationEffect.kt (51%) rename core/design/src/main/kotlin/edu/stanford/spezi/core/design/{views => validation}/validation/ValidationEngine.kt (93%) rename core/design/src/main/kotlin/edu/stanford/spezi/core/design/{views => validation}/validation/ValidationModifier.kt (58%) rename core/design/src/main/kotlin/edu/stanford/spezi/core/design/{views => validation}/validation/ValidationRule.kt (88%) rename core/design/src/main/kotlin/edu/stanford/spezi/core/design/{views => validation}/validation/ValidationRuleDefaults.kt (96%) rename core/design/src/main/kotlin/edu/stanford/spezi/core/design/{views => validation}/validation/configuration/ValidationDebounceDuration.kt (77%) create mode 100644 core/design/src/main/kotlin/edu/stanford/spezi/core/design/validation/validation/configuration/ValidationEngine.kt create mode 100644 core/design/src/main/kotlin/edu/stanford/spezi/core/design/validation/validation/configuration/ValidationEngineConfiguration.kt rename core/design/src/main/kotlin/edu/stanford/spezi/core/design/{views => validation}/validation/state/CapturedValidationState.kt (83%) rename core/design/src/main/kotlin/edu/stanford/spezi/core/design/{views => validation}/validation/state/CapturedValidationStateEntries.kt (87%) rename core/design/src/main/kotlin/edu/stanford/spezi/core/design/{views => validation}/validation/state/FailedValidationResult.kt (75%) rename core/design/src/main/kotlin/edu/stanford/spezi/core/design/{views => validation}/validation/state/ReceiveValidation.kt (91%) rename core/design/src/main/kotlin/edu/stanford/spezi/core/design/{views => validation}/validation/state/ValidationContext.kt (95%) rename core/design/src/main/kotlin/edu/stanford/spezi/core/design/{views => validation}/validation/views/ValidationResultsComposable.kt (80%) rename core/design/src/main/kotlin/edu/stanford/spezi/core/design/{views => validation}/validation/views/VerifiableTextField.kt (94%) rename core/design/src/main/kotlin/edu/stanford/spezi/core/design/{views => validation}/views/compositionLocal/ProcessingDebounceDuration.kt (71%) rename core/design/src/main/kotlin/edu/stanford/spezi/core/design/{views => validation}/views/layout/DescriptionGridRow.kt (96%) rename core/design/src/main/kotlin/edu/stanford/spezi/core/design/{views => validation}/views/model/OperationState.kt (50%) rename core/design/src/main/kotlin/edu/stanford/spezi/core/design/{views => validation}/views/model/ViewState.kt (90%) rename core/design/src/main/kotlin/edu/stanford/spezi/core/design/{views => validation}/views/viewModifier/viewState/OperationStateAlert.kt (73%) rename core/design/src/main/kotlin/edu/stanford/spezi/core/design/{views => validation}/views/viewModifier/viewState/ViewStateAlert.kt (76%) rename core/design/src/main/kotlin/edu/stanford/spezi/core/design/{views => validation}/views/viewModifier/viewState/ViewStateMapper.kt (60%) rename core/design/src/main/kotlin/edu/stanford/spezi/core/design/{views => validation}/views/views/button/ProcessingOverlay.kt (60%) rename core/design/src/main/kotlin/edu/stanford/spezi/core/design/{views => validation}/views/views/button/SuspendButton.kt (76%) create mode 100644 core/design/src/main/kotlin/edu/stanford/spezi/core/design/validation/views/views/text/Markdown.kt delete mode 100644 core/design/src/main/kotlin/edu/stanford/spezi/core/design/views/validation/configuration/ValidationEngine.kt delete mode 100644 core/design/src/main/kotlin/edu/stanford/spezi/core/design/views/validation/configuration/ValidationEngineConfiguration.kt diff --git a/core/design/src/androidTest/kotlin/edu/stanford/spezi/core/design/personalInfo/UserProfileTest.kt b/core/design/src/androidTest/kotlin/edu/stanford/spezi/core/design/personalInfo/UserProfileTest.kt index 7385cfd58..c36d75fcf 100644 --- a/core/design/src/androidTest/kotlin/edu/stanford/spezi/core/design/personalInfo/UserProfileTest.kt +++ b/core/design/src/androidTest/kotlin/edu/stanford/spezi/core/design/personalInfo/UserProfileTest.kt @@ -23,7 +23,8 @@ class UserProfileTest { fun testUserProfile() { userProfile { assertUserInitialsExists(true, "PS") - assertUserInitialsExists(false, "LS") + assertUserInitialsExists(true, "LS") + waitUntilUserInitialsDisappear("LS") assertImageExists() } } diff --git a/core/design/src/androidTest/kotlin/edu/stanford/spezi/core/design/personalInfo/composables/NameFieldsTestComposable.kt b/core/design/src/androidTest/kotlin/edu/stanford/spezi/core/design/personalInfo/composables/NameFieldsTestComposable.kt index 9bcf547bb..81b1d9fb6 100644 --- a/core/design/src/androidTest/kotlin/edu/stanford/spezi/core/design/personalInfo/composables/NameFieldsTestComposable.kt +++ b/core/design/src/androidTest/kotlin/edu/stanford/spezi/core/design/personalInfo/composables/NameFieldsTestComposable.kt @@ -7,8 +7,8 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import edu.stanford.spezi.core.design.component.StringResource -import edu.stanford.spezi.core.design.views.personalInfo.PersonNameComponents -import edu.stanford.spezi.core.design.views.personalInfo.fields.NameFieldRow +import edu.stanford.spezi.core.design.validation.personalInfo.PersonNameComponents +import edu.stanford.spezi.core.design.validation.personalInfo.fields.NameFieldRow @Composable fun NameFieldsTestComposable() { diff --git a/core/design/src/androidTest/kotlin/edu/stanford/spezi/core/design/personalInfo/composables/UserProfileTestComposable.kt b/core/design/src/androidTest/kotlin/edu/stanford/spezi/core/design/personalInfo/composables/UserProfileTestComposable.kt index 5383b3147..a86bd1132 100644 --- a/core/design/src/androidTest/kotlin/edu/stanford/spezi/core/design/personalInfo/composables/UserProfileTestComposable.kt +++ b/core/design/src/androidTest/kotlin/edu/stanford/spezi/core/design/personalInfo/composables/UserProfileTestComposable.kt @@ -8,8 +8,10 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp import edu.stanford.spezi.core.design.component.ImageResource -import edu.stanford.spezi.core.design.views.personalInfo.PersonNameComponents -import edu.stanford.spezi.core.design.views.personalInfo.UserProfileComposable +import edu.stanford.spezi.core.design.validation.personalInfo.PersonNameComponents +import edu.stanford.spezi.core.design.validation.personalInfo.UserProfileComposable +import kotlinx.coroutines.delay +import kotlin.time.Duration.Companion.seconds @Composable fun UserProfileTestComposable() { @@ -20,8 +22,10 @@ fun UserProfileTestComposable() { ) UserProfileComposable( Modifier.height(200.dp), - PersonNameComponents(givenName = "Leland", familyName = "Stanford"), - ImageResource.Vector(Icons.Default.Person) - ) + PersonNameComponents(givenName = "Leland", familyName = "Stanford") + ) { + delay(0.5.seconds) + return@UserProfileComposable ImageResource.Vector(Icons.Default.Person) + } } } diff --git a/core/design/src/androidTest/kotlin/edu/stanford/spezi/core/design/personalInfo/simulators/UserProfileTestSimulator.kt b/core/design/src/androidTest/kotlin/edu/stanford/spezi/core/design/personalInfo/simulators/UserProfileTestSimulator.kt index 5c5d0133c..552d409df 100644 --- a/core/design/src/androidTest/kotlin/edu/stanford/spezi/core/design/personalInfo/simulators/UserProfileTestSimulator.kt +++ b/core/design/src/androidTest/kotlin/edu/stanford/spezi/core/design/personalInfo/simulators/UserProfileTestSimulator.kt @@ -3,6 +3,7 @@ package edu.stanford.spezi.core.design.personalInfo.simulators import androidx.compose.ui.test.assertCountEquals import androidx.compose.ui.test.junit4.ComposeTestRule import androidx.compose.ui.test.onAllNodesWithContentDescription +import androidx.compose.ui.test.onAllNodesWithText import androidx.compose.ui.test.onNodeWithText class UserProfileTestSimulator( @@ -19,6 +20,13 @@ class UserProfileTestSimulator( } } + fun waitUntilUserInitialsDisappear(text: String, timeoutMillis: Long = 1_000) { + composeTestRule.waitUntil(timeoutMillis = timeoutMillis) { + composeTestRule.onAllNodesWithText(text) + .fetchSemanticsNodes().isEmpty() + } + } + fun assertImageExists() { composeTestRule .onAllNodesWithContentDescription("") diff --git a/core/design/src/androidTest/kotlin/edu/stanford/spezi/core/design/views/ValidationTest.kt b/core/design/src/androidTest/kotlin/edu/stanford/spezi/core/design/validation/ValidationTest.kt similarity index 80% rename from core/design/src/androidTest/kotlin/edu/stanford/spezi/core/design/views/ValidationTest.kt rename to core/design/src/androidTest/kotlin/edu/stanford/spezi/core/design/validation/ValidationTest.kt index 8f3ab5b26..340a5d733 100644 --- a/core/design/src/androidTest/kotlin/edu/stanford/spezi/core/design/views/ValidationTest.kt +++ b/core/design/src/androidTest/kotlin/edu/stanford/spezi/core/design/validation/ValidationTest.kt @@ -1,8 +1,8 @@ -package edu.stanford.spezi.core.design.views +package edu.stanford.spezi.core.design.validation import androidx.compose.ui.test.junit4.createComposeRule -import edu.stanford.spezi.core.design.views.composables.FocusValidationRules -import edu.stanford.spezi.core.design.views.simulators.FocusValidationRulesSimulator +import edu.stanford.spezi.core.design.validation.composables.FocusValidationRules +import edu.stanford.spezi.core.design.validation.simulators.FocusValidationRulesSimulator import org.junit.Before import org.junit.Rule import org.junit.Test diff --git a/core/design/src/androidTest/kotlin/edu/stanford/spezi/core/design/validation/composables/DefaultValidationRules.kt b/core/design/src/androidTest/kotlin/edu/stanford/spezi/core/design/validation/composables/DefaultValidationRules.kt new file mode 100644 index 000000000..55b0bb61c --- /dev/null +++ b/core/design/src/androidTest/kotlin/edu/stanford/spezi/core/design/validation/composables/DefaultValidationRules.kt @@ -0,0 +1,38 @@ +package edu.stanford.spezi.core.design.validation.composables + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import edu.stanford.spezi.core.design.component.StringResource +import edu.stanford.spezi.core.design.validation.validation.Validate +import edu.stanford.spezi.core.design.validation.validation.ValidationRule +import edu.stanford.spezi.core.design.validation.validation.asciiLettersOnly +import edu.stanford.spezi.core.design.validation.validation.mediumPassword +import edu.stanford.spezi.core.design.validation.validation.minimalEmail +import edu.stanford.spezi.core.design.validation.validation.minimalPassword +import edu.stanford.spezi.core.design.validation.validation.nonEmpty +import edu.stanford.spezi.core.design.validation.validation.strongPassword +import edu.stanford.spezi.core.design.validation.validation.unicodeLettersOnly +import edu.stanford.spezi.core.design.validation.validation.views.VerifiableTextField + +@Composable +fun DefaultValidationRules() { + val input = remember { mutableStateOf("") } + val rules = remember { + listOf( + ValidationRule.nonEmpty, + ValidationRule.unicodeLettersOnly, + ValidationRule.asciiLettersOnly, + ValidationRule.minimalEmail, + ValidationRule.minimalPassword, + ValidationRule.mediumPassword, + ValidationRule.strongPassword + ) + } + Validate(input.value, rules) { + VerifiableTextField( + StringResource("Field"), + text = input + ) + } +} diff --git a/core/design/src/androidTest/kotlin/edu/stanford/spezi/core/design/views/composables/FocusValidationRules.kt b/core/design/src/androidTest/kotlin/edu/stanford/spezi/core/design/validation/composables/FocusValidationRules.kt similarity index 77% rename from core/design/src/androidTest/kotlin/edu/stanford/spezi/core/design/views/composables/FocusValidationRules.kt rename to core/design/src/androidTest/kotlin/edu/stanford/spezi/core/design/validation/composables/FocusValidationRules.kt index 276a82651..e7a55a163 100644 --- a/core/design/src/androidTest/kotlin/edu/stanford/spezi/core/design/views/composables/FocusValidationRules.kt +++ b/core/design/src/androidTest/kotlin/edu/stanford/spezi/core/design/validation/composables/FocusValidationRules.kt @@ -1,4 +1,4 @@ -package edu.stanford.spezi.core.design.views.composables +package edu.stanford.spezi.core.design.validation.composables import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row @@ -9,13 +9,13 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import edu.stanford.spezi.core.design.component.StringResource -import edu.stanford.spezi.core.design.views.validation.Validate -import edu.stanford.spezi.core.design.views.validation.ValidationRule -import edu.stanford.spezi.core.design.views.validation.minimalPassword -import edu.stanford.spezi.core.design.views.validation.nonEmpty -import edu.stanford.spezi.core.design.views.validation.state.ReceiveValidation -import edu.stanford.spezi.core.design.views.validation.state.ValidationContext -import edu.stanford.spezi.core.design.views.validation.views.VerifiableTextField +import edu.stanford.spezi.core.design.validation.validation.Validate +import edu.stanford.spezi.core.design.validation.validation.ValidationRule +import edu.stanford.spezi.core.design.validation.validation.minimalPassword +import edu.stanford.spezi.core.design.validation.validation.nonEmpty +import edu.stanford.spezi.core.design.validation.validation.state.ReceiveValidation +import edu.stanford.spezi.core.design.validation.validation.state.ValidationContext +import edu.stanford.spezi.core.design.validation.validation.views.VerifiableTextField enum class Field { INPUT, NON_EMPTY_INPUT diff --git a/core/design/src/androidTest/kotlin/edu/stanford/spezi/core/design/views/simulators/FocusValidationRulesSimulator.kt b/core/design/src/androidTest/kotlin/edu/stanford/spezi/core/design/validation/simulators/FocusValidationRulesSimulator.kt similarity index 96% rename from core/design/src/androidTest/kotlin/edu/stanford/spezi/core/design/views/simulators/FocusValidationRulesSimulator.kt rename to core/design/src/androidTest/kotlin/edu/stanford/spezi/core/design/validation/simulators/FocusValidationRulesSimulator.kt index 7f35f12f3..62d828e16 100644 --- a/core/design/src/androidTest/kotlin/edu/stanford/spezi/core/design/views/simulators/FocusValidationRulesSimulator.kt +++ b/core/design/src/androidTest/kotlin/edu/stanford/spezi/core/design/validation/simulators/FocusValidationRulesSimulator.kt @@ -1,4 +1,4 @@ -package edu.stanford.spezi.core.design.views.simulators +package edu.stanford.spezi.core.design.validation.simulators import androidx.compose.ui.test.assertHasClickAction import androidx.compose.ui.test.junit4.ComposeTestRule diff --git a/core/design/src/androidTest/kotlin/edu/stanford/spezi/core/design/views/MarkdownTest.kt b/core/design/src/androidTest/kotlin/edu/stanford/spezi/core/design/views/MarkdownTest.kt new file mode 100644 index 000000000..56a568be3 --- /dev/null +++ b/core/design/src/androidTest/kotlin/edu/stanford/spezi/core/design/views/MarkdownTest.kt @@ -0,0 +1,42 @@ +package edu.stanford.spezi.core.design.views + +import androidx.compose.ui.test.junit4.createComposeRule +import edu.stanford.spezi.core.design.views.composables.MarkdownTestComposable +import edu.stanford.spezi.core.design.views.simulators.MarkdownTestSimulator +import org.junit.Before +import org.junit.Rule +import org.junit.Test + +class MarkdownTest { + + @get:Rule + val composeTestRule = createComposeRule() + + @Before + fun init() { + composeTestRule.setContent { + MarkdownTestComposable() + } + } + + @Test + fun testMarkdown() { + markdown { + waitForTextToAppear( + "This is a markdown example.", + timeoutMillis = 100 + ) + assertTextExists( + "This is a markdown example taking half a second to load.", + exists = false + ) + waitForTextToAppear( + "This is a markdown example taking half a second to load.", + ) + } + } + + private fun markdown(block: MarkdownTestSimulator.() -> Unit) { + MarkdownTestSimulator(composeTestRule).apply(block) + } +} diff --git a/core/design/src/androidTest/kotlin/edu/stanford/spezi/core/design/views/SuspendButtonTest.kt b/core/design/src/androidTest/kotlin/edu/stanford/spezi/core/design/views/SuspendButtonTest.kt new file mode 100644 index 000000000..f5df1198a --- /dev/null +++ b/core/design/src/androidTest/kotlin/edu/stanford/spezi/core/design/views/SuspendButtonTest.kt @@ -0,0 +1,39 @@ +package edu.stanford.spezi.core.design.views + +import androidx.compose.ui.test.junit4.createComposeRule +import edu.stanford.spezi.core.design.views.composables.SuspendButtonTestComposable +import edu.stanford.spezi.core.design.views.simulators.SuspendButtonTestSimulator +import org.junit.Before +import org.junit.Rule +import org.junit.Test + +class SuspendButtonTest { + + @get:Rule + val composeTestRule = createComposeRule() + + @Before + fun init() { + composeTestRule.setContent { + SuspendButtonTestComposable() + } + } + + @Test + fun testSuspendButton() { + suspendButton { + clickHelloWorldButton() + waitForHelloWorldButtonAction() + resetHelloWorldButtonAction() + + clickHelloThrowingWorldButton() + assertViewStateAlertAppeared("Error was thrown!") + dismissViewStateAlert() + assertHelloThrowingWorldButtonIsEnabled() + } + } + + private fun suspendButton(block: SuspendButtonTestSimulator.() -> Unit) { + SuspendButtonTestSimulator(composeTestRule).apply(block) + } +} diff --git a/core/design/src/androidTest/kotlin/edu/stanford/spezi/core/design/views/composables/DefaultValidationRules.kt b/core/design/src/androidTest/kotlin/edu/stanford/spezi/core/design/views/composables/DefaultValidationRules.kt deleted file mode 100644 index 8599fcd84..000000000 --- a/core/design/src/androidTest/kotlin/edu/stanford/spezi/core/design/views/composables/DefaultValidationRules.kt +++ /dev/null @@ -1,38 +0,0 @@ -package edu.stanford.spezi.core.design.views.composables - -import androidx.compose.runtime.Composable -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import edu.stanford.spezi.core.design.component.StringResource -import edu.stanford.spezi.core.design.views.validation.Validate -import edu.stanford.spezi.core.design.views.validation.ValidationRule -import edu.stanford.spezi.core.design.views.validation.asciiLettersOnly -import edu.stanford.spezi.core.design.views.validation.mediumPassword -import edu.stanford.spezi.core.design.views.validation.minimalEmail -import edu.stanford.spezi.core.design.views.validation.minimalPassword -import edu.stanford.spezi.core.design.views.validation.nonEmpty -import edu.stanford.spezi.core.design.views.validation.strongPassword -import edu.stanford.spezi.core.design.views.validation.unicodeLettersOnly -import edu.stanford.spezi.core.design.views.validation.views.VerifiableTextField - -@Composable -fun DefaultValidationRules() { - val input = remember { mutableStateOf("") } - val rules = remember { - listOf( - ValidationRule.nonEmpty, - ValidationRule.unicodeLettersOnly, - ValidationRule.asciiLettersOnly, - ValidationRule.minimalEmail, - ValidationRule.minimalPassword, - ValidationRule.mediumPassword, - ValidationRule.strongPassword - ) - } - Validate(input.value, rules) { - VerifiableTextField( - StringResource("Field"), - text = input - ) - } -} diff --git a/core/design/src/androidTest/kotlin/edu/stanford/spezi/core/design/views/composables/MarkdownTestComposable.kt b/core/design/src/androidTest/kotlin/edu/stanford/spezi/core/design/views/composables/MarkdownTestComposable.kt new file mode 100644 index 000000000..d223778c2 --- /dev/null +++ b/core/design/src/androidTest/kotlin/edu/stanford/spezi/core/design/views/composables/MarkdownTestComposable.kt @@ -0,0 +1,24 @@ +package edu.stanford.spezi.core.design.views.composables + +import androidx.compose.foundation.layout.Column +import androidx.compose.runtime.Composable +import edu.stanford.spezi.core.design.validation.views.views.text.MarkdownBytes +import edu.stanford.spezi.core.design.validation.views.views.text.MarkdownString +import kotlinx.coroutines.delay +import java.nio.charset.StandardCharsets +import kotlin.time.Duration.Companion.milliseconds + +@Composable +fun MarkdownTestComposable() { + Column { + MarkdownBytes( + bytes = { + delay(500.milliseconds) + "This is a markdown **example** taking half a second to load." + .toByteArray(StandardCharsets.UTF_8) + } + ) + + MarkdownString("This is a markdown **example**.") + } +} diff --git a/core/design/src/androidTest/kotlin/edu/stanford/spezi/core/design/views/composables/SuspendButtonTestComposable.kt b/core/design/src/androidTest/kotlin/edu/stanford/spezi/core/design/views/composables/SuspendButtonTestComposable.kt new file mode 100644 index 000000000..13af8d14d --- /dev/null +++ b/core/design/src/androidTest/kotlin/edu/stanford/spezi/core/design/views/composables/SuspendButtonTestComposable.kt @@ -0,0 +1,46 @@ +package edu.stanford.spezi.core.design.views.composables + +import androidx.compose.foundation.layout.Column +import androidx.compose.material3.Button +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import edu.stanford.spezi.core.design.component.StringResource +import edu.stanford.spezi.core.design.validation.views.model.ViewState +import edu.stanford.spezi.core.design.validation.views.viewModifier.viewState.ViewStateAlert +import edu.stanford.spezi.core.design.validation.views.views.button.SuspendButton +import kotlinx.coroutines.delay +import kotlin.time.Duration.Companion.milliseconds + +class CustomError : Throwable() { + override val message = "Error was thrown!" +} + +@Composable +fun SuspendButtonTestComposable() { + var showCompleted by remember { mutableStateOf(false) } + val viewState = remember { mutableStateOf(ViewState.Idle) } + + ViewStateAlert(viewState) + + Column { + if (showCompleted) { + Text("Action executed") + Button(onClick = { showCompleted = false }) { + Text("Reset") + } + } else { + SuspendButton(StringResource("Hello World")) { + delay(500.milliseconds) + showCompleted = true + } + + SuspendButton(StringResource("Hello Throwing World"), viewState) { + throw CustomError() + } + } + } +} diff --git a/core/design/src/androidTest/kotlin/edu/stanford/spezi/core/design/views/simulators/MarkdownTestSimulator.kt b/core/design/src/androidTest/kotlin/edu/stanford/spezi/core/design/views/simulators/MarkdownTestSimulator.kt new file mode 100644 index 000000000..8acbf488f --- /dev/null +++ b/core/design/src/androidTest/kotlin/edu/stanford/spezi/core/design/views/simulators/MarkdownTestSimulator.kt @@ -0,0 +1,25 @@ +package edu.stanford.spezi.core.design.views.simulators + +import androidx.compose.ui.test.junit4.ComposeTestRule +import androidx.compose.ui.test.onAllNodesWithText +import androidx.compose.ui.test.onNodeWithText + +class MarkdownTestSimulator( + private val composeTestRule: ComposeTestRule, +) { + fun assertTextExists(text: String, exists: Boolean = true) { + val node = composeTestRule.onNodeWithText(text) + if (exists) { + node.assertExists() + } else { + node.assertDoesNotExist() + } + } + + fun waitForTextToAppear(text: String, timeoutMillis: Long = 1_000) { + composeTestRule.waitUntil(timeoutMillis) { + composeTestRule.onAllNodesWithText(text) + .fetchSemanticsNodes().isNotEmpty() + } + } +} diff --git a/core/design/src/androidTest/kotlin/edu/stanford/spezi/core/design/views/simulators/SuspendButtonTestSimulator.kt b/core/design/src/androidTest/kotlin/edu/stanford/spezi/core/design/views/simulators/SuspendButtonTestSimulator.kt new file mode 100644 index 000000000..8542f64f4 --- /dev/null +++ b/core/design/src/androidTest/kotlin/edu/stanford/spezi/core/design/views/simulators/SuspendButtonTestSimulator.kt @@ -0,0 +1,70 @@ +package edu.stanford.spezi.core.design.views.simulators + +import androidx.compose.ui.test.assertHasClickAction +import androidx.compose.ui.test.assertIsEnabled +import androidx.compose.ui.test.junit4.ComposeTestRule +import androidx.compose.ui.test.onAllNodesWithText +import androidx.compose.ui.test.onNodeWithText +import androidx.compose.ui.test.performClick + +class SuspendButtonTestSimulator( + private val composeTestRule: ComposeTestRule, +) { + + fun clickHelloWorldButton() { + composeTestRule + .onNodeWithText("Hello World") + .assertHasClickAction() + .performClick() + } + + fun waitForHelloWorldButtonAction() { + composeTestRule.waitUntil { + composeTestRule.onAllNodesWithText("Action executed") + .fetchSemanticsNodes().size == 1 + } + + composeTestRule + .onNodeWithText("Action executed") + .assertExists() + } + + fun resetHelloWorldButtonAction() { + composeTestRule + .onNodeWithText("Reset") + .assertHasClickAction() + .performClick() + } + + fun clickHelloThrowingWorldButton() { + composeTestRule + .onNodeWithText("Hello Throwing World") + .assertHasClickAction() + .assertIsEnabled() + .performClick() + } + + fun assertViewStateAlertAppeared(message: String) { + composeTestRule + .onNodeWithText("Error") + .assertExists() + + composeTestRule + .onNodeWithText(message) + .assertExists() + } + + fun dismissViewStateAlert() { + composeTestRule + .onNodeWithText("OK") + .assertHasClickAction() + .performClick() + } + + fun assertHelloThrowingWorldButtonIsEnabled() { + composeTestRule + .onNodeWithText("Hello Throwing World") + .assertHasClickAction() + .assertIsEnabled() + } +} diff --git a/core/design/src/main/kotlin/edu/stanford/spezi/core/design/component/markdown/MarkdownElement.kt b/core/design/src/main/kotlin/edu/stanford/spezi/core/design/component/markdown/MarkdownElement.kt index e8e8d9f4f..f88d0b038 100644 --- a/core/design/src/main/kotlin/edu/stanford/spezi/core/design/component/markdown/MarkdownElement.kt +++ b/core/design/src/main/kotlin/edu/stanford/spezi/core/design/component/markdown/MarkdownElement.kt @@ -1,8 +1,8 @@ package edu.stanford.spezi.core.design.component.markdown -sealed class MarkdownElement { - data class Heading(val level: Int, val text: String) : MarkdownElement() - data class Paragraph(val text: String) : MarkdownElement() - data class Bold(val text: String) : MarkdownElement() - data class ListItem(val text: String) : MarkdownElement() +sealed interface MarkdownElement { + data class Heading(val level: Int, val text: String) : MarkdownElement + data class Paragraph(val text: String) : MarkdownElement + data class Bold(val text: String) : MarkdownElement + data class ListItem(val text: String) : MarkdownElement } diff --git a/core/design/src/main/kotlin/edu/stanford/spezi/core/design/views/personalInfo/PersonNameComponents.kt b/core/design/src/main/kotlin/edu/stanford/spezi/core/design/validation/personalInfo/PersonNameComponents.kt similarity index 94% rename from core/design/src/main/kotlin/edu/stanford/spezi/core/design/views/personalInfo/PersonNameComponents.kt rename to core/design/src/main/kotlin/edu/stanford/spezi/core/design/validation/personalInfo/PersonNameComponents.kt index 471037d08..97406c988 100644 --- a/core/design/src/main/kotlin/edu/stanford/spezi/core/design/views/personalInfo/PersonNameComponents.kt +++ b/core/design/src/main/kotlin/edu/stanford/spezi/core/design/validation/personalInfo/PersonNameComponents.kt @@ -1,4 +1,4 @@ -package edu.stanford.spezi.core.design.views.personalInfo +package edu.stanford.spezi.core.design.validation.personalInfo data class PersonNameComponents( var namePrefix: String? = null, diff --git a/core/design/src/main/kotlin/edu/stanford/spezi/core/design/views/personalInfo/UserProfileComposable.kt b/core/design/src/main/kotlin/edu/stanford/spezi/core/design/validation/personalInfo/UserProfileComposable.kt similarity index 67% rename from core/design/src/main/kotlin/edu/stanford/spezi/core/design/views/personalInfo/UserProfileComposable.kt rename to core/design/src/main/kotlin/edu/stanford/spezi/core/design/validation/personalInfo/UserProfileComposable.kt index 4ddab4c19..77e8db0d2 100644 --- a/core/design/src/main/kotlin/edu/stanford/spezi/core/design/views/personalInfo/UserProfileComposable.kt +++ b/core/design/src/main/kotlin/edu/stanford/spezi/core/design/validation/personalInfo/UserProfileComposable.kt @@ -1,4 +1,4 @@ -package edu.stanford.spezi.core.design.views.personalInfo +package edu.stanford.spezi.core.design.validation.personalInfo import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box @@ -8,8 +8,11 @@ import androidx.compose.foundation.layout.size import androidx.compose.foundation.shape.CircleShape import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip @@ -21,20 +24,32 @@ import edu.stanford.spezi.core.design.component.ImageResource import edu.stanford.spezi.core.design.component.ImageResourceComposable import edu.stanford.spezi.core.design.theme.Colors import edu.stanford.spezi.core.design.theme.lighten +import edu.stanford.spezi.core.logging.SpeziLogger import kotlin.math.min @Composable fun UserProfileComposable( modifier: Modifier = Modifier, name: PersonNameComponents, - image: ImageResource? = null, + imageLoader: suspend () -> ImageResource? = { null }, ) { - val size = remember { mutableStateOf(IntSize.Zero) } + var size by remember { mutableStateOf(IntSize.Zero) } + var loadedImage by remember { mutableStateOf(null) } - Box(modifier.onSizeChanged { size.value = it }.aspectRatio(1f)) { - val sideLength = min(size.value.height, size.value.width).dp + LaunchedEffect(Unit) { + loadedImage = runCatching { imageLoader() } + .onFailure { SpeziLogger.e(it) { "Failed to load image" } } + .getOrNull() + } + + val formattedName = remember(name) { + name.formatted(PersonNameComponents.FormatStyle.ABBREVIATED) + } + + Box(modifier.onSizeChanged { size = it }.aspectRatio(1f)) { + val sideLength = min(size.height, size.width).dp Box(modifier.size(sideLength, sideLength), contentAlignment = Alignment.Center) { - image?.let { + loadedImage?.let { ImageResourceComposable( it, "", // TODO: Add contentDescription to ImageResource directly? @@ -45,7 +60,7 @@ fun UserProfileComposable( } ?: run { Box(Modifier.background(Colors.secondary, CircleShape).fillMaxSize(), contentAlignment = Alignment.Center) { Text( - name.formatted(PersonNameComponents.FormatStyle.ABBREVIATED), + formattedName, fontSize = (sideLength.value * 0.2).sp, color = Colors.secondary.lighten(), ) diff --git a/core/design/src/main/kotlin/edu/stanford/spezi/core/design/views/personalInfo/fields/NameFieldRow.kt b/core/design/src/main/kotlin/edu/stanford/spezi/core/design/validation/personalInfo/fields/NameFieldRow.kt similarity index 89% rename from core/design/src/main/kotlin/edu/stanford/spezi/core/design/views/personalInfo/fields/NameFieldRow.kt rename to core/design/src/main/kotlin/edu/stanford/spezi/core/design/validation/personalInfo/fields/NameFieldRow.kt index 48102fe1f..3c4f5e046 100644 --- a/core/design/src/main/kotlin/edu/stanford/spezi/core/design/views/personalInfo/fields/NameFieldRow.kt +++ b/core/design/src/main/kotlin/edu/stanford/spezi/core/design/validation/personalInfo/fields/NameFieldRow.kt @@ -1,4 +1,4 @@ -package edu.stanford.spezi.core.design.views.personalInfo.fields +package edu.stanford.spezi.core.design.validation.personalInfo.fields import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.padding @@ -12,8 +12,8 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp import edu.stanford.spezi.core.design.component.StringResource import edu.stanford.spezi.core.design.theme.ThemePreviews -import edu.stanford.spezi.core.design.views.personalInfo.PersonNameComponents -import edu.stanford.spezi.core.design.views.views.layout.DescriptionGridRow +import edu.stanford.spezi.core.design.validation.personalInfo.PersonNameComponents +import edu.stanford.spezi.core.design.validation.views.layout.DescriptionGridRow import kotlin.reflect.KMutableProperty1 @Composable diff --git a/core/design/src/main/kotlin/edu/stanford/spezi/core/design/views/personalInfo/fields/NameTextField.kt b/core/design/src/main/kotlin/edu/stanford/spezi/core/design/validation/personalInfo/fields/NameTextField.kt similarity index 92% rename from core/design/src/main/kotlin/edu/stanford/spezi/core/design/views/personalInfo/fields/NameTextField.kt rename to core/design/src/main/kotlin/edu/stanford/spezi/core/design/validation/personalInfo/fields/NameTextField.kt index 5ffacc1cc..2b0528625 100644 --- a/core/design/src/main/kotlin/edu/stanford/spezi/core/design/views/personalInfo/fields/NameTextField.kt +++ b/core/design/src/main/kotlin/edu/stanford/spezi/core/design/validation/personalInfo/fields/NameTextField.kt @@ -1,4 +1,4 @@ -package edu.stanford.spezi.core.design.views.personalInfo.fields +package edu.stanford.spezi.core.design.validation.personalInfo.fields import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.material3.Text @@ -9,7 +9,7 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import edu.stanford.spezi.core.design.component.StringResource import edu.stanford.spezi.core.design.theme.ThemePreviews -import edu.stanford.spezi.core.design.views.personalInfo.PersonNameComponents +import edu.stanford.spezi.core.design.validation.personalInfo.PersonNameComponents import kotlin.reflect.KMutableProperty1 @Composable diff --git a/core/design/src/main/kotlin/edu/stanford/spezi/core/design/views/validation/CascadingValidationEffect.kt b/core/design/src/main/kotlin/edu/stanford/spezi/core/design/validation/validation/CascadingValidationEffect.kt similarity index 51% rename from core/design/src/main/kotlin/edu/stanford/spezi/core/design/views/validation/CascadingValidationEffect.kt rename to core/design/src/main/kotlin/edu/stanford/spezi/core/design/validation/validation/CascadingValidationEffect.kt index e1676ff23..75ba0a587 100644 --- a/core/design/src/main/kotlin/edu/stanford/spezi/core/design/views/validation/CascadingValidationEffect.kt +++ b/core/design/src/main/kotlin/edu/stanford/spezi/core/design/validation/validation/CascadingValidationEffect.kt @@ -1,4 +1,4 @@ -package edu.stanford.spezi.core.design.views.validation +package edu.stanford.spezi.core.design.validation.validation enum class CascadingValidationEffect { CONTINUE, INTERCEPT diff --git a/core/design/src/main/kotlin/edu/stanford/spezi/core/design/views/validation/ValidationEngine.kt b/core/design/src/main/kotlin/edu/stanford/spezi/core/design/validation/validation/ValidationEngine.kt similarity index 93% rename from core/design/src/main/kotlin/edu/stanford/spezi/core/design/views/validation/ValidationEngine.kt rename to core/design/src/main/kotlin/edu/stanford/spezi/core/design/validation/validation/ValidationEngine.kt index 42f50e87d..39f333125 100644 --- a/core/design/src/main/kotlin/edu/stanford/spezi/core/design/views/validation/ValidationEngine.kt +++ b/core/design/src/main/kotlin/edu/stanford/spezi/core/design/validation/validation/ValidationEngine.kt @@ -1,8 +1,8 @@ -package edu.stanford.spezi.core.design.views.validation +package edu.stanford.spezi.core.design.validation.validation import androidx.compose.runtime.mutableStateOf -import edu.stanford.spezi.core.design.views.validation.configuration.DEFAULT_VALIDATION_DEBOUNCE_DURATION -import edu.stanford.spezi.core.design.views.validation.state.FailedValidationResult +import edu.stanford.spezi.core.design.validation.validation.configuration.DEFAULT_VALIDATION_DEBOUNCE_DURATION +import edu.stanford.spezi.core.design.validation.validation.state.FailedValidationResult import kotlinx.coroutines.DelicateCoroutinesApi import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.Job diff --git a/core/design/src/main/kotlin/edu/stanford/spezi/core/design/views/validation/ValidationModifier.kt b/core/design/src/main/kotlin/edu/stanford/spezi/core/design/validation/validation/ValidationModifier.kt similarity index 58% rename from core/design/src/main/kotlin/edu/stanford/spezi/core/design/views/validation/ValidationModifier.kt rename to core/design/src/main/kotlin/edu/stanford/spezi/core/design/validation/validation/ValidationModifier.kt index 02a87f526..7c7019f60 100644 --- a/core/design/src/main/kotlin/edu/stanford/spezi/core/design/views/validation/ValidationModifier.kt +++ b/core/design/src/main/kotlin/edu/stanford/spezi/core/design/validation/validation/ValidationModifier.kt @@ -1,4 +1,4 @@ -package edu.stanford.spezi.core.design.views.validation +package edu.stanford.spezi.core.design.validation.validation import android.annotation.SuppressLint import androidx.compose.runtime.Composable @@ -7,12 +7,11 @@ import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import edu.stanford.spezi.core.design.component.StringResource -import edu.stanford.spezi.core.design.views.validation.configuration.LocalValidationDebounce -import edu.stanford.spezi.core.design.views.validation.configuration.LocalValidationEngine -import edu.stanford.spezi.core.design.views.validation.configuration.LocalValidationEngineConfiguration -import edu.stanford.spezi.core.design.views.validation.state.CapturedValidationState -import edu.stanford.spezi.core.design.views.validation.state.LocalCapturedValidationStateEntries -import kotlin.time.Duration +import edu.stanford.spezi.core.design.validation.validation.configuration.LocalValidationDebounce +import edu.stanford.spezi.core.design.validation.validation.configuration.LocalValidationEngine +import edu.stanford.spezi.core.design.validation.validation.configuration.LocalValidationEngineConfiguration +import edu.stanford.spezi.core.design.validation.validation.state.CapturedValidationState +import edu.stanford.spezi.core.design.validation.validation.state.LocalCapturedValidationStateEntries @Composable fun Validate( @@ -20,10 +19,12 @@ fun Validate( message: StringResource, content: @Composable () -> Unit, ) { - val rule = ValidationRule( - rule = { it.isEmpty() }, - message = message - ) + val rule = remember { + ValidationRule( + rule = { it.isEmpty() }, + message = message + ) + } Validate( input = if (predicate) "" else "FALSE", rules = listOf(rule), @@ -39,10 +40,14 @@ fun Validate( content: @Composable () -> Unit, ) { val validationDebounce = LocalValidationDebounce.current - val previousValidationDebounce = remember { mutableStateOf(null) } val validationEngineConfiguration = LocalValidationEngineConfiguration.current - val previousValidationEngineConfiguration = remember { mutableStateOf(null) } - val engine = remember { ValidationEngineImpl(rules, validationDebounce, validationEngineConfiguration) } + val engine = remember { + ValidationEngineImpl( + rules, + validationDebounce, + validationEngineConfiguration + ) + } LaunchedEffect(input) { engine.submit(input, debounce = true) @@ -50,12 +55,10 @@ fun Validate( LaunchedEffect(validationDebounce) { engine.debounceDuration = validationDebounce - previousValidationDebounce.value = validationDebounce } LaunchedEffect(validationEngineConfiguration) { engine.configuration = validationEngineConfiguration - previousValidationEngineConfiguration.value = validationEngineConfiguration } val hasFocus = remember { mutableStateOf(false) } diff --git a/core/design/src/main/kotlin/edu/stanford/spezi/core/design/views/validation/ValidationRule.kt b/core/design/src/main/kotlin/edu/stanford/spezi/core/design/validation/validation/ValidationRule.kt similarity index 88% rename from core/design/src/main/kotlin/edu/stanford/spezi/core/design/views/validation/ValidationRule.kt rename to core/design/src/main/kotlin/edu/stanford/spezi/core/design/validation/validation/ValidationRule.kt index a4f61f9f1..e488f4da3 100644 --- a/core/design/src/main/kotlin/edu/stanford/spezi/core/design/views/validation/ValidationRule.kt +++ b/core/design/src/main/kotlin/edu/stanford/spezi/core/design/validation/validation/ValidationRule.kt @@ -1,7 +1,7 @@ -package edu.stanford.spezi.core.design.views.validation +package edu.stanford.spezi.core.design.validation.validation import edu.stanford.spezi.core.design.component.StringResource -import edu.stanford.spezi.core.design.views.validation.state.FailedValidationResult +import edu.stanford.spezi.core.design.validation.validation.state.FailedValidationResult import edu.stanford.spezi.core.utils.UUID import java.util.UUID diff --git a/core/design/src/main/kotlin/edu/stanford/spezi/core/design/views/validation/ValidationRuleDefaults.kt b/core/design/src/main/kotlin/edu/stanford/spezi/core/design/validation/validation/ValidationRuleDefaults.kt similarity index 96% rename from core/design/src/main/kotlin/edu/stanford/spezi/core/design/views/validation/ValidationRuleDefaults.kt rename to core/design/src/main/kotlin/edu/stanford/spezi/core/design/validation/validation/ValidationRuleDefaults.kt index a6aa86bc7..791d4c37f 100644 --- a/core/design/src/main/kotlin/edu/stanford/spezi/core/design/views/validation/ValidationRuleDefaults.kt +++ b/core/design/src/main/kotlin/edu/stanford/spezi/core/design/validation/validation/ValidationRuleDefaults.kt @@ -1,4 +1,4 @@ -package edu.stanford.spezi.core.design.views.validation +package edu.stanford.spezi.core.design.validation.validation import edu.stanford.spezi.core.design.component.StringResource import java.nio.charset.StandardCharsets diff --git a/core/design/src/main/kotlin/edu/stanford/spezi/core/design/views/validation/configuration/ValidationDebounceDuration.kt b/core/design/src/main/kotlin/edu/stanford/spezi/core/design/validation/validation/configuration/ValidationDebounceDuration.kt similarity index 77% rename from core/design/src/main/kotlin/edu/stanford/spezi/core/design/views/validation/configuration/ValidationDebounceDuration.kt rename to core/design/src/main/kotlin/edu/stanford/spezi/core/design/validation/validation/configuration/ValidationDebounceDuration.kt index 15cef5d76..000da7721 100644 --- a/core/design/src/main/kotlin/edu/stanford/spezi/core/design/views/validation/configuration/ValidationDebounceDuration.kt +++ b/core/design/src/main/kotlin/edu/stanford/spezi/core/design/validation/validation/configuration/ValidationDebounceDuration.kt @@ -1,4 +1,4 @@ -package edu.stanford.spezi.core.design.views.validation.configuration +package edu.stanford.spezi.core.design.validation.validation.configuration import androidx.compose.runtime.compositionLocalOf import kotlin.time.Duration.Companion.seconds diff --git a/core/design/src/main/kotlin/edu/stanford/spezi/core/design/validation/validation/configuration/ValidationEngine.kt b/core/design/src/main/kotlin/edu/stanford/spezi/core/design/validation/validation/configuration/ValidationEngine.kt new file mode 100644 index 000000000..4ba7077af --- /dev/null +++ b/core/design/src/main/kotlin/edu/stanford/spezi/core/design/validation/validation/configuration/ValidationEngine.kt @@ -0,0 +1,6 @@ +package edu.stanford.spezi.core.design.validation.validation.configuration + +import androidx.compose.runtime.compositionLocalOf +import edu.stanford.spezi.core.design.validation.validation.ValidationEngine + +val LocalValidationEngine = compositionLocalOf { null } diff --git a/core/design/src/main/kotlin/edu/stanford/spezi/core/design/validation/validation/configuration/ValidationEngineConfiguration.kt b/core/design/src/main/kotlin/edu/stanford/spezi/core/design/validation/validation/configuration/ValidationEngineConfiguration.kt new file mode 100644 index 000000000..117c756e5 --- /dev/null +++ b/core/design/src/main/kotlin/edu/stanford/spezi/core/design/validation/validation/configuration/ValidationEngineConfiguration.kt @@ -0,0 +1,9 @@ +package edu.stanford.spezi.core.design.validation.validation.configuration + +import androidx.compose.runtime.compositionLocalOf +import edu.stanford.spezi.core.design.validation.validation.ValidationEngine +import edu.stanford.spezi.core.design.validation.validation.ValidationEngineConfiguration + +val LocalValidationEngineConfiguration = compositionLocalOf { + ValidationEngineConfiguration.noneOf(ValidationEngine.ConfigurationOption::class.java) +} diff --git a/core/design/src/main/kotlin/edu/stanford/spezi/core/design/views/validation/state/CapturedValidationState.kt b/core/design/src/main/kotlin/edu/stanford/spezi/core/design/validation/validation/state/CapturedValidationState.kt similarity index 83% rename from core/design/src/main/kotlin/edu/stanford/spezi/core/design/views/validation/state/CapturedValidationState.kt rename to core/design/src/main/kotlin/edu/stanford/spezi/core/design/validation/validation/state/CapturedValidationState.kt index 5288bb5f7..c04602159 100644 --- a/core/design/src/main/kotlin/edu/stanford/spezi/core/design/views/validation/state/CapturedValidationState.kt +++ b/core/design/src/main/kotlin/edu/stanford/spezi/core/design/validation/validation/state/CapturedValidationState.kt @@ -1,7 +1,7 @@ -package edu.stanford.spezi.core.design.views.validation.state +package edu.stanford.spezi.core.design.validation.validation.state import androidx.compose.runtime.MutableState -import edu.stanford.spezi.core.design.views.validation.ValidationEngine +import edu.stanford.spezi.core.design.validation.validation.ValidationEngine data class CapturedValidationState internal constructor( private val engine: ValidationEngine, diff --git a/core/design/src/main/kotlin/edu/stanford/spezi/core/design/views/validation/state/CapturedValidationStateEntries.kt b/core/design/src/main/kotlin/edu/stanford/spezi/core/design/validation/validation/state/CapturedValidationStateEntries.kt similarity index 87% rename from core/design/src/main/kotlin/edu/stanford/spezi/core/design/views/validation/state/CapturedValidationStateEntries.kt rename to core/design/src/main/kotlin/edu/stanford/spezi/core/design/validation/validation/state/CapturedValidationStateEntries.kt index 50d5e1583..949a771b3 100644 --- a/core/design/src/main/kotlin/edu/stanford/spezi/core/design/views/validation/state/CapturedValidationStateEntries.kt +++ b/core/design/src/main/kotlin/edu/stanford/spezi/core/design/validation/validation/state/CapturedValidationStateEntries.kt @@ -1,4 +1,4 @@ -package edu.stanford.spezi.core.design.views.validation.state +package edu.stanford.spezi.core.design.validation.validation.state import androidx.compose.runtime.compositionLocalOf diff --git a/core/design/src/main/kotlin/edu/stanford/spezi/core/design/views/validation/state/FailedValidationResult.kt b/core/design/src/main/kotlin/edu/stanford/spezi/core/design/validation/validation/state/FailedValidationResult.kt similarity index 75% rename from core/design/src/main/kotlin/edu/stanford/spezi/core/design/views/validation/state/FailedValidationResult.kt rename to core/design/src/main/kotlin/edu/stanford/spezi/core/design/validation/validation/state/FailedValidationResult.kt index f46992f52..35bc2b51c 100644 --- a/core/design/src/main/kotlin/edu/stanford/spezi/core/design/views/validation/state/FailedValidationResult.kt +++ b/core/design/src/main/kotlin/edu/stanford/spezi/core/design/validation/validation/state/FailedValidationResult.kt @@ -1,7 +1,7 @@ -package edu.stanford.spezi.core.design.views.validation.state +package edu.stanford.spezi.core.design.validation.validation.state import edu.stanford.spezi.core.design.component.StringResource -import edu.stanford.spezi.core.design.views.validation.ValidationRule +import edu.stanford.spezi.core.design.validation.validation.ValidationRule import java.util.UUID data class FailedValidationResult( diff --git a/core/design/src/main/kotlin/edu/stanford/spezi/core/design/views/validation/state/ReceiveValidation.kt b/core/design/src/main/kotlin/edu/stanford/spezi/core/design/validation/validation/state/ReceiveValidation.kt similarity index 91% rename from core/design/src/main/kotlin/edu/stanford/spezi/core/design/views/validation/state/ReceiveValidation.kt rename to core/design/src/main/kotlin/edu/stanford/spezi/core/design/validation/validation/state/ReceiveValidation.kt index 94ce4e850..bddafd645 100644 --- a/core/design/src/main/kotlin/edu/stanford/spezi/core/design/views/validation/state/ReceiveValidation.kt +++ b/core/design/src/main/kotlin/edu/stanford/spezi/core/design/validation/validation/state/ReceiveValidation.kt @@ -1,4 +1,4 @@ -package edu.stanford.spezi.core.design.views.validation.state +package edu.stanford.spezi.core.design.validation.validation.state import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider diff --git a/core/design/src/main/kotlin/edu/stanford/spezi/core/design/views/validation/state/ValidationContext.kt b/core/design/src/main/kotlin/edu/stanford/spezi/core/design/validation/validation/state/ValidationContext.kt similarity index 95% rename from core/design/src/main/kotlin/edu/stanford/spezi/core/design/views/validation/state/ValidationContext.kt rename to core/design/src/main/kotlin/edu/stanford/spezi/core/design/validation/validation/state/ValidationContext.kt index a12a71d86..18873e6a8 100644 --- a/core/design/src/main/kotlin/edu/stanford/spezi/core/design/views/validation/state/ValidationContext.kt +++ b/core/design/src/main/kotlin/edu/stanford/spezi/core/design/validation/validation/state/ValidationContext.kt @@ -1,4 +1,4 @@ -package edu.stanford.spezi.core.design.views.validation.state +package edu.stanford.spezi.core.design.validation.validation.state data class ValidationContext internal constructor( private val entries: List = emptyList(), diff --git a/core/design/src/main/kotlin/edu/stanford/spezi/core/design/views/validation/views/ValidationResultsComposable.kt b/core/design/src/main/kotlin/edu/stanford/spezi/core/design/validation/validation/views/ValidationResultsComposable.kt similarity index 80% rename from core/design/src/main/kotlin/edu/stanford/spezi/core/design/views/validation/views/ValidationResultsComposable.kt rename to core/design/src/main/kotlin/edu/stanford/spezi/core/design/validation/validation/views/ValidationResultsComposable.kt index b07aabf20..29fb9bb0b 100644 --- a/core/design/src/main/kotlin/edu/stanford/spezi/core/design/views/validation/views/ValidationResultsComposable.kt +++ b/core/design/src/main/kotlin/edu/stanford/spezi/core/design/validation/validation/views/ValidationResultsComposable.kt @@ -1,4 +1,4 @@ -package edu.stanford.spezi.core.design.views.validation.views +package edu.stanford.spezi.core.design.validation.validation.views import androidx.compose.foundation.layout.Column import androidx.compose.material3.Text @@ -6,7 +6,7 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.graphics.Color import edu.stanford.spezi.core.design.theme.TextStyles -import edu.stanford.spezi.core.design.views.validation.state.FailedValidationResult +import edu.stanford.spezi.core.design.validation.validation.state.FailedValidationResult @Composable fun ValidationResultsComposable( diff --git a/core/design/src/main/kotlin/edu/stanford/spezi/core/design/views/validation/views/VerifiableTextField.kt b/core/design/src/main/kotlin/edu/stanford/spezi/core/design/validation/validation/views/VerifiableTextField.kt similarity index 94% rename from core/design/src/main/kotlin/edu/stanford/spezi/core/design/views/validation/views/VerifiableTextField.kt rename to core/design/src/main/kotlin/edu/stanford/spezi/core/design/validation/validation/views/VerifiableTextField.kt index 6177b04be..838ecd806 100644 --- a/core/design/src/main/kotlin/edu/stanford/spezi/core/design/views/validation/views/VerifiableTextField.kt +++ b/core/design/src/main/kotlin/edu/stanford/spezi/core/design/validation/validation/views/VerifiableTextField.kt @@ -1,4 +1,4 @@ -package edu.stanford.spezi.core.design.views.validation.views +package edu.stanford.spezi.core.design.validation.validation.views import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row @@ -13,7 +13,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.text.input.PasswordVisualTransformation import edu.stanford.spezi.core.design.component.StringResource -import edu.stanford.spezi.core.design.views.validation.configuration.LocalValidationEngine +import edu.stanford.spezi.core.design.validation.validation.configuration.LocalValidationEngine enum class TextFieldType { TEXT, SECURE diff --git a/core/design/src/main/kotlin/edu/stanford/spezi/core/design/views/views/compositionLocal/ProcessingDebounceDuration.kt b/core/design/src/main/kotlin/edu/stanford/spezi/core/design/validation/views/compositionLocal/ProcessingDebounceDuration.kt similarity index 71% rename from core/design/src/main/kotlin/edu/stanford/spezi/core/design/views/views/compositionLocal/ProcessingDebounceDuration.kt rename to core/design/src/main/kotlin/edu/stanford/spezi/core/design/validation/views/compositionLocal/ProcessingDebounceDuration.kt index 2c96fa2a8..86318136c 100644 --- a/core/design/src/main/kotlin/edu/stanford/spezi/core/design/views/views/compositionLocal/ProcessingDebounceDuration.kt +++ b/core/design/src/main/kotlin/edu/stanford/spezi/core/design/validation/views/compositionLocal/ProcessingDebounceDuration.kt @@ -1,4 +1,4 @@ -package edu.stanford.spezi.core.design.views.views.compositionLocal +package edu.stanford.spezi.core.design.validation.views.compositionLocal import androidx.compose.runtime.compositionLocalOf import kotlin.time.Duration.Companion.milliseconds diff --git a/core/design/src/main/kotlin/edu/stanford/spezi/core/design/views/views/layout/DescriptionGridRow.kt b/core/design/src/main/kotlin/edu/stanford/spezi/core/design/validation/views/layout/DescriptionGridRow.kt similarity index 96% rename from core/design/src/main/kotlin/edu/stanford/spezi/core/design/views/views/layout/DescriptionGridRow.kt rename to core/design/src/main/kotlin/edu/stanford/spezi/core/design/validation/views/layout/DescriptionGridRow.kt index 230ba0101..11689d46a 100644 --- a/core/design/src/main/kotlin/edu/stanford/spezi/core/design/views/views/layout/DescriptionGridRow.kt +++ b/core/design/src/main/kotlin/edu/stanford/spezi/core/design/validation/views/layout/DescriptionGridRow.kt @@ -1,4 +1,4 @@ -package edu.stanford.spezi.core.design.views.views.layout +package edu.stanford.spezi.core.design.validation.views.layout import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box diff --git a/core/design/src/main/kotlin/edu/stanford/spezi/core/design/views/views/model/OperationState.kt b/core/design/src/main/kotlin/edu/stanford/spezi/core/design/validation/views/model/OperationState.kt similarity index 50% rename from core/design/src/main/kotlin/edu/stanford/spezi/core/design/views/views/model/OperationState.kt rename to core/design/src/main/kotlin/edu/stanford/spezi/core/design/validation/views/model/OperationState.kt index e252a1615..3b2380d32 100644 --- a/core/design/src/main/kotlin/edu/stanford/spezi/core/design/views/views/model/OperationState.kt +++ b/core/design/src/main/kotlin/edu/stanford/spezi/core/design/validation/views/model/OperationState.kt @@ -1,4 +1,4 @@ -package edu.stanford.spezi.core.design.views.views.model +package edu.stanford.spezi.core.design.validation.views.model interface OperationState { val representation: ViewState diff --git a/core/design/src/main/kotlin/edu/stanford/spezi/core/design/views/views/model/ViewState.kt b/core/design/src/main/kotlin/edu/stanford/spezi/core/design/validation/views/model/ViewState.kt similarity index 90% rename from core/design/src/main/kotlin/edu/stanford/spezi/core/design/views/views/model/ViewState.kt rename to core/design/src/main/kotlin/edu/stanford/spezi/core/design/validation/views/model/ViewState.kt index a73b14109..a32b361b9 100644 --- a/core/design/src/main/kotlin/edu/stanford/spezi/core/design/views/views/model/ViewState.kt +++ b/core/design/src/main/kotlin/edu/stanford/spezi/core/design/validation/views/model/ViewState.kt @@ -1,4 +1,4 @@ -package edu.stanford.spezi.core.design.views.views.model +package edu.stanford.spezi.core.design.validation.views.model import androidx.compose.runtime.Composable import androidx.compose.runtime.ReadOnlyComposable diff --git a/core/design/src/main/kotlin/edu/stanford/spezi/core/design/views/views/viewModifier/viewState/OperationStateAlert.kt b/core/design/src/main/kotlin/edu/stanford/spezi/core/design/validation/views/viewModifier/viewState/OperationStateAlert.kt similarity index 73% rename from core/design/src/main/kotlin/edu/stanford/spezi/core/design/views/views/viewModifier/viewState/OperationStateAlert.kt rename to core/design/src/main/kotlin/edu/stanford/spezi/core/design/validation/views/viewModifier/viewState/OperationStateAlert.kt index cb2eb4abb..07b6086e0 100644 --- a/core/design/src/main/kotlin/edu/stanford/spezi/core/design/views/views/viewModifier/viewState/OperationStateAlert.kt +++ b/core/design/src/main/kotlin/edu/stanford/spezi/core/design/validation/views/viewModifier/viewState/OperationStateAlert.kt @@ -1,10 +1,10 @@ -package edu.stanford.spezi.core.design.views.views.viewModifier.viewState +package edu.stanford.spezi.core.design.validation.views.viewModifier.viewState import androidx.compose.runtime.Composable import androidx.compose.runtime.MutableState import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember -import edu.stanford.spezi.core.design.views.views.model.OperationState +import edu.stanford.spezi.core.design.validation.views.model.OperationState @Composable fun OperationStateAlert( diff --git a/core/design/src/main/kotlin/edu/stanford/spezi/core/design/views/views/viewModifier/viewState/ViewStateAlert.kt b/core/design/src/main/kotlin/edu/stanford/spezi/core/design/validation/views/viewModifier/viewState/ViewStateAlert.kt similarity index 76% rename from core/design/src/main/kotlin/edu/stanford/spezi/core/design/views/views/viewModifier/viewState/ViewStateAlert.kt rename to core/design/src/main/kotlin/edu/stanford/spezi/core/design/validation/views/viewModifier/viewState/ViewStateAlert.kt index 43455d706..df5891385 100644 --- a/core/design/src/main/kotlin/edu/stanford/spezi/core/design/views/views/viewModifier/viewState/ViewStateAlert.kt +++ b/core/design/src/main/kotlin/edu/stanford/spezi/core/design/validation/views/viewModifier/viewState/ViewStateAlert.kt @@ -1,11 +1,12 @@ -package edu.stanford.spezi.core.design.views.views.viewModifier.viewState +package edu.stanford.spezi.core.design.validation.views.viewModifier.viewState import androidx.compose.material3.AlertDialog import androidx.compose.material3.Text import androidx.compose.material3.TextButton import androidx.compose.runtime.Composable import androidx.compose.runtime.MutableState -import edu.stanford.spezi.core.design.views.views.model.ViewState +import edu.stanford.spezi.core.design.component.StringResource +import edu.stanford.spezi.core.design.validation.views.model.ViewState @Composable fun ViewStateAlert(state: MutableState) { @@ -26,7 +27,7 @@ fun ViewStateAlert(state: MutableState) { state.value = ViewState.Idle } ) { - Text("Okay") + Text(StringResource("OK").text()) } } ) diff --git a/core/design/src/main/kotlin/edu/stanford/spezi/core/design/views/views/viewModifier/viewState/ViewStateMapper.kt b/core/design/src/main/kotlin/edu/stanford/spezi/core/design/validation/views/viewModifier/viewState/ViewStateMapper.kt similarity index 60% rename from core/design/src/main/kotlin/edu/stanford/spezi/core/design/views/views/viewModifier/viewState/ViewStateMapper.kt rename to core/design/src/main/kotlin/edu/stanford/spezi/core/design/validation/views/viewModifier/viewState/ViewStateMapper.kt index 1ca174b1d..bff40fbad 100644 --- a/core/design/src/main/kotlin/edu/stanford/spezi/core/design/views/views/viewModifier/viewState/ViewStateMapper.kt +++ b/core/design/src/main/kotlin/edu/stanford/spezi/core/design/validation/views/viewModifier/viewState/ViewStateMapper.kt @@ -1,10 +1,10 @@ -package edu.stanford.spezi.core.design.views.views.viewModifier.viewState +package edu.stanford.spezi.core.design.validation.views.viewModifier.viewState import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.MutableState -import edu.stanford.spezi.core.design.views.views.model.OperationState -import edu.stanford.spezi.core.design.views.views.model.ViewState +import edu.stanford.spezi.core.design.validation.views.model.OperationState +import edu.stanford.spezi.core.design.validation.views.model.ViewState @Composable fun MapOperationStateToViewState(state: State, viewState: MutableState) { diff --git a/core/design/src/main/kotlin/edu/stanford/spezi/core/design/views/views/views/button/ProcessingOverlay.kt b/core/design/src/main/kotlin/edu/stanford/spezi/core/design/validation/views/views/button/ProcessingOverlay.kt similarity index 60% rename from core/design/src/main/kotlin/edu/stanford/spezi/core/design/views/views/views/button/ProcessingOverlay.kt rename to core/design/src/main/kotlin/edu/stanford/spezi/core/design/validation/views/views/button/ProcessingOverlay.kt index b6cf3357b..b78c7dddf 100644 --- a/core/design/src/main/kotlin/edu/stanford/spezi/core/design/views/views/views/button/ProcessingOverlay.kt +++ b/core/design/src/main/kotlin/edu/stanford/spezi/core/design/validation/views/views/button/ProcessingOverlay.kt @@ -1,11 +1,15 @@ -package edu.stanford.spezi.core.design.views.views.views.button +package edu.stanford.spezi.core.design.validation.views.views.button +import androidx.compose.animation.core.animate import androidx.compose.foundation.layout.Box import androidx.compose.material3.CircularProgressIndicator import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.mutableFloatStateOf +import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.draw.alpha -import edu.stanford.spezi.core.design.views.views.model.ViewState +import edu.stanford.spezi.core.design.validation.views.model.ViewState @Composable fun ProcessingOverlay( @@ -26,8 +30,15 @@ fun ProcessingOverlay( processingContent: @Composable () -> Unit = { CircularProgressIndicator() }, content: @Composable () -> Unit, ) { + val alpha = remember { mutableFloatStateOf(0f) } + LaunchedEffect(isProcessing) { + val newValue = if (isProcessing) 0f else 1f + animate(1f - newValue, newValue) { value, _ -> + alpha.floatValue = value + } + } Box { - Box(Modifier.alpha(if (isProcessing) 0f else 1f)) { + Box(Modifier.alpha(alpha.floatValue)) { content() } diff --git a/core/design/src/main/kotlin/edu/stanford/spezi/core/design/views/views/views/button/SuspendButton.kt b/core/design/src/main/kotlin/edu/stanford/spezi/core/design/validation/views/views/button/SuspendButton.kt similarity index 76% rename from core/design/src/main/kotlin/edu/stanford/spezi/core/design/views/views/views/button/SuspendButton.kt rename to core/design/src/main/kotlin/edu/stanford/spezi/core/design/validation/views/views/button/SuspendButton.kt index 7efbf01a3..2239a953c 100644 --- a/core/design/src/main/kotlin/edu/stanford/spezi/core/design/views/views/views/button/SuspendButton.kt +++ b/core/design/src/main/kotlin/edu/stanford/spezi/core/design/validation/views/views/button/SuspendButton.kt @@ -1,14 +1,15 @@ -package edu.stanford.spezi.core.design.views.views.views.button +package edu.stanford.spezi.core.design.validation.views.views.button +import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.MutableState import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import edu.stanford.spezi.core.design.component.Button -import edu.stanford.spezi.core.design.views.views.compositionLocal.LocalProcessingDebounceDuration -import edu.stanford.spezi.core.design.views.views.model.ViewState -import kotlinx.coroutines.cancel +import edu.stanford.spezi.core.design.component.StringResource +import edu.stanford.spezi.core.design.validation.views.compositionLocal.LocalProcessingDebounceDuration +import edu.stanford.spezi.core.design.validation.views.model.ViewState import kotlinx.coroutines.delay import kotlinx.coroutines.isActive import kotlinx.coroutines.launch @@ -19,7 +20,18 @@ private enum class SuspendButtonState { @Composable fun SuspendButton( - state: MutableState, + title: StringResource, + state: MutableState = remember { mutableStateOf(ViewState.Idle) }, + action: suspend () -> Unit, +) { + SuspendButton(state, action) { + Text(title.text()) + } +} + +@Composable +fun SuspendButton( + state: MutableState = remember { mutableStateOf(ViewState.Idle) }, action: suspend () -> Unit, label: @Composable () -> Unit, ) { @@ -43,16 +55,13 @@ fun SuspendButton( } } - // TODO: iOS animates this assignment specifically - is this possible in Jetpack Compose? state.value = ViewState.Processing - coroutineScope.launch { runCatching { action() debounceJob.cancel() if (state.value != ViewState.Idle) { - // TODO: iOS animates this assignment specifically - is this possible in Jetpack Compose? state.value = ViewState.Idle } }.onFailure { diff --git a/core/design/src/main/kotlin/edu/stanford/spezi/core/design/validation/views/views/text/Markdown.kt b/core/design/src/main/kotlin/edu/stanford/spezi/core/design/validation/views/views/text/Markdown.kt new file mode 100644 index 000000000..0e865340d --- /dev/null +++ b/core/design/src/main/kotlin/edu/stanford/spezi/core/design/validation/views/views/text/Markdown.kt @@ -0,0 +1,82 @@ +package edu.stanford.spezi.core.design.validation.views.views.text + +import androidx.compose.material3.CircularProgressIndicator +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.MutableState +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import edu.stanford.spezi.core.design.component.markdown.MarkdownComponent +import edu.stanford.spezi.core.design.component.markdown.MarkdownElement +import edu.stanford.spezi.core.design.component.markdown.MarkdownParser +import edu.stanford.spezi.core.design.validation.views.model.ViewState +import java.nio.charset.StandardCharsets + +@Composable +fun MarkdownBytes( + bytes: ByteArray, + state: MutableState = remember { mutableStateOf(ViewState.Idle) }, +) { + MarkdownBytes( + bytes = { bytes }, + state = state, + ) +} + +@Composable +fun MarkdownBytes( + bytes: suspend () -> ByteArray, + state: MutableState = remember { mutableStateOf(ViewState.Idle) }, +) { + MarkdownString( + string = { bytes().toString(StandardCharsets.UTF_8) }, + state = state, + ) +} + +@Composable +fun MarkdownString( + string: String, + state: MutableState = remember { mutableStateOf(ViewState.Idle) }, +) { + MarkdownString( + string = { string }, + state = state, + ) +} + +@Composable +fun MarkdownString( + string: suspend () -> String, + state: MutableState = remember { mutableStateOf(ViewState.Idle) }, +) { + Markdown( + build = { MarkdownParser().parse(string()) }, + state = state, + ) +} + +@Composable +fun Markdown( + build: suspend () -> List, + state: MutableState = remember { mutableStateOf(ViewState.Idle) }, +) { + var markdownContent by remember { mutableStateOf?>(null) } + + @Suppress("detekt:TooGenericExceptionCaught") + LaunchedEffect(Unit) { + state.value = ViewState.Processing + try { + markdownContent = build() + state.value = ViewState.Idle + } catch (throwable: Throwable) { + state.value = ViewState.Error(throwable) + } + } + + markdownContent?.let { + MarkdownComponent(it) + } ?: CircularProgressIndicator() +} diff --git a/core/design/src/main/kotlin/edu/stanford/spezi/core/design/views/validation/configuration/ValidationEngine.kt b/core/design/src/main/kotlin/edu/stanford/spezi/core/design/views/validation/configuration/ValidationEngine.kt deleted file mode 100644 index f02fdc40a..000000000 --- a/core/design/src/main/kotlin/edu/stanford/spezi/core/design/views/validation/configuration/ValidationEngine.kt +++ /dev/null @@ -1,6 +0,0 @@ -package edu.stanford.spezi.core.design.views.validation.configuration - -import androidx.compose.runtime.compositionLocalOf -import edu.stanford.spezi.core.design.views.validation.ValidationEngine - -val LocalValidationEngine = compositionLocalOf { null } diff --git a/core/design/src/main/kotlin/edu/stanford/spezi/core/design/views/validation/configuration/ValidationEngineConfiguration.kt b/core/design/src/main/kotlin/edu/stanford/spezi/core/design/views/validation/configuration/ValidationEngineConfiguration.kt deleted file mode 100644 index 30b193192..000000000 --- a/core/design/src/main/kotlin/edu/stanford/spezi/core/design/views/validation/configuration/ValidationEngineConfiguration.kt +++ /dev/null @@ -1,9 +0,0 @@ -package edu.stanford.spezi.core.design.views.validation.configuration - -import androidx.compose.runtime.compositionLocalOf -import edu.stanford.spezi.core.design.views.validation.ValidationEngine -import edu.stanford.spezi.core.design.views.validation.ValidationEngineConfiguration - -val LocalValidationEngineConfiguration = compositionLocalOf { - ValidationEngineConfiguration.noneOf(ValidationEngine.ConfigurationOption::class.java) -} diff --git a/core/design/src/test/kotlin/edu/stanford/spezi/core/design/SpeziValidationTest.kt b/core/design/src/test/kotlin/edu/stanford/spezi/core/design/SpeziValidationTest.kt index fd7c4203d..b2c8200ae 100644 --- a/core/design/src/test/kotlin/edu/stanford/spezi/core/design/SpeziValidationTest.kt +++ b/core/design/src/test/kotlin/edu/stanford/spezi/core/design/SpeziValidationTest.kt @@ -1,9 +1,9 @@ package edu.stanford.spezi.core.design import com.google.common.truth.Truth.assertThat -import edu.stanford.spezi.core.design.views.validation.ValidationEngineImpl -import edu.stanford.spezi.core.design.views.validation.ValidationRule -import edu.stanford.spezi.core.design.views.validation.nonEmpty +import edu.stanford.spezi.core.design.validation.validation.ValidationEngineImpl +import edu.stanford.spezi.core.design.validation.validation.ValidationRule +import edu.stanford.spezi.core.design.validation.validation.nonEmpty import org.junit.Test class SpeziValidationTest { diff --git a/modules/contact/src/androidTest/kotlin/edu/stanford/spezi/modules/contact/simulator/ContactComposableSimulator.kt b/modules/contact/src/androidTest/kotlin/edu/stanford/spezi/modules/contact/simulator/ContactComposableSimulator.kt index 7db3a40cc..d13d0e6ae 100644 --- a/modules/contact/src/androidTest/kotlin/edu/stanford/spezi/modules/contact/simulator/ContactComposableSimulator.kt +++ b/modules/contact/src/androidTest/kotlin/edu/stanford/spezi/modules/contact/simulator/ContactComposableSimulator.kt @@ -10,7 +10,7 @@ import androidx.compose.ui.test.onNodeWithTag import androidx.test.platform.app.InstrumentationRegistry import edu.stanford.spezi.core.design.component.ImageResource import edu.stanford.spezi.core.design.component.StringResource -import edu.stanford.spezi.core.design.views.personalInfo.PersonNameComponents +import edu.stanford.spezi.core.design.validation.personalInfo.PersonNameComponents import edu.stanford.spezi.core.testing.assertImageIdentifier import edu.stanford.spezi.core.testing.onNodeWithIdentifier import edu.stanford.spezi.modules.contact.ContactComposableTestIdentifier