diff --git a/donextv2/src/main/java/com/wismna/geoffroy/donext/data/local/dao/TaskDao.kt b/donextv2/src/main/java/com/wismna/geoffroy/donext/data/local/dao/TaskDao.kt index ffcee93..ecc2b1a 100644 --- a/donextv2/src/main/java/com/wismna/geoffroy/donext/data/local/dao/TaskDao.kt +++ b/donextv2/src/main/java/com/wismna/geoffroy/donext/data/local/dao/TaskDao.kt @@ -21,7 +21,7 @@ interface TaskDao { fun getDueTodayTasks(todayStart: Long, todayEnd: Long): Flow> @Query("SELECT * FROM tasks WHERE deleted = 1") - suspend fun getDeletedTasks(): List + fun getDeletedTasks(): Flow> @Insert(onConflict = OnConflictStrategy.REPLACE) suspend fun insertTask(task: TaskEntity) @@ -40,4 +40,7 @@ interface TaskDao { @Query("DELETE FROM tasks WHERE id = :taskId") suspend fun permanentDeleteTask(taskId: Long) + + @Query("DELETE FROM tasks WHERE deleted = 1") + suspend fun permanentDeleteAllDeletedTasks() } \ No newline at end of file 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 9ddea76..8c9d8aa 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 @@ -11,6 +11,7 @@ import com.wismna.geoffroy.donext.domain.repository.TaskRepository import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.map import javax.inject.Inject +import kotlin.collections.map class TaskRepositoryImpl @Inject constructor( private val taskDao: TaskDao, @@ -21,11 +22,11 @@ class TaskRepositoryImpl @Inject constructor( } override fun getDueTodayTasks(todayStart: Long, todayEnd: Long): Flow> { - return taskDao.getDueTodayTasks(todayStart, todayEnd).map {entity -> entity.map { it.toDomain() }} + return taskDao.getDueTodayTasks(todayStart, todayEnd).map {entity -> entity.map { it.toDomain() }} } - override suspend fun getDeletedTasks(): List { - return taskDao.getDeletedTasks().map {entity -> entity.toDomain() } + override fun getDeletedTasks(): Flow> { + return taskDao.getDeletedTasks().map {entity -> entity.map { it.toDomain() }} } override suspend fun insertTask(task: Task) { @@ -48,6 +49,10 @@ class TaskRepositoryImpl @Inject constructor( taskDao.permanentDeleteTask(taskId) } + override suspend fun permanentlyDeleteAllDeletedTask() { + taskDao.permanentDeleteAllDeletedTasks() + } + override fun getTaskLists(): Flow> { return taskListDao.getTaskLists().map {entities -> entities.map { it.toDomain() }} } 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 01af93e..1694fba 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 @@ -8,12 +8,13 @@ import kotlinx.coroutines.flow.Flow interface TaskRepository { fun getTasksForList(listId: Long): Flow> fun getDueTodayTasks(todayStart: Long, todayEnd: Long): Flow> - suspend fun getDeletedTasks(): List + fun getDeletedTasks(): Flow> suspend fun insertTask(task: Task) suspend fun updateTask(task: Task) suspend fun toggleTaskDeleted(taskId: Long, isDeleted: Boolean) suspend fun toggleTaskDone(taskId: Long, isDone: Boolean) suspend fun permanentlyDeleteTask(taskId: Long) + suspend fun permanentlyDeleteAllDeletedTask() fun getTaskLists(): Flow> suspend fun insertTaskList(taskList: TaskList) diff --git a/donextv2/src/main/java/com/wismna/geoffroy/donext/domain/usecase/EmptyRecycleBinUseCase.kt b/donextv2/src/main/java/com/wismna/geoffroy/donext/domain/usecase/EmptyRecycleBinUseCase.kt new file mode 100644 index 0000000..670efa4 --- /dev/null +++ b/donextv2/src/main/java/com/wismna/geoffroy/donext/domain/usecase/EmptyRecycleBinUseCase.kt @@ -0,0 +1,12 @@ +package com.wismna.geoffroy.donext.domain.usecase + +import com.wismna.geoffroy.donext.domain.repository.TaskRepository +import javax.inject.Inject + +class EmptyRecycleBinUseCase @Inject constructor( + private val repository: TaskRepository +) { + suspend operator fun invoke() { + repository.permanentlyDeleteAllDeletedTask() + } +} \ No newline at end of file diff --git a/donextv2/src/main/java/com/wismna/geoffroy/donext/domain/usecase/GetDeletedTasksUseCase.kt b/donextv2/src/main/java/com/wismna/geoffroy/donext/domain/usecase/GetDeletedTasksUseCase.kt index 6930b0f..258e110 100644 --- a/donextv2/src/main/java/com/wismna/geoffroy/donext/domain/usecase/GetDeletedTasksUseCase.kt +++ b/donextv2/src/main/java/com/wismna/geoffroy/donext/domain/usecase/GetDeletedTasksUseCase.kt @@ -2,8 +2,9 @@ package com.wismna.geoffroy.donext.domain.usecase import com.wismna.geoffroy.donext.domain.model.Task import com.wismna.geoffroy.donext.domain.repository.TaskRepository +import kotlinx.coroutines.flow.Flow import javax.inject.Inject class GetDeletedTasksUseCase @Inject constructor(private val repository: TaskRepository) { - suspend operator fun invoke(): List = repository.getDeletedTasks() + operator fun invoke(): Flow> = repository.getDeletedTasks() } \ No newline at end of file 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 b980da9..cd4a428 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 @@ -6,9 +6,6 @@ import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items -import androidx.compose.material3.Card -import androidx.compose.material3.CardDefaults -import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment @@ -42,25 +39,20 @@ fun DueTodayTasksScreen( modifier = modifier.padding(8.dp) ) { items(tasks, key = { it.id!! }) { task -> - Card( - onClick = { onTaskClick(task) }, - //elevation = CardDefaults.elevatedCardElevation(defaultElevation = 2.dp), - colors = CardDefaults.cardColors( - containerColor = MaterialTheme.colorScheme.surfaceContainer, - ), - ) { - TaskItemScreen( - viewModel = TaskItemViewModel(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() - } - ) - } + TaskItemScreen( + modifier = Modifier.animateItem(), + viewModel = TaskItemViewModel(task), + onTaskClick = { onTaskClick(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() + } + ) } } } 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 7052a3b..48a299b 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 @@ -26,6 +26,7 @@ import androidx.compose.material3.ModalNavigationDrawer import androidx.compose.material3.Scaffold import androidx.compose.material3.Surface import androidx.compose.material3.Text +import androidx.compose.material3.TextButton import androidx.compose.material3.TopAppBar import androidx.compose.material3.TopAppBarDefaults import androidx.compose.material3.rememberDrawerState @@ -136,10 +137,18 @@ fun AppContent( } }, actions = { - if (viewModel.currentDestination is AppDestination.ManageLists) { - IconButton(onClick = { viewModel.showAddListSheet = true }) { - Icon(Icons.Default.Add, contentDescription = "Add List") + when (viewModel.currentDestination) { + is AppDestination.ManageLists -> { + IconButton(onClick = { viewModel.showAddListSheet = true }) { + Icon(Icons.Default.Add, contentDescription = "Add List") + } } + is AppDestination.RecycleBin -> { + TextButton(onClick = { viewModel.emptyRecycleBin() }) { + Text(text = "Empty Recycle Bin", color = MaterialTheme.colorScheme.onPrimary) + } + } + else -> null } } ) @@ -179,23 +188,21 @@ fun AppContent( 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( + 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( @@ -214,7 +221,11 @@ fun AppContent( } composable(AppDestination.RecycleBin.route) { RecycleBinScreen( - modifier = Modifier + modifier = Modifier, + onTaskClick = { task -> + taskViewModel.startEditTask(task) + viewModel.showTaskSheet = true + } ) } } 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 c522b72..be23fd5 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 @@ -6,9 +6,6 @@ import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items -import androidx.compose.material3.Card -import androidx.compose.material3.CardDefaults -import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment @@ -16,6 +13,7 @@ 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.RecycleBinViewModel import com.wismna.geoffroy.donext.presentation.viewmodel.TaskItemViewModel @@ -23,6 +21,7 @@ import com.wismna.geoffroy.donext.presentation.viewmodel.TaskItemViewModel fun RecycleBinScreen( modifier: Modifier = Modifier, viewModel: RecycleBinViewModel = hiltViewModel(), + onTaskClick: (task: Task) -> Unit ) { val tasks = viewModel.deletedTasks @@ -40,25 +39,21 @@ fun RecycleBinScreen( modifier = modifier.padding(8.dp) ) { items(tasks, key = { it.id!! }) { task -> - Card( - //onClick = { onTaskClick(task) }, - //elevation = CardDefaults.elevatedCardElevation(defaultElevation = 2.dp), - colors = CardDefaults.cardColors( - containerColor = MaterialTheme.colorScheme.surfaceContainer, - ), - ) { - TaskItemScreen( - viewModel = TaskItemViewModel(task), - onSwipeLeft = { - viewModel.restore(task.id!!) - Toast.makeText(context, "Task restored", Toast.LENGTH_SHORT).show() - }, - onSwipeRight = { - viewModel.deleteForever(task.id!!) - Toast.makeText(context, "Task deleted", Toast.LENGTH_SHORT).show() - } - ) - } + + TaskItemScreen( + modifier = Modifier.animateItem(), + viewModel = TaskItemViewModel(task), + onTaskClick = { onTaskClick(task) }, + onSwipeLeft = { + viewModel.restore(task.id!!) + Toast.makeText(context, "Task restored", Toast.LENGTH_SHORT).show() + }, + onSwipeRight = { + viewModel.deleteForever(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 228c8e0..cb55057 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 @@ -16,6 +16,8 @@ import androidx.compose.material.icons.filled.Close import androidx.compose.material.icons.filled.Delete import androidx.compose.material.icons.filled.Done import androidx.compose.material3.Badge +import androidx.compose.material3.Card +import androidx.compose.material3.CardDefaults import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.SwipeToDismissBox @@ -41,6 +43,7 @@ import com.wismna.geoffroy.donext.presentation.viewmodel.TaskItemViewModel fun TaskItemScreen( modifier: Modifier = Modifier, viewModel: TaskItemViewModel, + onTaskClick: (taskId: Long) -> Unit, onSwipeLeft: () -> Unit, onSwipeRight: () -> Unit ) { @@ -69,83 +72,95 @@ fun TaskItemScreen( }, textDecoration = if (viewModel.isDone) TextDecoration.LineThrough else TextDecoration.None ) - - SwipeToDismissBox( - state = dismissState, + Card( modifier = modifier, - backgroundContent = { DismissBackground(dismissState, viewModel.isDone, viewModel.isDeleted) }, - content = { - Row( - modifier = modifier - .fillMaxWidth() - .background(MaterialTheme.colorScheme.surfaceContainer) - .padding(8.dp) - .alpha(if (viewModel.isDone || viewModel.priority == Priority.LOW) 0.5f else 1f), - verticalAlignment = Alignment.CenterVertically // centers checkbox + content - ) { - Box( + onClick = { onTaskClick(viewModel.id) }, + colors = CardDefaults.cardColors( + containerColor = MaterialTheme.colorScheme.surfaceContainer, + ), + ) { + SwipeToDismissBox( + state = dismissState, + backgroundContent = { + DismissBackground( + dismissState, + viewModel.isDone, + viewModel.isDeleted + ) + }, + content = { + Row( modifier = Modifier - .weight(1f) - .padding(start = 8.dp) - .height(IntrinsicSize.Min) // shrink to fit title/description + .fillMaxWidth() + .background(MaterialTheme.colorScheme.surfaceContainer) + .padding(8.dp) + .alpha(if (viewModel.isDone || viewModel.priority == Priority.LOW) 0.5f else 1f), + verticalAlignment = Alignment.CenterVertically // centers checkbox + content ) { - // Title - Text( - text = viewModel.name, - fontSize = 18.sp, - style = baseStyle, - modifier = Modifier - .align( - if (viewModel.description.isNullOrBlank()) Alignment.CenterStart - else Alignment.TopStart - ), - overflow = TextOverflow.Ellipsis, - maxLines = 1, - ) - - // Due date badge - viewModel.dueDateText?.let { dueMillis -> - Badge( - modifier = Modifier - .align( - if (viewModel.description.isNullOrBlank()) Alignment.CenterEnd - else Alignment.TopEnd - ), - containerColor = if (viewModel.isOverdue) MaterialTheme.colorScheme.error else MaterialTheme.colorScheme.primaryContainer - ) { - Text( - modifier = Modifier.padding(start = 1.dp, end = 1.dp), - text = viewModel.dueDateText, - color = if (viewModel.isOverdue) Color.White else MaterialTheme.colorScheme.onPrimaryContainer, - style = MaterialTheme.typography.bodySmall - ) - } - } - - // Optional description Box( modifier = Modifier - .fillMaxWidth() - .height(40.dp) - .padding(top = 24.dp), - contentAlignment = Alignment.TopStart + .weight(1f) + .padding(start = 8.dp) + .height(IntrinsicSize.Min) // shrink to fit title/description ) { - if (!viewModel.description.isNullOrBlank()) { - Text( - text = viewModel.description, - color = MaterialTheme.colorScheme.tertiary, - style = baseStyle.copy( - fontSize = MaterialTheme.typography.bodyMedium.fontSize, - fontStyle = FontStyle.Italic + // Title + Text( + text = viewModel.name, + fontSize = 18.sp, + style = baseStyle, + modifier = Modifier + .align( + if (viewModel.description.isNullOrBlank()) Alignment.CenterStart + else Alignment.TopStart ), - maxLines = 2, - overflow = TextOverflow.Ellipsis - ) + overflow = TextOverflow.Ellipsis, + maxLines = 1, + ) + + // Due date badge + viewModel.dueDateText?.let { dueMillis -> + Badge( + modifier = Modifier + .align( + if (viewModel.description.isNullOrBlank()) Alignment.CenterEnd + else Alignment.TopEnd + ), + containerColor = if (viewModel.isOverdue) MaterialTheme.colorScheme.error else MaterialTheme.colorScheme.primaryContainer + ) { + Text( + modifier = Modifier.padding(start = 1.dp, end = 1.dp), + text = viewModel.dueDateText, + color = if (viewModel.isOverdue) Color.White else MaterialTheme.colorScheme.onPrimaryContainer, + style = MaterialTheme.typography.bodySmall + ) + } + } + + // Optional description + Box( + modifier = Modifier + .fillMaxWidth() + .height(40.dp) + .padding(top = 24.dp), + contentAlignment = Alignment.TopStart + ) { + if (!viewModel.description.isNullOrBlank()) { + Text( + text = viewModel.description, + color = MaterialTheme.colorScheme.tertiary, + style = baseStyle.copy( + fontSize = MaterialTheme.typography.bodyMedium.fontSize, + fontStyle = FontStyle.Italic + ), + maxLines = 2, + overflow = TextOverflow.Ellipsis + ) + } } } } - } - }) + }) + } } @Composable 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 c6d8ffe..b42dac0 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 @@ -10,12 +10,9 @@ import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Add -import androidx.compose.material3.Card -import androidx.compose.material3.CardDefaults import androidx.compose.material3.ExtendedFloatingActionButton import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.Icon -import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.remember @@ -51,26 +48,19 @@ fun TaskListScreen( items = active, key = { it.id!! } ) { task -> - Card( - onClick = { onTaskClick(task) }, - //elevation = CardDefaults.elevatedCardElevation(defaultElevation = 2.dp), - colors = CardDefaults.cardColors( - containerColor = MaterialTheme.colorScheme.surfaceContainer, - ), - ) { - TaskItemScreen( - modifier = Modifier.animateItem(), - viewModel = TaskItemViewModel(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() - } - ) - } + TaskItemScreen( + modifier = Modifier.animateItem(), + viewModel = TaskItemViewModel(task), + onTaskClick = { onTaskClick(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() + } + ) } // Divider between active and done (optional) @@ -89,27 +79,21 @@ fun TaskListScreen( items = done, key = { it.id!! } ) { task -> - Card( - onClick = { onTaskClick(task) }, - //elevation = CardDefaults.elevatedCardElevation(defaultElevation = 2.dp), - colors = CardDefaults.cardColors( - containerColor = MaterialTheme.colorScheme.surfaceContainer, - ), - ) { - TaskItemScreen( - modifier = Modifier.animateItem(), - viewModel = TaskItemViewModel(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() - }, - ) - } + TaskItemScreen( + modifier = Modifier.animateItem(), + viewModel = TaskItemViewModel(task), + onTaskClick = { onTaskClick(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() + }, + ) } + } } diff --git a/donextv2/src/main/java/com/wismna/geoffroy/donext/presentation/screen/TaskScreen.kt b/donextv2/src/main/java/com/wismna/geoffroy/donext/presentation/screen/TaskScreen.kt index 6c343fb..af36967 100644 --- a/donextv2/src/main/java/com/wismna/geoffroy/donext/presentation/screen/TaskScreen.kt +++ b/donextv2/src/main/java/com/wismna/geoffroy/donext/presentation/screen/TaskScreen.kt @@ -42,6 +42,7 @@ import com.wismna.geoffroy.donext.domain.model.Priority import com.wismna.geoffroy.donext.presentation.viewmodel.TaskViewModel import java.time.Instant import java.time.LocalDate +import java.time.ZoneId import java.time.ZoneOffset import java.time.format.DateTimeFormatter import java.time.format.FormatStyle @@ -108,7 +109,7 @@ fun TaskBottomSheet( var showDatePicker by remember { mutableStateOf(false) } val formattedDate = viewModel.dueDate?.let { Instant.ofEpochMilli(it) - .atZone(ZoneOffset.UTC) + .atZone(ZoneId.systemDefault()) .toLocalDate() .format(DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM)) } ?: "" 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 bf5753a..37ea138 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 @@ -7,15 +7,18 @@ 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.EmptyRecycleBinUseCase import com.wismna.geoffroy.donext.domain.usecase.GetTaskListsUseCase 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 MainViewModel @Inject constructor( - getTaskLists: GetTaskListsUseCase + getTaskListsUseCase: GetTaskListsUseCase, + private val emptyRecycleBinUseCase: EmptyRecycleBinUseCase ) : ViewModel() { var isLoading by mutableStateOf(true) @@ -34,7 +37,7 @@ class MainViewModel @Inject constructor( var showAddListSheet by mutableStateOf(false) init { - getTaskLists() + getTaskListsUseCase() .onEach { lists -> destinations = lists.map { taskList -> AppDestination.TaskList(taskList.id!!, taskList.name) @@ -61,4 +64,10 @@ class MainViewModel @Inject constructor( } } ?: startDestination } + + fun emptyRecycleBin() { + viewModelScope.launch { + emptyRecycleBinUseCase() + } + } } \ No newline at end of file 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 ffc8504..cc9952f 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.GetDeletedTasksUseCase import com.wismna.geoffroy.donext.domain.usecase.PermanentlyDeleteTaskUseCase import com.wismna.geoffroy.donext.domain.usecase.ToggleTaskDeletedUseCase import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch import javax.inject.Inject @@ -28,9 +30,11 @@ class RecycleBinViewModel @Inject constructor( } fun loadDeletedTasks() { - viewModelScope.launch { - deletedTasks = getDeletedTasks() - } + getDeletedTasks() + .onEach { tasks -> + deletedTasks = tasks + } + .launchIn(viewModelScope) } fun restore(taskId: Long) { 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 a302d3e..4480993 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 @@ -4,7 +4,7 @@ import com.wismna.geoffroy.donext.domain.model.Priority import com.wismna.geoffroy.donext.domain.model.Task import java.time.Instant import java.time.LocalDate -import java.time.ZoneOffset +import java.time.ZoneId import java.time.format.DateTimeFormatter import java.time.format.FormatStyle import java.time.format.TextStyle @@ -18,11 +18,11 @@ class TaskItemViewModel(task: Task) { val isDeleted: Boolean = task.isDeleted val priority: Priority = task.priority - val today: LocalDate = LocalDate.now(ZoneOffset.UTC) + val today: LocalDate = LocalDate.now(ZoneId.systemDefault()) val isOverdue: Boolean = task.dueDate?.let { millis -> val dueDate = Instant.ofEpochMilli(millis) - .atZone(ZoneOffset.UTC) + .atZone(ZoneId.systemDefault()) .toLocalDate() dueDate.isBefore(today) } ?: false @@ -30,7 +30,7 @@ class TaskItemViewModel(task: Task) { val dueDateText: String? = task.dueDate?.let { formatDueDate(it) } private fun formatDueDate(dueMillis: Long): String { - val dueDate = Instant.ofEpochMilli(dueMillis).atZone(ZoneOffset.UTC).toLocalDate() + val dueDate = Instant.ofEpochMilli(dueMillis).atZone(ZoneId.systemDefault()).toLocalDate() return when { dueDate.isEqual(today) -> "Today"