From 2be67abffa02b7b547f9c282637d52c9e44d83a2 Mon Sep 17 00:00:00 2001 From: Geoffroy Bonneville <24917789+wismna@users.noreply.github.com> Date: Thu, 18 Sep 2025 15:40:19 -0400 Subject: [PATCH] Fix navigation (once and for all ?) Implement inline edit lists feature Improve task list bottom sheet design --- donextv2/build.gradle.kts | 2 +- .../local/repository/TaskRepositoryImpl.kt | 4 + .../domain/repository/TaskRepository.kt | 1 + .../domain/usecase/UpdateTaskListUseCase.kt | 20 ++ .../donext/presentation/screen/MainScreen.kt | 219 +++++++++--------- .../presentation/screen/ManageListsScreen.kt | 95 ++++---- .../presentation/viewmodel/MainViewModel.kt | 23 +- .../viewmodel/ManageListsViewModel.kt | 7 + 8 files changed, 216 insertions(+), 155 deletions(-) create mode 100644 donextv2/src/main/java/com/wismna/geoffroy/donext/domain/usecase/UpdateTaskListUseCase.kt diff --git a/donextv2/build.gradle.kts b/donextv2/build.gradle.kts index c801980..a94586b 100644 --- a/donextv2/build.gradle.kts +++ b/donextv2/build.gradle.kts @@ -46,7 +46,7 @@ android { } dependencies { - implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.9.3") + implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.9.4") implementation("androidx.activity:activity-compose:1.11.0") implementation(platform("androidx.compose:compose-bom:2025.09.00")) implementation("androidx.compose.ui:ui") diff --git a/donextv2/src/main/java/com/wismna/geoffroy/donext/data/local/repository/TaskRepositoryImpl.kt b/donextv2/src/main/java/com/wismna/geoffroy/donext/data/local/repository/TaskRepositoryImpl.kt index 523ea52..dfa860b 100644 --- a/donextv2/src/main/java/com/wismna/geoffroy/donext/data/local/repository/TaskRepositoryImpl.kt +++ b/donextv2/src/main/java/com/wismna/geoffroy/donext/data/local/repository/TaskRepositoryImpl.kt @@ -44,6 +44,10 @@ class TaskRepositoryImpl @Inject constructor( taskListDao.insertTaskList(taskList.toEntity()) } + override suspend fun updateTaskList(taskList: TaskList) { + taskListDao.updateTaskList(taskList.toEntity()) + } + override suspend fun deleteTaskList(taskListId: Long, isDeleted: Boolean) { taskDao.deleteAllTasksFromList(taskListId, isDeleted) taskListDao.deleteTaskList(taskListId, isDeleted) diff --git a/donextv2/src/main/java/com/wismna/geoffroy/donext/domain/repository/TaskRepository.kt b/donextv2/src/main/java/com/wismna/geoffroy/donext/domain/repository/TaskRepository.kt index ea0d982..dbd33f3 100644 --- a/donextv2/src/main/java/com/wismna/geoffroy/donext/domain/repository/TaskRepository.kt +++ b/donextv2/src/main/java/com/wismna/geoffroy/donext/domain/repository/TaskRepository.kt @@ -14,6 +14,7 @@ interface TaskRepository { fun getTaskLists(): Flow> suspend fun insertTaskList(taskList: TaskList) + suspend fun updateTaskList(taskList: TaskList) suspend fun deleteTaskList(taskListId: Long, isDeleted: Boolean) fun getTaskListsWithOverdue(nowMillis: Long): Flow> } \ No newline at end of file diff --git a/donextv2/src/main/java/com/wismna/geoffroy/donext/domain/usecase/UpdateTaskListUseCase.kt b/donextv2/src/main/java/com/wismna/geoffroy/donext/domain/usecase/UpdateTaskListUseCase.kt new file mode 100644 index 0000000..422f8b8 --- /dev/null +++ b/donextv2/src/main/java/com/wismna/geoffroy/donext/domain/usecase/UpdateTaskListUseCase.kt @@ -0,0 +1,20 @@ +package com.wismna.geoffroy.donext.domain.usecase + +import com.wismna.geoffroy.donext.domain.model.TaskList +import com.wismna.geoffroy.donext.domain.repository.TaskRepository +import javax.inject.Inject + +class UpdateTaskListUseCase @Inject constructor( + private val repository: TaskRepository +) { + suspend operator fun invoke(taskListId: Long, title: String, order: Int) { + repository.updateTaskList( + TaskList( + id = taskListId, + name = title, + order = order, + isDeleted = false + ) + ) + } +} \ No newline at end of file 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 31739e8..77ebfe1 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 @@ -15,6 +15,7 @@ import androidx.compose.material.icons.filled.Add import androidx.compose.material.icons.filled.ArrowBack import androidx.compose.material.icons.filled.Menu import androidx.compose.material3.CircularProgressIndicator +import androidx.compose.material3.DrawerState import androidx.compose.material3.DrawerValue import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon @@ -31,15 +32,13 @@ import androidx.compose.material3.rememberDrawerState import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope -import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.dp import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel +import androidx.navigation.NavHostController import androidx.navigation.NavType import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable @@ -50,6 +49,7 @@ import com.wismna.geoffroy.donext.domain.model.AppDestination import com.wismna.geoffroy.donext.presentation.viewmodel.MainViewModel import com.wismna.geoffroy.donext.presentation.viewmodel.TaskListViewModel import com.wismna.geoffroy.donext.presentation.viewmodel.TaskViewModel +import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch @Composable @@ -58,8 +58,6 @@ fun MainScreen( viewModel: MainViewModel = hiltViewModel() ) { val navController = rememberNavController() - var showTaskSheet by remember { mutableStateOf(false) } - var showAddListSheet by remember { mutableStateOf(false) } val drawerState = rememberDrawerState(DrawerValue.Closed) val scope = rememberCoroutineScope() val taskViewModel: TaskViewModel = hiltViewModel() @@ -71,23 +69,20 @@ fun MainScreen( return } - val startDestination = viewModel.destinations.firstOrNull() ?: AppDestination.ManageLists - if (showTaskSheet) { - TaskBottomSheet(taskViewModel) { showTaskSheet = false } + if (viewModel.showTaskSheet) { + TaskBottomSheet(taskViewModel) { viewModel.showTaskSheet = false } } - if (showAddListSheet) { - AddListBottomSheet { showAddListSheet = false } + if (viewModel.showAddListSheet) { + AddListBottomSheet { viewModel.showAddListSheet = false } } val navBackStackEntry by navController.currentBackStackEntryAsState() - val currentDestination = - viewModel.deriveDestination(navBackStackEntry?.destination?.route) - ?: startDestination + viewModel.setCurrentDestination(navBackStackEntry) ModalNavigationDrawer( drawerContent = { MenuScreen ( - currentDestination = currentDestination, + currentDestination = viewModel.currentDestination, onNavigate = { route -> scope.launch { drawerState.close() } navController.navigate(route) { @@ -98,101 +93,113 @@ fun MainScreen( }, drawerState = drawerState ) { - Scaffold( - modifier = modifier.background(MaterialTheme.colorScheme.primaryContainer), - containerColor = Color.Transparent, - topBar = { - TopAppBar( - title = { Text(currentDestination!!.title) }, - colors = TopAppBarDefaults.topAppBarColors( - containerColor = MaterialTheme.colorScheme.primaryContainer, - titleContentColor = MaterialTheme.colorScheme.onPrimaryContainer, - actionIconContentColor = MaterialTheme.colorScheme.onPrimaryContainer - ), - navigationIcon = { - CompositionLocalProvider(LocalContentColor provides MaterialTheme.colorScheme.onPrimaryContainer) { - if (currentDestination!!.showBackButton) { - IconButton(onClick = { navController.popBackStack() }) { - Icon(Icons.Default.ArrowBack, contentDescription = "Back") - } - } else { - IconButton(onClick = { scope.launch { drawerState.open() } }) { - Icon( - Icons.Default.Menu, - contentDescription = "Open navigation drawer" - ) - } - } - } - }, - actions = { - if (currentDestination is AppDestination.ManageLists) { - IconButton(onClick = { showAddListSheet = true }) { - Icon(Icons.Default.Add, contentDescription = "Add List") - } - } - } - ) - }, - floatingActionButton = { - when (val dest = currentDestination) { - is AppDestination.TaskList -> { - TaskListFab( - taskListId = dest.taskListId, - showBottomSheet = { showTaskSheet = it } - ) - } - else -> null - } - } - ) { contentPadding -> - Surface( - modifier = Modifier - .padding(top = contentPadding.calculateTopPadding()) - .fillMaxSize(), - shape = RoundedCornerShape(topStart = 16.dp, topEnd = 16.dp), - color = MaterialTheme.colorScheme.surfaceContainer, - ) { - NavHost( - navController = navController, - startDestination = startDestination.route, - enterTransition = { - slideInHorizontally(initialOffsetX = { fullWidth -> fullWidth }, animationSpec = tween(300)) - }, - exitTransition = { - slideOutHorizontally(targetOffsetX = { fullWidth -> -fullWidth }, animationSpec = tween(300)) - }, - popEnterTransition = { - slideInHorizontally(initialOffsetX = { fullWidth -> -fullWidth }, animationSpec = tween(300)) - }, - popExitTransition = { - slideOutHorizontally(targetOffsetX = { fullWidth -> fullWidth }, animationSpec = tween(300)) - } - ) { - viewModel.destinations.forEach { destination -> - composable( - route = "taskList/{taskListId}", - arguments = listOf(navArgument("taskListId") { - type = NavType.LongType - }) - ) { navBackStackEntry -> - val viewModel: TaskListViewModel = hiltViewModel(navBackStackEntry) - TaskListScreen( - viewModel = viewModel, - onTaskClick = { task -> - taskViewModel.startEditTask(task) - showTaskSheet = true - } - ) - } - } + AppContent(viewModel = viewModel, taskViewModel = taskViewModel, navController = navController, scope = scope, drawerState = drawerState) + } +} - composable(AppDestination.ManageLists.route) { - ManageListsScreen( - modifier = Modifier, - showAddListSheet = {showAddListSheet = true} +@Composable +fun AppContent( + modifier : Modifier = Modifier, + viewModel: MainViewModel, + taskViewModel: TaskViewModel, + navController: NavHostController, + scope: CoroutineScope, + drawerState: DrawerState +) { + Scaffold( + modifier = modifier.background(MaterialTheme.colorScheme.primaryContainer), + containerColor = Color.Transparent, + topBar = { + TopAppBar( + title = { Text(viewModel.currentDestination.title) }, + colors = TopAppBarDefaults.topAppBarColors( + containerColor = MaterialTheme.colorScheme.primaryContainer, + titleContentColor = MaterialTheme.colorScheme.onPrimaryContainer, + actionIconContentColor = MaterialTheme.colorScheme.onPrimaryContainer + ), + navigationIcon = { + CompositionLocalProvider(LocalContentColor provides MaterialTheme.colorScheme.onPrimaryContainer) { + if (viewModel.currentDestination.showBackButton) { + IconButton(onClick = { navController.popBackStack() }) { + Icon(Icons.Default.ArrowBack, contentDescription = "Back") + } + } else { + IconButton(onClick = { scope.launch { drawerState.open() } }) { + Icon( + Icons.Default.Menu, + contentDescription = "Open navigation drawer" + ) + } + } + } + }, + actions = { + if (viewModel.currentDestination is AppDestination.ManageLists) { + IconButton(onClick = { viewModel.showAddListSheet = true }) { + Icon(Icons.Default.Add, contentDescription = "Add List") + } + } + } + ) + }, + floatingActionButton = { + when (val dest = viewModel.currentDestination) { + is AppDestination.TaskList -> { + TaskListFab( + taskListId = dest.taskListId, + showBottomSheet = { viewModel.showTaskSheet = it } + ) + } + else -> null + } + } + ) { contentPadding -> + Surface( + modifier = Modifier + .padding(top = contentPadding.calculateTopPadding()) + .fillMaxSize(), + shape = RoundedCornerShape(topStart = 16.dp, topEnd = 16.dp), + color = MaterialTheme.colorScheme.surfaceContainer, + ) { + NavHost( + navController = navController, + startDestination = viewModel.startDestination.route, + enterTransition = { + slideInHorizontally(initialOffsetX = { fullWidth -> fullWidth }, animationSpec = tween(300)) + }, + exitTransition = { + slideOutHorizontally(targetOffsetX = { fullWidth -> -fullWidth }, animationSpec = tween(300)) + }, + popEnterTransition = { + slideInHorizontally(initialOffsetX = { fullWidth -> -fullWidth }, animationSpec = tween(300)) + }, + popExitTransition = { + slideOutHorizontally(targetOffsetX = { fullWidth -> fullWidth }, animationSpec = tween(300)) + } + ) { + //viewModel.destinations.forEach { destination -> + composable( + route = "taskList/{taskListId}", + arguments = listOf(navArgument("taskListId") { + type = NavType.LongType + }) + ) { navBackStackEntry -> + val taskListViewModel: TaskListViewModel = hiltViewModel(navBackStackEntry) + TaskListScreen( + viewModel = taskListViewModel, + onTaskClick = { task -> + taskViewModel.startEditTask(task) + viewModel.showTaskSheet = true + } ) } + //} + + composable(AppDestination.ManageLists.route) { + ManageListsScreen( + modifier = Modifier, + showAddListSheet = {viewModel.showAddListSheet = true} + ) } } } diff --git a/donextv2/src/main/java/com/wismna/geoffroy/donext/presentation/screen/ManageListsScreen.kt b/donextv2/src/main/java/com/wismna/geoffroy/donext/presentation/screen/ManageListsScreen.kt index d729b84..c27e9ec 100644 --- a/donextv2/src/main/java/com/wismna/geoffroy/donext/presentation/screen/ManageListsScreen.kt +++ b/donextv2/src/main/java/com/wismna/geoffroy/donext/presentation/screen/ManageListsScreen.kt @@ -7,11 +7,11 @@ import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.width import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Check +import androidx.compose.material.icons.filled.Close import androidx.compose.material.icons.filled.Delete import androidx.compose.material.icons.filled.Edit import androidx.compose.material3.Button @@ -23,9 +23,8 @@ import androidx.compose.material3.ListItem import androidx.compose.material3.LocalContentColor import androidx.compose.material3.MaterialTheme import androidx.compose.material3.ModalBottomSheet +import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.Text -import androidx.compose.material3.TextButton -import androidx.compose.material3.TextField import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.LaunchedEffect @@ -33,12 +32,11 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember 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.unit.dp import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel -import com.wismna.geoffroy.donext.domain.model.TaskList import com.wismna.geoffroy.donext.presentation.viewmodel.ManageListsViewModel @OptIn(ExperimentalMaterial3Api::class) @@ -52,17 +50,46 @@ fun ManageListsScreen( LazyColumn(modifier = modifier.fillMaxWidth().padding()) { itemsIndexed(lists, key = { _, list -> list.id!! }) { index, list -> + + var isInEditMode by remember { mutableStateOf(false) } + var editedName by remember { mutableStateOf(list.name) } ListItem( modifier = Modifier.animateItem(), - headlineContent = { Text(list.name) }, + headlineContent = { + if (isInEditMode) { + OutlinedTextField( + value = editedName, + onValueChange = { editedName = it }, + singleLine = true + ) + } else { + Text(list.name) + } + }, trailingContent = { - Row { - CompositionLocalProvider(LocalContentColor provides MaterialTheme.colorScheme.onPrimaryContainer) { - IconButton(onClick = { /* TODO: edit list */ }) { - Icon(Icons.Default.Edit, contentDescription = "Edit") + if (isInEditMode) { + Row { + CompositionLocalProvider(LocalContentColor provides MaterialTheme.colorScheme.onPrimaryContainer) { + IconButton(onClick = { isInEditMode = false }) { + Icon(Icons.Default.Close, contentDescription = "Cancel") + } + IconButton(onClick = { + viewModel.updateTaskListName(list.copy(name = editedName)) + isInEditMode = false + }) { + Icon(Icons.Default.Check, contentDescription = "Save") + } } - IconButton(onClick = { viewModel.deleteTaskList(list.id!!) }) { - Icon(Icons.Default.Delete, contentDescription = "Delete") + } + } else { + Row { + CompositionLocalProvider(LocalContentColor provides MaterialTheme.colorScheme.onPrimaryContainer) { + IconButton(onClick = { isInEditMode = true }) { + Icon(Icons.Default.Edit, contentDescription = "Edit") + } + IconButton(onClick = { viewModel.deleteTaskList(list.id!!) }) { + Icon(Icons.Default.Delete, contentDescription = "Delete") + } } } } @@ -73,34 +100,6 @@ fun ManageListsScreen( } } -@Composable -fun EditableListRow( - list: TaskList, - onNameChange: (String) -> Unit, - //onTypeChange: (ListType) -> Unit, - onDone: () -> Unit -) { - var name by remember { mutableStateOf(list.name) } - //var type by remember { mutableStateOf(list.type) } - - Row( - verticalAlignment = Alignment.CenterVertically, - modifier = Modifier.fillMaxWidth().padding(8.dp) - ) { - TextField( - value = name, - onValueChange = { name = it }, - modifier = Modifier.weight(1f), - singleLine = true - ) - // TODO: implement type - //DropdownSelector(selected = type, onSelect = { type = it; onTypeChange(it) }) - IconButton(onClick = onDone) { - Icon(Icons.Default.Check, contentDescription = "Save") - } - } -} - @OptIn(ExperimentalMaterial3Api::class) @Composable fun AddListBottomSheet( @@ -122,11 +121,21 @@ fun AddListBottomSheet( Text("Create New List", style = MaterialTheme.typography.titleMedium) Spacer(Modifier.height(8.dp)) - TextField( + /*TextField( value = name, onValueChange = { name = it }, label = { Text("List Name") }, singleLine = true + )*/ + OutlinedTextField( + value = name, + singleLine = true, + onValueChange = { name = it }, + label = { Text("Title") }, + modifier = Modifier + .fillMaxWidth() + .focusRequester(titleFocusRequester), + isError = name.isEmpty(), ) Spacer(Modifier.height(8.dp)) @@ -142,8 +151,8 @@ fun AddListBottomSheet( Spacer(Modifier.height(16.dp)) Row(horizontalArrangement = Arrangement.End, modifier = Modifier.fillMaxWidth()) { - TextButton(onClick = onDismiss) { Text("Cancel") } - Spacer(Modifier.width(8.dp)) + //TextButton(onClick = onDismiss) { Text("Cancel") } + //Spacer(Modifier.width(8.dp)) Button(onClick = { viewModel.createTaskList(name/*, type, description*/, 1) onDismiss() 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 48c6ef3..a0e0a79 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 @@ -5,6 +5,7 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue 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.usecase.GetTaskListsUseCase import dagger.hilt.android.lifecycle.HiltViewModel @@ -23,6 +24,15 @@ class MainViewModel @Inject constructor( var destinations by mutableStateOf>(emptyList()) private set + var startDestination by mutableStateOf(AppDestination.ManageLists) + private set + + var currentDestination by mutableStateOf(startDestination) + private set + + var showTaskSheet by mutableStateOf(false) + var showAddListSheet by mutableStateOf(false) + init { getTaskLists() .onEach { lists -> @@ -30,17 +40,20 @@ class MainViewModel @Inject constructor( AppDestination.TaskList(taskList.id!!, taskList.name) } + AppDestination.ManageLists isLoading = false + if (!destinations.isEmpty()) startDestination = destinations.first() } .launchIn(viewModelScope) } - fun deriveDestination(route: String?): AppDestination? { - if (route == null) return null - return destinations.firstOrNull { dest -> + fun setCurrentDestination(navBackStackEntry: NavBackStackEntry?) { + val route = navBackStackEntry?.destination?.route + val taskListId = navBackStackEntry?.arguments?.getLong("taskListId") + + currentDestination = destinations.firstOrNull { dest -> when (dest) { - is AppDestination.TaskList -> route.startsWith("tasklist/") + is AppDestination.TaskList -> taskListId != null && dest.taskListId == taskListId else -> dest.route == route } - } + } ?: startDestination } } \ No newline at end of file diff --git a/donextv2/src/main/java/com/wismna/geoffroy/donext/presentation/viewmodel/ManageListsViewModel.kt b/donextv2/src/main/java/com/wismna/geoffroy/donext/presentation/viewmodel/ManageListsViewModel.kt index ae9d3b8..038cbcf 100644 --- a/donextv2/src/main/java/com/wismna/geoffroy/donext/presentation/viewmodel/ManageListsViewModel.kt +++ b/donextv2/src/main/java/com/wismna/geoffroy/donext/presentation/viewmodel/ManageListsViewModel.kt @@ -9,6 +9,7 @@ import com.wismna.geoffroy.donext.domain.model.TaskList import com.wismna.geoffroy.donext.domain.usecase.AddTaskListUseCase import com.wismna.geoffroy.donext.domain.usecase.DeleteTaskListUseCase import com.wismna.geoffroy.donext.domain.usecase.GetTaskListsUseCase +import com.wismna.geoffroy.donext.domain.usecase.UpdateTaskListUseCase import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach @@ -19,6 +20,7 @@ import javax.inject.Inject class ManageListsViewModel @Inject constructor( getTaskListsUseCase: GetTaskListsUseCase, private val addTaskListUseCase: AddTaskListUseCase, + private val updateTaskListUseCase: UpdateTaskListUseCase, private val deleteTaskListUseCase: DeleteTaskListUseCase ) : ViewModel() { @@ -38,6 +40,11 @@ class ManageListsViewModel @Inject constructor( addTaskListUseCase(title, order) } } + fun updateTaskListName(taskList: TaskList) { + viewModelScope.launch { + updateTaskListUseCase(taskList.id!!, taskList.name, taskList.order) + } + } fun deleteTaskList(taskId: Long) { viewModelScope.launch { deleteTaskListUseCase(taskId)