diff --git a/app/src/main/java/wottrich/github/io/smartchecklist/di/AppModule.kt b/app/src/main/java/wottrich/github/io/smartchecklist/di/AppModule.kt index c3c0e432..3bd78f4a 100644 --- a/app/src/main/java/wottrich/github/io/smartchecklist/di/AppModule.kt +++ b/app/src/main/java/wottrich/github/io/smartchecklist/di/AppModule.kt @@ -7,7 +7,6 @@ import wottrich.github.io.smartchecklist.uisupport.di.supportModule import wottrich.github.io.smartchecklist.datasource.injection.databaseModule import wottrich.github.io.smartchecklist.di.taskModule import wottrich.github.io.smartchecklist.intent.di.intentExtensionsModule -import wottrich.github.io.smartchecklist.quicklychecklist.di.quicklyChecklistModule /** * @author Wottrich @@ -35,7 +34,6 @@ object AppModule { taskModule, supportModule, newChecklistModule, - quicklyChecklistModule, //Database databaseModule, diff --git a/app/src/main/java/wottrich/github/io/smartchecklist/di/HomeModule.kt b/app/src/main/java/wottrich/github/io/smartchecklist/di/HomeModule.kt index ea396c4e..3cdfd414 100644 --- a/app/src/main/java/wottrich/github/io/smartchecklist/di/HomeModule.kt +++ b/app/src/main/java/wottrich/github/io/smartchecklist/di/HomeModule.kt @@ -26,12 +26,7 @@ val featureHomeModules = module { factory { SimpleChecklistModelMapper() } factory { GetChecklistDrawerUseCase(get(), get()) } factory { ObserveSimpleSelectedChecklistModelUseCase(get(), get()) } - viewModel { - ChecklistSettingsViewModel( - getSelectedChecklistUseCase = get(), - shareChecklistAsTextUseCase = get(), - ) - } + viewModelOf(::ChecklistSettingsViewModel) viewModelOf(::DeleteChecklistBottomSheetViewModel) viewModel { HomeDrawerViewModel(get(), get(), get(), get()) } viewModel { diff --git a/app/src/main/java/wottrich/github/io/smartchecklist/domain/usecase/GetChecklistDrawerUseCase.kt b/app/src/main/java/wottrich/github/io/smartchecklist/domain/usecase/GetChecklistDrawerUseCase.kt index 2380488c..fe0b1209 100644 --- a/app/src/main/java/wottrich/github/io/smartchecklist/domain/usecase/GetChecklistDrawerUseCase.kt +++ b/app/src/main/java/wottrich/github/io/smartchecklist/domain/usecase/GetChecklistDrawerUseCase.kt @@ -16,7 +16,9 @@ class GetChecklistDrawerUseCase( ) : FlowableUseCase>() { override suspend fun execute(params: UseCase.None): Flow>> { return checklistRepository.observeAllChecklistsWithTask().mapNotNull { - val checklists = it.map { newChecklistWithNewTasks -> + val checklists = it.filter { embedded -> + embedded.checklist.parentUuid == null + }.map { newChecklistWithNewTasks -> mapper.mapToHomeDrawerChecklistItemModelMapper(newChecklistWithNewTasks) } Result.success(checklists) diff --git a/baseui/src/main/java/wottrich/github/io/smartchecklist/baseui/ui/ListItem.kt b/baseui/src/main/java/wottrich/github/io/smartchecklist/baseui/ui/ListItem.kt index a0c569f1..a3822e38 100644 --- a/baseui/src/main/java/wottrich/github/io/smartchecklist/baseui/ui/ListItem.kt +++ b/baseui/src/main/java/wottrich/github/io/smartchecklist/baseui/ui/ListItem.kt @@ -22,6 +22,7 @@ import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign import wottrich.github.io.smartchecklist.baseui.StyledText import wottrich.github.io.smartchecklist.baseui.TextTwoLine +import wottrich.github.io.smartchecklist.baseui.ui.fonts.robotoFontFamily import wottrich.github.io.smartchecklist.baseui.ui.pallet.SmartChecklistTheme /** @@ -148,6 +149,7 @@ fun TextStateComponent( modifier = modifier, text = textState.text, color = textState.color.copy(alpha = textState.alpha), + fontFamily = robotoFontFamily, fontWeight = textState.fontWeight, textAlign = textState.textAlign, style = textState.style diff --git a/datasource/src/main/java/wottrich/github/io/smartchecklist/datasource/repository/ChecklistDatasourceImpl.kt b/datasource/src/main/java/wottrich/github/io/smartchecklist/datasource/repository/ChecklistDatasourceImpl.kt index 3d6f9db4..dee86f67 100644 --- a/datasource/src/main/java/wottrich/github/io/smartchecklist/datasource/repository/ChecklistDatasourceImpl.kt +++ b/datasource/src/main/java/wottrich/github/io/smartchecklist/datasource/repository/ChecklistDatasourceImpl.kt @@ -51,6 +51,7 @@ class ChecklistDatasourceImpl( Checklist( uuid = it.uuid, name = it.name, + parentUuid = it.parentUuid, isSelected = it.isSelected ) }, @@ -120,6 +121,7 @@ class ChecklistDatasourceImpl( Checklist( uuid = dto.uuid, name = dto.name, + parentUuid = dto.parentUuid, isSelected = dto.isSelected ) } @@ -139,6 +141,7 @@ class ChecklistDatasourceImpl( Checklist( uuid = this.uuid, name = this.name, + parentUuid = this.parentUuid, isSelected = this.isSelected ) @@ -146,6 +149,7 @@ class ChecklistDatasourceImpl( ChecklistDTO( uuid = this.uuid, name = this.name, + parentUuid = this.parentUuid, isSelected = this.isSelected ) } \ No newline at end of file diff --git a/domain/coroutines/src/main/java/wottrich/github/io/smartchecklist/coroutines/UseCaseFunctions.kt b/domain/coroutines/src/main/java/wottrich/github/io/smartchecklist/coroutines/UseCaseFunctions.kt index b0b4c065..4f40aac1 100644 --- a/domain/coroutines/src/main/java/wottrich/github/io/smartchecklist/coroutines/UseCaseFunctions.kt +++ b/domain/coroutines/src/main/java/wottrich/github/io/smartchecklist/coroutines/UseCaseFunctions.kt @@ -1,6 +1,5 @@ package wottrich.github.io.smartchecklist.coroutines -import wottrich.github.io.smartchecklist.coroutines.UseCase import wottrich.github.io.smartchecklist.coroutines.base.Result fun successEmptyResult(): Result = Result.success(UseCase.Empty()) diff --git a/features/newchecklist/impl/src/main/kotlin/wottrich/github/io/smartchecklist/newchecklist/domain/usecase/AddNewChecklistUseCaseImpl.kt b/features/newchecklist/impl/src/main/kotlin/wottrich/github/io/smartchecklist/newchecklist/domain/usecase/AddNewChecklistUseCaseImpl.kt index f04dd240..79345b4f 100644 --- a/features/newchecklist/impl/src/main/kotlin/wottrich/github/io/smartchecklist/newchecklist/domain/usecase/AddNewChecklistUseCaseImpl.kt +++ b/features/newchecklist/impl/src/main/kotlin/wottrich/github/io/smartchecklist/newchecklist/domain/usecase/AddNewChecklistUseCaseImpl.kt @@ -10,7 +10,7 @@ class AddNewChecklistUseCaseImpl( ) : AddNewChecklistUseCase() { override suspend fun execute(params: NewChecklistModel): Result { return try { - val checklist = Checklist(uuid = params.uuid, name = params.name) + val checklist = Checklist(uuid = params.uuid, parentUuid = params.parentUuid, name = params.name) Result.success(checklistRepository.insertChecklist(checklist)) } catch (ex: Exception) { Result.failure(ex) diff --git a/features/task/impl/src/main/java/wottrich/github/io/smartchecklist/di/TaskInjection.kt b/features/task/impl/src/main/java/wottrich/github/io/smartchecklist/di/TaskInjection.kt index 95510c4c..7da2968b 100644 --- a/features/task/impl/src/main/java/wottrich/github/io/smartchecklist/di/TaskInjection.kt +++ b/features/task/impl/src/main/java/wottrich/github/io/smartchecklist/di/TaskInjection.kt @@ -1,7 +1,9 @@ package wottrich.github.io.smartchecklist.di import org.koin.androidx.viewmodel.dsl.viewModel +import org.koin.androidx.viewmodel.dsl.viewModelOf import org.koin.core.module.Module +import org.koin.core.module.dsl.factoryOf import org.koin.dsl.bind import org.koin.dsl.module import wottrich.github.io.smartchecklist.domain.repository.TaskRepository @@ -15,6 +17,7 @@ import wottrich.github.io.smartchecklist.domain.usecase.ChangeTaskStatusUseCase import wottrich.github.io.smartchecklist.domain.usecase.DeleteTaskUseCase import wottrich.github.io.smartchecklist.domain.usecase.GetSortItemListUseCase import wottrich.github.io.smartchecklist.domain.usecase.GetTasksFromSelectedChecklistUseCase +import wottrich.github.io.smartchecklist.domain.usecase.GetTasksSortedUseCase import wottrich.github.io.smartchecklist.domain.usecase.GetTasksUseCase import wottrich.github.io.smartchecklist.domain.usecase.ObserveChecklistWithTasksUseCase import wottrich.github.io.smartchecklist.domain.usecase.ObserveSortItemSelectedUseCase @@ -37,6 +40,7 @@ val taskModule = module { } private fun Module.injectUseCases() { + factoryOf(::GetTasksSortedUseCase) factory { GetTasksFromSelectedChecklistUseCase(get()) } factory { InsertNewTaskUseCase(get()) } factory { ChangeTaskStatusUseCase(get()) } @@ -51,18 +55,7 @@ private fun Module.injectUseCases() { } private fun Module.injectViewModels() { - viewModel { - TaskComponentViewModel( - observeSortItemSelectedUseCase = get(), - observeSelectedChecklistUuidUseCase = get(), - getTasksFromSelectedChecklistUseCase = get(), - insertNewTaskUseCase = get(), - changeTaskStatusUseCase = get(), - deleteTaskUseCase = get(), - sortTasksBySelectedSortUseCase = get(), - reverseTasksIfNeededUseCase = get() - ) - } + viewModelOf(::TaskComponentViewModel) viewModel { CompletableCountBottomSheetViewModel( getSelectedChecklistUseCase = get(), diff --git a/features/task/impl/src/main/java/wottrich/github/io/smartchecklist/domain/model/ChecklistComponentState.kt b/features/task/impl/src/main/java/wottrich/github/io/smartchecklist/domain/model/ChecklistComponentState.kt new file mode 100644 index 00000000..8d16d158 --- /dev/null +++ b/features/task/impl/src/main/java/wottrich/github/io/smartchecklist/domain/model/ChecklistComponentState.kt @@ -0,0 +1,6 @@ +package wottrich.github.io.smartchecklist.domain.model + +sealed class ChecklistComponentState { + data object Loading : ChecklistComponentState() + data class Success(val data: TaskComponentModel) : ChecklistComponentState() +} \ No newline at end of file diff --git a/features/task/impl/src/main/java/wottrich/github/io/smartchecklist/domain/model/ChecklistSectionWithTasks.kt b/features/task/impl/src/main/java/wottrich/github/io/smartchecklist/domain/model/ChecklistSectionWithTasks.kt new file mode 100644 index 00000000..4393089c --- /dev/null +++ b/features/task/impl/src/main/java/wottrich/github/io/smartchecklist/domain/model/ChecklistSectionWithTasks.kt @@ -0,0 +1,4 @@ +package wottrich.github.io.smartchecklist.domain.model + +class ChecklistSectionWithTasks { +} \ No newline at end of file diff --git a/features/task/impl/src/main/java/wottrich/github/io/smartchecklist/domain/model/TaskComponentModel.kt b/features/task/impl/src/main/java/wottrich/github/io/smartchecklist/domain/model/TaskComponentModel.kt new file mode 100644 index 00000000..0347a01d --- /dev/null +++ b/features/task/impl/src/main/java/wottrich/github/io/smartchecklist/domain/model/TaskComponentModel.kt @@ -0,0 +1,11 @@ +package wottrich.github.io.smartchecklist.domain.model + +import wottrich.github.io.smartchecklist.datasource.data.model.Checklist +import wottrich.github.io.smartchecklist.datasource.data.model.ChecklistSectionWithTask +import wottrich.github.io.smartchecklist.presentation.task.model.BaseTaskListItem + +data class TaskComponentModel( + val checklist: Checklist, + val tasks: List, + val sectionChecklists: List +) \ No newline at end of file diff --git a/features/task/impl/src/main/java/wottrich/github/io/smartchecklist/domain/usecase/GetTasksSortedUseCase.kt b/features/task/impl/src/main/java/wottrich/github/io/smartchecklist/domain/usecase/GetTasksSortedUseCase.kt new file mode 100644 index 00000000..f319872c --- /dev/null +++ b/features/task/impl/src/main/java/wottrich/github/io/smartchecklist/domain/usecase/GetTasksSortedUseCase.kt @@ -0,0 +1,45 @@ +package wottrich.github.io.smartchecklist.domain.usecase + +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.distinctUntilChanged +import wottrich.github.io.smartchecklist.coroutines.FlowableUseCase +import wottrich.github.io.smartchecklist.coroutines.UseCase +import wottrich.github.io.smartchecklist.coroutines.base.Result +import wottrich.github.io.smartchecklist.domain.model.ChecklistComponentState +import wottrich.github.io.smartchecklist.domain.model.SortItemType +import wottrich.github.io.smartchecklist.domain.model.TaskComponentModel + +class GetTasksSortedUseCase( + private val observeSortItemSelectedUseCase: ObserveSortItemSelectedUseCase, + private val observeChecklistWithTasksUseCase: ObserveChecklistWithTasksUseCase, + private val sortTasksBySelectedSortUseCase: SortTasksBySelectedSortUseCase, +) : FlowableUseCase() { + override suspend fun execute(params: UseCase.None): Flow> { + return combine( + observeChecklistWithTasksUseCase(), + observeSortItemSelectedUseCase() + ) { resultChecklist, resultSortItem -> + val checklist = resultChecklist.getOrNull() + val sortItem = resultSortItem.getOrNull() ?: SortItemType.UNSELECTED_SORT + if (checklist != null) { + Result.success( + ChecklistComponentState.Success( + TaskComponentModel( + checklist = checklist.checklist, + tasks = sortTasksBySelectedSortUseCase( + SortTasksBySelectedSortUseCase.Params( + selectedSortItem = sortItem, + tasks = checklist.tasks + ) + ).getOrNull().orEmpty(), + sectionChecklists = checklist.checklistSectionEmbedded + ) + ) + ) + } else { + Result.success(ChecklistComponentState.Loading) + } + }.distinctUntilChanged() + } +} \ No newline at end of file diff --git a/features/task/impl/src/main/java/wottrich/github/io/smartchecklist/domain/usecase/SortTasksBySelectedSortUseCase.kt b/features/task/impl/src/main/java/wottrich/github/io/smartchecklist/domain/usecase/SortTasksBySelectedSortUseCase.kt index 10826b56..e5b40ed3 100644 --- a/features/task/impl/src/main/java/wottrich/github/io/smartchecklist/domain/usecase/SortTasksBySelectedSortUseCase.kt +++ b/features/task/impl/src/main/java/wottrich/github/io/smartchecklist/domain/usecase/SortTasksBySelectedSortUseCase.kt @@ -32,7 +32,7 @@ class SortTasksBySelectedSortUseCase : uncompletedTasks + BaseTaskListItem.SectionItem(R.string.task_sort_completed_task_item) + completedTasks } - SortItemType.UNSELECTED_SORT -> taskItems + SortItemType.UNSELECTED_SORT -> taskItems.reversed() } } diff --git a/features/task/impl/src/main/java/wottrich/github/io/smartchecklist/presentation/action/TaskComponentViewModelAction.kt b/features/task/impl/src/main/java/wottrich/github/io/smartchecklist/presentation/action/TaskComponentViewModelAction.kt index 4dc89623..57207e08 100644 --- a/features/task/impl/src/main/java/wottrich/github/io/smartchecklist/presentation/action/TaskComponentViewModelAction.kt +++ b/features/task/impl/src/main/java/wottrich/github/io/smartchecklist/presentation/action/TaskComponentViewModelAction.kt @@ -6,7 +6,8 @@ interface TaskComponentViewModelAction { fun sendAction(action: Action) sealed class Action { - object AddTask : Action() + data object AddTask : Action() + data object AddSection : Action() data class ChangeTaskStatus(val task: Task) : Action() data class DeleteTask(val task: Task) : Action() data class OnTextChanged(val text: String) : Action() diff --git a/features/task/impl/src/main/java/wottrich/github/io/smartchecklist/presentation/state/TaskComponentUiState.kt b/features/task/impl/src/main/java/wottrich/github/io/smartchecklist/presentation/state/TaskComponentUiState.kt index 68d15cc2..0b351b1d 100644 --- a/features/task/impl/src/main/java/wottrich/github/io/smartchecklist/presentation/state/TaskComponentUiState.kt +++ b/features/task/impl/src/main/java/wottrich/github/io/smartchecklist/presentation/state/TaskComponentUiState.kt @@ -1,8 +1,11 @@ package wottrich.github.io.smartchecklist.presentation.state import wottrich.github.io.smartchecklist.domain.model.SortItemType +import wottrich.github.io.smartchecklist.domain.model.TaskComponentModel data class TaskComponentUiState( val taskName: String = "", + val checklist: TaskComponentModel? = null, + val showSectionButton: Boolean = false, val selectedSortItem: SortItemType = SortItemType.UNSELECTED_SORT ) \ No newline at end of file diff --git a/features/task/impl/src/main/java/wottrich/github/io/smartchecklist/presentation/ui/TaskContentComponent.kt b/features/task/impl/src/main/java/wottrich/github/io/smartchecklist/presentation/ui/TaskContentComponent.kt index c4a21ca0..bf7dc813 100644 --- a/features/task/impl/src/main/java/wottrich/github/io/smartchecklist/presentation/ui/TaskContentComponent.kt +++ b/features/task/impl/src/main/java/wottrich/github/io/smartchecklist/presentation/ui/TaskContentComponent.kt @@ -15,8 +15,9 @@ import androidx.compose.ui.Modifier import kotlinx.coroutines.flow.Flow import org.koin.androidx.compose.getViewModel import wottrich.github.io.smartchecklist.datasource.data.model.Task +import wottrich.github.io.smartchecklist.domain.model.TaskComponentModel import wottrich.github.io.smartchecklist.presentation.action.TaskComponentViewModelAction -import wottrich.github.io.smartchecklist.presentation.task.model.BaseTaskListItem +import wottrich.github.io.smartchecklist.presentation.state.TaskComponentUiState import wottrich.github.io.smartchecklist.presentation.ui.checklistinformationheader.ChecklistInformationHeaderComponent import wottrich.github.io.smartchecklist.presentation.viewmodel.TaskComponentViewModel import wottrich.github.io.smartchecklist.presentation.viewmodel.TaskComponentViewModelUiEffect @@ -56,11 +57,15 @@ fun TaskContentComponent( TaskEditHeaderVisibility( showHeaderComponent = showHeaderComponent, taskName = state.taskName, + showSectionButton = state.showSectionButton, onTextFieldValueChange = { viewModel.sendAction(TaskComponentViewModelAction.Action.OnTextChanged(it)) }, onAddItem = { viewModel.sendAction(TaskComponentViewModelAction.Action.AddTask) + }, + onAddSection = { + viewModel.sendAction(TaskComponentViewModelAction.Action.AddSection) } ) Divider() @@ -68,19 +73,21 @@ fun TaskContentComponent( ChecklistInformationHeaderComponent(onTaskCounterClicked = onTaskCounterClicked) } Divider() - TaskList( - tasks = viewModel.tasks, - showDeleteIcon = showDeleteIcon, - onCheckChange = { - viewModel.sendAction( - TaskComponentViewModelAction.Action.ChangeTaskStatus(it) - ) - onUpdateClicked(it) - }, - onDeleteTask = { - viewModel.sendAction(TaskComponentViewModelAction.Action.DeleteTask(it)) - } - ) + state.checklist?.let { + TaskList( + tasks = it, + showDeleteIcon = showDeleteIcon, + onCheckChange = { + viewModel.sendAction( + TaskComponentViewModelAction.Action.ChangeTaskStatus(it) + ) + onUpdateClicked(it) + }, + onDeleteTask = { + viewModel.sendAction(TaskComponentViewModelAction.Action.DeleteTask(it)) + } + ) + } } } @@ -98,25 +105,41 @@ private fun Effects( } } +@Composable +private fun Screen( + showHeaderComponent: Boolean, + state: TaskComponentUiState, + onTextFieldValueChange: (String) -> Unit, + onAddItem: () -> Unit, + onAddSection: () -> Unit, + onTaskCounterClicked: () -> Unit, +) { + +} + @Composable private fun ColumnScope.TaskEditHeaderVisibility( showHeaderComponent: Boolean, taskName: String, + showSectionButton: Boolean, onTextFieldValueChange: (String) -> Unit, onAddItem: () -> Unit, + onAddSection: () -> Unit, ) { AnimatedVisibility(visible = showHeaderComponent) { TaskEditHeaderComponent( textFieldValue = taskName, + showSectionButton = showSectionButton, onTextFieldValueChange = onTextFieldValueChange, - onAddItem = onAddItem + onAddItem = onAddItem, + onAddSection = onAddSection ) } } @Composable private fun TaskList( - tasks: List, + tasks: TaskComponentModel, showDeleteIcon: Boolean, onCheckChange: (Task) -> Unit, onDeleteTask: (Task) -> Unit diff --git a/features/task/impl/src/main/java/wottrich/github/io/smartchecklist/presentation/ui/TaskEditHeaderVisibility.kt b/features/task/impl/src/main/java/wottrich/github/io/smartchecklist/presentation/ui/TaskEditHeaderVisibility.kt index 48c10577..44612908 100644 --- a/features/task/impl/src/main/java/wottrich/github/io/smartchecklist/presentation/ui/TaskEditHeaderVisibility.kt +++ b/features/task/impl/src/main/java/wottrich/github/io/smartchecklist/presentation/ui/TaskEditHeaderVisibility.kt @@ -1,6 +1,7 @@ package wottrich.github.io.smartchecklist.presentation.ui import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.text.KeyboardActions @@ -11,8 +12,10 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.input.ImeAction +import androidx.compose.ui.tooling.preview.Preview import wottrich.github.io.smartchecklist.baseui.TextOneLine import wottrich.github.io.smartchecklist.baseui.components.SmartChecklistButton +import wottrich.github.io.smartchecklist.baseui.ui.ApplicationTheme import wottrich.github.io.smartchecklist.baseui.ui.Dimens import wottrich.github.io.smartchecklist.baseui.ui.color.defaultOutlinedTextFieldColors import wottrich.github.io.smartchecklist.task.R @@ -20,8 +23,10 @@ import wottrich.github.io.smartchecklist.task.R @Composable fun TaskEditHeaderComponent( textFieldValue: String, + showSectionButton: Boolean, onTextFieldValueChange: ((String) -> Unit), - onAddItem: (() -> Unit) + onAddItem: (() -> Unit), + onAddSection: (() -> Unit) ) { Column(modifier = Modifier.padding(horizontal = Dimens.BaseFour.SizeThree)) { TextOneLine( @@ -35,20 +40,49 @@ fun TaskEditHeaderComponent( onValueChange = onTextFieldValueChange, modifier = Modifier.fillMaxWidth(), placeholder = { - Text(text = stringResource(id = R.string.task_header_type_task_name_hint)) + Text(text = stringResource(id = R.string.task_header_type_hint)) }, keyboardOptions = KeyboardOptions.Default.copy(imeAction = ImeAction.Done), keyboardActions = KeyboardActions(onDone = { onAddItem() }), colors = defaultOutlinedTextFieldColors() ) - SmartChecklistButton( - modifier = Modifier - .padding(top = Dimens.BaseFour.SizeTwo) - .fillMaxWidth(), - enabled = textFieldValue.isNotEmpty(), - onClick = onAddItem, - ) { - Text(text = stringResource(id = R.string.task_header_add_item)) + Row { + if (showSectionButton) { + SmartChecklistButton( + modifier = Modifier + .padding(top = Dimens.BaseFour.SizeTwo) + .fillMaxWidth() + .weight(1f), + enabled = textFieldValue.isNotEmpty(), + onClick = onAddSection, + ) { + Text(text = stringResource(id = R.string.task_header_add_section)) + } + } + SmartChecklistButton( + modifier = Modifier + .padding(top = Dimens.BaseFour.SizeTwo) + .fillMaxWidth() + .weight(1f), + enabled = textFieldValue.isNotEmpty(), + onClick = onAddItem, + ) { + Text(text = stringResource(id = R.string.task_header_add_item)) + } } } +} + +@Preview(showBackground = true) +@Composable +private fun TaskEditHeaderComponentPreview() { + ApplicationTheme { + TaskEditHeaderComponent( + "", + showSectionButton = false, + {}, + {}, + {} + ) + } } \ No newline at end of file diff --git a/features/task/impl/src/main/java/wottrich/github/io/smartchecklist/presentation/ui/TaskLazyColumnComponent.kt b/features/task/impl/src/main/java/wottrich/github/io/smartchecklist/presentation/ui/TaskLazyColumnComponent.kt index 02094b38..08d94e8b 100644 --- a/features/task/impl/src/main/java/wottrich/github/io/smartchecklist/presentation/ui/TaskLazyColumnComponent.kt +++ b/features/task/impl/src/main/java/wottrich/github/io/smartchecklist/presentation/ui/TaskLazyColumnComponent.kt @@ -1,5 +1,8 @@ package wottrich.github.io.smartchecklist.presentation.ui +import androidx.compose.foundation.ExperimentalFoundationApi +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth @@ -7,32 +10,82 @@ import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.Divider import androidx.compose.material.MaterialTheme import androidx.compose.material.Text import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.unit.sp import wottrich.github.io.smartchecklist.baseui.StyledText import wottrich.github.io.smartchecklist.baseui.components.checkbox.CheckboxItemComponent import wottrich.github.io.smartchecklist.baseui.ui.Dimens +import wottrich.github.io.smartchecklist.baseui.ui.fonts.robotoFontFamily import wottrich.github.io.smartchecklist.datasource.data.model.Task +import wottrich.github.io.smartchecklist.domain.model.TaskComponentModel import wottrich.github.io.smartchecklist.presentation.task.model.BaseTaskListItem +@OptIn(ExperimentalFoundationApi::class) @Composable fun TaskLazyColumnComponent( - taskList: List, + taskList: TaskComponentModel, onCheckChange: (Task) -> Unit, onDeleteTask: (Task) -> Unit, showDeleteItem: Boolean = true ) { LazyColumn( content = { - items(taskList) { - when (it) { - is BaseTaskListItem.SectionItem -> TaskSection(sectionItem = it) - is BaseTaskListItem.TaskItem -> TaskItem( - task = it.task, + items(taskList.tasks) { item -> + when (item) { + is BaseTaskListItem.SectionItem -> TaskSection(sectionItem = item) + is BaseTaskListItem.TaskItem -> + TaskItem( + task = item.task, + showDeleteItem = showDeleteItem, + onCheckChange = onCheckChange, + onDeleteTask = onDeleteTask + ) + } + } + taskList.sectionChecklists.forEach { embedded -> + stickyHeader { + Column( + modifier = Modifier + .padding(top = Dimens.BaseFour.SizeTwo) + .fillMaxWidth(), + horizontalAlignment = Alignment.CenterHorizontally + ) { + Box( + modifier = Modifier + .padding(horizontal = Dimens.BaseFour.SizeOne) + .clip(RoundedCornerShape(Dimens.BaseFour.SizeFour)) + .background(MaterialTheme.colors.surface), + ) { + Text( + modifier = Modifier.padding( + vertical = Dimens.BaseFour.SizeTwo, + horizontal = Dimens.BaseFour.SizeFour + ), + text = embedded.checklistSection.name, + fontFamily = robotoFontFamily, + fontWeight = FontWeight.Thin, + fontSize = Dimens.BaseFour.SizeFive.value.sp, + textAlign = TextAlign.Center, + maxLines = 1, + overflow = TextOverflow.Ellipsis + ) + } + } + } + items(embedded.tasks) { task -> + TaskItem( + task = task, showDeleteItem = showDeleteItem, onCheckChange = onCheckChange, onDeleteTask = onDeleteTask diff --git a/features/task/impl/src/main/java/wottrich/github/io/smartchecklist/presentation/ui/checklistinformationheader/ChecklistInformationHeaderComponent.kt b/features/task/impl/src/main/java/wottrich/github/io/smartchecklist/presentation/ui/checklistinformationheader/ChecklistInformationHeaderComponent.kt index 9a0ff4f0..71ba24f7 100644 --- a/features/task/impl/src/main/java/wottrich/github/io/smartchecklist/presentation/ui/checklistinformationheader/ChecklistInformationHeaderComponent.kt +++ b/features/task/impl/src/main/java/wottrich/github/io/smartchecklist/presentation/ui/checklistinformationheader/ChecklistInformationHeaderComponent.kt @@ -28,6 +28,7 @@ import androidx.compose.ui.geometry.CornerRadius import androidx.compose.ui.geometry.Offset import androidx.compose.ui.graphics.Color import androidx.compose.ui.layout.onSizeChanged +import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.tooling.preview.Preview import org.koin.androidx.compose.getViewModel import wottrich.github.io.smartchecklist.baseui.ui.ApplicationTheme @@ -72,7 +73,8 @@ private fun ChecklistInformationHeader( TextStateComponent( textState = RowDefaults.text( state.checklistName, - style = MaterialTheme.typography.h5 + style = MaterialTheme.typography.h5, + fontWeight = FontWeight.Bold ) ) CompletableCountComponent( @@ -80,11 +82,6 @@ private fun ChecklistInformationHeader( onTaskCounterClicked ) } - CompletableProgressComponent( - itemWidth.intValue.toFloat(), - state.completedTasksCount, - state.totalTasksCount - ) } } diff --git a/features/task/impl/src/main/java/wottrich/github/io/smartchecklist/presentation/ui/checklistinformationheader/ChecklistInformationHeaderViewModel.kt b/features/task/impl/src/main/java/wottrich/github/io/smartchecklist/presentation/ui/checklistinformationheader/ChecklistInformationHeaderViewModel.kt index 97c652a9..a40d7049 100644 --- a/features/task/impl/src/main/java/wottrich/github/io/smartchecklist/presentation/ui/checklistinformationheader/ChecklistInformationHeaderViewModel.kt +++ b/features/task/impl/src/main/java/wottrich/github/io/smartchecklist/presentation/ui/checklistinformationheader/ChecklistInformationHeaderViewModel.kt @@ -6,6 +6,7 @@ import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow import wottrich.github.io.smartchecklist.android.BaseViewModel import wottrich.github.io.smartchecklist.coroutines.base.onSuccess +import wottrich.github.io.smartchecklist.datasource.data.model.ChecklistSectionWithTask import wottrich.github.io.smartchecklist.datasource.data.model.Task import wottrich.github.io.smartchecklist.domain.usecase.ObserveChecklistWithTasksUseCase @@ -25,7 +26,10 @@ class ChecklistInformationHeaderViewModel( _uiState.value = uiState.value.copy( isLoading = false, checklistName = checklistWithTasks.checklist.name, - completedTasksCount = getCompletedTasksCount(checklistWithTasks.tasks), + completedTasksCount = getCompletedTasksCount( + checklistWithTasks.tasks, + checklistWithTasks.checklistSectionEmbedded + ), totalTasksCount = checklistWithTasks.tasks.size ) } @@ -34,8 +38,12 @@ class ChecklistInformationHeaderViewModel( } } - private fun getCompletedTasksCount(tasks: List): Int { - return tasks.filter { it.isCompleted }.size + private fun getCompletedTasksCount( + tasks: List, + checklistSection: List + ): Int { + val sectionTasks = checklistSection.map { it.tasks }.flatten() + return tasks.filter { it.isCompleted }.size + sectionTasks.filter { it.isCompleted }.size } } diff --git a/features/task/impl/src/main/java/wottrich/github/io/smartchecklist/presentation/viewmodel/TaskComponentViewModel.kt b/features/task/impl/src/main/java/wottrich/github/io/smartchecklist/presentation/viewmodel/TaskComponentViewModel.kt index 19d465f9..86d216eb 100644 --- a/features/task/impl/src/main/java/wottrich/github/io/smartchecklist/presentation/viewmodel/TaskComponentViewModel.kt +++ b/features/task/impl/src/main/java/wottrich/github/io/smartchecklist/presentation/viewmodel/TaskComponentViewModel.kt @@ -1,28 +1,26 @@ package wottrich.github.io.smartchecklist.presentation.viewmodel +import android.util.Log import androidx.annotation.StringRes import androidx.compose.runtime.mutableStateListOf import kotlinx.coroutines.InternalCoroutinesApi import kotlinx.coroutines.Job import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.FlowCollector import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.withContext import wottrich.github.io.smartchecklist.android.BaseViewModel -import wottrich.github.io.smartchecklist.checklist.domain.ObserveSelectedChecklistUuidUseCase import wottrich.github.io.smartchecklist.coroutines.base.onFailure import wottrich.github.io.smartchecklist.coroutines.base.onSuccess import wottrich.github.io.smartchecklist.datasource.data.model.Task -import wottrich.github.io.smartchecklist.domain.model.SortItemType +import wottrich.github.io.smartchecklist.domain.model.ChecklistComponentState import wottrich.github.io.smartchecklist.domain.usecase.InsertNewTaskUseCase import wottrich.github.io.smartchecklist.domain.usecase.ChangeTaskStatusUseCase import wottrich.github.io.smartchecklist.domain.usecase.DeleteTaskUseCase -import wottrich.github.io.smartchecklist.domain.usecase.GetTasksFromSelectedChecklistUseCase -import wottrich.github.io.smartchecklist.domain.usecase.ObserveSortItemSelectedUseCase -import wottrich.github.io.smartchecklist.domain.usecase.ReverseTasksIfNeededUseCase -import wottrich.github.io.smartchecklist.domain.usecase.SortTasksBySelectedSortUseCase +import wottrich.github.io.smartchecklist.domain.usecase.GetTasksSortedUseCase import wottrich.github.io.smartchecklist.kotlin.SingleShotEventBus +import wottrich.github.io.smartchecklist.newchecklist.domain.model.NewChecklistModel +import wottrich.github.io.smartchecklist.newchecklist.domain.usecase.AddNewChecklistUseCase import wottrich.github.io.smartchecklist.presentation.action.TaskComponentViewModelAction import wottrich.github.io.smartchecklist.presentation.action.TaskComponentViewModelAction.Action import wottrich.github.io.smartchecklist.presentation.action.TaskComponentViewModelAction.Action.AddTask @@ -35,14 +33,11 @@ import wottrich.github.io.smartchecklist.task.R @OptIn(InternalCoroutinesApi::class) class TaskComponentViewModel( - private val observeSortItemSelectedUseCase: ObserveSortItemSelectedUseCase, - private val observeSelectedChecklistUuidUseCase: ObserveSelectedChecklistUuidUseCase, - private val getTasksFromSelectedChecklistUseCase: GetTasksFromSelectedChecklistUseCase, + private val getTasksSortedUseCase: GetTasksSortedUseCase, + private val addNewChecklistUseCase: AddNewChecklistUseCase, private val insertNewTaskUseCase: InsertNewTaskUseCase, private val changeTaskStatusUseCase: ChangeTaskStatusUseCase, private val deleteTaskUseCase: DeleteTaskUseCase, - private val sortTasksBySelectedSortUseCase: SortTasksBySelectedSortUseCase, - private val reverseTasksIfNeededUseCase: ReverseTasksIfNeededUseCase ) : BaseViewModel(), TaskComponentViewModelAction { private var checklistUuidReference: String? = null @@ -59,84 +54,33 @@ class TaskComponentViewModel( private var sortItemsJob: Job? = null init { - launchIO { - observeSelectedChecklistUuidUseCase().collect( - FlowCollector { - checklistUuidReference = it.getOrNull() - loadSortItems() - } - ) - } + loadSortItems() } private fun loadSortItems() { sortItemsJob?.cancel() sortItemsJob = launchIO { - observeSortItemSelectedUseCase().collect( - FlowCollector { - val sortItemSelected = it.getOrNull() - if (sortItemSelected != null) { - withMainContext { - _uiState.value = uiState.value.copy( - selectedSortItem = sortItemSelected - ) + getTasksSortedUseCase().collect { + it.onSuccess { state -> + when (state) { + ChecklistComponentState.Loading -> Unit + is ChecklistComponentState.Success -> { + val checklistComponentModel = state.data + withContext(main()) { + checklistUuidReference = checklistComponentModel.checklist.uuid + _uiState.value = uiState.value.copy( + checklist = checklistComponentModel, + showSectionButton = checklistComponentModel.checklist.parentUuid == null + ) + } } } - loadTasks() - } - ) - } - } - - private fun loadTasks() { - launchIO { - getTasksFromSelectedChecklistUseCase(checkNotNull(checklistUuidReference)) - .onSuccess { tasksFromSelectedChecklist -> - sortTasksBySelectedSort( - uiState.value.selectedSortItem, - tasksFromSelectedChecklist - ) - }.onFailure { + }.onFailure { error -> + Log.d("FAILURE_CHECK", error.message.orEmpty()) emitLoadTasksFailure() } - } - } - - private suspend fun sortTasksBySelectedSort( - selectedSortItem: SortItemType, - tasks: List - ) = sortTasksBySelectedSortUseCase( - SortTasksBySelectedSortUseCase.Params( - selectedSortItem, - tasks - ) - ).onSuccess { items -> - val hasNoSelectedSort = selectedSortItem == SortItemType.UNSELECTED_SORT - reverseTasksIfNeeded( - shouldReverse = hasNoSelectedSort, - tasks = items - ) - }.onFailure { - emitLoadTasksFailure() - } - - private suspend fun reverseTasksIfNeeded( - shouldReverse: Boolean, - tasks: List - ) = reverseTasksIfNeededUseCase( - ReverseTasksIfNeededUseCase.Params( - shouldReverseList = shouldReverse, - tasks = tasks - ) - ).onSuccess { items -> - withContext(main()) { - this@TaskComponentViewModel.apply { - this.tasks.clear() - this.tasks.addAll(items) } } - }.onFailure { - emitLoadTasksFailure() } private suspend fun emitLoadTasksFailure() { @@ -146,6 +90,7 @@ class TaskComponentViewModel( override fun sendAction(action: Action) { when (action) { AddTask -> handleAddTaskAction() + Action.AddSection -> handleAddSectionAction() is ChangeTaskStatus -> handleChangeTaskStatus(action.task) is DeleteTask -> handleDeleteTask(action.task) is Action.OnTextChanged -> { @@ -156,6 +101,23 @@ class TaskComponentViewModel( } } + private fun handleAddSectionAction() { + launchIO { + val checklistUuid = checkNotNull(checklistUuidReference) + val checklistName = checkNotNull(uiState.value).taskName.ifEmpty { + return@launchIO onFailureInsertTask() + } + val checklist = NewChecklistModel( + parentUuid = checklistUuid, + name = checklistName + ) + resetNameField() + addNewChecklistUseCase(checklist).onFailure { + Log.d("ADD_NEW_CHECKLIST", it.message ?: "Error") + } + } + } + private fun handleAddTaskAction() { launchIO { val checklistUuid = checkNotNull(checklistUuidReference) @@ -171,11 +133,9 @@ class TaskComponentViewModel( parentUuid = checklistUuid, name = taskName ) - _uiState.value = _uiState.value.copy( - taskName = "" - ) + resetNameField() insertNewTaskUseCase(newTask).onSuccess { - loadTasks() +//TODO loadSortItems() }.onFailure { _uiEffect.emit(OnError(stringRes = R.string.checklist_add_new_task_unknown_error)) } @@ -188,7 +148,7 @@ class TaskComponentViewModel( private fun handleChangeTaskStatus(task: Task) { launchIO { changeTaskStatusUseCase(task).onSuccess { - loadTasks() +//TODO loadSortItems() }.onFailure { _uiEffect.emit(OnError(stringRes = R.string.checklist_change_task_state_failure)) } @@ -198,10 +158,16 @@ class TaskComponentViewModel( private fun handleDeleteTask(task: Task) { launchIO { deleteTaskUseCase(task).onSuccess { - loadTasks() +//TODO loadSortItems() } } } + + private fun resetNameField() { + _uiState.value = _uiState.value.copy( + taskName = "" + ) + } } sealed class TaskComponentViewModelUiEffect { diff --git a/features/task/impl/src/main/res/values-pt-rBR/strings.xml b/features/task/impl/src/main/res/values-pt-rBR/strings.xml index 87854060..8982df7b 100644 --- a/features/task/impl/src/main/res/values-pt-rBR/strings.xml +++ b/features/task/impl/src/main/res/values-pt-rBR/strings.xml @@ -1,9 +1,10 @@ - Insira tarefas na sua checklist: - Digite o nome da tarefa - Adicionar + O que você precisa fazer? + Digite para não esquecer! + Adicionar tarefa + Adicionar seção Clique para deletar o item %s diff --git a/features/task/impl/src/main/res/values/strings.xml b/features/task/impl/src/main/res/values/strings.xml index 8b3da38b..ba719b87 100644 --- a/features/task/impl/src/main/res/values/strings.xml +++ b/features/task/impl/src/main/res/values/strings.xml @@ -2,9 +2,10 @@ - Put tasks in your checklist: - Enter a task name - Add + What do you need to do? + Type so you don\'t forget + Add task + Add section Click to delete %s item