mirror of
https://github.com/wismna/DoNext.git
synced 2025-10-03 07:30:13 -04:00
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:
@@ -1,9 +1,9 @@
|
||||
package com.wismna.geoffroy.donext.data
|
||||
|
||||
import androidx.room.TypeConverter
|
||||
import com.wismna.geoffroy.donext.domain.model.Priority
|
||||
import java.time.Instant
|
||||
|
||||
|
||||
class Converters {
|
||||
@TypeConverter
|
||||
fun fromTimestamp(value: Long?): Instant? {
|
||||
@@ -14,4 +14,10 @@ class Converters {
|
||||
fun instantToTimestamp(instant: Instant?): Long? {
|
||||
return instant?.toEpochMilli()
|
||||
}
|
||||
|
||||
@TypeConverter
|
||||
fun fromPriority(priority: Priority): Int = priority.value
|
||||
|
||||
@TypeConverter
|
||||
fun toPriority(value: Int): Priority = Priority.fromValue(value)
|
||||
}
|
@@ -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.domain.model.Task
|
||||
import com.wismna.geoffroy.donext.domain.model.TaskList
|
||||
import java.time.Instant
|
||||
|
||||
fun TaskEntity.toDomain() = Task(
|
||||
id = id,
|
||||
name = name,
|
||||
taskListId = taskListId,
|
||||
description = description,
|
||||
cycle = cycle,
|
||||
isDone = isDone,
|
||||
isDeleted = isDeleted,
|
||||
dueDate = if (dueDate == null) null else Instant.ofEpochMilli(dueDate),
|
||||
dueDate = dueDate,
|
||||
priority = priority,
|
||||
order = order
|
||||
)
|
||||
|
||||
fun Task.toEntity() = TaskEntity(
|
||||
@@ -24,12 +21,10 @@ fun Task.toEntity() = TaskEntity(
|
||||
name = name,
|
||||
taskListId = taskListId,
|
||||
description = description,
|
||||
cycle = cycle,
|
||||
priority = priority,
|
||||
order = order,
|
||||
isDone = isDone,
|
||||
isDeleted = isDeleted,
|
||||
dueDate = dueDate?.toEpochMilli()
|
||||
dueDate = dueDate
|
||||
)
|
||||
|
||||
fun TaskListEntity.toDomain() = TaskList(
|
||||
|
@@ -3,6 +3,8 @@ package com.wismna.geoffroy.donext.data.entities
|
||||
import androidx.room.ColumnInfo
|
||||
import androidx.room.Entity
|
||||
import androidx.room.PrimaryKey
|
||||
import com.wismna.geoffroy.donext.domain.model.Priority
|
||||
import java.time.Instant
|
||||
|
||||
@Entity(tableName = "tasks")
|
||||
data class TaskEntity(
|
||||
@@ -10,10 +12,7 @@ data class TaskEntity(
|
||||
val id: Long = 0,
|
||||
val name: String,
|
||||
val description: String?,
|
||||
val cycle: Int = 0,
|
||||
val priority: Int,
|
||||
@ColumnInfo(name = "display_order")
|
||||
val order: Int,
|
||||
val priority: Priority,
|
||||
@ColumnInfo(name = "done")
|
||||
val isDone: Boolean = false,
|
||||
@ColumnInfo(name = "deleted")
|
||||
@@ -21,5 +20,5 @@ data class TaskEntity(
|
||||
@ColumnInfo(name = "task_list_id")
|
||||
val taskListId: Long,
|
||||
@ColumnInfo(name = "due_date")
|
||||
val dueDate: Long? = null
|
||||
val dueDate: Instant? = null
|
||||
)
|
||||
|
@@ -41,9 +41,7 @@ abstract class AppDatabase : RoomDatabase() {
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
name TEXT NOT NULL,
|
||||
description TEXT,
|
||||
cycle INTEGER NOT NULL DEFAULT 0,
|
||||
priority INTEGER NOT NULL,
|
||||
display_order INTEGER NOT NULL,
|
||||
done INTEGER NOT NULL DEFAULT 0,
|
||||
deleted INTEGER NOT NULL DEFAULT 0,
|
||||
task_list_id INTEGER NOT NULL,
|
||||
@@ -57,16 +55,14 @@ abstract class AppDatabase : RoomDatabase() {
|
||||
db.execSQL(
|
||||
"""
|
||||
INSERT INTO tasks_new (
|
||||
id, name, description, cycle, priority, display_order,
|
||||
id, name, description, priority,
|
||||
done, deleted, task_list_id, due_date
|
||||
)
|
||||
SELECT
|
||||
_id, -- old '_id' mapped to id
|
||||
name,
|
||||
description,
|
||||
cycle,
|
||||
priority,
|
||||
displayorder, -- old 'displayorder' mapped to display_order
|
||||
done,
|
||||
deleted,
|
||||
list, -- old 'list' mapped to task_list_id
|
||||
|
@@ -10,7 +10,7 @@ import kotlinx.coroutines.flow.Flow
|
||||
|
||||
@Dao
|
||||
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>>
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
@@ -25,9 +25,6 @@ interface TaskDao {
|
||||
@Query("UPDATE tasks SET deleted = :deleted WHERE id = :taskId")
|
||||
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")
|
||||
suspend fun deleteAllTasksFromList(taskListId: Long, deleted: Boolean)
|
||||
}
|
@@ -31,14 +31,10 @@ class TaskRepositoryImpl @Inject constructor(
|
||||
taskDao.markTaskDeleted(taskId, isDeleted)
|
||||
}
|
||||
|
||||
override suspend fun closeTask(taskId: Long, isDone: Boolean) {
|
||||
override suspend fun toggleTaskDone(taskId: Long, isDone: Boolean) {
|
||||
taskDao.markTaskDone(taskId, isDone)
|
||||
}
|
||||
|
||||
override suspend fun increaseTaskCycle(taskId: Long) {
|
||||
taskDao.increaseCycle(taskId)
|
||||
}
|
||||
|
||||
override fun getTaskLists(): Flow<List<TaskList>> {
|
||||
return taskListDao.getTaskLists().map {entities -> entities.map { it.toDomain() }}
|
||||
}
|
||||
|
@@ -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
|
||||
}
|
||||
}
|
@@ -6,9 +6,7 @@ data class Task(
|
||||
val id: Long? = null,
|
||||
val name: String,
|
||||
val description: String?,
|
||||
val cycle: Int,
|
||||
val priority: Int,
|
||||
val order: Int,
|
||||
val priority: Priority,
|
||||
val isDone: Boolean,
|
||||
val isDeleted: Boolean,
|
||||
val taskListId: Long,
|
||||
|
@@ -9,8 +9,7 @@ interface TaskRepository {
|
||||
suspend fun insertTask(task: Task)
|
||||
suspend fun updateTask(task: Task)
|
||||
suspend fun deleteTask(taskId: Long, isDeleted: Boolean)
|
||||
suspend fun closeTask(taskId: Long, isDone: Boolean)
|
||||
suspend fun increaseTaskCycle(taskId: Long)
|
||||
suspend fun toggleTaskDone(taskId: Long, isDone: Boolean)
|
||||
|
||||
fun getTaskLists(): Flow<List<TaskList>>
|
||||
suspend fun insertTaskList(taskList: TaskList)
|
||||
|
@@ -1,5 +1,6 @@
|
||||
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
|
||||
@@ -7,17 +8,15 @@ import javax.inject.Inject
|
||||
class AddTaskUseCase @Inject constructor(
|
||||
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(
|
||||
Task(
|
||||
taskListId = taskListId,
|
||||
name = title,
|
||||
description = description ?: "",
|
||||
isDeleted = false,
|
||||
cycle = 0,
|
||||
isDone = false,
|
||||
priority = 0,
|
||||
order = 0
|
||||
priority = priority,
|
||||
)
|
||||
)
|
||||
}
|
||||
|
@@ -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)
|
||||
}
|
||||
}
|
@@ -1,3 +1,5 @@
|
||||
@file:OptIn(ExperimentalMaterial3Api::class)
|
||||
|
||||
package com.wismna.geoffroy.donext.presentation.screen
|
||||
|
||||
import androidx.compose.foundation.layout.Box
|
||||
@@ -19,9 +21,13 @@ 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
|
||||
import androidx.compose.material3.SegmentedButtonDefaults
|
||||
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
|
||||
@@ -30,6 +36,8 @@ 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
|
||||
@@ -39,11 +47,11 @@ 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
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun MainScreen(
|
||||
viewModel: MainViewModel,
|
||||
@@ -62,39 +70,7 @@ fun MainScreen(
|
||||
var selectedDestination by rememberSaveable { mutableIntStateOf(0) }
|
||||
|
||||
if (showBottomSheet) {
|
||||
ModalBottomSheet(onDismissRequest = { 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")
|
||||
}
|
||||
}
|
||||
}
|
||||
AddTaskBottomSheet(viewModel, selectedDestination, { showBottomSheet = false })
|
||||
}
|
||||
|
||||
Scaffold(
|
||||
@@ -123,7 +99,6 @@ fun MainScreen(
|
||||
}
|
||||
}
|
||||
}) { contentPadding ->
|
||||
// NavHost will now automatically be below the tabs
|
||||
Box(modifier = Modifier
|
||||
.padding(contentPadding)
|
||||
.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
|
||||
fun AddNewTaskButton(onClick: () -> Unit) {
|
||||
ExtendedFloatingActionButton(
|
||||
onClick = { onClick() },
|
||||
onClick = onClick,
|
||||
icon = { Icon(Icons.Filled.Add, "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) }
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,21 +1,31 @@
|
||||
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.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
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.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.draw.alpha
|
||||
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.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
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
|
||||
|
||||
@Composable
|
||||
@@ -23,21 +33,96 @@ fun TaskListScreen(viewModel: TaskListViewModel = hiltViewModel()) {
|
||||
val tasks = viewModel.tasks
|
||||
|
||||
LazyColumn(
|
||||
verticalArrangement = Arrangement.spacedBy(4.dp)) {
|
||||
items(tasks) { task ->
|
||||
modifier = Modifier.fillMaxSize()
|
||||
) {
|
||||
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(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(8.dp)
|
||||
.alpha(if (task.isDone) 0.5f else 1f),
|
||||
) {
|
||||
Column {
|
||||
}
|
||||
Column {
|
||||
val textStyle = TextStyle(textDecoration = if (task.isDeleted) TextDecoration.LineThrough else TextDecoration.None)
|
||||
Text(task.name, fontSize = 18.sp, color = Color.DarkGray, style = textStyle)
|
||||
Text(task.description.orEmpty(), fontSize = 14.sp, color = Color.LightGray, style = textStyle, maxLines = 3)
|
||||
}
|
||||
/*Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(8.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {*/
|
||||
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
|
||||
)
|
||||
}
|
||||
}
|
||||
// }
|
||||
}
|
||||
}
|
@@ -5,6 +5,7 @@ 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
|
||||
@@ -30,6 +31,8 @@ class MainViewModel @Inject constructor(
|
||||
private set
|
||||
var description by mutableStateOf("")
|
||||
private set
|
||||
var priority by mutableStateOf(Priority.NORMAL)
|
||||
private set
|
||||
|
||||
val isTitleValid: Boolean
|
||||
get() = title.isNotBlank()
|
||||
@@ -46,15 +49,23 @@ class MainViewModel @Inject constructor(
|
||||
fun createTask(taskListId: Long) {
|
||||
if (!isTitleValid) return
|
||||
viewModelScope.launch {
|
||||
addTask(taskListId, title, description)
|
||||
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
|
||||
}
|
||||
}
|
@@ -7,15 +7,18 @@ import androidx.lifecycle.SavedStateHandle
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
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 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 TaskListViewModel @Inject constructor(
|
||||
getTasks: GetTasksForListUseCase,
|
||||
private val toggleTaskDone: ToggleTaskDoneUseCase,
|
||||
savedStateHandle: SavedStateHandle
|
||||
) : ViewModel() {
|
||||
|
||||
@@ -35,4 +38,10 @@ class TaskListViewModel @Inject constructor(
|
||||
}
|
||||
.launchIn(viewModelScope)
|
||||
}
|
||||
|
||||
fun updateTaskDone(taskId: Long, isDone: Boolean) {
|
||||
viewModelScope.launch {
|
||||
toggleTaskDone(taskId, isDone)
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user