diff --git a/donextv2/src/main/java/com/wismna/geoffroy/donext/presentation/screen/DueTodayTasksScreen.kt b/donextv2/src/main/java/com/wismna/geoffroy/donext/presentation/screen/DueTodayTasksScreen.kt index ed3cfff..8ff489f 100644 --- a/donextv2/src/main/java/com/wismna/geoffroy/donext/presentation/screen/DueTodayTasksScreen.kt +++ b/donextv2/src/main/java/com/wismna/geoffroy/donext/presentation/screen/DueTodayTasksScreen.kt @@ -1,6 +1,5 @@ package com.wismna.geoffroy.donext.presentation.screen -import android.widget.Toast import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding @@ -10,7 +9,6 @@ import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment 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.presentation.viewmodel.DueTodayViewModel @@ -31,7 +29,6 @@ fun DueTodayTasksScreen( Text("Nothing due today !") } } else { - val context = LocalContext.current LazyColumn( modifier = modifier.padding(8.dp) ) { @@ -39,15 +36,8 @@ fun DueTodayTasksScreen( TaskItemScreen( modifier = Modifier.animateItem(), task = task, - onSwipeLeft = { - viewModel.updateTaskDone(task.id!!) - Toast.makeText(context, "Task done", Toast.LENGTH_SHORT).show() - }, - onSwipeRight = { - viewModel.deleteTask(task.id!!) - Toast.makeText(context, "Task moved to recycle bin", Toast.LENGTH_SHORT) - .show() - } + onSwipeLeft = { viewModel.updateTaskDone(task.id!!) }, + onSwipeRight = { viewModel.deleteTask(task.id!!) } ) } } diff --git a/donextv2/src/main/java/com/wismna/geoffroy/donext/presentation/screen/MainScreen.kt b/donextv2/src/main/java/com/wismna/geoffroy/donext/presentation/screen/MainScreen.kt index a472650..f3da355 100644 --- a/donextv2/src/main/java/com/wismna/geoffroy/donext/presentation/screen/MainScreen.kt +++ b/donextv2/src/main/java/com/wismna/geoffroy/donext/presentation/screen/MainScreen.kt @@ -25,6 +25,10 @@ import androidx.compose.material3.LocalContentColor import androidx.compose.material3.MaterialTheme import androidx.compose.material3.ModalNavigationDrawer import androidx.compose.material3.Scaffold +import androidx.compose.material3.SnackbarDuration +import androidx.compose.material3.SnackbarHost +import androidx.compose.material3.SnackbarHostState +import androidx.compose.material3.SnackbarResult import androidx.compose.material3.Surface import androidx.compose.material3.Text import androidx.compose.material3.TopAppBar @@ -53,7 +57,6 @@ import com.wismna.geoffroy.donext.domain.model.AppDestination import com.wismna.geoffroy.donext.presentation.ui.events.UiEvent import com.wismna.geoffroy.donext.presentation.viewmodel.MainViewModel import com.wismna.geoffroy.donext.presentation.viewmodel.TaskListViewModel -import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.launch @@ -63,7 +66,6 @@ fun MainScreen( ) { val navController = rememberNavController() val drawerState = rememberDrawerState(DrawerValue.Closed) - val scope = rememberCoroutineScope() if (viewModel.isLoading) { Box(Modifier.fillMaxSize(), contentAlignment = Alignment.Center) { @@ -88,7 +90,7 @@ fun MainScreen( }, drawerState = drawerState ) { - AppContent(viewModel = viewModel, navController = navController, scope = scope, drawerState = drawerState) + AppContent(viewModel = viewModel, navController = navController, drawerState = drawerState) } } @@ -97,9 +99,11 @@ fun AppContent( modifier : Modifier = Modifier, viewModel: MainViewModel, navController: NavHostController, - scope: CoroutineScope, drawerState: DrawerState ) { + val scope = rememberCoroutineScope() + val snackbarHostState = remember { SnackbarHostState() } + LaunchedEffect(Unit) { viewModel.uiEventBus.events.collectLatest { event -> when (event) { @@ -109,13 +113,24 @@ fun AppContent( } is UiEvent.NavigateBack -> navController.popBackStack() is UiEvent.EditTask -> { viewModel.showTaskSheet = true } - else -> {} + is UiEvent.ShowUndoSnackbar -> { + val result = snackbarHostState.showSnackbar( + message = event.message, + actionLabel = "Undo", + duration = SnackbarDuration.Short + ) + if (result == SnackbarResult.ActionPerformed) { + event.undoAction() + } + } + else -> Unit } } } Scaffold( modifier = modifier.background(MaterialTheme.colorScheme.primaryContainer), containerColor = Color.Transparent, + snackbarHost = { SnackbarHost(hostState = snackbarHostState) }, topBar = { TopAppBar( title = { Text(viewModel.currentDestination.title) }, diff --git a/donextv2/src/main/java/com/wismna/geoffroy/donext/presentation/screen/RecycleBinScreen.kt b/donextv2/src/main/java/com/wismna/geoffroy/donext/presentation/screen/RecycleBinScreen.kt index f89de08..1a45618 100644 --- a/donextv2/src/main/java/com/wismna/geoffroy/donext/presentation/screen/RecycleBinScreen.kt +++ b/donextv2/src/main/java/com/wismna/geoffroy/donext/presentation/screen/RecycleBinScreen.kt @@ -75,11 +75,9 @@ fun RecycleBinScreen( TaskItemScreen( modifier = Modifier.animateItem(), task = item.task, - onSwipeLeft = { - viewModel.restore(item.task.id!!) - Toast.makeText(context, "Task restored", Toast.LENGTH_SHORT).show() - }, + onSwipeLeft = { viewModel.restore(item.task.id!!) }, onSwipeRight = { + // TODO: add confirmation dialog viewModel.deleteForever(item.task.id!!) Toast.makeText(context, "Task deleted", Toast.LENGTH_SHORT).show() } diff --git a/donextv2/src/main/java/com/wismna/geoffroy/donext/presentation/screen/TaskItemScreen.kt b/donextv2/src/main/java/com/wismna/geoffroy/donext/presentation/screen/TaskItemScreen.kt index 192989a..2000be4 100644 --- a/donextv2/src/main/java/com/wismna/geoffroy/donext/presentation/screen/TaskItemScreen.kt +++ b/donextv2/src/main/java/com/wismna/geoffroy/donext/presentation/screen/TaskItemScreen.kt @@ -123,7 +123,7 @@ fun TaskItemScreen( ) // Due date badge - viewModel.dueDateText?.let { dueMillis -> + viewModel.dueDate?.let { dueMillis -> Badge( modifier = Modifier .align( @@ -134,7 +134,7 @@ fun TaskItemScreen( ) { Text( modifier = Modifier.padding(start = 1.dp, end = 1.dp), - text = viewModel.dueDateText, + text = viewModel.dueDate!!, color = if (viewModel.isOverdue) Color.White else MaterialTheme.colorScheme.onPrimaryContainer, style = MaterialTheme.typography.bodySmall ) diff --git a/donextv2/src/main/java/com/wismna/geoffroy/donext/presentation/screen/TaskListScreen.kt b/donextv2/src/main/java/com/wismna/geoffroy/donext/presentation/screen/TaskListScreen.kt index e0dbbb7..a4aa864 100644 --- a/donextv2/src/main/java/com/wismna/geoffroy/donext/presentation/screen/TaskListScreen.kt +++ b/donextv2/src/main/java/com/wismna/geoffroy/donext/presentation/screen/TaskListScreen.kt @@ -1,7 +1,7 @@ package com.wismna.geoffroy.donext.presentation.screen -import android.widget.Toast import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth @@ -9,26 +9,39 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items import androidx.compose.material3.HorizontalDivider +import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.remember import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel import com.wismna.geoffroy.donext.presentation.viewmodel.TaskListViewModel @Composable fun TaskListScreen( + modifier: Modifier = Modifier, viewModel: TaskListViewModel = hiltViewModel(), ) { val tasks = viewModel.tasks + if (tasks.isEmpty()) { + // Placeholder when recycle bin is empty + Column ( + modifier = modifier.fillMaxSize().padding(10.dp), + Arrangement.Center + ) { + Text("Nothing here!", modifier.fillMaxWidth(), textAlign = TextAlign.Center) + Text("Add tasks with the Create Task button", modifier.fillMaxWidth(), textAlign = TextAlign.Center) + } + return + } + // Split tasks into active and done val (active, done) = remember(tasks) { tasks.partition { !it.isDone } } - val context = LocalContext.current LazyColumn( modifier = Modifier.fillMaxSize(), contentPadding = PaddingValues(8.dp), @@ -42,14 +55,8 @@ fun TaskListScreen( TaskItemScreen( modifier = Modifier.animateItem(), task = task, - onSwipeLeft = { - viewModel.updateTaskDone(task.id!!, true) - Toast.makeText(context, "Task done", Toast.LENGTH_SHORT).show() - }, - onSwipeRight = { - viewModel.deleteTask(task.id!!) - Toast.makeText(context, "Task moved to recycle bin", Toast.LENGTH_SHORT).show() - } + onSwipeLeft = { viewModel.updateTaskDone(task.id!!, true) }, + onSwipeRight = { viewModel.deleteTask(task.id!!) } ) } @@ -72,14 +79,8 @@ fun TaskListScreen( TaskItemScreen( modifier = Modifier.animateItem(), task = task, - onSwipeLeft = { - viewModel.updateTaskDone(task.id!!, false) - Toast.makeText(context, "Task in progress", Toast.LENGTH_SHORT).show() - }, - onSwipeRight = { - viewModel.deleteTask(task.id!!) - Toast.makeText(context, "Task moved to recycle bin", Toast.LENGTH_SHORT).show() - }, + onSwipeLeft = { viewModel.updateTaskDone(task.id!!, false) }, + onSwipeRight = { viewModel.deleteTask(task.id!!) }, ) } diff --git a/donextv2/src/main/java/com/wismna/geoffroy/donext/presentation/ui/events/UiEvent.kt b/donextv2/src/main/java/com/wismna/geoffroy/donext/presentation/ui/events/UiEvent.kt index e28e439..5202ca2 100644 --- a/donextv2/src/main/java/com/wismna/geoffroy/donext/presentation/ui/events/UiEvent.kt +++ b/donextv2/src/main/java/com/wismna/geoffroy/donext/presentation/ui/events/UiEvent.kt @@ -5,9 +5,11 @@ import com.wismna.geoffroy.donext.domain.model.Task sealed class UiEvent { data class Navigate(val route: String) : UiEvent() data object NavigateBack : UiEvent() - data class ShowSnackbar(val message: String) : UiEvent() - data class EditTask(val task: Task) : UiEvent() data class CreateNewTask(val taskListId: Long) : UiEvent() data object CloseTask : UiEvent() + data class ShowUndoSnackbar( + val message: String, + val undoAction: () -> Unit + ) : UiEvent() } \ No newline at end of file diff --git a/donextv2/src/main/java/com/wismna/geoffroy/donext/presentation/viewmodel/DueTodayViewModel.kt b/donextv2/src/main/java/com/wismna/geoffroy/donext/presentation/viewmodel/DueTodayViewModel.kt index b4bbb1d..163ca37 100644 --- a/donextv2/src/main/java/com/wismna/geoffroy/donext/presentation/viewmodel/DueTodayViewModel.kt +++ b/donextv2/src/main/java/com/wismna/geoffroy/donext/presentation/viewmodel/DueTodayViewModel.kt @@ -9,6 +9,8 @@ import com.wismna.geoffroy.donext.domain.model.Task import com.wismna.geoffroy.donext.domain.usecase.GetDueTodayTasksUseCase import com.wismna.geoffroy.donext.domain.usecase.ToggleTaskDeletedUseCase import com.wismna.geoffroy.donext.domain.usecase.ToggleTaskDoneUseCase +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.flow.launchIn import kotlinx.coroutines.flow.onEach @@ -17,16 +19,17 @@ import javax.inject.Inject @HiltViewModel class DueTodayViewModel @Inject constructor( - getDueTodayTasks: GetDueTodayTasksUseCase, - private val toggleTaskDeleted: ToggleTaskDeletedUseCase, - private val toggleTaskDone: ToggleTaskDoneUseCase + getDueTodayTasksUseCase: GetDueTodayTasksUseCase, + private val toggleTaskDeletedUseCase: ToggleTaskDeletedUseCase, + private val toggleTaskDoneUseCase: ToggleTaskDoneUseCase, + private val uiEventBus: UiEventBus ) : ViewModel() { var dueTodayTasks by mutableStateOf>(emptyList()) private set init { - getDueTodayTasks() + getDueTodayTasksUseCase() .onEach { tasks -> dueTodayTasks = tasks } @@ -35,12 +38,35 @@ class DueTodayViewModel @Inject constructor( fun updateTaskDone(taskId: Long) { viewModelScope.launch { - toggleTaskDone(taskId, true) + toggleTaskDoneUseCase(taskId, true) + + uiEventBus.send( + UiEvent.ShowUndoSnackbar( + message = "Task done", + undoAction = { + viewModelScope.launch { + toggleTaskDoneUseCase(taskId, false) + } + } + ) + ) } } + fun deleteTask(taskId: Long) { viewModelScope.launch { - toggleTaskDeleted(taskId, true) + toggleTaskDeletedUseCase(taskId, true) + + uiEventBus.send( + UiEvent.ShowUndoSnackbar( + message = "Task moved to trash", + undoAction = { + viewModelScope.launch { + toggleTaskDeletedUseCase(taskId, false) + } + } + ) + ) } } } \ No newline at end of file diff --git a/donextv2/src/main/java/com/wismna/geoffroy/donext/presentation/viewmodel/MainViewModel.kt b/donextv2/src/main/java/com/wismna/geoffroy/donext/presentation/viewmodel/MainViewModel.kt index a6a5470..d2a5f6a 100644 --- a/donextv2/src/main/java/com/wismna/geoffroy/donext/presentation/viewmodel/MainViewModel.kt +++ b/donextv2/src/main/java/com/wismna/geoffroy/donext/presentation/viewmodel/MainViewModel.kt @@ -46,10 +46,10 @@ class MainViewModel @Inject constructor( AppDestination.ManageLists + AppDestination.RecycleBin + AppDestination.DueTodayList - isLoading = false if (startDestination == AppDestination.ManageLists && destinations.isNotEmpty()) { startDestination = destinations.first() } + isLoading = false } .launchIn(viewModelScope) } diff --git a/donextv2/src/main/java/com/wismna/geoffroy/donext/presentation/viewmodel/MenuViewModel.kt b/donextv2/src/main/java/com/wismna/geoffroy/donext/presentation/viewmodel/MenuViewModel.kt index 90334fb..63b50bb 100644 --- a/donextv2/src/main/java/com/wismna/geoffroy/donext/presentation/viewmodel/MenuViewModel.kt +++ b/donextv2/src/main/java/com/wismna/geoffroy/donext/presentation/viewmodel/MenuViewModel.kt @@ -6,7 +6,6 @@ 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.AppDestination import com.wismna.geoffroy.donext.domain.model.TaskListWithOverdue import com.wismna.geoffroy.donext.domain.usecase.GetDueTodayTasksUseCase import com.wismna.geoffroy.donext.domain.usecase.GetTaskListsWithOverdueUseCase diff --git a/donextv2/src/main/java/com/wismna/geoffroy/donext/presentation/viewmodel/RecycleBinViewModel.kt b/donextv2/src/main/java/com/wismna/geoffroy/donext/presentation/viewmodel/RecycleBinViewModel.kt index 0e7defd..6a71ab4 100644 --- a/donextv2/src/main/java/com/wismna/geoffroy/donext/presentation/viewmodel/RecycleBinViewModel.kt +++ b/donextv2/src/main/java/com/wismna/geoffroy/donext/presentation/viewmodel/RecycleBinViewModel.kt @@ -10,6 +10,8 @@ import com.wismna.geoffroy.donext.domain.usecase.EmptyRecycleBinUseCase import com.wismna.geoffroy.donext.domain.usecase.GetDeletedTasksUseCase import com.wismna.geoffroy.donext.domain.usecase.PermanentlyDeleteTaskUseCase import com.wismna.geoffroy.donext.domain.usecase.ToggleTaskDeletedUseCase +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.flow.launchIn import kotlinx.coroutines.flow.onEach @@ -18,10 +20,11 @@ import javax.inject.Inject @HiltViewModel class RecycleBinViewModel @Inject constructor( - private val getDeletedTasks: GetDeletedTasksUseCase, - private val restoreTask: ToggleTaskDeletedUseCase, - private val permanentlyDeleteTask: PermanentlyDeleteTaskUseCase, - private val emptyRecycleBinUseCase: EmptyRecycleBinUseCase + private val getDeletedTasksUseCase: GetDeletedTasksUseCase, + private val toggleTaskDeletedUseCase: ToggleTaskDeletedUseCase, + private val permanentlyDeleteTaskUseCase: PermanentlyDeleteTaskUseCase, + private val emptyRecycleBinUseCase: EmptyRecycleBinUseCase, + private val uiEventBus: UiEventBus ) : ViewModel() { var deletedTasks by mutableStateOf>(emptyList()) @@ -32,7 +35,7 @@ class RecycleBinViewModel @Inject constructor( } fun loadDeletedTasks() { - getDeletedTasks() + getDeletedTasksUseCase() .onEach { tasks -> deletedTasks = tasks } @@ -41,14 +44,25 @@ class RecycleBinViewModel @Inject constructor( fun restore(taskId: Long) { viewModelScope.launch { - restoreTask(taskId, false) + toggleTaskDeletedUseCase(taskId, false) loadDeletedTasks() + + uiEventBus.send( + UiEvent.ShowUndoSnackbar( + message = "Task restored", + undoAction = { + viewModelScope.launch { + toggleTaskDeletedUseCase(taskId, true) + } + } + ) + ) } } fun deleteForever(taskId: Long) { viewModelScope.launch { - permanentlyDeleteTask(taskId) + permanentlyDeleteTaskUseCase(taskId) loadDeletedTasks() } } diff --git a/donextv2/src/main/java/com/wismna/geoffroy/donext/presentation/viewmodel/TaskItemViewModel.kt b/donextv2/src/main/java/com/wismna/geoffroy/donext/presentation/viewmodel/TaskItemViewModel.kt index 8aa1cc6..5be6666 100644 --- a/donextv2/src/main/java/com/wismna/geoffroy/donext/presentation/viewmodel/TaskItemViewModel.kt +++ b/donextv2/src/main/java/com/wismna/geoffroy/donext/presentation/viewmodel/TaskItemViewModel.kt @@ -24,41 +24,25 @@ class TaskItemViewModel @Inject constructor( var id: Long? = null var name: String? = null var description: String? = null - var dueDate: Long? = null + var dueDate: String? = null var isDone: Boolean = false var isDeleted: Boolean = false var priority: Priority = Priority.NORMAL - + var isOverdue: Boolean = false val today: LocalDate = LocalDate.now(ZoneId.systemDefault()) - val isOverdue: Boolean = dueDate?.let { millis -> - val dueDate = millis.toLocalDate() - dueDate.isBefore(today) - } ?: false - - val dueDateText: String? = dueDate?.let { formatDueDate(it) } - - private fun formatDueDate(dueMillis: Long): String { - val dueDate = dueMillis.toLocalDate() - - return when { - dueDate.isEqual(today) -> "Today" - dueDate.isEqual(today.plusDays(1)) -> "Tomorrow" - dueDate.isEqual(today.minusDays(1)) -> "Yesterday" - dueDate.isAfter(today) && dueDate.isBefore(today.plusDays(7)) -> - dueDate.dayOfWeek.getDisplayName(TextStyle.SHORT, Locale.getDefault()) - else -> - dueDate.format(DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM).withLocale(Locale.getDefault())) - } - } - fun populateTask(task: Task) { id = task.id!! name = task.name description = task.description + dueDate = task.dueDate?.let { formatDueDate(it) } isDone = task.isDone isDeleted = task.isDeleted priority = task.priority + isOverdue = task.dueDate?.let { millis -> + val dueDate = millis.toLocalDate() + dueDate.isBefore(today) + } ?: false } fun onTaskClicked(task: Task) { @@ -66,4 +50,19 @@ class TaskItemViewModel @Inject constructor( uiEventBus.send(UiEvent.EditTask(task)) } } + + private fun formatDueDate(dueMillis: Long): String { + val dueDateLocal = dueMillis.toLocalDate() + + return when { + dueDateLocal.isEqual(today) -> "Today" + dueDateLocal.isEqual(today.plusDays(1)) -> "Tomorrow" + dueDateLocal.isEqual(today.minusDays(1)) -> "Yesterday" + dueDateLocal.isAfter(today) && dueDateLocal.isBefore(today.plusDays(7)) -> + dueDateLocal.dayOfWeek.getDisplayName(TextStyle.SHORT, Locale.getDefault()) + else -> + dueDateLocal.format(DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM).withLocale(Locale.getDefault())) + } + } + } \ No newline at end of file diff --git a/donextv2/src/main/java/com/wismna/geoffroy/donext/presentation/viewmodel/TaskListViewModel.kt b/donextv2/src/main/java/com/wismna/geoffroy/donext/presentation/viewmodel/TaskListViewModel.kt index 7200185..1c8b1f1 100644 --- a/donextv2/src/main/java/com/wismna/geoffroy/donext/presentation/viewmodel/TaskListViewModel.kt +++ b/donextv2/src/main/java/com/wismna/geoffroy/donext/presentation/viewmodel/TaskListViewModel.kt @@ -10,6 +10,8 @@ import com.wismna.geoffroy.donext.domain.model.Task import com.wismna.geoffroy.donext.domain.usecase.GetTasksForListUseCase import com.wismna.geoffroy.donext.domain.usecase.ToggleTaskDeletedUseCase import com.wismna.geoffroy.donext.domain.usecase.ToggleTaskDoneUseCase +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.flow.launchIn import kotlinx.coroutines.flow.onEach @@ -18,10 +20,11 @@ import javax.inject.Inject @HiltViewModel class TaskListViewModel @Inject constructor( - getTasks: GetTasksForListUseCase, savedStateHandle: SavedStateHandle, - private val toggleTaskDone: ToggleTaskDoneUseCase, - private val toggleTaskDeleted: ToggleTaskDeletedUseCase, + getTasksUseCase: GetTasksForListUseCase, + private val toggleTaskDoneUseCase: ToggleTaskDoneUseCase, + private val toggleTaskDeletedUseCase: ToggleTaskDeletedUseCase, + private val uiEventBus: UiEventBus ) : ViewModel() { var tasks by mutableStateOf>(emptyList()) @@ -32,7 +35,7 @@ class TaskListViewModel @Inject constructor( private val taskListId: Long = checkNotNull(savedStateHandle["taskListId"]) init { - getTasks(taskListId) + getTasksUseCase(taskListId) .onEach { list -> tasks = list isLoading = false @@ -42,12 +45,34 @@ class TaskListViewModel @Inject constructor( fun updateTaskDone(taskId: Long, isDone: Boolean) { viewModelScope.launch { - toggleTaskDone(taskId, isDone) + toggleTaskDoneUseCase(taskId, isDone) + + uiEventBus.send( + UiEvent.ShowUndoSnackbar( + message = "Task done", + undoAction = { + viewModelScope.launch { + toggleTaskDoneUseCase(taskId, !isDone) + } + } + ) + ) } } fun deleteTask(taskId: Long) { viewModelScope.launch { - toggleTaskDeleted(taskId, true) + toggleTaskDeletedUseCase(taskId, true) + + uiEventBus.send( + UiEvent.ShowUndoSnackbar( + message = "Task moved to trash", + undoAction = { + viewModelScope.launch { + toggleTaskDeletedUseCase(taskId, false) + } + } + ) + ) } } } \ No newline at end of file