mirror of
https://github.com/wismna/DoNext.git
synced 2025-10-03 07:30:13 -04:00
Tapping on a task allows edition
Refactoring of the bottom sheet to allow edition Create a Task ViewModel
This commit is contained in:
@@ -47,15 +47,15 @@ android {
|
||||
|
||||
dependencies {
|
||||
implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.9.3")
|
||||
implementation("androidx.activity:activity-compose:1.10.1")
|
||||
implementation(platform("androidx.compose:compose-bom:2025.08.01"))
|
||||
implementation("androidx.activity:activity-compose:1.11.0")
|
||||
implementation(platform("androidx.compose:compose-bom:2025.09.00"))
|
||||
implementation("androidx.compose.ui:ui")
|
||||
implementation("androidx.compose.ui:ui-graphics")
|
||||
implementation("androidx.compose.ui:ui-tooling-preview")
|
||||
implementation("androidx.compose.material3:material3")
|
||||
implementation("androidx.navigation:navigation-compose:2.9.4")
|
||||
implementation("androidx.hilt:hilt-navigation-compose:1.2.0")
|
||||
androidTestImplementation(platform("androidx.compose:compose-bom:2025.08.01"))
|
||||
implementation("androidx.hilt:hilt-navigation-compose:1.3.0")
|
||||
androidTestImplementation(platform("androidx.compose:compose-bom:2025.09.00"))
|
||||
androidTestImplementation("androidx.compose.ui:ui-test-junit4")
|
||||
debugImplementation("androidx.compose.ui:ui-tooling")
|
||||
debugImplementation("androidx.compose.ui:ui-test-manifest")
|
||||
|
@@ -0,0 +1,12 @@
|
||||
package com.wismna.geoffroy.donext.domain.usecase
|
||||
|
||||
import com.wismna.geoffroy.donext.domain.repository.TaskRepository
|
||||
import javax.inject.Inject
|
||||
|
||||
class DeleteTaskUseCase @Inject constructor(
|
||||
private val repository: TaskRepository
|
||||
) {
|
||||
suspend operator fun invoke(taskId: Long) {
|
||||
repository.deleteTask(taskId, true)
|
||||
}
|
||||
}
|
@@ -0,0 +1,24 @@
|
||||
package com.wismna.geoffroy.donext.domain.usecase
|
||||
|
||||
import com.wismna.geoffroy.donext.domain.model.Priority
|
||||
import com.wismna.geoffroy.donext.domain.model.Task
|
||||
import com.wismna.geoffroy.donext.domain.repository.TaskRepository
|
||||
import javax.inject.Inject
|
||||
|
||||
class UpdateTaskUseCase @Inject constructor(
|
||||
private val repository: TaskRepository
|
||||
) {
|
||||
suspend operator fun invoke(taskId: Long, taskListId: Long, title: String, description: String?, priority: Priority) {
|
||||
repository.updateTask(
|
||||
Task(
|
||||
id = taskId,
|
||||
taskListId = taskListId,
|
||||
name = title,
|
||||
description = description ?: "",
|
||||
isDeleted = false,
|
||||
isDone = false,
|
||||
priority = priority,
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
@@ -8,7 +8,7 @@ import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||
import com.wismna.geoffroy.donext.presentation.screen.MainScreen
|
||||
import com.wismna.geoffroy.donext.presentation.ui.theme.DoNextTheme
|
||||
import com.wismna.geoffroy.donext.presentation.viewmodel.MainViewModel
|
||||
|
@@ -3,22 +3,14 @@
|
||||
package com.wismna.geoffroy.donext.presentation.screen
|
||||
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Add
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.CircularProgressIndicator
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.ExtendedFloatingActionButton
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.ModalBottomSheet
|
||||
import androidx.compose.material3.OutlinedTextField
|
||||
import androidx.compose.material3.PrimaryTabRow
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.SegmentedButton
|
||||
@@ -27,7 +19,6 @@ import androidx.compose.material3.SingleChoiceSegmentedButtonRow
|
||||
import androidx.compose.material3.Tab
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableIntStateOf
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
@@ -36,21 +27,16 @@ import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.focus.FocusRequester
|
||||
import androidx.compose.ui.focus.focusRequester
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import androidx.navigation.NavHostController
|
||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||
import androidx.navigation.NavType
|
||||
import androidx.navigation.compose.NavHost
|
||||
import androidx.navigation.compose.composable
|
||||
import androidx.navigation.compose.rememberNavController
|
||||
import androidx.navigation.navArgument
|
||||
import com.wismna.geoffroy.donext.domain.model.Priority
|
||||
import com.wismna.geoffroy.donext.domain.model.TaskList
|
||||
import com.wismna.geoffroy.donext.presentation.viewmodel.MainViewModel
|
||||
import com.wismna.geoffroy.donext.presentation.viewmodel.TaskListViewModel
|
||||
import com.wismna.geoffroy.donext.presentation.viewmodel.TaskViewModel
|
||||
|
||||
@Composable
|
||||
fun MainScreen(
|
||||
@@ -65,18 +51,21 @@ fun MainScreen(
|
||||
CircularProgressIndicator()
|
||||
}
|
||||
} else {
|
||||
val taskViewModel: TaskViewModel = hiltViewModel()
|
||||
val startDestination = viewModel.taskLists[0]
|
||||
// TODO: get last opened tab from saved settings
|
||||
var selectedDestination by rememberSaveable { mutableIntStateOf(0) }
|
||||
|
||||
if (showBottomSheet) {
|
||||
AddTaskBottomSheet(viewModel, selectedDestination, { showBottomSheet = false })
|
||||
TaskBottomSheet(taskViewModel, { showBottomSheet = false })
|
||||
}
|
||||
|
||||
Scaffold(
|
||||
modifier = modifier,
|
||||
floatingActionButton = {
|
||||
AddNewTaskButton {
|
||||
val currentListId = viewModel.taskLists[selectedDestination].id
|
||||
taskViewModel.startNewTask(currentListId)
|
||||
showBottomSheet = true
|
||||
}
|
||||
}, topBar = {
|
||||
@@ -103,96 +92,25 @@ fun MainScreen(
|
||||
.padding(contentPadding)
|
||||
.fillMaxSize()
|
||||
) {
|
||||
AppNavHost(navController, startDestination, viewModel)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun AppNavHost(
|
||||
navController: NavHostController,
|
||||
startDestination: TaskList,
|
||||
viewModel: MainViewModel
|
||||
) {
|
||||
NavHost(
|
||||
navController,
|
||||
startDestination = "taskList/${startDestination.id}"
|
||||
) {
|
||||
viewModel.taskLists.forEach { destination ->
|
||||
composable(
|
||||
route = "taskList/{taskListId}",
|
||||
arguments = listOf(navArgument("taskListId") { type = NavType.LongType })) {
|
||||
val viewModel: TaskListViewModel = hiltViewModel<TaskListViewModel>()
|
||||
TaskListScreen(viewModel)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun AddTaskBottomSheet(
|
||||
viewModel: MainViewModel,
|
||||
selectedListIndex: Int,
|
||||
onDismiss: () -> Unit
|
||||
) {
|
||||
val titleFocusRequester = remember { FocusRequester() }
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
titleFocusRequester.requestFocus()
|
||||
}
|
||||
|
||||
ModalBottomSheet(onDismissRequest = onDismiss) {
|
||||
Column(Modifier.padding(16.dp)) {
|
||||
Text(
|
||||
"New Task",
|
||||
style = MaterialTheme.typography.titleLarge
|
||||
)
|
||||
Spacer(Modifier.height(8.dp))
|
||||
|
||||
// --- Title ---
|
||||
OutlinedTextField(
|
||||
value = viewModel.title,
|
||||
singleLine = true,
|
||||
onValueChange = { viewModel.onTitleChanged(it) },
|
||||
label = { Text("Title") },
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.focusRequester(titleFocusRequester),
|
||||
isError = !viewModel.isTitleValid && viewModel.title.isNotEmpty(),
|
||||
)
|
||||
Spacer(Modifier.height(8.dp))
|
||||
|
||||
// --- Description ---
|
||||
OutlinedTextField(
|
||||
value = viewModel.description,
|
||||
onValueChange = { viewModel.onDescriptionChanged(it) },
|
||||
label = { Text("Description") },
|
||||
maxLines = 3,
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
)
|
||||
Spacer(Modifier.height(8.dp))
|
||||
|
||||
// --- Priority ---
|
||||
Text("Priority", style = MaterialTheme.typography.labelLarge)
|
||||
Spacer(Modifier.height(4.dp))
|
||||
SingleChoiceSegmentedButton(
|
||||
value = viewModel.priority,
|
||||
onValueChange = { viewModel.onPriorityChanged(it) }
|
||||
)
|
||||
Spacer(Modifier.height(16.dp))
|
||||
|
||||
// --- Add Button ---
|
||||
Button(
|
||||
onClick = {
|
||||
val currentListId = viewModel.taskLists[selectedListIndex].id
|
||||
viewModel.createTask(currentListId)
|
||||
onDismiss()
|
||||
viewModel.resetTaskForm()
|
||||
},
|
||||
modifier = Modifier.align(Alignment.End)
|
||||
) {
|
||||
Text("Add")
|
||||
NavHost(
|
||||
navController,
|
||||
startDestination = "taskList/${startDestination.id}"
|
||||
) {
|
||||
viewModel.taskLists.forEach { destination ->
|
||||
composable(
|
||||
route = "taskList/{taskListId}",
|
||||
arguments = listOf(navArgument("taskListId") {
|
||||
type = NavType.LongType
|
||||
})
|
||||
) {
|
||||
TaskListScreen(
|
||||
onTaskClick = { task ->
|
||||
taskViewModel.startEditTask(task)
|
||||
showBottomSheet = true
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,5 +1,6 @@
|
||||
package com.wismna.geoffroy.donext.presentation.screen
|
||||
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
@@ -23,13 +24,13 @@ import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextDecoration
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||
import com.wismna.geoffroy.donext.domain.model.Priority
|
||||
import com.wismna.geoffroy.donext.domain.model.Task
|
||||
import com.wismna.geoffroy.donext.presentation.viewmodel.TaskListViewModel
|
||||
|
||||
@Composable
|
||||
fun TaskListScreen(viewModel: TaskListViewModel = hiltViewModel()) {
|
||||
fun TaskListScreen(viewModel: TaskListViewModel = hiltViewModel(), onTaskClick: (Task) -> Unit) {
|
||||
val tasks = viewModel.tasks
|
||||
|
||||
LazyColumn(
|
||||
@@ -58,6 +59,7 @@ fun TaskListScreen(viewModel: TaskListViewModel = hiltViewModel()) {
|
||||
|
||||
TaskItem(
|
||||
task = task,
|
||||
onClick = { onTaskClick(task) },
|
||||
onToggleDone = { isChecked ->
|
||||
viewModel.updateTaskDone(task.id!!, isChecked)
|
||||
}
|
||||
@@ -69,6 +71,7 @@ fun TaskListScreen(viewModel: TaskListViewModel = hiltViewModel()) {
|
||||
@Composable
|
||||
fun TaskItem(
|
||||
task: Task,
|
||||
onClick: () -> Unit,
|
||||
onToggleDone: (Boolean) -> Unit
|
||||
) {
|
||||
val baseStyle = MaterialTheme.typography.bodyLarge.copy(
|
||||
@@ -87,15 +90,10 @@ fun TaskItem(
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clickable { onClick() }
|
||||
.padding(8.dp)
|
||||
.alpha(if (task.isDone) 0.5f else 1f),
|
||||
) {
|
||||
/*Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(8.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {*/
|
||||
Checkbox(
|
||||
checked = task.isDone,
|
||||
onCheckedChange = onToggleDone,
|
||||
|
@@ -0,0 +1,102 @@
|
||||
package com.wismna.geoffroy.donext.presentation.screen
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.ButtonDefaults
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.ModalBottomSheet
|
||||
import androidx.compose.material3.OutlinedTextField
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.focus.FocusRequester
|
||||
import androidx.compose.ui.focus.focusRequester
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.wismna.geoffroy.donext.presentation.viewmodel.TaskViewModel
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun TaskBottomSheet(
|
||||
viewModel: TaskViewModel,
|
||||
onDismiss: () -> Unit
|
||||
) {
|
||||
val titleFocusRequester = remember { FocusRequester() }
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
titleFocusRequester.requestFocus()
|
||||
}
|
||||
|
||||
ModalBottomSheet(onDismissRequest = onDismiss) {
|
||||
Column(Modifier.padding(16.dp)) {
|
||||
Text(
|
||||
"New Task",
|
||||
style = MaterialTheme.typography.titleLarge
|
||||
)
|
||||
Spacer(Modifier.height(8.dp))
|
||||
|
||||
// --- Title ---
|
||||
OutlinedTextField(
|
||||
value = viewModel.title,
|
||||
singleLine = true,
|
||||
onValueChange = { viewModel.onTitleChanged(it) },
|
||||
label = { Text("Title") },
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.focusRequester(titleFocusRequester),
|
||||
isError = viewModel.title.isEmpty(),
|
||||
)
|
||||
Spacer(Modifier.height(8.dp))
|
||||
|
||||
// --- Description ---
|
||||
OutlinedTextField(
|
||||
value = viewModel.description,
|
||||
onValueChange = { viewModel.onDescriptionChanged(it) },
|
||||
label = { Text("Description") },
|
||||
maxLines = 3,
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
)
|
||||
Spacer(Modifier.height(8.dp))
|
||||
|
||||
// --- Priority ---
|
||||
Text("Priority", style = MaterialTheme.typography.labelLarge)
|
||||
Spacer(Modifier.height(4.dp))
|
||||
SingleChoiceSegmentedButton(
|
||||
value = viewModel.priority,
|
||||
onValueChange = { viewModel.onPriorityChanged(it) }
|
||||
)
|
||||
Spacer(Modifier.height(16.dp))
|
||||
|
||||
Row (modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween) {
|
||||
// --- Delete Button ---
|
||||
if (viewModel.isEditing()) {
|
||||
Button(
|
||||
onClick = { viewModel.delete(); onDismiss() },
|
||||
colors = ButtonDefaults.textButtonColors(
|
||||
contentColor = MaterialTheme.colorScheme.error
|
||||
)
|
||||
) { Text("Delete") }
|
||||
}
|
||||
// --- Save Button ---
|
||||
Button(
|
||||
onClick = {
|
||||
viewModel.save()
|
||||
onDismiss()
|
||||
},
|
||||
enabled = viewModel.title.isNotBlank(),
|
||||
//modifier = Modifier.align(Alignment.End)
|
||||
) {
|
||||
Text(if (viewModel.isEditing()) "Save" else "Create")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -5,20 +5,16 @@ import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.wismna.geoffroy.donext.domain.model.Priority
|
||||
import com.wismna.geoffroy.donext.domain.model.TaskList
|
||||
import com.wismna.geoffroy.donext.domain.usecase.AddTaskUseCase
|
||||
import com.wismna.geoffroy.donext.domain.usecase.GetTaskListsUseCase
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
|
||||
@HiltViewModel
|
||||
class MainViewModel @Inject constructor(
|
||||
getTaskLists: GetTaskListsUseCase,
|
||||
private val addTask: AddTaskUseCase
|
||||
getTaskLists: GetTaskListsUseCase
|
||||
) : ViewModel() {
|
||||
|
||||
var taskLists by mutableStateOf<List<TaskList>>(emptyList())
|
||||
@@ -26,17 +22,6 @@ class MainViewModel @Inject constructor(
|
||||
var isLoading by mutableStateOf(true)
|
||||
private set
|
||||
|
||||
// --- Form state ---
|
||||
var title by mutableStateOf("")
|
||||
private set
|
||||
var description by mutableStateOf("")
|
||||
private set
|
||||
var priority by mutableStateOf(Priority.NORMAL)
|
||||
private set
|
||||
|
||||
val isTitleValid: Boolean
|
||||
get() = title.isNotBlank()
|
||||
|
||||
init {
|
||||
getTaskLists()
|
||||
.onEach { lists ->
|
||||
@@ -45,27 +30,4 @@ class MainViewModel @Inject constructor(
|
||||
}
|
||||
.launchIn(viewModelScope)
|
||||
}
|
||||
|
||||
fun createTask(taskListId: Long) {
|
||||
if (!isTitleValid) return
|
||||
viewModelScope.launch {
|
||||
addTask(taskListId, title, description, priority)
|
||||
}
|
||||
}
|
||||
|
||||
fun onTitleChanged(newTitle: String) {
|
||||
title = newTitle
|
||||
}
|
||||
fun onDescriptionChanged(newDesc: String) {
|
||||
description = newDesc
|
||||
}
|
||||
fun onPriorityChanged(newPriority: Priority) {
|
||||
priority = newPriority
|
||||
}
|
||||
|
||||
fun resetTaskForm() {
|
||||
title = ""
|
||||
description = ""
|
||||
priority = Priority.NORMAL
|
||||
}
|
||||
}
|
@@ -0,0 +1,88 @@
|
||||
package com.wismna.geoffroy.donext.presentation.viewmodel
|
||||
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.wismna.geoffroy.donext.domain.model.Priority
|
||||
import com.wismna.geoffroy.donext.domain.model.Task
|
||||
import com.wismna.geoffroy.donext.domain.usecase.AddTaskUseCase
|
||||
import com.wismna.geoffroy.donext.domain.usecase.DeleteTaskUseCase
|
||||
import com.wismna.geoffroy.donext.domain.usecase.UpdateTaskUseCase
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
|
||||
@HiltViewModel
|
||||
class TaskViewModel @Inject constructor(
|
||||
private val createTaskUseCase: AddTaskUseCase,
|
||||
private val updateTaskUseCase: UpdateTaskUseCase,
|
||||
private val deleteTaskUseCase: DeleteTaskUseCase
|
||||
) : ViewModel() {
|
||||
|
||||
var title by mutableStateOf("")
|
||||
private set
|
||||
var description by mutableStateOf("")
|
||||
private set
|
||||
var priority by mutableStateOf(Priority.NORMAL)
|
||||
private set
|
||||
|
||||
private var editingTaskId: Long? = null
|
||||
private var taskListId: Long? = null
|
||||
|
||||
fun isEditing(): Boolean = editingTaskId != null
|
||||
|
||||
fun startNewTask(selectedListId: Long) {
|
||||
editingTaskId = null
|
||||
taskListId = selectedListId
|
||||
title = ""
|
||||
description = ""
|
||||
priority = Priority.NORMAL
|
||||
}
|
||||
|
||||
fun startEditTask(task: Task) {
|
||||
editingTaskId = task.id
|
||||
taskListId = task.taskListId
|
||||
title = task.name
|
||||
description = task.description ?: ""
|
||||
priority = task.priority
|
||||
}
|
||||
|
||||
fun onTitleChanged(value: String) { title = value }
|
||||
fun onDescriptionChanged(value: String) { description = value }
|
||||
fun onPriorityChanged(value: Priority) { priority = value }
|
||||
|
||||
fun save(onDone: (() -> Unit)? = null) {
|
||||
if (title.isBlank()) return
|
||||
|
||||
viewModelScope.launch {
|
||||
if (isEditing()) {
|
||||
updateTaskUseCase(editingTaskId!!, taskListId!!, title, description, priority)
|
||||
} else {
|
||||
createTaskUseCase(taskListId!!, title, description, priority)
|
||||
}
|
||||
// reset state after save
|
||||
reset()
|
||||
onDone?.invoke()
|
||||
}
|
||||
}
|
||||
|
||||
fun delete() {
|
||||
editingTaskId?.let { id ->
|
||||
viewModelScope.launch {
|
||||
deleteTaskUseCase(id)
|
||||
reset()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Optional: manual reset */
|
||||
fun reset() {
|
||||
editingTaskId = null
|
||||
taskListId = null
|
||||
title = ""
|
||||
description = ""
|
||||
priority = Priority.NORMAL
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user