mirror of
https://github.com/wismna/DoNext.git
synced 2025-12-06 00:02:40 -05:00
Compare commits
7 Commits
e07f389fac
...
038a97672f
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
038a97672f | ||
|
|
30d3efa9de | ||
|
|
53e716a690 | ||
|
|
6c9e5efe38 | ||
|
|
c579a5d252 | ||
|
|
c57210494a | ||
|
|
c3dd615d15 |
6
.idea/deploymentTargetSelector.xml
generated
6
.idea/deploymentTargetSelector.xml
generated
@@ -2,15 +2,15 @@
|
|||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="deploymentTargetSelector">
|
<component name="deploymentTargetSelector">
|
||||||
<selectionStates>
|
<selectionStates>
|
||||||
<SelectionState runConfigName="app">
|
|
||||||
<option name="selectionMode" value="DROPDOWN" />
|
|
||||||
</SelectionState>
|
|
||||||
<SelectionState runConfigName="donextv2">
|
<SelectionState runConfigName="donextv2">
|
||||||
<option name="selectionMode" value="DROPDOWN" />
|
<option name="selectionMode" value="DROPDOWN" />
|
||||||
</SelectionState>
|
</SelectionState>
|
||||||
<SelectionState runConfigName="overdueCount_correctlyCalculated()">
|
<SelectionState runConfigName="overdueCount_correctlyCalculated()">
|
||||||
<option name="selectionMode" value="DROPDOWN" />
|
<option name="selectionMode" value="DROPDOWN" />
|
||||||
</SelectionState>
|
</SelectionState>
|
||||||
|
<SelectionState runConfigName="donext">
|
||||||
|
<option name="selectionMode" value="DROPDOWN" />
|
||||||
|
</SelectionState>
|
||||||
</selectionStates>
|
</selectionStates>
|
||||||
</component>
|
</component>
|
||||||
</project>
|
</project>
|
||||||
@@ -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,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
package com.wismna.geoffroy.donext.presentation.screen
|
package com.wismna.geoffroy.donext.presentation.screen
|
||||||
|
|
||||||
import android.widget.Toast
|
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
@@ -10,17 +9,14 @@ import androidx.compose.material3.Text
|
|||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.platform.LocalContext
|
|
||||||
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.domain.model.Task
|
|
||||||
import com.wismna.geoffroy.donext.presentation.viewmodel.DueTodayViewModel
|
import com.wismna.geoffroy.donext.presentation.viewmodel.DueTodayViewModel
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun DueTodayTasksScreen(
|
fun DueTodayTasksScreen(
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
viewModel: DueTodayViewModel = hiltViewModel(),
|
viewModel: DueTodayViewModel = hiltViewModel(),
|
||||||
onTaskClick: (task: Task) -> Unit
|
|
||||||
) {
|
) {
|
||||||
val tasks = viewModel.dueTodayTasks
|
val tasks = viewModel.dueTodayTasks
|
||||||
|
|
||||||
@@ -33,7 +29,6 @@ fun DueTodayTasksScreen(
|
|||||||
Text("Nothing due today !")
|
Text("Nothing due today !")
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
val context = LocalContext.current
|
|
||||||
LazyColumn(
|
LazyColumn(
|
||||||
modifier = modifier.padding(8.dp)
|
modifier = modifier.padding(8.dp)
|
||||||
) {
|
) {
|
||||||
@@ -41,16 +36,9 @@ fun DueTodayTasksScreen(
|
|||||||
TaskItemScreen(
|
TaskItemScreen(
|
||||||
modifier = Modifier.animateItem(),
|
modifier = Modifier.animateItem(),
|
||||||
task = task,
|
task = task,
|
||||||
onTaskClick = { onTaskClick(task) },
|
onSwipeLeft = { viewModel.updateTaskDone(task.id!!) },
|
||||||
onSwipeLeft = {
|
onSwipeRight = { viewModel.deleteTask(task.id!!) },
|
||||||
viewModel.updateTaskDone(task.id!!)
|
onTaskClick = { viewModel.onTaskClicked(task) }
|
||||||
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()
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,6 +25,10 @@ import androidx.compose.material3.LocalContentColor
|
|||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.ModalNavigationDrawer
|
import androidx.compose.material3.ModalNavigationDrawer
|
||||||
import androidx.compose.material3.Scaffold
|
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.Surface
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.material3.TopAppBar
|
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.ui.events.UiEvent
|
||||||
import com.wismna.geoffroy.donext.presentation.viewmodel.MainViewModel
|
import com.wismna.geoffroy.donext.presentation.viewmodel.MainViewModel
|
||||||
import com.wismna.geoffroy.donext.presentation.viewmodel.TaskListViewModel
|
import com.wismna.geoffroy.donext.presentation.viewmodel.TaskListViewModel
|
||||||
import kotlinx.coroutines.CoroutineScope
|
|
||||||
import kotlinx.coroutines.flow.collectLatest
|
import kotlinx.coroutines.flow.collectLatest
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
@@ -63,7 +66,6 @@ fun MainScreen(
|
|||||||
) {
|
) {
|
||||||
val navController = rememberNavController()
|
val navController = rememberNavController()
|
||||||
val drawerState = rememberDrawerState(DrawerValue.Closed)
|
val drawerState = rememberDrawerState(DrawerValue.Closed)
|
||||||
val scope = rememberCoroutineScope()
|
|
||||||
|
|
||||||
if (viewModel.isLoading) {
|
if (viewModel.isLoading) {
|
||||||
Box(Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
|
Box(Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
|
||||||
@@ -88,7 +90,7 @@ fun MainScreen(
|
|||||||
},
|
},
|
||||||
drawerState = drawerState
|
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,
|
modifier : Modifier = Modifier,
|
||||||
viewModel: MainViewModel,
|
viewModel: MainViewModel,
|
||||||
navController: NavHostController,
|
navController: NavHostController,
|
||||||
scope: CoroutineScope,
|
|
||||||
drawerState: DrawerState
|
drawerState: DrawerState
|
||||||
) {
|
) {
|
||||||
|
val scope = rememberCoroutineScope()
|
||||||
|
val snackbarHostState = remember { SnackbarHostState() }
|
||||||
|
|
||||||
LaunchedEffect(Unit) {
|
LaunchedEffect(Unit) {
|
||||||
viewModel.uiEventBus.events.collectLatest { event ->
|
viewModel.uiEventBus.events.collectLatest { event ->
|
||||||
when (event) {
|
when (event) {
|
||||||
@@ -108,13 +112,25 @@ fun AppContent(
|
|||||||
navController.navigate(event.route)
|
navController.navigate(event.route)
|
||||||
}
|
}
|
||||||
is UiEvent.NavigateBack -> navController.popBackStack()
|
is UiEvent.NavigateBack -> navController.popBackStack()
|
||||||
else -> {}
|
is UiEvent.EditTask -> { viewModel.showTaskSheet = true }
|
||||||
|
is UiEvent.ShowUndoSnackbar -> {
|
||||||
|
val result = snackbarHostState.showSnackbar(
|
||||||
|
message = event.message,
|
||||||
|
actionLabel = "Undo",
|
||||||
|
duration = SnackbarDuration.Short
|
||||||
|
)
|
||||||
|
if (result == SnackbarResult.ActionPerformed) {
|
||||||
|
event.undoAction()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> Unit
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Scaffold(
|
Scaffold(
|
||||||
modifier = modifier.background(MaterialTheme.colorScheme.primaryContainer),
|
modifier = modifier.background(MaterialTheme.colorScheme.primaryContainer),
|
||||||
containerColor = Color.Transparent,
|
containerColor = Color.Transparent,
|
||||||
|
snackbarHost = { SnackbarHost(hostState = snackbarHostState) },
|
||||||
topBar = {
|
topBar = {
|
||||||
TopAppBar(
|
TopAppBar(
|
||||||
title = { Text(viewModel.currentDestination.title) },
|
title = { Text(viewModel.currentDestination.title) },
|
||||||
@@ -207,29 +223,17 @@ fun AppContent(
|
|||||||
}
|
}
|
||||||
|
|
||||||
val taskListViewModel: TaskListViewModel = hiltViewModel(navBackStackEntry)
|
val taskListViewModel: TaskListViewModel = hiltViewModel(navBackStackEntry)
|
||||||
TaskListScreen(
|
TaskListScreen(viewModel = taskListViewModel)
|
||||||
viewModel = taskListViewModel,
|
|
||||||
onTaskClick = { task -> viewModel.onTaskClicked(task) }
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
composable(AppDestination.ManageLists.route) {
|
composable(AppDestination.ManageLists.route) {
|
||||||
ManageListsScreen(
|
ManageListsScreen(modifier = Modifier)
|
||||||
modifier = Modifier,
|
|
||||||
showAddListSheet = {viewModel.showAddListSheet = true}
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
composable(AppDestination.DueTodayList.route) {
|
composable(AppDestination.DueTodayList.route) {
|
||||||
DueTodayTasksScreen (
|
DueTodayTasksScreen (modifier = Modifier)
|
||||||
modifier = Modifier,
|
|
||||||
onTaskClick = { task -> viewModel.onTaskClicked(task) }
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
composable(AppDestination.RecycleBin.route) {
|
composable(AppDestination.RecycleBin.route) {
|
||||||
RecycleBinScreen(
|
RecycleBinScreen(modifier = Modifier)
|
||||||
modifier = Modifier,
|
|
||||||
onTaskClick = { task -> viewModel.onTaskClicked(task) }
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -58,10 +60,21 @@ import sh.calvin.reorderable.rememberReorderableLazyListState
|
|||||||
@Composable
|
@Composable
|
||||||
fun ManageListsScreen(
|
fun ManageListsScreen(
|
||||||
modifier: Modifier,
|
modifier: Modifier,
|
||||||
viewModel: ManageListsViewModel = hiltViewModel(),
|
viewModel: ManageListsViewModel = hiltViewModel()
|
||||||
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,
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ fun MenuScreen(
|
|||||||
},
|
},
|
||||||
icon = { Icon(Icons.Default.Today, contentDescription = "Due Today") },
|
icon = { Icon(Icons.Default.Today, contentDescription = "Due Today") },
|
||||||
selected = currentDestination is AppDestination.DueTodayList,
|
selected = currentDestination is AppDestination.DueTodayList,
|
||||||
onClick = { viewModel.navigateTo(AppDestination.DueTodayList.route) },
|
onClick = { viewModel.navigateTo(AppDestination.DueTodayList.route, currentDestination.route) },
|
||||||
modifier = Modifier.padding(NavigationDrawerItemDefaults.ItemPadding)
|
modifier = Modifier.padding(NavigationDrawerItemDefaults.ItemPadding)
|
||||||
)
|
)
|
||||||
HorizontalDivider()
|
HorizontalDivider()
|
||||||
@@ -73,7 +73,7 @@ fun MenuScreen(
|
|||||||
icon = { Icon(Icons.Default.LineWeight, contentDescription = list.name) },
|
icon = { Icon(Icons.Default.LineWeight, contentDescription = list.name) },
|
||||||
selected = currentDestination is AppDestination.TaskList &&
|
selected = currentDestination is AppDestination.TaskList &&
|
||||||
currentDestination.taskListId == list.id,
|
currentDestination.taskListId == list.id,
|
||||||
onClick = { viewModel.navigateTo("taskList/${list.id}") },
|
onClick = { viewModel.navigateTo("taskList/${list.id}", currentDestination.route) },
|
||||||
badge = {
|
badge = {
|
||||||
if (list.overdueCount > 0) {
|
if (list.overdueCount > 0) {
|
||||||
Badge { Text(list.overdueCount.toString()) }
|
Badge { Text(list.overdueCount.toString()) }
|
||||||
@@ -90,14 +90,14 @@ fun MenuScreen(
|
|||||||
label = { Text("Recycle Bin") },
|
label = { Text("Recycle Bin") },
|
||||||
icon = { Icon(Icons.Default.Delete, contentDescription = "Recycle Bin") },
|
icon = { Icon(Icons.Default.Delete, contentDescription = "Recycle Bin") },
|
||||||
selected = currentDestination is AppDestination.RecycleBin,
|
selected = currentDestination is AppDestination.RecycleBin,
|
||||||
onClick = { viewModel.navigateTo(AppDestination.RecycleBin.route) },
|
onClick = { viewModel.navigateTo(AppDestination.RecycleBin.route, currentDestination.route) },
|
||||||
modifier = Modifier.padding(NavigationDrawerItemDefaults.ItemPadding)
|
modifier = Modifier.padding(NavigationDrawerItemDefaults.ItemPadding)
|
||||||
)
|
)
|
||||||
NavigationDrawerItem(
|
NavigationDrawerItem(
|
||||||
label = { Text("Edit Lists") },
|
label = { Text("Edit Lists") },
|
||||||
icon = { Icon(Icons.Default.EditNote, contentDescription = "Edit Lists") },
|
icon = { Icon(Icons.Default.EditNote, contentDescription = "Edit Lists") },
|
||||||
selected = currentDestination is AppDestination.ManageLists,
|
selected = currentDestination is AppDestination.ManageLists,
|
||||||
onClick = { viewModel.navigateTo(AppDestination.ManageLists.route) },
|
onClick = { viewModel.navigateTo(AppDestination.ManageLists.route, currentDestination.route) },
|
||||||
modifier = Modifier.padding(NavigationDrawerItemDefaults.ItemPadding)
|
modifier = Modifier.padding(NavigationDrawerItemDefaults.ItemPadding)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,14 +29,12 @@ import androidx.compose.ui.platform.LocalContext
|
|||||||
import androidx.compose.ui.text.font.FontStyle
|
import androidx.compose.ui.text.font.FontStyle
|
||||||
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.domain.model.Task
|
|
||||||
import com.wismna.geoffroy.donext.presentation.viewmodel.RecycleBinViewModel
|
import com.wismna.geoffroy.donext.presentation.viewmodel.RecycleBinViewModel
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun RecycleBinScreen(
|
fun RecycleBinScreen(
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
viewModel: RecycleBinViewModel = hiltViewModel(),
|
viewModel: RecycleBinViewModel = hiltViewModel(),
|
||||||
onTaskClick: (task: Task) -> Unit
|
|
||||||
) {
|
) {
|
||||||
val tasks = viewModel.deletedTasks
|
val tasks = viewModel.deletedTasks
|
||||||
|
|
||||||
@@ -77,15 +75,13 @@ fun RecycleBinScreen(
|
|||||||
TaskItemScreen(
|
TaskItemScreen(
|
||||||
modifier = Modifier.animateItem(),
|
modifier = Modifier.animateItem(),
|
||||||
task = item.task,
|
task = item.task,
|
||||||
onTaskClick = { onTaskClick(item.task) },
|
onSwipeLeft = { viewModel.restore(item.task.id!!) },
|
||||||
onSwipeLeft = {
|
|
||||||
viewModel.restore(item.task.id!!)
|
|
||||||
Toast.makeText(context, "Task restored", Toast.LENGTH_SHORT).show()
|
|
||||||
},
|
|
||||||
onSwipeRight = {
|
onSwipeRight = {
|
||||||
|
// TODO: add confirmation dialog
|
||||||
viewModel.deleteForever(item.task.id!!)
|
viewModel.deleteForever(item.task.id!!)
|
||||||
Toast.makeText(context, "Task deleted", Toast.LENGTH_SHORT).show()
|
Toast.makeText(context, "Task deleted", Toast.LENGTH_SHORT).show()
|
||||||
}
|
},
|
||||||
|
onTaskClick = { viewModel.onTaskClicked(item.task) }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,12 +45,12 @@ import com.wismna.geoffroy.donext.presentation.viewmodel.TaskItemViewModel
|
|||||||
fun TaskItemScreen(
|
fun TaskItemScreen(
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
task: Task,
|
task: Task,
|
||||||
onTaskClick: (taskId: Long) -> Unit,
|
|
||||||
onSwipeLeft: () -> Unit,
|
onSwipeLeft: () -> Unit,
|
||||||
onSwipeRight: () -> Unit
|
onSwipeRight: () -> Unit,
|
||||||
|
onTaskClick: (task: Task) -> Unit
|
||||||
) {
|
) {
|
||||||
val viewModel = TaskItemViewModel(task)
|
val viewModel = TaskItemViewModel(task)
|
||||||
// TODO: change this
|
|
||||||
val dismissState = rememberSwipeToDismissBoxState(
|
val dismissState = rememberSwipeToDismissBoxState(
|
||||||
confirmValueChange = {
|
confirmValueChange = {
|
||||||
when (it) {
|
when (it) {
|
||||||
@@ -63,6 +63,7 @@ fun TaskItemScreen(
|
|||||||
// positional threshold of 25%
|
// positional threshold of 25%
|
||||||
positionalThreshold = { it * .25f }
|
positionalThreshold = { it * .25f }
|
||||||
)
|
)
|
||||||
|
|
||||||
val baseStyle = MaterialTheme.typography.bodyLarge.copy(
|
val baseStyle = MaterialTheme.typography.bodyLarge.copy(
|
||||||
fontWeight = when (viewModel.priority) {
|
fontWeight = when (viewModel.priority) {
|
||||||
Priority.HIGH -> FontWeight.Bold
|
Priority.HIGH -> FontWeight.Bold
|
||||||
@@ -78,7 +79,7 @@ fun TaskItemScreen(
|
|||||||
)
|
)
|
||||||
Card(
|
Card(
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
onClick = { onTaskClick(viewModel.id) },
|
onClick = { onTaskClick(task) },
|
||||||
colors = CardDefaults.cardColors(
|
colors = CardDefaults.cardColors(
|
||||||
containerColor = MaterialTheme.colorScheme.surfaceContainer,
|
containerColor = MaterialTheme.colorScheme.surfaceContainer,
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package com.wismna.geoffroy.donext.presentation.screen
|
package com.wismna.geoffroy.donext.presentation.screen
|
||||||
|
|
||||||
import android.widget.Toast
|
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
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
|
||||||
@@ -9,27 +9,38 @@ import androidx.compose.foundation.layout.padding
|
|||||||
import androidx.compose.foundation.lazy.LazyColumn
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
import androidx.compose.foundation.lazy.items
|
import androidx.compose.foundation.lazy.items
|
||||||
import androidx.compose.material3.HorizontalDivider
|
import androidx.compose.material3.HorizontalDivider
|
||||||
|
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.platform.LocalContext
|
|
||||||
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.domain.model.Task
|
|
||||||
import com.wismna.geoffroy.donext.presentation.viewmodel.TaskListViewModel
|
import com.wismna.geoffroy.donext.presentation.viewmodel.TaskListViewModel
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun TaskListScreen(
|
fun TaskListScreen(
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
viewModel: TaskListViewModel = hiltViewModel<TaskListViewModel>(),
|
viewModel: TaskListViewModel = hiltViewModel<TaskListViewModel>(),
|
||||||
onTaskClick: (Task) -> Unit) {
|
) {
|
||||||
val tasks = viewModel.tasks
|
val tasks = viewModel.tasks
|
||||||
|
|
||||||
|
if (tasks.isEmpty()) {
|
||||||
|
// Placeholder when recycle bin is empty
|
||||||
|
Box(
|
||||||
|
modifier = modifier.fillMaxSize(),
|
||||||
|
contentAlignment = Alignment.Center
|
||||||
|
) {
|
||||||
|
Text("Tap + to create a new task.")
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// Split tasks into active and done
|
// Split tasks into active and done
|
||||||
val (active, done) = remember(tasks) {
|
val (active, done) = remember(tasks) {
|
||||||
tasks.partition { !it.isDone }
|
tasks.partition { !it.isDone }
|
||||||
}
|
}
|
||||||
|
|
||||||
val context = LocalContext.current
|
|
||||||
LazyColumn(
|
LazyColumn(
|
||||||
modifier = Modifier.fillMaxSize(),
|
modifier = Modifier.fillMaxSize(),
|
||||||
contentPadding = PaddingValues(8.dp),
|
contentPadding = PaddingValues(8.dp),
|
||||||
@@ -43,15 +54,9 @@ fun TaskListScreen(
|
|||||||
TaskItemScreen(
|
TaskItemScreen(
|
||||||
modifier = Modifier.animateItem(),
|
modifier = Modifier.animateItem(),
|
||||||
task = task,
|
task = task,
|
||||||
onTaskClick = { onTaskClick(task) },
|
onSwipeLeft = { viewModel.updateTaskDone(task.id!!, true) },
|
||||||
onSwipeLeft = {
|
onSwipeRight = { viewModel.deleteTask(task.id!!) },
|
||||||
viewModel.updateTaskDone(task.id!!, true)
|
onTaskClick = { viewModel.onTaskClicked(task) }
|
||||||
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()
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -74,17 +79,10 @@ fun TaskListScreen(
|
|||||||
TaskItemScreen(
|
TaskItemScreen(
|
||||||
modifier = Modifier.animateItem(),
|
modifier = Modifier.animateItem(),
|
||||||
task = task,
|
task = task,
|
||||||
onTaskClick = { onTaskClick(task) },
|
onSwipeLeft = { viewModel.updateTaskDone(task.id!!, false) },
|
||||||
onSwipeLeft = {
|
onSwipeRight = { viewModel.deleteTask(task.id!!) },
|
||||||
viewModel.updateTaskDone(task.id!!, false)
|
onTaskClick = { viewModel.onTaskClicked(task) }
|
||||||
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()
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -5,9 +5,11 @@ import com.wismna.geoffroy.donext.domain.model.Task
|
|||||||
sealed class UiEvent {
|
sealed class UiEvent {
|
||||||
data class Navigate(val route: String) : UiEvent()
|
data class Navigate(val route: String) : UiEvent()
|
||||||
data object NavigateBack : UiEvent()
|
data object NavigateBack : UiEvent()
|
||||||
data class ShowSnackbar(val message: String) : UiEvent()
|
|
||||||
|
|
||||||
data class EditTask(val task: Task) : UiEvent()
|
data class EditTask(val task: Task) : UiEvent()
|
||||||
data class CreateNewTask(val taskListId: Long) : UiEvent()
|
data class CreateNewTask(val taskListId: Long) : UiEvent()
|
||||||
data object CloseTask : UiEvent()
|
data object CloseTask : UiEvent()
|
||||||
|
data class ShowUndoSnackbar(
|
||||||
|
val message: String,
|
||||||
|
val undoAction: () -> Unit
|
||||||
|
) : UiEvent()
|
||||||
}
|
}
|
||||||
@@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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.GetDueTodayTasksUseCase
|
||||||
import com.wismna.geoffroy.donext.domain.usecase.ToggleTaskDeletedUseCase
|
import com.wismna.geoffroy.donext.domain.usecase.ToggleTaskDeletedUseCase
|
||||||
import com.wismna.geoffroy.donext.domain.usecase.ToggleTaskDoneUseCase
|
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 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
|
||||||
@@ -17,30 +19,60 @@ import javax.inject.Inject
|
|||||||
|
|
||||||
@HiltViewModel
|
@HiltViewModel
|
||||||
class DueTodayViewModel @Inject constructor(
|
class DueTodayViewModel @Inject constructor(
|
||||||
getDueTodayTasks: GetDueTodayTasksUseCase,
|
getDueTodayTasksUseCase: GetDueTodayTasksUseCase,
|
||||||
private val toggleTaskDeleted: ToggleTaskDeletedUseCase,
|
private val toggleTaskDeletedUseCase: ToggleTaskDeletedUseCase,
|
||||||
private val toggleTaskDone: ToggleTaskDoneUseCase
|
private val toggleTaskDoneUseCase: ToggleTaskDoneUseCase,
|
||||||
|
private val uiEventBus: UiEventBus
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
|
|
||||||
var dueTodayTasks by mutableStateOf<List<Task>>(emptyList())
|
var dueTodayTasks by mutableStateOf<List<Task>>(emptyList())
|
||||||
private set
|
private set
|
||||||
|
|
||||||
init {
|
init {
|
||||||
getDueTodayTasks()
|
getDueTodayTasksUseCase()
|
||||||
.onEach { tasks ->
|
.onEach { tasks ->
|
||||||
dueTodayTasks = tasks
|
dueTodayTasks = tasks
|
||||||
}
|
}
|
||||||
.launchIn(viewModelScope)
|
.launchIn(viewModelScope)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun onTaskClicked(task: Task) {
|
||||||
|
viewModelScope.launch {
|
||||||
|
uiEventBus.send(UiEvent.EditTask(task))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun updateTaskDone(taskId: Long) {
|
fun updateTaskDone(taskId: Long) {
|
||||||
viewModelScope.launch {
|
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) {
|
fun deleteTask(taskId: Long) {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
toggleTaskDeleted(taskId, true)
|
toggleTaskDeletedUseCase(taskId, true)
|
||||||
|
|
||||||
|
uiEventBus.send(
|
||||||
|
UiEvent.ShowUndoSnackbar(
|
||||||
|
message = "Task moved to recycle bin",
|
||||||
|
undoAction = {
|
||||||
|
viewModelScope.launch {
|
||||||
|
toggleTaskDeletedUseCase(taskId, false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -7,7 +7,6 @@ import androidx.lifecycle.ViewModel
|
|||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import androidx.navigation.NavBackStackEntry
|
import androidx.navigation.NavBackStackEntry
|
||||||
import com.wismna.geoffroy.donext.domain.model.AppDestination
|
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.domain.usecase.GetTaskListsUseCase
|
||||||
import com.wismna.geoffroy.donext.presentation.ui.events.UiEvent
|
import com.wismna.geoffroy.donext.presentation.ui.events.UiEvent
|
||||||
import com.wismna.geoffroy.donext.presentation.ui.events.UiEventBus
|
import com.wismna.geoffroy.donext.presentation.ui.events.UiEventBus
|
||||||
@@ -47,10 +46,10 @@ class MainViewModel @Inject constructor(
|
|||||||
AppDestination.ManageLists +
|
AppDestination.ManageLists +
|
||||||
AppDestination.RecycleBin +
|
AppDestination.RecycleBin +
|
||||||
AppDestination.DueTodayList
|
AppDestination.DueTodayList
|
||||||
isLoading = false
|
|
||||||
if (startDestination == AppDestination.ManageLists && destinations.isNotEmpty()) {
|
if (startDestination == AppDestination.ManageLists && destinations.isNotEmpty()) {
|
||||||
startDestination = destinations.first()
|
startDestination = destinations.first()
|
||||||
}
|
}
|
||||||
|
isLoading = false
|
||||||
}
|
}
|
||||||
.launchIn(viewModelScope)
|
.launchIn(viewModelScope)
|
||||||
}
|
}
|
||||||
@@ -68,13 +67,6 @@ class MainViewModel @Inject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onTaskClicked(task: Task) {
|
|
||||||
showTaskSheet = true
|
|
||||||
viewModelScope.launch {
|
|
||||||
uiEventBus.send(UiEvent.EditTask(task))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun onDismissTaskSheet() {
|
fun onDismissTaskSheet() {
|
||||||
showTaskSheet = false
|
showTaskSheet = false
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
|
|||||||
@@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -42,9 +42,11 @@ class MenuViewModel @Inject constructor(
|
|||||||
.launchIn(viewModelScope)
|
.launchIn(viewModelScope)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun navigateTo(route: String) {
|
fun navigateTo(route: String, currentRoute: String) {
|
||||||
|
if (route != currentRoute) {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
uiEventBus.send(UiEvent.Navigate(route))
|
uiEventBus.send(UiEvent.Navigate(route))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -5,11 +5,14 @@ import androidx.compose.runtime.mutableStateOf
|
|||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import com.wismna.geoffroy.donext.domain.model.Task
|
||||||
import com.wismna.geoffroy.donext.domain.model.TaskWithListName
|
import com.wismna.geoffroy.donext.domain.model.TaskWithListName
|
||||||
import com.wismna.geoffroy.donext.domain.usecase.EmptyRecycleBinUseCase
|
import com.wismna.geoffroy.donext.domain.usecase.EmptyRecycleBinUseCase
|
||||||
import com.wismna.geoffroy.donext.domain.usecase.GetDeletedTasksUseCase
|
import com.wismna.geoffroy.donext.domain.usecase.GetDeletedTasksUseCase
|
||||||
import com.wismna.geoffroy.donext.domain.usecase.PermanentlyDeleteTaskUseCase
|
import com.wismna.geoffroy.donext.domain.usecase.PermanentlyDeleteTaskUseCase
|
||||||
import com.wismna.geoffroy.donext.domain.usecase.ToggleTaskDeletedUseCase
|
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 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
|
||||||
@@ -18,10 +21,11 @@ import javax.inject.Inject
|
|||||||
|
|
||||||
@HiltViewModel
|
@HiltViewModel
|
||||||
class RecycleBinViewModel @Inject constructor(
|
class RecycleBinViewModel @Inject constructor(
|
||||||
private val getDeletedTasks: GetDeletedTasksUseCase,
|
private val getDeletedTasksUseCase: GetDeletedTasksUseCase,
|
||||||
private val restoreTask: ToggleTaskDeletedUseCase,
|
private val toggleTaskDeletedUseCase: ToggleTaskDeletedUseCase,
|
||||||
private val permanentlyDeleteTask: PermanentlyDeleteTaskUseCase,
|
private val permanentlyDeleteTaskUseCase: PermanentlyDeleteTaskUseCase,
|
||||||
private val emptyRecycleBinUseCase: EmptyRecycleBinUseCase
|
private val emptyRecycleBinUseCase: EmptyRecycleBinUseCase,
|
||||||
|
private val uiEventBus: UiEventBus
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
|
|
||||||
var deletedTasks by mutableStateOf<List<TaskWithListName>>(emptyList())
|
var deletedTasks by mutableStateOf<List<TaskWithListName>>(emptyList())
|
||||||
@@ -31,8 +35,14 @@ class RecycleBinViewModel @Inject constructor(
|
|||||||
loadDeletedTasks()
|
loadDeletedTasks()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun onTaskClicked(task: Task) {
|
||||||
|
viewModelScope.launch {
|
||||||
|
uiEventBus.send(UiEvent.EditTask(task))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun loadDeletedTasks() {
|
fun loadDeletedTasks() {
|
||||||
getDeletedTasks()
|
getDeletedTasksUseCase()
|
||||||
.onEach { tasks ->
|
.onEach { tasks ->
|
||||||
deletedTasks = tasks
|
deletedTasks = tasks
|
||||||
}
|
}
|
||||||
@@ -41,14 +51,25 @@ class RecycleBinViewModel @Inject constructor(
|
|||||||
|
|
||||||
fun restore(taskId: Long) {
|
fun restore(taskId: Long) {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
restoreTask(taskId, false)
|
toggleTaskDeletedUseCase(taskId, false)
|
||||||
loadDeletedTasks()
|
loadDeletedTasks()
|
||||||
|
|
||||||
|
uiEventBus.send(
|
||||||
|
UiEvent.ShowUndoSnackbar(
|
||||||
|
message = "Task restored",
|
||||||
|
undoAction = {
|
||||||
|
viewModelScope.launch {
|
||||||
|
toggleTaskDeletedUseCase(taskId, true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun deleteForever(taskId: Long) {
|
fun deleteForever(taskId: Long) {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
permanentlyDeleteTask(taskId)
|
permanentlyDeleteTaskUseCase(taskId)
|
||||||
loadDeletedTasks()
|
loadDeletedTasks()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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.GetTasksForListUseCase
|
||||||
import com.wismna.geoffroy.donext.domain.usecase.ToggleTaskDeletedUseCase
|
import com.wismna.geoffroy.donext.domain.usecase.ToggleTaskDeletedUseCase
|
||||||
import com.wismna.geoffroy.donext.domain.usecase.ToggleTaskDoneUseCase
|
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 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
|
||||||
@@ -18,10 +20,11 @@ import javax.inject.Inject
|
|||||||
|
|
||||||
@HiltViewModel
|
@HiltViewModel
|
||||||
class TaskListViewModel @Inject constructor(
|
class TaskListViewModel @Inject constructor(
|
||||||
getTasks: GetTasksForListUseCase,
|
|
||||||
savedStateHandle: SavedStateHandle,
|
savedStateHandle: SavedStateHandle,
|
||||||
private val toggleTaskDone: ToggleTaskDoneUseCase,
|
getTasksUseCase: GetTasksForListUseCase,
|
||||||
private val toggleTaskDeleted: ToggleTaskDeletedUseCase,
|
private val toggleTaskDoneUseCase: ToggleTaskDoneUseCase,
|
||||||
|
private val toggleTaskDeletedUseCase: ToggleTaskDeletedUseCase,
|
||||||
|
private val uiEventBus: UiEventBus
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
|
|
||||||
var tasks by mutableStateOf<List<Task>>(emptyList())
|
var tasks by mutableStateOf<List<Task>>(emptyList())
|
||||||
@@ -32,7 +35,7 @@ class TaskListViewModel @Inject constructor(
|
|||||||
private val taskListId: Long = checkNotNull(savedStateHandle["taskListId"])
|
private val taskListId: Long = checkNotNull(savedStateHandle["taskListId"])
|
||||||
|
|
||||||
init {
|
init {
|
||||||
getTasks(taskListId)
|
getTasksUseCase(taskListId)
|
||||||
.onEach { list ->
|
.onEach { list ->
|
||||||
tasks = list
|
tasks = list
|
||||||
isLoading = false
|
isLoading = false
|
||||||
@@ -40,14 +43,42 @@ class TaskListViewModel @Inject constructor(
|
|||||||
.launchIn(viewModelScope)
|
.launchIn(viewModelScope)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun onTaskClicked(task: Task) {
|
||||||
|
viewModelScope.launch {
|
||||||
|
uiEventBus.send(UiEvent.EditTask(task))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun updateTaskDone(taskId: Long, isDone: Boolean) {
|
fun updateTaskDone(taskId: Long, isDone: Boolean) {
|
||||||
viewModelScope.launch {
|
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) {
|
fun deleteTask(taskId: Long) {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
toggleTaskDeleted(taskId, true)
|
toggleTaskDeletedUseCase(taskId, true)
|
||||||
|
|
||||||
|
uiEventBus.send(
|
||||||
|
UiEvent.ShowUndoSnackbar(
|
||||||
|
message = "Task moved to recycle bin",
|
||||||
|
undoAction = {
|
||||||
|
viewModelScope.launch {
|
||||||
|
toggleTaskDeletedUseCase(taskId, false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -45,8 +45,8 @@ class TaskViewModel @Inject constructor(
|
|||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
uiEventBus.events.collect { event ->
|
uiEventBus.events.collect { event ->
|
||||||
when (event) {
|
when (event) {
|
||||||
is UiEvent.EditTask -> startEditTask(event.task)
|
|
||||||
is UiEvent.CreateNewTask -> startNewTask(event.taskListId)
|
is UiEvent.CreateNewTask -> startNewTask(event.taskListId)
|
||||||
|
is UiEvent.EditTask -> startEditTask(event.task)
|
||||||
is UiEvent.CloseTask -> reset()
|
is UiEvent.CloseTask -> reset()
|
||||||
else -> {}
|
else -> {}
|
||||||
}
|
}
|
||||||
@@ -56,28 +56,6 @@ class TaskViewModel @Inject constructor(
|
|||||||
|
|
||||||
fun screenTitle(): String = if (isDeleted) "Task details" else if (isEditing()) "Edit Task" else "New Task"
|
fun screenTitle(): String = if (isDeleted) "Task details" else if (isEditing()) "Edit Task" else "New Task"
|
||||||
fun isEditing(): Boolean = editingTaskId != null
|
fun isEditing(): Boolean = editingTaskId != null
|
||||||
|
|
||||||
private fun startNewTask(selectedListId: Long) {
|
|
||||||
editingTaskId = null
|
|
||||||
taskListId = selectedListId
|
|
||||||
title = ""
|
|
||||||
description = ""
|
|
||||||
priority = Priority.NORMAL
|
|
||||||
dueDate = null
|
|
||||||
isDeleted = false
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun startEditTask(task: Task) {
|
|
||||||
editingTaskId = task.id
|
|
||||||
taskListId = task.taskListId
|
|
||||||
title = task.name
|
|
||||||
description = task.description ?: ""
|
|
||||||
priority = task.priority
|
|
||||||
dueDate = task.dueDate
|
|
||||||
isDone = task.isDone
|
|
||||||
isDeleted = task.isDeleted
|
|
||||||
}
|
|
||||||
|
|
||||||
fun onTitleChanged(value: String) { title = value }
|
fun onTitleChanged(value: String) { title = value }
|
||||||
fun onDescriptionChanged(value: String) { description = value }
|
fun onDescriptionChanged(value: String) { description = value }
|
||||||
fun onPriorityChanged(value: Priority) { priority = value }
|
fun onPriorityChanged(value: Priority) { priority = value }
|
||||||
@@ -104,7 +82,28 @@ class TaskViewModel @Inject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun reset() {
|
private fun startNewTask(selectedListId: Long) {
|
||||||
|
editingTaskId = null
|
||||||
|
taskListId = selectedListId
|
||||||
|
title = ""
|
||||||
|
description = ""
|
||||||
|
priority = Priority.NORMAL
|
||||||
|
dueDate = null
|
||||||
|
isDeleted = false
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun startEditTask(task: Task) {
|
||||||
|
editingTaskId = task.id
|
||||||
|
taskListId = task.taskListId
|
||||||
|
title = task.name
|
||||||
|
description = task.description ?: ""
|
||||||
|
priority = task.priority
|
||||||
|
dueDate = task.dueDate
|
||||||
|
isDone = task.isDone
|
||||||
|
isDeleted = task.isDeleted
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun reset() {
|
||||||
editingTaskId = null
|
editingTaskId = null
|
||||||
taskListId = null
|
taskListId = null
|
||||||
title = ""
|
title = ""
|
||||||
|
|||||||
Reference in New Issue
Block a user