From 5a32a647d290fb3b62ef7d0f8ba6ff28bc0fb060 Mon Sep 17 00:00:00 2001 From: Faltenreich Date: Sun, 7 Jan 2024 17:36:08 +0100 Subject: [PATCH] Open tag detail from list --- .../navigation/screen/TagDetailScreen.kt | 13 ++++- .../navigation/screen/TagListScreen.kt | 2 +- .../diaguard/tag/detail/TagDetail.kt | 50 ++++++++++++++++++- .../diaguard/tag/detail/TagDetailState.kt | 1 + .../diaguard/tag/detail/TagDetailViewModel.kt | 25 +++++++++- .../{TagFormViewState.kt => TagFormState.kt} | 2 +- .../diaguard/tag/form/TagFormViewModel.kt | 4 +- .../faltenreich/diaguard/tag/list/TagList.kt | 8 ++- .../diaguard/tag/list/TagListIntent.kt | 4 +- .../diaguard/tag/list/TagListItem.kt | 16 +----- .../diaguard/tag/list/TagListViewModel.kt | 8 +-- 11 files changed, 105 insertions(+), 28 deletions(-) rename shared/src/commonMain/kotlin/com/faltenreich/diaguard/tag/form/{TagFormViewState.kt => TagFormState.kt} (71%) diff --git a/shared/src/commonMain/kotlin/com/faltenreich/diaguard/navigation/screen/TagDetailScreen.kt b/shared/src/commonMain/kotlin/com/faltenreich/diaguard/navigation/screen/TagDetailScreen.kt index fde14751d..96b7449ba 100644 --- a/shared/src/commonMain/kotlin/com/faltenreich/diaguard/navigation/screen/TagDetailScreen.kt +++ b/shared/src/commonMain/kotlin/com/faltenreich/diaguard/navigation/screen/TagDetailScreen.kt @@ -1,4 +1,15 @@ package com.faltenreich.diaguard.navigation.screen -class TagDetailScreen { +import androidx.compose.runtime.Composable +import com.faltenreich.diaguard.shared.di.getViewModel +import com.faltenreich.diaguard.tag.Tag +import com.faltenreich.diaguard.tag.detail.TagDetail +import org.koin.core.parameter.parametersOf + +data class TagDetailScreen(private val tag: Tag) : Screen { + + @Composable + override fun Content() { + TagDetail(viewModel = getViewModel { parametersOf(tag) }) + } } \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/com/faltenreich/diaguard/navigation/screen/TagListScreen.kt b/shared/src/commonMain/kotlin/com/faltenreich/diaguard/navigation/screen/TagListScreen.kt index 48cff1c50..4613e91eb 100644 --- a/shared/src/commonMain/kotlin/com/faltenreich/diaguard/navigation/screen/TagListScreen.kt +++ b/shared/src/commonMain/kotlin/com/faltenreich/diaguard/navigation/screen/TagListScreen.kt @@ -25,7 +25,7 @@ data object TagListScreen : Screen { get() = BottomAppBarStyle.Visible( floatingActionButton = { val viewModel = getViewModel() - FloatingActionButton(onClick = { viewModel.dispatchIntent(TagListIntent.Create) }) { + FloatingActionButton(onClick = { viewModel.dispatchIntent(TagListIntent.CreateTag) }) { Icon( painter = painterResource(MR.images.ic_add), contentDescription = getString(MR.strings.tag_new), diff --git a/shared/src/commonMain/kotlin/com/faltenreich/diaguard/tag/detail/TagDetail.kt b/shared/src/commonMain/kotlin/com/faltenreich/diaguard/tag/detail/TagDetail.kt index 2395d814d..0f8e13b45 100644 --- a/shared/src/commonMain/kotlin/com/faltenreich/diaguard/tag/detail/TagDetail.kt +++ b/shared/src/commonMain/kotlin/com/faltenreich/diaguard/tag/detail/TagDetail.kt @@ -1,4 +1,52 @@ package com.faltenreich.diaguard.tag.detail -class TagDetail { +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items +import androidx.compose.material3.Divider +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import com.faltenreich.diaguard.MR +import com.faltenreich.diaguard.entry.list.EntryListItem +import com.faltenreich.diaguard.shared.di.inject +import com.faltenreich.diaguard.shared.localization.getString +import com.faltenreich.diaguard.shared.view.FormRow +import com.faltenreich.diaguard.shared.view.TextInput +import com.faltenreich.diaguard.tag.EntryTag + +@Composable +fun TagDetail( + modifier: Modifier = Modifier, + viewModel: TagDetailViewModel = inject(), +) { + val state = viewModel.collectState() + var name by rememberSaveable { mutableStateOf("") } + + LazyColumn(modifier = modifier) { + stickyHeader { + FormRow { + TextInput( + input = name, + onInputChange = { name = it }, + label = getString(MR.strings.name), + modifier = Modifier.fillMaxWidth(), + supportingText = { Text(state?.inputError ?: "") }, + isError = state?.inputError != null, + ) + } + } + item { Divider() } + items( + items = state?.entryTags ?: emptyList(), + key = EntryTag::id, + ) { entryTag -> + EntryListItem(entry = entryTag.entry) + Divider() + } + } } \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/com/faltenreich/diaguard/tag/detail/TagDetailState.kt b/shared/src/commonMain/kotlin/com/faltenreich/diaguard/tag/detail/TagDetailState.kt index 71cd5e32d..ff0888c04 100644 --- a/shared/src/commonMain/kotlin/com/faltenreich/diaguard/tag/detail/TagDetailState.kt +++ b/shared/src/commonMain/kotlin/com/faltenreich/diaguard/tag/detail/TagDetailState.kt @@ -6,4 +6,5 @@ import com.faltenreich.diaguard.tag.Tag data class TagDetailState( val tag: Tag, val entryTags: List, + val inputError: String?, ) \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/com/faltenreich/diaguard/tag/detail/TagDetailViewModel.kt b/shared/src/commonMain/kotlin/com/faltenreich/diaguard/tag/detail/TagDetailViewModel.kt index 92aceed66..5d62b1928 100644 --- a/shared/src/commonMain/kotlin/com/faltenreich/diaguard/tag/detail/TagDetailViewModel.kt +++ b/shared/src/commonMain/kotlin/com/faltenreich/diaguard/tag/detail/TagDetailViewModel.kt @@ -1,34 +1,57 @@ package com.faltenreich.diaguard.tag.detail +import com.faltenreich.diaguard.MR import com.faltenreich.diaguard.navigation.NavigateToScreenUseCase import com.faltenreich.diaguard.navigation.OpenModalUseCase import com.faltenreich.diaguard.navigation.modal.TagDeleteModal import com.faltenreich.diaguard.navigation.screen.EntryFormScreen import com.faltenreich.diaguard.shared.architecture.ViewModel import com.faltenreich.diaguard.shared.di.inject +import com.faltenreich.diaguard.shared.localization.getString +import com.faltenreich.diaguard.shared.validation.ValidateUseCase import com.faltenreich.diaguard.tag.Tag +import com.faltenreich.diaguard.tag.form.RedundantTagException +import com.faltenreich.diaguard.tag.form.UniqueTagRule import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.flowOf class TagDetailViewModel( tag: Tag, getEntriesOfTag: GetEntriesOfTagUseCase = inject(), + private val validate: ValidateUseCase = inject(), private val openModal: OpenModalUseCase = inject(), private val navigateToScreen: NavigateToScreenUseCase = inject(), ) : ViewModel() { + private val error = MutableStateFlow(null) + override val state: Flow = combine( flowOf(tag), getEntriesOfTag(tag), + error, ::TagDetailState, ) override fun onIntent(intent: TagDetailIntent) { when (intent) { - is TagDetailIntent.EditTag -> Unit + is TagDetailIntent.EditTag -> editTagIfValid(intent.tag, intent.name) is TagDetailIntent.DeleteTag -> openModal(TagDeleteModal(intent.tag)) is TagDetailIntent.OpenEntry -> navigateToScreen(EntryFormScreen(intent.entry)) } } + + private fun editTagIfValid(tag: Tag, name: String) { + val result = validate(name, UniqueTagRule()) + if (result.isSuccess) { + // TODO + error.value = null + } else { + error.value = when (result.exceptionOrNull()) { + is RedundantTagException -> getString(MR.strings.tag_already_taken) + else -> getString(MR.strings.error_unknown) + } + } + } } \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/com/faltenreich/diaguard/tag/form/TagFormViewState.kt b/shared/src/commonMain/kotlin/com/faltenreich/diaguard/tag/form/TagFormState.kt similarity index 71% rename from shared/src/commonMain/kotlin/com/faltenreich/diaguard/tag/form/TagFormViewState.kt rename to shared/src/commonMain/kotlin/com/faltenreich/diaguard/tag/form/TagFormState.kt index 664669f88..0c1a4a0bf 100644 --- a/shared/src/commonMain/kotlin/com/faltenreich/diaguard/tag/form/TagFormViewState.kt +++ b/shared/src/commonMain/kotlin/com/faltenreich/diaguard/tag/form/TagFormState.kt @@ -1,5 +1,5 @@ package com.faltenreich.diaguard.tag.form -data class TagFormViewState( +data class TagFormState( val inputError: String?, ) \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/com/faltenreich/diaguard/tag/form/TagFormViewModel.kt b/shared/src/commonMain/kotlin/com/faltenreich/diaguard/tag/form/TagFormViewModel.kt index cf11585bb..7b09ccabb 100644 --- a/shared/src/commonMain/kotlin/com/faltenreich/diaguard/tag/form/TagFormViewModel.kt +++ b/shared/src/commonMain/kotlin/com/faltenreich/diaguard/tag/form/TagFormViewModel.kt @@ -12,11 +12,11 @@ class TagFormViewModel( private val validate: ValidateUseCase, private val createTag: CreateTagUseCase, private val closeModal: CloseModalUseCase, -) : ViewModel() { +) : ViewModel() { private val error = MutableStateFlow(null) - override val state = error.map(::TagFormViewState) + override val state = error.map(::TagFormState) override fun onIntent(intent: TagFormIntent) { when (intent) { diff --git a/shared/src/commonMain/kotlin/com/faltenreich/diaguard/tag/list/TagList.kt b/shared/src/commonMain/kotlin/com/faltenreich/diaguard/tag/list/TagList.kt index dde826099..459653207 100644 --- a/shared/src/commonMain/kotlin/com/faltenreich/diaguard/tag/list/TagList.kt +++ b/shared/src/commonMain/kotlin/com/faltenreich/diaguard/tag/list/TagList.kt @@ -1,5 +1,7 @@ package com.faltenreich.diaguard.tag.list +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items import androidx.compose.material3.Divider @@ -20,8 +22,10 @@ fun TagList( items(viewState.tags, key = Tag::id) { tag -> TagListItem( tag = tag, - onDelete = { viewModel.dispatchIntent(TagListIntent.Delete(tag)) }, - modifier = Modifier.animateItemPlacement(), + modifier = Modifier + .animateItemPlacement() + .fillMaxWidth() + .clickable { viewModel.dispatchIntent(TagListIntent.OpenTag(tag)) }, ) Divider() } diff --git a/shared/src/commonMain/kotlin/com/faltenreich/diaguard/tag/list/TagListIntent.kt b/shared/src/commonMain/kotlin/com/faltenreich/diaguard/tag/list/TagListIntent.kt index 3e70bb888..f840edc19 100644 --- a/shared/src/commonMain/kotlin/com/faltenreich/diaguard/tag/list/TagListIntent.kt +++ b/shared/src/commonMain/kotlin/com/faltenreich/diaguard/tag/list/TagListIntent.kt @@ -4,5 +4,7 @@ import com.faltenreich.diaguard.tag.Tag sealed interface TagListIntent { - data object Create : TagListIntent + data object CreateTag : TagListIntent + + data class OpenTag(val tag: Tag) : TagListIntent } \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/com/faltenreich/diaguard/tag/list/TagListItem.kt b/shared/src/commonMain/kotlin/com/faltenreich/diaguard/tag/list/TagListItem.kt index b8f8f0419..22fadc156 100644 --- a/shared/src/commonMain/kotlin/com/faltenreich/diaguard/tag/list/TagListItem.kt +++ b/shared/src/commonMain/kotlin/com/faltenreich/diaguard/tag/list/TagListItem.kt @@ -1,31 +1,17 @@ package com.faltenreich.diaguard.tag.list -import androidx.compose.material3.IconButton import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier -import com.faltenreich.diaguard.MR -import com.faltenreich.diaguard.shared.localization.getString import com.faltenreich.diaguard.shared.view.FormRow -import com.faltenreich.diaguard.shared.view.ResourceIcon import com.faltenreich.diaguard.tag.Tag @Composable fun TagListItem( tag: Tag, - onDelete: () -> Unit, modifier: Modifier = Modifier, ) { FormRow(modifier = modifier) { - Text( - text = tag.name, - modifier = Modifier.weight(1f), - ) - IconButton(onClick = { onDelete() }) { - ResourceIcon( - imageResource = MR.images.ic_delete, - contentDescription = getString(MR.strings.tag_delete), - ) - } + Text(tag.name) } } \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/com/faltenreich/diaguard/tag/list/TagListViewModel.kt b/shared/src/commonMain/kotlin/com/faltenreich/diaguard/tag/list/TagListViewModel.kt index 2d6342935..4dcd0b291 100644 --- a/shared/src/commonMain/kotlin/com/faltenreich/diaguard/tag/list/TagListViewModel.kt +++ b/shared/src/commonMain/kotlin/com/faltenreich/diaguard/tag/list/TagListViewModel.kt @@ -1,14 +1,16 @@ package com.faltenreich.diaguard.tag.list +import com.faltenreich.diaguard.navigation.NavigateToScreenUseCase import com.faltenreich.diaguard.navigation.OpenModalUseCase -import com.faltenreich.diaguard.navigation.modal.TagDeleteModal import com.faltenreich.diaguard.navigation.modal.TagFormModal +import com.faltenreich.diaguard.navigation.screen.TagDetailScreen import com.faltenreich.diaguard.shared.architecture.ViewModel import kotlinx.coroutines.flow.map class TagListViewModel( getTags: GetTagsUseCase, private val openModal: OpenModalUseCase, + private val navigateToScreen: NavigateToScreenUseCase, ) : ViewModel() { private val tags = getTags() @@ -16,8 +18,8 @@ class TagListViewModel( override fun onIntent(intent: TagListIntent) { when (intent) { - is TagListIntent.Create -> openModal(TagFormModal) - is TagListIntent.Delete -> openModal(TagDeleteModal(intent.tag)) + is TagListIntent.CreateTag -> openModal(TagFormModal) + is TagListIntent.OpenTag -> navigateToScreen(TagDetailScreen(intent.tag)) } } } \ No newline at end of file