Use events for task clicks

This commit is contained in:
Geoffroy Bonneville
2025-10-10 10:59:32 -04:00
parent e07f389fac
commit c3dd615d15
9 changed files with 63 additions and 45 deletions

View File

@@ -13,14 +13,12 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
import com.wismna.geoffroy.donext.domain.model.Task
import com.wismna.geoffroy.donext.presentation.viewmodel.DueTodayViewModel
@Composable
fun DueTodayTasksScreen(
modifier: Modifier = Modifier,
viewModel: DueTodayViewModel = hiltViewModel(),
onTaskClick: (task: Task) -> Unit
) {
val tasks = viewModel.dueTodayTasks
@@ -41,7 +39,6 @@ fun DueTodayTasksScreen(
TaskItemScreen(
modifier = Modifier.animateItem(),
task = task,
onTaskClick = { onTaskClick(task) },
onSwipeLeft = {
viewModel.updateTaskDone(task.id!!)
Toast.makeText(context, "Task done", Toast.LENGTH_SHORT).show()

View File

@@ -108,6 +108,7 @@ fun AppContent(
navController.navigate(event.route)
}
is UiEvent.NavigateBack -> navController.popBackStack()
is UiEvent.EditTask -> { viewModel.showTaskSheet = true }
else -> {}
}
}
@@ -207,10 +208,7 @@ fun AppContent(
}
val taskListViewModel: TaskListViewModel = hiltViewModel(navBackStackEntry)
TaskListScreen(
viewModel = taskListViewModel,
onTaskClick = { task -> viewModel.onTaskClicked(task) }
)
TaskListScreen(viewModel = taskListViewModel)
}
composable(AppDestination.ManageLists.route) {
@@ -220,16 +218,10 @@ fun AppContent(
)
}
composable(AppDestination.DueTodayList.route) {
DueTodayTasksScreen (
modifier = Modifier,
onTaskClick = { task -> viewModel.onTaskClicked(task) }
)
DueTodayTasksScreen (modifier = Modifier)
}
composable(AppDestination.RecycleBin.route) {
RecycleBinScreen(
modifier = Modifier,
onTaskClick = { task -> viewModel.onTaskClicked(task) }
)
RecycleBinScreen(modifier = Modifier)
}
}
}

View File

