Added undo snackbar for list deletion

Made all destinations show the menu instead of the back button (for now)
This commit is contained in:
Geoffroy Bonneville
2025-10-10 16:04:18 -04:00
parent c579a5d252
commit 6c9e5efe38
7 changed files with 43 additions and 16 deletions

View File

@@ -13,16 +13,16 @@ sealed class AppDestination(
object DueTodayList : AppDestination( object DueTodayList : AppDestination(
route = "todayList", route = "todayList",
title = "Due Today", title = "Due Today",
showBackButton = true, showBackButton = false,
) )
object ManageLists : AppDestination( object ManageLists : AppDestination(
route = "manageLists", route = "manageLists",
title = "Manage Lists", title = "Manage Lists",
showBackButton = true, showBackButton = false,
) )
object RecycleBin : AppDestination( object RecycleBin : AppDestination(
route = "recycleBin", route = "recycleBin",
title = "Recycle Bin", title = "Recycle Bin",
showBackButton = true, showBackButton = false,
) )
} }

View File

@@ -6,7 +6,7 @@ import javax.inject.Inject
class DeleteTaskListUseCase@Inject constructor( class DeleteTaskListUseCase@Inject constructor(
private val repository: TaskRepository private val repository: TaskRepository
) { ) {
suspend operator fun invoke(taskListId: Long) { suspend operator fun invoke(taskListId: Long, isDeleted: Boolean) {
repository.deleteTaskList(taskListId, true) repository.deleteTaskList(taskListId, isDeleted)
} }
} }

View File

@@ -6,10 +6,12 @@ import androidx.compose.animation.fadeOut
import androidx.compose.animation.togetherWith import androidx.compose.animation.togetherWith
import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer 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.height
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
@@ -62,6 +64,18 @@ fun ManageListsScreen(
showAddListSheet: () -> Unit showAddListSheet: () -> Unit
) { ) {
var lists = viewModel.taskLists.toMutableList() var lists = viewModel.taskLists.toMutableList()
if (lists.isEmpty()) {
// Placeholder when no task lists exist
Box(
modifier = modifier.fillMaxSize(),
contentAlignment = Alignment.Center
) {
Text("Tap + to create a new task list.")
}
return
}
val lazyListState = rememberLazyListState() val lazyListState = rememberLazyListState()
val reorderState = rememberReorderableLazyListState( val reorderState = rememberReorderableLazyListState(
lazyListState = lazyListState, lazyListState = lazyListState,

View File

@@ -1,7 +1,7 @@
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.Arrangement
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
@@ -12,8 +12,8 @@ import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
import com.wismna.geoffroy.donext.presentation.viewmodel.TaskListViewModel import com.wismna.geoffroy.donext.presentation.viewmodel.TaskListViewModel
@@ -27,12 +27,11 @@ fun TaskListScreen(
if (tasks.isEmpty()) { if (tasks.isEmpty()) {
// Placeholder when recycle bin is empty // Placeholder when recycle bin is empty
Column ( Box(
modifier = modifier.fillMaxSize().padding(10.dp), modifier = modifier.fillMaxSize(),
Arrangement.Center contentAlignment = Alignment.Center
) { ) {
Text("Nothing here!", modifier.fillMaxWidth(), textAlign = TextAlign.Center) Text("Tap + to create a new task.")
Text("Add tasks with the Create Task button", modifier.fillMaxWidth(), textAlign = TextAlign.Center)
} }
return return
} }

View File

@@ -59,7 +59,7 @@ class DueTodayViewModel @Inject constructor(
uiEventBus.send( uiEventBus.send(
UiEvent.ShowUndoSnackbar( UiEvent.ShowUndoSnackbar(
message = "Task moved to trash", message = "Task moved to recycle bin",
undoAction = { undoAction = {
viewModelScope.launch { viewModelScope.launch {
toggleTaskDeletedUseCase(taskId, false) toggleTaskDeletedUseCase(taskId, false)

View File

@@ -11,6 +11,8 @@ import com.wismna.geoffroy.donext.domain.usecase.AddTaskListUseCase
import com.wismna.geoffroy.donext.domain.usecase.DeleteTaskListUseCase import com.wismna.geoffroy.donext.domain.usecase.DeleteTaskListUseCase
import com.wismna.geoffroy.donext.domain.usecase.GetTaskListsUseCase import com.wismna.geoffroy.donext.domain.usecase.GetTaskListsUseCase
import com.wismna.geoffroy.donext.domain.usecase.UpdateTaskListUseCase import com.wismna.geoffroy.donext.domain.usecase.UpdateTaskListUseCase
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 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
@@ -22,7 +24,8 @@ class ManageListsViewModel @Inject constructor(
getTaskListsUseCase: GetTaskListsUseCase, getTaskListsUseCase: GetTaskListsUseCase,
private val addTaskListUseCase: AddTaskListUseCase, private val addTaskListUseCase: AddTaskListUseCase,
private val updateTaskListUseCase: UpdateTaskListUseCase, private val updateTaskListUseCase: UpdateTaskListUseCase,
private val deleteTaskListUseCase: DeleteTaskListUseCase private val deleteTaskListUseCase: DeleteTaskListUseCase,
private val uiEventBus: UiEventBus
) : ViewModel() { ) : ViewModel() {
var taskLists by mutableStateOf<List<TaskList>>(emptyList()) var taskLists by mutableStateOf<List<TaskList>>(emptyList())
@@ -51,7 +54,18 @@ class ManageListsViewModel @Inject constructor(
} }
fun deleteTaskList(taskListId: Long) { fun deleteTaskList(taskListId: Long) {
viewModelScope.launch { viewModelScope.launch {
deleteTaskListUseCase(taskListId) deleteTaskListUseCase(taskListId, true)
uiEventBus.send(
UiEvent.ShowUndoSnackbar(
message = "Task list moved to recycle bin",
undoAction = {
viewModelScope.launch {
deleteTaskListUseCase(taskListId, false)
}
}
)
)
} }
} }

View File

@@ -65,7 +65,7 @@ class TaskListViewModel @Inject constructor(
uiEventBus.send( uiEventBus.send(
UiEvent.ShowUndoSnackbar( UiEvent.ShowUndoSnackbar(
message = "Task moved to trash", message = "Task moved to recycle bin",
undoAction = { undoAction = {
viewModelScope.launch { viewModelScope.launch {
toggleTaskDeletedUseCase(taskId, false) toggleTaskDeletedUseCase(taskId, false)