Removed order from tasks

Tasks are ordered by priority and display is a bit different for each
Done tasks are shown at the bottom
Add done checkbox
Add priority segmented button to bottom sheet
Use database converters instead of manual mapping
Some refactoring
This commit is contained in:
Geoffroy Bonneville
2025-09-11 22:40:40 -04:00
parent d469fbc2ba
commit ef73319613
15 changed files with 270 additions and 92 deletions

View File

@@ -1,9 +1,9 @@
package com.wismna.geoffroy.donext.data package com.wismna.geoffroy.donext.data
import androidx.room.TypeConverter import androidx.room.TypeConverter
import com.wismna.geoffroy.donext.domain.model.Priority
import java.time.Instant import java.time.Instant
class Converters { class Converters {
@TypeConverter @TypeConverter
fun fromTimestamp(value: Long?): Instant? { fun fromTimestamp(value: Long?): Instant? {
@@ -14,4 +14,10 @@ class Converters {
fun instantToTimestamp(instant: Instant?): Long? { fun instantToTimestamp(instant: Instant?): Long? {
return instant?.toEpochMilli() return instant?.toEpochMilli()
} }
@TypeConverter
fun fromPriority(priority: Priority): Int = priority.value
@TypeConverter
fun toPriority(value: Int): Priority = Priority.fromValue(value)
} }

View File

@@ -4,19 +4,16 @@ import com.wismna.geoffroy.donext.data.entities.TaskEntity
import com.wismna.geoffroy.donext.data.entities.TaskListEntity import com.wismna.geoffroy.donext.data.entities.TaskListEntity
import com.wismna.geoffroy.donext.domain.model.Task import com.wismna.geoffroy.donext.domain.model.Task
import com.wismna.geoffroy.donext.domain.model.TaskList import com.wismna.geoffroy.donext.domain.model.TaskList
import java.time.Instant
fun TaskEntity.toDomain() = Task( fun TaskEntity.toDomain() = Task(
id = id, id = id,
name = name, name = name,
taskListId = taskListId, taskListId = taskListId,
description = description, description = description,
cycle = cycle,
isDone = isDone, isDone = isDone,
isDeleted = isDeleted, isDeleted = isDeleted,
dueDate = if (dueDate == null) null else Instant.ofEpochMilli(dueDate), dueDate = dueDate,
priority = priority, priority = priority,
order = order
) )
fun Task.toEntity() = TaskEntity( fun Task.toEntity() = TaskEntity(
@@ -24,12 +21,10 @@ fun Task.toEntity() = TaskEntity(
name = name, name = name,
taskListId = taskListId, taskListId = taskListId,
description = description, description = description,
cycle = cycle,
priority = priority, priority = priority,
order = order,
isDone = isDone, isDone = isDone,
isDeleted = isDeleted, isDeleted = isDeleted,
dueDate = dueDate?.toEpochMilli() dueDate = dueDate
) )
fun TaskListEntity.toDomain() = TaskList( fun TaskListEntity.toDomain() = TaskList(

View File

@@ -3,6 +3,8 @@ package com.wismna.geoffroy.donext.data.entities
import androidx.room.ColumnInfo import androidx.room.ColumnInfo
import androidx.room.Entity import androidx.room.Entity
import androidx.room.PrimaryKey import androidx.room.PrimaryKey
import com.wismna.geoffroy.donext.domain.model.Priority
import java.time.Instant
@Entity(tableName = "tasks") @Entity(tableName = "tasks")
data class TaskEntity( data class TaskEntity(
@@ -10,10 +12,7 @@ data class TaskEntity(
val id: Long = 0, val id: Long = 0,
val name: String, val name: String,
val description: String?, val description: String?,
val cycle: Int = 0, val priority: Priority,
val priority: Int,
@ColumnInfo(name = "display_order")
val order: Int,
@ColumnInfo(name = "done") @ColumnInfo(name = "done")
val isDone: Boolean = false, val isDone: Boolean = false,
@ColumnInfo(name = "deleted") @ColumnInfo(name = "deleted")
@@ -21,5 +20,5 @@ data class TaskEntity(
@ColumnInfo(name = "task_list_id") @ColumnInfo(name = "task_list_id")
val taskListId: Long, val taskListId: Long,
@ColumnInfo(name = "due_date") @ColumnInfo(name = "due_date")
val dueDate: Long? = null val dueDate: Instant? = null
) )

View File

@@ -41,9 +41,7 @@ abstract class AppDatabase : RoomDatabase() {
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
name TEXT NOT NULL, name TEXT NOT NULL,
description TEXT, description TEXT,
cycle INTEGER NOT NULL DEFAULT 0,
priority INTEGER NOT NULL, priority INTEGER NOT NULL,
display_order INTEGER NOT NULL,
done INTEGER NOT NULL DEFAULT 0, done INTEGER NOT NULL DEFAULT 0,
deleted INTEGER NOT NULL DEFAULT 0, deleted INTEGER NOT NULL DEFAULT 0,
task_list_id INTEGER NOT NULL, task_list_id INTEGER NOT NULL,
@@ -57,16 +55,14 @@ abstract class AppDatabase : RoomDatabase() {
db.execSQL( db.execSQL(
""" """
INSERT INTO tasks_new ( INSERT INTO tasks_new (
id, name, description, cycle, priority, display_order, id, name, description, priority,
done, deleted, task_list_id, due_date done, deleted, task_list_id, due_date
) )
SELECT SELECT
_id, -- old '_id' mapped to id _id, -- old '_id' mapped to id
name, name,
description, description,
cycle,
priority, priority,
displayorder, -- old 'displayorder' mapped to display_order
done, done,
deleted, deleted,
list, -- old 'list' mapped to task_list_id list, -- old 'list' mapped to task_list_id

View File

@@ -10,7 +10,7 @@ import kotlinx.coroutines.flow.Flow
@Dao @Dao
interface TaskDao { interface TaskDao {
@Query("SELECT * FROM tasks WHERE task_list_id = :listId ORDER BY display_order ASC") @Query("SELECT * FROM tasks WHERE task_list_id = :listId ORDER BY done ASC, priority DESC")
fun getTasksForList(listId: Long): Flow<List<TaskEntity>> fun getTasksForList(listId: Long): Flow<List<TaskEntity>>
@Insert(onConflict = OnConflictStrategy.REPLACE) @Insert(onConflict = OnConflictStrategy.REPLACE)
@@ -25,9 +25,6 @@ interface TaskDao {
@Query("UPDATE tasks SET deleted = :deleted WHERE id = :taskId") @Query("UPDATE tasks SET deleted = :deleted WHERE id = :taskId")
suspend fun markTaskDeleted(taskId: Long, deleted: Boolean) suspend fun markTaskDeleted(taskId: Long, deleted: Boolean)
@Query("UPDATE tasks SET cycle = cycle + 1 WHERE id = :taskId")
suspend fun increaseCycle(taskId: Long)
@Query("UPDATE tasks SET deleted = :deleted WHERE id = :taskListId") @Query("UPDATE tasks SET deleted = :deleted WHERE id = :taskListId")
suspend fun deleteAllTasksFromList(taskListId: Long, deleted: Boolean) suspend fun deleteAllTasksFromList(taskListId: Long, deleted: Boolean)
} }

View File

@@ -31,14 +31,10 @@ class TaskRepositoryImpl @Inject constructor(
taskDao.markTaskDeleted(taskId, isDeleted) taskDao.markTaskDeleted(taskId, isDeleted)
} }
override suspend fun closeTask(taskId: Long, isDone: Boolean) { override suspend fun toggleTaskDone(taskId: Long, isDone: Boolean) {
taskDao.markTaskDone(taskId, isDone) taskDao.markTaskDone(taskId, isDone)
} }
override suspend fun increaseTaskCycle(taskId: Long) {
taskDao.increaseCycle(taskId)
}
override fun getTaskLists(): Flow<List<TaskList>> { override fun getTaskLists(): Flow<List<TaskList>> {
return taskListDao.getTaskLists().map {entities -> entities.map { it.toDomain() }} return taskListDao.getTaskLists().map {entities -> entities.map { it.toDomain() }}
} }

View File

@@ -0,0 +1,12 @@
package com.wismna.geoffroy.donext.domain.model
enum class Priority(val value: Int, val label: String) {
LOW(0, "Low"),
NORMAL(1, "Normal"),
HIGH(2, "High");
companion object {
fun fromValue(value: Int): Priority =
Priority.entries.firstOrNull { it.value == value } ?: NORMAL
}
}

View File

@@ -6,9 +6,7 @@ data class Task(
val id: Long? = null, val id: Long? = null,
val name: String, val name: String,
val description: String?, val description: String?,
val cycle: Int, val priority: Priority,
val priority: Int,
val order: Int,
val isDone: Boolean, val isDone: Boolean,
val isDeleted: Boolean, val isDeleted: Boolean,
val taskListId: Long, val taskListId: Long,

View File

@@ -9,8 +9,7 @@ interface TaskRepository {
suspend fun insertTask(task: Task) suspend fun insertTask(task: Task)
suspend fun updateTask(task: Task) suspend fun updateTask(task: Task)
suspend fun deleteTask(taskId: Long, isDeleted: Boolean) suspend fun deleteTask(taskId: Long, isDeleted: Boolean)
suspend fun closeTask(taskId: Long, isDone: Boolean) suspend fun toggleTaskDone(taskId: Long, isDone: Boolean)
suspend fun increaseTaskCycle(taskId: Long)
fun getTaskLists(): Flow<List<TaskList>> fun getTaskLists(): Flow<List<TaskList>>
suspend fun insertTaskList(taskList: TaskList) suspend fun insertTaskList(taskList: TaskList)

View File

@@ -1,5 +1,6 @@
package com.wismna.geoffroy.donext.domain.usecase 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.model.Task
import com.wismna.geoffroy.donext.domain.repository.TaskRepository import com.wismna.geoffroy.donext.domain.repository.TaskRepository
import javax.inject.Inject import javax.inject.Inject
@@ -7,17 +8,15 @@ import javax.inject.Inject
class AddTaskUseCase @Inject constructor( class AddTaskUseCase @Inject constructor(
private val repository: TaskRepository private val repository: TaskRepository
) { ) {
suspend operator fun invoke(taskListId: Long, title: String, description: String?) { suspend operator fun invoke(taskListId: Long, title: String, description: String?, priority: Priority) {
repository.insertTask( repository.insertTask(
Task( Task(
taskListId = taskListId, taskListId = taskListId,
name = title, name = title,
description = description ?: "", description = description ?: "",
isDeleted = false, isDeleted = false,
cycle = 0,
isDone = false, isDone = false,
priority = 0, priority = priority,
order = 0
) )
) )
} }

View File

@@ -0,0 +1,12 @@
package com.wismna.geoffroy.donext.domain.usecase
import com.wismna.geoffroy.donext.domain.repository.TaskRepository
import javax.inject.Inject
class ToggleTaskDoneUseCase @Inject constructor(
private val repository: TaskRepository
) {
suspend operator fun invoke(taskId: Long, isDone: Boolean) {
repository.toggleTaskDone(taskId, isDone)
}
}

View File

@@ -1,3 +1,5 @@
@file:OptIn(ExperimentalMaterial3Api::class)
package com.wismna.geoffroy.donext.presentation.screen package com.wismna.geoffroy.donext.presentation.screen
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
@@ -19,9 +21,13 @@ import androidx.compose.material3.ModalBottomSheet
import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.PrimaryTabRow import androidx.compose.material3.PrimaryTabRow
import androidx.compose.material3.Scaffold import androidx.compose.material3.Scaffold
import androidx.compose.material3.SegmentedButton
import androidx.compose.material3.SegmentedButtonDefaults
import androidx.compose.material3.SingleChoiceSegmentedButtonRow
import androidx.compose.material3.Tab import androidx.compose.material3.Tab
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
@@ -30,6 +36,8 @@ import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier 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.text.style.TextOverflow
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel import androidx.hilt.navigation.compose.hiltViewModel
@@ -39,11 +47,11 @@ import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController import androidx.navigation.compose.rememberNavController
import androidx.navigation.navArgument 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.domain.model.TaskList
import com.wismna.geoffroy.donext.presentation.viewmodel.MainViewModel import com.wismna.geoffroy.donext.presentation.viewmodel.MainViewModel
import com.wismna.geoffroy.donext.presentation.viewmodel.TaskListViewModel import com.wismna.geoffroy.donext.presentation.viewmodel.TaskListViewModel
@OptIn(ExperimentalMaterial3Api::class)
@Composable @Composable
fun MainScreen( fun MainScreen(
viewModel: MainViewModel, viewModel: MainViewModel,
@@ -62,39 +70,7 @@ fun MainScreen(
var selectedDestination by rememberSaveable { mutableIntStateOf(0) } var selectedDestination by rememberSaveable { mutableIntStateOf(0) }
if (showBottomSheet) { if (showBottomSheet) {
ModalBottomSheet(onDismissRequest = { showBottomSheet = false }) { AddTaskBottomSheet(viewModel, selectedDestination, { showBottomSheet = false })
Column(Modifier.padding(16.dp)) {
Text("New Task", style = MaterialTheme.typography.titleLarge)
Spacer(Modifier.height(8.dp))
OutlinedTextField(
value = viewModel.title,
singleLine = true,
onValueChange = { viewModel.onTitleChanged(it) },
label = { Text("Title") },
modifier = Modifier.fillMaxWidth(),
isError = !viewModel.isTitleValid && viewModel.title.isNotEmpty(),
)
Spacer(Modifier.height(8.dp))
OutlinedTextField(
value = viewModel.description,
onValueChange = { viewModel.onDescriptionChanged(it) },
label = { Text("Description") },
maxLines = 3,
modifier = Modifier.fillMaxWidth()
)
Spacer(Modifier.height(16.dp))
Button(
onClick = {
val currentListId = viewModel.taskLists[selectedDestination].id
viewModel.createTask(currentListId)
showBottomSheet = false
},
modifier = Modifier.align(Alignment.End)
) {
Text("Add")
}
}
}
} }
Scaffold( Scaffold(
@@ -123,7 +99,6 @@ fun MainScreen(
} }
} }
}) { contentPadding -> }) { contentPadding ->
// NavHost will now automatically be below the tabs
Box(modifier = Modifier Box(modifier = Modifier
.padding(contentPadding) .padding(contentPadding)
.fillMaxSize() .fillMaxSize()
@@ -155,11 +130,100 @@ fun AppNavHost(
} }
} }
@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")
}
}
}
}
@Composable @Composable
fun AddNewTaskButton(onClick: () -> Unit) { fun AddNewTaskButton(onClick: () -> Unit) {
ExtendedFloatingActionButton( ExtendedFloatingActionButton(
onClick = { onClick() }, onClick = onClick,
icon = { Icon(Icons.Filled.Add, "Create a task.") }, icon = { Icon(Icons.Filled.Add, "Create a task.") },
text = { Text(text = "Create a task") }, text = { Text(text = "Create a task") },
) )
} }
@Composable
fun SingleChoiceSegmentedButton(
value: Priority,
onValueChange: (Priority) -> Unit) {
val options = listOf(Priority.LOW.label, Priority.NORMAL.label, Priority.HIGH.label)
SingleChoiceSegmentedButtonRow {
options.forEachIndexed { index, label ->
SegmentedButton(
shape = SegmentedButtonDefaults.itemShape(
index = index,
count = options.size
),
onClick = { onValueChange(Priority.fromValue(index)) },
selected = index == value.value,
label = { Text(label) }
)
}
}
}

View File

@@ -1,21 +1,31 @@
package com.wismna.geoffroy.donext.presentation.screen package com.wismna.geoffroy.donext.presentation.screen
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material3.Checkbox
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color import androidx.compose.ui.draw.alpha
import androidx.compose.ui.text.TextStyle import androidx.compose.ui.draw.clip
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextDecoration import androidx.compose.ui.text.style.TextDecoration
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.hilt.navigation.compose.hiltViewModel import androidx.hilt.navigation.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 import com.wismna.geoffroy.donext.presentation.viewmodel.TaskListViewModel
@Composable @Composable
@@ -23,21 +33,96 @@ fun TaskListScreen(viewModel: TaskListViewModel = hiltViewModel()) {
val tasks = viewModel.tasks val tasks = viewModel.tasks
LazyColumn( LazyColumn(
verticalArrangement = Arrangement.spacedBy(4.dp)) { modifier = Modifier.fillMaxSize()
items(tasks) { task -> ) {
itemsIndexed(tasks, key = { _, task -> task.id!! }) { index, task ->
if (index > 0) {
val prev = tasks[index - 1]
when {
// Divider between non-done and done tasks
!prev.isDone && task.isDone -> {
HorizontalDivider(
thickness = 1.dp,
color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.2f),
modifier = Modifier.padding(vertical = 8.dp)
)
}
// Extra spacing between different priorities (only if done status is same)
prev.priority != task.priority && prev.isDone == task.isDone -> {
Spacer(modifier = Modifier.height(20.dp))
}
}
}
TaskItem(
task = task,
onToggleDone = { isChecked ->
viewModel.updateTaskDone(task.id!!, isChecked)
}
)
}
}
}
@Composable
fun TaskItem(
task: Task,
onToggleDone: (Boolean) -> Unit
) {
val baseStyle = MaterialTheme.typography.bodyLarge.copy(
fontWeight = when (task.priority) {
Priority.HIGH -> FontWeight.Bold
Priority.NORMAL -> FontWeight.Normal
Priority.LOW -> FontWeight.Normal
},
color = when (task.priority) {
Priority.HIGH -> MaterialTheme.colorScheme.onSurface
Priority.NORMAL -> MaterialTheme.colorScheme.onSurface
Priority.LOW -> MaterialTheme.colorScheme.onSurfaceVariant
},
textDecoration = if (task.isDone) TextDecoration.LineThrough else TextDecoration.None)
Row( Row(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.padding(8.dp) .padding(8.dp)
.alpha(if (task.isDone) 0.5f else 1f),
) { ) {
Column { /*Row(
} modifier = Modifier
Column { .fillMaxWidth()
val textStyle = TextStyle(textDecoration = if (task.isDeleted) TextDecoration.LineThrough else TextDecoration.None) .padding(8.dp),
Text(task.name, fontSize = 18.sp, color = Color.DarkGray, style = textStyle) verticalAlignment = Alignment.CenterVertically
Text(task.description.orEmpty(), fontSize = 14.sp, color = Color.LightGray, style = textStyle, maxLines = 3) ) {*/
} Checkbox(
checked = task.isDone,
onCheckedChange = onToggleDone,
modifier = Modifier
.size(40.dp) // Adjust size as needed
.clip(CircleShape)
)
Column(
modifier = Modifier.weight(1f)
) {
Text(
text = task.name,
style = baseStyle
)
if (!task.description.isNullOrBlank()) {
Text(
text = task.description,
style = baseStyle.copy(
fontSize = MaterialTheme.typography.bodyMedium.fontSize
),
maxLines = 3,
overflow = TextOverflow.Ellipsis
)
} }
} }
// }
} }
} }

View File

@@ -5,6 +5,7 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope 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.model.TaskList
import com.wismna.geoffroy.donext.domain.usecase.AddTaskUseCase import com.wismna.geoffroy.donext.domain.usecase.AddTaskUseCase
import com.wismna.geoffroy.donext.domain.usecase.GetTaskListsUseCase import com.wismna.geoffroy.donext.domain.usecase.GetTaskListsUseCase
@@ -30,6 +31,8 @@ class MainViewModel @Inject constructor(
private set private set
var description by mutableStateOf("") var description by mutableStateOf("")
private set private set
var priority by mutableStateOf(Priority.NORMAL)
private set
val isTitleValid: Boolean val isTitleValid: Boolean
get() = title.isNotBlank() get() = title.isNotBlank()
@@ -46,15 +49,23 @@ class MainViewModel @Inject constructor(
fun createTask(taskListId: Long) { fun createTask(taskListId: Long) {
if (!isTitleValid) return if (!isTitleValid) return
viewModelScope.launch { viewModelScope.launch {
addTask(taskListId, title, description) addTask(taskListId, title, description, priority)
} }
} }
fun onTitleChanged(newTitle: String) { fun onTitleChanged(newTitle: String) {
title = newTitle title = newTitle
} }
fun onDescriptionChanged(newDesc: String) { fun onDescriptionChanged(newDesc: String) {
description = newDesc description = newDesc
} }
fun onPriorityChanged(newPriority: Priority) {
priority = newPriority
}
fun resetTaskForm() {
title = ""
description = ""
priority = Priority.NORMAL
}
} }

View File

@@ -7,15 +7,18 @@ import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import com.wismna.geoffroy.donext.domain.model.Task import com.wismna.geoffroy.donext.domain.model.Task
import com.wismna.geoffroy.donext.domain.usecase.ToggleTaskDoneUseCase
import com.wismna.geoffroy.donext.domain.usecase.GetTasksForListUseCase import com.wismna.geoffroy.donext.domain.usecase.GetTasksForListUseCase
import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch
import javax.inject.Inject import javax.inject.Inject
@HiltViewModel @HiltViewModel
class TaskListViewModel @Inject constructor( class TaskListViewModel @Inject constructor(
getTasks: GetTasksForListUseCase, getTasks: GetTasksForListUseCase,
private val toggleTaskDone: ToggleTaskDoneUseCase,
savedStateHandle: SavedStateHandle savedStateHandle: SavedStateHandle
) : ViewModel() { ) : ViewModel() {
@@ -35,4 +38,10 @@ class TaskListViewModel @Inject constructor(
} }
.launchIn(viewModelScope) .launchIn(viewModelScope)
} }
fun updateTaskDone(taskId: Long, isDone: Boolean) {
viewModelScope.launch {
toggleTaskDone(taskId, isDone)
}
}
} }