@@ -29,14 +29,12 @@ import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.font.FontStyle
import androidx.compose.ui.unit.dp
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
import com.wismna.geoffroy.donext.domain.model.Task
import com.wismna.geoffroy.donext.presentation.viewmodel.RecycleBinViewModel
@Composable
fun RecycleBinScreen(
modifier: Modifier = Modifier,
viewModel: RecycleBinViewModel = hiltViewModel(),
onTaskClick: (task: Task) -> Unit
) {
val tasks = viewModel.deletedTasks
@@ -77,7 +75,6 @@ fun RecycleBinScreen(
TaskItemScreen(
modifier = Modifier.animateItem(),
task = item.task,
onTaskClick = { onTaskClick(item.task) },
onSwipeLeft = {
viewModel.restore(item.task.id!!)
Toast.makeText(context, "Task restored", Toast.LENGTH_SHORT).show()

View File

@@ -37,6 +37,7 @@ 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.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.TaskItemViewModel
@@ -45,11 +46,11 @@ import com.wismna.geoffroy.donext.presentation.viewmodel.TaskItemViewModel
fun TaskItemScreen(
modifier: Modifier = Modifier,
task: Task,
onTaskClick: (taskId: Long) -> Unit,
viewModel: TaskItemViewModel = hiltViewModel<TaskItemViewModel>(),
onSwipeLeft: () -> Unit,
onSwipeRight: () -> Unit
) {
val viewModel = TaskItemViewModel(task)
viewModel.populateTask(task)
// TODO: change this
val dismissState = rememberSwipeToDismissBoxState(
confirmValueChange = {
@@ -78,7 +79,7 @@ fun TaskItemScreen(
)
Card(
modifier = modifier,
onClick = { onTaskClick(viewModel.id) },
onClick = { viewModel.onTaskClicked(task) },
colors = CardDefaults.cardColors(
containerColor = MaterialTheme.colorScheme.surfaceContainer,
),
@@ -109,7 +110,7 @@ fun TaskItemScreen(
) {
// Title
Text(
text = viewModel.name,
text = viewModel.name!!,
fontSize = 18.sp,
style = baseStyle,
modifier = Modifier
@@ -150,7 +151,7 @@ fun TaskItemScreen(
) {
if (!viewModel.description.isNullOrBlank()) {
Text(
text = viewModel.description,
text = viewModel.description!!,
color = MaterialTheme.colorScheme.tertiary,
style = baseStyle.copy(
fontSize = MaterialTheme.typography.bodyMedium.fontSize,

View File

@@ -15,13 +15,12 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
import com.wismna.geoffroy.donext.domain.model.Task
import com.wismna.geoffroy.donext.presentation.viewmodel.TaskListViewModel
@Composable
fun TaskListScreen(
viewModel: TaskListViewModel = hiltViewModel<TaskListViewModel>(),
onTaskClick: (Task) -> Unit) {
) {
val tasks = viewModel.tasks
// Split tasks into active and done
@@ -43,7 +42,6 @@ fun TaskListScreen(
TaskItemScreen(
modifier = Modifier.animateItem(),
task = task,
onTaskClick = { onTaskClick(task) },
onSwipeLeft = {
viewModel.updateTaskDone(task.id!!, true)
Toast.makeText(context, "Task done", Toast.LENGTH_SHORT).show()
@@ -74,7 +72,6 @@ fun TaskListScreen(
TaskItemScreen(
modifier = Modifier.animateItem(),
task = task,
onTaskClick = { onTaskClick(task) },
onSwipeLeft = {
viewModel.updateTaskDone(task.id!!, false)
Toast.makeText(context, "Task in progress", Toast.LENGTH_SHORT).show()

View File

@@ -0,0 +1,16 @@
package com.wismna.geoffroy.donext.presentation.ui.events
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.asSharedFlow
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class UiEventBus @Inject constructor() {
private val _events = MutableSharedFlow<UiEvent>(replay = 1)
val events = _events.asSharedFlow()
suspend fun send(event: UiEvent) {
_events.emit(event)
}
}

View File

@@ -7,7 +7,6 @@ import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import androidx.navigation.NavBackStackEntry
import com.wismna.geoffroy.donext.domain.model.AppDestination
import com.wismna.geoffroy.donext.domain.model.Task
import com.wismna.geoffroy.donext.domain.usecase.GetTaskListsUseCase
import com.wismna.geoffroy.donext.presentation.ui.events.UiEvent
import com.wismna.geoffroy.donext.presentation.ui.events.UiEventBus
@@ -68,13 +67,6 @@ class MainViewModel @Inject constructor(
}
}
fun onTaskClicked(task: Task) {
showTaskSheet = true
viewModelScope.launch {
uiEventBus.send(UiEvent.EditTask(task))
}
}
fun onDismissTaskSheet() {
showTaskSheet = false
viewModelScope.launch {

View File

@@ -1,31 +1,42 @@
package com.wismna.geoffroy.donext.presentation.viewmodel
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.wismna.geoffroy.donext.domain.extension.toLocalDate
import com.wismna.geoffroy.donext.domain.model.Priority
import com.wismna.geoffroy.donext.domain.model.Task
import com.wismna.geoffroy.donext.presentation.ui.events.UiEvent
import com.wismna.geoffroy.donext.presentation.ui.events.UiEventBus
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.launch
import java.time.LocalDate
import java.time.ZoneId
import java.time.format.DateTimeFormatter
import java.time.format.FormatStyle
import java.time.format.TextStyle
import java.util.Locale
import javax.inject.Inject
class TaskItemViewModel(task: Task) {
val id: Long = task.id!!
val name: String = task.name
val description: String? = task.description
val isDone: Boolean = task.isDone
val isDeleted: Boolean = task.isDeleted
val priority: Priority = task.priority
@HiltViewModel
class TaskItemViewModel @Inject constructor(
private val uiEventBus: UiEventBus
): ViewModel() {
var id: Long? = null
var name: String? = null
var description: String? = null
var dueDate: Long? = null
var isDone: Boolean = false
var isDeleted: Boolean = false
var priority: Priority = Priority.NORMAL
val today: LocalDate = LocalDate.now(ZoneId.systemDefault())
val isOverdue: Boolean = task.dueDate?.let { millis ->
val isOverdue: Boolean = dueDate?.let { millis ->
val dueDate = millis.toLocalDate()
dueDate.isBefore(today)
} ?: false
val dueDateText: String? = task.dueDate?.let { formatDueDate(it) }
val dueDateText: String? = dueDate?.let { formatDueDate(it) }
private fun formatDueDate(dueMillis: Long): String {
val dueDate = dueMillis.toLocalDate()
@@ -40,4 +51,19 @@ class TaskItemViewModel(task: Task) {
dueDate.format(DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM).withLocale(Locale.getDefault()))
}
}
fun populateTask(task: Task) {
id = task.id!!
name = task.name
description = task.description
isDone = task.isDone
isDeleted = task.isDeleted
priority = task.priority
}
fun onTaskClicked(task: Task) {
viewModelScope.launch {
uiEventBus.send(UiEvent.EditTask(task))
}
}
}

View File

@@ -45,8 +45,8 @@ class TaskViewModel @Inject constructor(
viewModelScope.launch {
uiEventBus.events.collect { event ->
when (event) {
is UiEvent.EditTask -> startEditTask(event.task)
is UiEvent.CreateNewTask -> startNewTask(event.taskListId)
is UiEvent.EditTask -> startEditTask(event.task)
is UiEvent.CloseTask -> reset()
else -> {}
}