-
Notifications
You must be signed in to change notification settings - Fork 41
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Apply architecture iteration: Update
AddTaskScreen
(#662)
- Loading branch information
Showing
20 changed files
with
687 additions
and
264 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
23 changes: 23 additions & 0 deletions
23
...kotlin/dev/sergiobelda/todometer/app/feature/addtask/navigation/AddTaskNavigationEvent.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
/* | ||
* Copyright 2025 Sergio Belda | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
package dev.sergiobelda.todometer.app.feature.addtask.navigation | ||
|
||
import dev.sergiobelda.todometer.common.ui.base.navigation.BaseNavigationEvent | ||
|
||
sealed class AddTaskNavigationEvent : BaseNavigationEvent { | ||
data object NavigateBack : AddTaskNavigationEvent() | ||
} |
27 changes: 27 additions & 0 deletions
27
...dev/sergiobelda/todometer/app/feature/addtask/navigation/AddTaskNavigationEventHandler.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
/* | ||
* Copyright 2025 Sergio Belda | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
package dev.sergiobelda.todometer.app.feature.addtask.navigation | ||
|
||
import dev.sergiobelda.todometer.common.ui.base.navigation.BaseNavigationEventHandler | ||
|
||
fun addTaskNavigationEventHandler( | ||
navigateBack: () -> Unit, | ||
): BaseNavigationEventHandler<AddTaskNavigationEvent> = BaseNavigationEventHandler { | ||
when (it) { | ||
AddTaskNavigationEvent.NavigateBack -> navigateBack() | ||
} | ||
} |
245 changes: 245 additions & 0 deletions
245
...commonMain/kotlin/dev/sergiobelda/todometer/app/feature/addtask/ui/AddTaskContentState.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,245 @@ | ||
/* | ||
* Copyright 2025 Sergio Belda | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
package dev.sergiobelda.todometer.app.feature.addtask.ui | ||
|
||
import androidx.compose.material3.DatePickerState | ||
import androidx.compose.material3.ExperimentalMaterial3Api | ||
import androidx.compose.material3.SnackbarHostState | ||
import androidx.compose.material3.TimePickerState | ||
import androidx.compose.material3.TopAppBarState | ||
import androidx.compose.material3.rememberDatePickerState | ||
import androidx.compose.material3.rememberTimePickerState | ||
import androidx.compose.material3.rememberTopAppBarState | ||
import androidx.compose.runtime.Composable | ||
import androidx.compose.runtime.getValue | ||
import androidx.compose.runtime.mutableStateListOf | ||
import androidx.compose.runtime.mutableStateOf | ||
import androidx.compose.runtime.remember | ||
import androidx.compose.runtime.saveable.Saver | ||
import androidx.compose.runtime.saveable.mapSaver | ||
import androidx.compose.runtime.saveable.rememberSaveable | ||
import androidx.compose.runtime.setValue | ||
import dev.sergiobelda.todometer.app.common.ui.extensions.selectedTimeMillis | ||
import dev.sergiobelda.todometer.common.domain.model.NewTask | ||
import dev.sergiobelda.todometer.common.domain.model.Tag | ||
import dev.sergiobelda.todometer.common.ui.base.BaseContentState | ||
import dev.sergiobelda.todometer.common.ui.base.BaseEvent | ||
|
||
@OptIn(ExperimentalMaterial3Api::class) | ||
data class AddTaskContentState internal constructor( | ||
val snackbarHostState: SnackbarHostState, | ||
val topAppBarState: TopAppBarState, | ||
val datePickerState: DatePickerState, | ||
val timePickerState: TimePickerState, | ||
) : BaseContentState { | ||
private val tags = enumValues<Tag>() | ||
|
||
var taskTitle by mutableStateOf("") | ||
private set | ||
|
||
var taskTitleInputError by mutableStateOf(false) | ||
private set | ||
|
||
var taskDescription by mutableStateOf("") | ||
private set | ||
|
||
var selectedTag by mutableStateOf(tags.firstOrNull() ?: Tag.UNSPECIFIED) | ||
|
||
var taskDueDate: Long? by mutableStateOf(null) | ||
|
||
val taskChecklistItems = mutableStateListOf<String>() | ||
|
||
var discardTaskAlertDialogVisible by mutableStateOf(false) | ||
private set | ||
|
||
var datePickerDialogVisible by mutableStateOf(false) | ||
private set | ||
|
||
var timePickerDialogVisible by mutableStateOf(false) | ||
private set | ||
|
||
suspend fun showSnackbar(message: String) = | ||
snackbarHostState.showSnackbar(message = message) | ||
|
||
override fun handleEvent(event: BaseEvent) { | ||
when (event) { | ||
is AddTaskEvent.OnBack -> checkOnBack(event) | ||
is AddTaskEvent.OnConfirmDatePickerDialog -> confirmDatePickerDialog() | ||
is AddTaskEvent.OnDismissDatePickerDialog -> dismissDatePickerDialog() | ||
is AddTaskEvent.OnShowDatePickerDialog -> showDatePickerDialog() | ||
is AddTaskEvent.OnConfirmTimePickerDialog -> confirmTimePickerDialog() | ||
is AddTaskEvent.OnDismissTimePickerDialog -> dismissTimePickerDialog() | ||
is AddTaskEvent.OnShowTimePickerDialog -> showTimePickerDialog() | ||
is AddTaskEvent.OnDismissDiscardTaskDialog -> dismissDiscardTaskDialog() | ||
is AddTaskEvent.ClearDateTime -> clearDateTime() | ||
is AddTaskEvent.TaskTitleValueChange -> taskTitleValueChange(event) | ||
is AddTaskEvent.OnTagSelected -> selectTag(event) | ||
is AddTaskEvent.OnAddTaskCheckListItem -> addTaskCheckListItem(event) | ||
is AddTaskEvent.OnDeleteTaskCheckListItem -> deleteTaskCheckListItem(event) | ||
is AddTaskEvent.TaskDescriptionValueChange -> taskDescriptionValueChange(event) | ||
is AddTaskEvent.OnSaveButtonClick -> onSaveButtonClick(event) | ||
} | ||
} | ||
|
||
private fun confirmDatePickerDialog() { | ||
datePickerDialogVisible = false | ||
updateTaskDueDate() | ||
} | ||
|
||
private fun dismissDatePickerDialog() { | ||
datePickerDialogVisible = false | ||
} | ||
|
||
private fun showDatePickerDialog() { | ||
datePickerDialogVisible = true | ||
} | ||
|
||
private fun confirmTimePickerDialog() { | ||
timePickerDialogVisible = false | ||
updateTaskDueDate() | ||
} | ||
|
||
private fun dismissTimePickerDialog() { | ||
timePickerDialogVisible = false | ||
} | ||
|
||
private fun showTimePickerDialog() { | ||
timePickerDialogVisible = true | ||
} | ||
|
||
private fun updateTaskDueDate() { | ||
taskDueDate = datePickerState.selectedDateMillis?.plus(timePickerState.selectedTimeMillis) | ||
} | ||
|
||
private fun dismissDiscardTaskDialog() { | ||
discardTaskAlertDialogVisible = false | ||
} | ||
|
||
private fun clearDateTime() { | ||
taskDueDate = null | ||
} | ||
|
||
private fun checkOnBack(event: AddTaskEvent.OnBack) { | ||
if (initialValuesUpdated()) { | ||
discardTaskAlertDialogVisible = true | ||
} else { | ||
event.navigateBack.invoke() | ||
} | ||
} | ||
|
||
private fun initialValuesUpdated(): Boolean = | ||
taskTitle.isNotBlank() || | ||
taskDueDate != null || | ||
taskDescription.isNotBlank() || | ||
taskChecklistItems.isNotEmpty() | ||
|
||
private fun taskTitleValueChange(event: AddTaskEvent.TaskTitleValueChange) { | ||
taskTitle = event.value | ||
} | ||
|
||
private fun selectTag(event: AddTaskEvent.OnTagSelected) { | ||
selectedTag = event.tag | ||
} | ||
|
||
private fun addTaskCheckListItem(event: AddTaskEvent.OnAddTaskCheckListItem) { | ||
taskChecklistItems.add(event.item) | ||
} | ||
|
||
private fun deleteTaskCheckListItem(event: AddTaskEvent.OnDeleteTaskCheckListItem) { | ||
taskChecklistItems.removeAt(index = event.index) | ||
} | ||
|
||
private fun taskDescriptionValueChange(event: AddTaskEvent.TaskDescriptionValueChange) { | ||
taskDescription = event.value | ||
} | ||
|
||
private fun onSaveButtonClick(event: AddTaskEvent.OnSaveButtonClick) { | ||
taskTitleInputError = false | ||
if (taskTitle.isBlank()) { | ||
taskTitleInputError = true | ||
} else { | ||
event.onInsertNewTask( | ||
NewTask( | ||
title = taskTitle, | ||
tag = selectedTag, | ||
description = taskDescription, | ||
dueDate = taskDueDate, | ||
taskChecklistItems = taskChecklistItems, | ||
), | ||
) | ||
} | ||
} | ||
|
||
companion object { | ||
internal fun Saver( | ||
snackbarHostState: SnackbarHostState, | ||
topAppBarState: TopAppBarState, | ||
datePickerState: DatePickerState, | ||
timePickerState: TimePickerState, | ||
): Saver<AddTaskContentState, *> = mapSaver( | ||
save = { | ||
mapOf( | ||
TaskTitleKey to it.taskTitle, | ||
TaskDescriptionKey to it.taskDescription, | ||
SelectedTagKey to it.selectedTag, | ||
TaskDueDateKey to it.taskDueDate, | ||
) | ||
}, | ||
restore = { map -> | ||
AddTaskContentState( | ||
snackbarHostState = snackbarHostState, | ||
topAppBarState = topAppBarState, | ||
datePickerState = datePickerState, | ||
timePickerState = timePickerState, | ||
).apply { | ||
taskTitle = map[TaskTitleKey] as String | ||
taskDescription = map[TaskDescriptionKey] as String | ||
selectedTag = map[SelectedTagKey] as Tag | ||
taskDueDate = map[TaskDueDateKey] as? Long | ||
} | ||
}, | ||
) | ||
|
||
private const val TaskTitleKey: String = "task_title" | ||
private const val TaskDescriptionKey: String = "task_description" | ||
private const val SelectedTagKey: String = "selected_tag" | ||
private const val TaskDueDateKey: String = "task_due_date" | ||
} | ||
} | ||
|
||
@OptIn(ExperimentalMaterial3Api::class) | ||
@Composable | ||
fun rememberAddTaskContentState( | ||
snackbarHostState: SnackbarHostState = remember { SnackbarHostState() }, | ||
topAppBarState: TopAppBarState = rememberTopAppBarState(), | ||
datePickerState: DatePickerState = rememberDatePickerState(), | ||
timePickerState: TimePickerState = rememberTimePickerState(), | ||
): AddTaskContentState = rememberSaveable( | ||
saver = AddTaskContentState.Saver( | ||
snackbarHostState = snackbarHostState, | ||
topAppBarState = topAppBarState, | ||
datePickerState = datePickerState, | ||
timePickerState = timePickerState, | ||
), | ||
) { | ||
AddTaskContentState( | ||
snackbarHostState = snackbarHostState, | ||
topAppBarState = topAppBarState, | ||
datePickerState = datePickerState, | ||
timePickerState = timePickerState, | ||
) | ||
} |
47 changes: 47 additions & 0 deletions
47
...sk/src/commonMain/kotlin/dev/sergiobelda/todometer/app/feature/addtask/ui/AddTaskEvent.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
/* | ||
* Copyright 2025 Sergio Belda | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
package dev.sergiobelda.todometer.app.feature.addtask.ui | ||
|
||
import dev.sergiobelda.todometer.common.domain.model.NewTask | ||
import dev.sergiobelda.todometer.common.domain.model.Tag | ||
import dev.sergiobelda.todometer.common.ui.base.BaseEvent | ||
|
||
sealed class AddTaskEvent : BaseEvent { | ||
data class OnBack( | ||
val navigateBack: () -> Unit, | ||
) : AddTaskEvent() | ||
|
||
data object OnConfirmDatePickerDialog : AddTaskEvent() | ||
data object OnDismissDatePickerDialog : AddTaskEvent() | ||
data object OnShowDatePickerDialog : AddTaskEvent() | ||
data object OnConfirmTimePickerDialog : AddTaskEvent() | ||
data object OnDismissTimePickerDialog : AddTaskEvent() | ||
data object OnShowTimePickerDialog : AddTaskEvent() | ||
data object OnDismissDiscardTaskDialog : AddTaskEvent() | ||
data object ClearDateTime : AddTaskEvent() | ||
|
||
data class TaskTitleValueChange(val value: String) : AddTaskEvent() | ||
data class OnTagSelected(val tag: Tag) : AddTaskEvent() | ||
data class OnAddTaskCheckListItem(val item: String) : AddTaskEvent() | ||
data class OnDeleteTaskCheckListItem(val index: Int) : AddTaskEvent() | ||
data class TaskDescriptionValueChange(val value: String) : AddTaskEvent() | ||
|
||
data class OnSaveButtonClick( | ||
val onInsertNewTask: (NewTask) -> Unit, | ||
) : AddTaskEvent() | ||
data class OnInsertNewTask(val newTask: NewTask) : AddTaskEvent() | ||
} |
Oops, something went wrong.