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">
|
||||
<component name="deploymentTargetSelector">
|
||||
<selectionStates>
|
||||
<SelectionState runConfigName="app">
|
||||
<option name="selectionMode" value="DROPDOWN" />
|
||||
</SelectionState>
|
||||
<SelectionState runConfigName="donextv2">
|
||||
<option name="selectionMode" value="DROPDOWN" />
|
||||
</SelectionState>
|
||||
<SelectionState runConfigName="overdueCount_correctlyCalculated()">
|
||||
<option name="selectionMode" value="DROPDOWN" />
|
||||
</SelectionState>
|
||||
<SelectionState runConfigName="donext">
|
||||
<option name="selectionMode" value="DROPDOWN" />
|
||||
</SelectionState>
|
||||
</selectionStates>
|
||||
</component>
|
||||
</project>
|
||||
@@ -13,16 +13,16 @@ sealed class AppDestination(
|
||||
object DueTodayList : AppDestination(
|
||||
route = "todayList",
|
||||
title = "Due Today",
|
||||
showBackButton = true,
|
||||
showBackButton = false,
|
||||
)
|
||||
object ManageLists : AppDestination(
|
||||
route = "manageLists",
|
||||
title = "Manage Lists",
|
||||
showBackButton = true,
|
||||
showBackButton = false,
|
||||
)
|
||||
object RecycleBin : AppDestination(
|
||||
route = "recycleBin",
|
||||
title = "Recycle Bin",
|
||||
showBackButton = true,
|
||||
showBackButton = false,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ import javax.inject.Inject
|
||||
class DeleteTaskListUseCase@Inject constructor(
|
||||
private val repository: TaskRepository
|
||||
) {
|
||||
suspend operator fun invoke(taskListId: Long) {
|
||||
repository.deleteTaskList(taskListId, true)
|
||||
suspend operator fun invoke(taskListId: Long, isDeleted: Boolean) {
|
||||
repository.deleteTaskList(taskListId, isDeleted)
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
package com.wismna.geoffroy.donext.presentation.screen
|
||||
|
||||
import android.widget.Toast
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.padding
|
||||
@@ -10,17 +9,14 @@ import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||
import com.wismna.geoffroy.donext.domain.model.Task
|
||||
import com.wismna.geoffroy.donext.presentation.viewmodel.DueTodayViewModel
|
||||
|
||||
@Composable
|
||||
fun DueTodayTasksScreen(
|
||||
modifier: Modifier = Modifier,
|
||||
viewModel: DueTodayViewModel = hiltViewModel(),
|
||||
onTaskClick: (task: Task) -> Unit
|
||||
) {
|
||||
val tasks = viewModel.dueTodayTasks
|
||||
|
||||
@@ -33,7 +29,6 @@ fun DueTodayTasksScreen(
|
||||
Text("Nothing due today !")
|
||||
}
|
||||
} else {
|
||||
val context = LocalContext.current
|
||||
LazyColumn(
|
||||
modifier = modifier.padding(8.dp)
|
||||
) {
|
||||
@@ -41,16 +36,9 @@ fun DueTodayTasksScreen(
|
||||
TaskItemScreen(
|
||||
modifier = Modifier.animateItem(),
|
||||
task = 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()
|
||||
}
|
||||
onSwipeLeft = { viewModel.updateTaskDone(task.id!!) },
|
||||
onSwipeRight = { viewModel.deleteTask(task.id!!) },
|
||||
onTaskClick = { viewModel.onTaskClicked(task) }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +25,10 @@ import androidx.compose.material3.LocalContentColor
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.ModalNavigationDrawer
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.SnackbarDuration
|
||||
import androidx.compose.material3.SnackbarHost
|
||||
import androidx.compose.material3.SnackbarHostState
|
||||
import androidx.compose.material3.SnackbarResult
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TopAppBar
|
||||
@@ -53,7 +57,6 @@ import com.wismna.geoffroy.donext.domain.model.AppDestination
|
||||
import com.wismna.geoffroy.donext.presentation.ui.events.UiEvent
|
||||
import com.wismna.geoffroy.donext.presentation.viewmodel.MainViewModel
|
||||
import com.wismna.geoffroy.donext.presentation.viewmodel.TaskListViewModel
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@@ -63,7 +66,6 @@ fun MainScreen(
|
||||
) {
|
||||
val navController = rememberNavController()
|
||||
val drawerState = rememberDrawerState(DrawerValue.Closed)
|
||||
val scope = rememberCoroutineScope()
|
||||
|
||||
if (viewModel.isLoading) {
|
||||
Box(Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
|
||||
@@ -88,7 +90,7 @@ fun MainScreen(
|
||||
},
|
||||
drawerState = drawerState
|
||||
) {
|
||||
AppContent(viewModel = viewModel, navController = navController, scope = scope, drawerState = drawerState)
|
||||
AppContent(viewModel = viewModel, navController = navController, drawerState = drawerState)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,9 +99,11 @@ fun AppContent(
|
||||
modifier : Modifier = Modifier,
|
||||
viewModel: MainViewModel,
|
||||
navController: NavHostController,
|
||||
scope: CoroutineScope,
|
||||
drawerState: DrawerState
|
||||
) {
|
||||
val scope = rememberCoroutineScope()
|
||||
val snackbarHostState = remember { SnackbarHostState() }
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
viewModel.uiEventBus.events.collectLatest { event ->
|
||||
when (event) {
|
||||
@@ -108,13 +112,25 @@ fun AppContent(
|
||||
navController.navigate(event.route)
|
||||
}
|
||||
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(
|
||||
modifier = modifier.background(MaterialTheme.colorScheme.primaryContainer),
|
||||
containerColor = Color.Transparent,
|
||||
snackbarHost = { SnackbarHost(hostState = snackbarHostState) },
|
||||
topBar = {
|
||||
TopAppBar(
|
||||
title = { Text(viewModel.currentDestination.title) },
|
||||
@@ -207,29 +223,17 @@ fun AppContent(
|
||||
}
|
||||
|
||||
val taskListViewModel: TaskListViewModel = hiltViewModel(navBackStackEntry)
|
||||
TaskListScreen(
|
||||
viewModel = taskListViewModel,
|
||||
onTaskClick = { task -> viewModel.onTaskClicked(task) }
|
||||
)
|
||||
TaskListScreen(viewModel = taskListViewModel)
|
||||
}
|
||||
|
||||
composable(AppDestination.ManageLists.route) {
|
||||
ManageListsScreen(
|
||||
modifier = Modifier,
|
||||
showAddListSheet = {viewModel.showAddListSheet = true}
|
||||
)
|
||||
ManageListsScreen(modifier = Modifier)
|
||||
}
|
||||
composable(AppDestination.DueTodayList.route) {
|
||||
DueTodayTasksScreen (
|
||||
modifier = Modifier,
|
||||
onTaskClick = { task -> viewModel.onTaskClicked(task) }
|
||||
)
|
||||
DueTodayTasksScreen (modifier = Modifier)
|
||||
}
|
||||
composable(AppDestination.RecycleBin.route) {
|
||||
RecycleBinScreen(
|
||||
modifier = Modifier,
|
||||
onTaskClick = { task -> viewModel.onTaskClicked(task) }
|
||||
)
|
||||
RecycleBinScreen(modifier = Modifier)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,10 +6,12 @@ import androidx.compose.animation.fadeOut
|
||||
import androidx.compose.animation.togetherWith
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
@@ -58,10 +60,21 @@ import sh.calvin.reorderable.rememberReorderableLazyListState
|
||||
@Composable
|
||||
fun ManageListsScreen(
|
||||
modifier: Modifier,
|
||||
viewModel: ManageListsViewModel = hiltViewModel(),
|
||||
showAddListSheet: () -> Unit
|
||||
viewModel: ManageListsViewModel = hiltViewModel()
|
||||
) {
|
||||
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 reorderState = rememberReorderableLazyListState(
|
||||
lazyListState = lazyListState,
|
||||
|
||||
@@ -57,7 +57,7 @@ fun MenuScreen(
|
||||
},
|
||||
icon = { Icon(Icons.Default.Today, contentDescription = "Due Today") },
|
||||
selected = currentDestination is AppDestination.DueTodayList,
|
||||
onClick = { viewModel.navigateTo(AppDestination.DueTodayList.route) },
|
||||
onClick = { viewModel.navigateTo(AppDestination.DueTodayList.route, currentDestination.route) },
|
||||
modifier = Modifier.padding(NavigationDrawerItemDefaults.ItemPadding)
|
||||
)
|
||||
HorizontalDivider()
|
||||
@@ -73,7 +73,7 @@ fun MenuScreen(
|
||||
icon = { Icon(Icons.Default.LineWeight, contentDescription = list.name) },
|
||||
selected = currentDestination is AppDestination.TaskList &&
|
||||
currentDestination.taskListId == list.id,
|
||||
onClick = { viewModel.navigateTo("taskList/${list.id}") },
|
||||
onClick = { viewModel.navigateTo("taskList/${list.id}", currentDestination.route) },
|
||||
badge = {
|
||||
if (list.overdueCount > 0) {
|
||||
Badge { Text(list.overdueCount.toString()) }
|
||||
@@ -90,14 +90,14 @@ fun MenuScreen(
|
||||
label = { Text("Recycle Bin") },
|
||||
icon = { Icon(Icons.Default.Delete, contentDescription = "Recycle Bin") },
|
||||
selected = currentDestination is AppDestination.RecycleBin,
|
||||
onClick = { viewModel.navigateTo(AppDestination.RecycleBin.route) },
|
||||
onClick = { viewModel.navigateTo(AppDestination.RecycleBin.route, currentDestination.route) },
|
||||
modifier = Modifier.padding(NavigationDrawerItemDefaults.ItemPadding)
|
||||
)
|
||||
NavigationDrawerItem(
|
||||
label = { Text("Edit Lists") },
|
||||
icon = { Icon(Icons.Default.EditNote, contentDescription = "Edit Lists") },
|
||||
selected = currentDestination is AppDestination.ManageLists,
|
||||
onClick = { viewModel.navigateTo(AppDestination.ManageLists.route) },
|
||||
onClick = { viewModel.navigateTo(AppDestination.ManageLists.route, currentDestination.route) },
|
||||
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.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
|
||||
|
||||
@Composable
|
||||
fun RecycleBinScreen(
|
||||
modifier: Modifier = Modifier,
|
||||
viewModel: RecycleBinViewModel = hiltViewModel(),
|
||||
onTaskClick: (task: Task) -> Unit
|
||||
) {
|
||||
val tasks = viewModel.deletedTasks
|
||||
|
||||
@@ -77,15 +75,13 @@ fun RecycleBinScreen(
|
||||
TaskItemScreen(
|
||||
modifier = Modifier.animateItem(),
|
||||
task = item.task,
|
||||
onTaskClick = { onTaskClick(item.task) },
|
||||
onSwipeLeft = {
|
||||
viewModel.restore(item.task.id!!)
|
||||
Toast.makeText(context, "Task restored", Toast.LENGTH_SHORT).show()
|
||||
},
|
||||
onSwipeLeft = { viewModel.restore(item.task.id!!) },
|
||||
onSwipeRight = {
|
||||
// TODO: add confirmation dialog
|
||||
viewModel.deleteForever(item.task.id!!)
|
||||
Toast.makeText(context, "Task deleted", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
},
|
||||
onTaskClick = { viewModel.onTaskClicked(item.task) }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,12 +45,12 @@ import com.wismna.geoffroy.donext.presentation.viewmodel.TaskItemViewModel
|
||||
fun TaskItemScreen(
|
||||
modifier: Modifier = Modifier,
|
||||
task: Task,
|
||||
onTaskClick: (taskId: Long) -> Unit,
|
||||
onSwipeLeft: () -> Unit,
|
||||
onSwipeRight: () -> Unit
|
||||
onSwipeRight: () -> Unit,
|
||||
onTaskClick: (task: Task) -> Unit
|
||||
) {
|
||||
val viewModel = TaskItemViewModel(task)
|
||||
// TODO: change this
|
||||
|
||||
val dismissState = rememberSwipeToDismissBoxState(
|
||||
confirmValueChange = {
|
||||
when (it) {
|
||||
@@ -63,6 +63,7 @@ fun TaskItemScreen(
|
||||
// positional threshold of 25%
|
||||
positionalThreshold = { it * .25f }
|
||||
)
|
||||
|
||||
val baseStyle = MaterialTheme.typography.bodyLarge.copy(
|
||||
fontWeight = when (viewModel.priority) {
|
||||
Priority.HIGH -> FontWeight.Bold
|
||||
@@ -78,7 +79,7 @@ fun TaskItemScreen(
|
||||
)
|
||||
Card(
|
||||
modifier = modifier,
|
||||
onClick = { onTaskClick(viewModel.id) },
|
||||
onClick = { onTaskClick(task) },
|
||||
colors = CardDefaults.cardColors(
|
||||
containerColor = MaterialTheme.colorScheme.surfaceContainer,
|
||||
),
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package com.wismna.geoffroy.donext.presentation.screen
|
||||
|
||||
import android.widget.Toast
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
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.items
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||
import com.wismna.geoffroy.donext.domain.model.Task
|
||||
import com.wismna.geoffroy.donext.presentation.viewmodel.TaskListViewModel
|
||||
|
||||
@Composable
|
||||
fun TaskListScreen(
|
||||
modifier: Modifier = Modifier,
|
||||
viewModel: TaskListViewModel = hiltViewModel<TaskListViewModel>(),
|
||||
onTaskClick: (Task) -> Unit) {
|
||||
) {
|
||||
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
|
||||
val (active, done) = remember(tasks) {
|
||||
tasks.partition { !it.isDone }
|
||||
}
|
||||
|
||||
val context = LocalContext.current
|
||||
LazyColumn(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
contentPadding = PaddingValues(8.dp),
|
||||
@@ -43,15 +54,9 @@ fun TaskListScreen(
|
||||
TaskItemScreen(
|
||||
modifier = Modifier.animateItem(),
|
||||
task = 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()
|
||||
}
|
||||
onSwipeLeft = { viewModel.updateTaskDone(task.id!!, true) },
|
||||
onSwipeRight = { viewModel.deleteTask(task.id!!) },
|
||||
onTaskClick = { viewModel.onTaskClicked(task) }
|
||||
)
|
||||
}
|
||||
|
||||
@@ -74,17 +79,10 @@ fun TaskListScreen(
|
||||
TaskItemScreen(
|
||||
modifier = Modifier.animateItem(),
|
||||
task = 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()
|
||||
},
|
||||
onSwipeLeft = { viewModel.updateTaskDone(task.id!!, false) },
|
||||
onSwipeRight = { viewModel.deleteTask(task.id!!) },
|
||||
onTaskClick = { viewModel.onTaskClicked(task) }
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -5,9 +5,11 @@ import com.wismna.geoffroy.donext.domain.model.Task
|
||||
sealed class UiEvent {
|
||||
data class Navigate(val route: String) : UiEvent()
|
||||
data object NavigateBack : UiEvent()
|
||||
data class ShowSnackbar(val message: String) : UiEvent()
|
||||
|
||||
data class EditTask(val task: Task) : UiEvent()
|
||||
data class CreateNewTask(val taskListId: Long) : UiEvent()
|
||||
data object CloseTask : UiEvent()
|
||||
data class ShowUndoSnackbar(
|
||||
val message: String,
|
||||
val undoAction: () -> Unit
|
||||
) : UiEvent()
|
||||
}
|
||||
@@ -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.ToggleTaskDeletedUseCase
|
||||
import com.wismna.geoffroy.donext.domain.usecase.ToggleTaskDoneUseCase
|
||||
import com.wismna.geoffroy.donext.presentation.ui.events.UiEvent
|
||||
import com.wismna.geoffroy.donext.presentation.ui.events.UiEventBus
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
@@ -17,30 +19,60 @@ import javax.inject.Inject
|
||||
|
||||
@HiltViewModel
|
||||
class DueTodayViewModel @Inject constructor(
|
||||
getDueTodayTasks: GetDueTodayTasksUseCase,
|
||||
private val toggleTaskDeleted: ToggleTaskDeletedUseCase,
|
||||
private val toggleTaskDone: ToggleTaskDoneUseCase
|
||||
getDueTodayTasksUseCase: GetDueTodayTasksUseCase,
|
||||
private val toggleTaskDeletedUseCase: ToggleTaskDeletedUseCase,
|
||||
private val toggleTaskDoneUseCase: ToggleTaskDoneUseCase,
|
||||
private val uiEventBus: UiEventBus
|
||||
) : ViewModel() {
|
||||
|
||||
var dueTodayTasks by mutableStateOf<List<Task>>(emptyList())
|
||||
private set
|
||||
|
||||
init {
|
||||
getDueTodayTasks()
|
||||
getDueTodayTasksUseCase()
|
||||
.onEach { tasks ->
|
||||
dueTodayTasks = tasks
|
||||
}
|
||||
.launchIn(viewModelScope)
|
||||
}
|
||||
|
||||
fun onTaskClicked(task: Task) {
|
||||
viewModelScope.launch {
|
||||
uiEventBus.send(UiEvent.EditTask(task))
|
||||
}
|
||||
}
|
||||
|
||||
fun updateTaskDone(taskId: Long) {
|
||||
viewModelScope.launch {
|
||||
toggleTaskDone(taskId, true)
|
||||
toggleTaskDoneUseCase(taskId, true)
|
||||
|
||||
uiEventBus.send(
|
||||
UiEvent.ShowUndoSnackbar(
|
||||
message = "Task done",
|
||||
undoAction = {
|
||||
viewModelScope.launch {
|
||||
toggleTaskDoneUseCase(taskId, false)
|
||||
}
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun deleteTask(taskId: Long) {
|
||||
viewModelScope.launch {
|
||||
toggleTaskDeleted(taskId, true)
|
||||
toggleTaskDeletedUseCase(taskId, true)
|
||||
|
||||
uiEventBus.send(
|
||||
UiEvent.ShowUndoSnackbar(
|
||||
message = "Task moved to recycle bin",
|
||||
undoAction = {
|
||||
viewModelScope.launch {
|
||||
toggleTaskDeletedUseCase(taskId, false)
|
||||
}
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,6 @@ 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.model.Task
|
||||
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.UiEventBus
|
||||
@@ -47,10 +46,10 @@ class MainViewModel @Inject constructor(
|
||||
AppDestination.ManageLists +
|
||||
AppDestination.RecycleBin +
|
||||
AppDestination.DueTodayList
|
||||
isLoading = false
|
||||
if (startDestination == AppDestination.ManageLists && destinations.isNotEmpty()) {
|
||||
startDestination = destinations.first()
|
||||
}
|
||||
isLoading = false
|
||||
}
|
||||
.launchIn(viewModelScope)
|
||||
}
|
||||
@@ -68,13 +67,6 @@ class MainViewModel @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
fun onTaskClicked(task: Task) {
|
||||
showTaskSheet = true
|
||||
viewModelScope.launch {
|
||||
uiEventBus.send(UiEvent.EditTask(task))
|
||||
}
|
||||
}
|
||||
|
||||
fun onDismissTaskSheet() {
|
||||
showTaskSheet = false
|
||||
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.GetTaskListsUseCase
|
||||
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 kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
@@ -22,7 +24,8 @@ class ManageListsViewModel @Inject constructor(
|
||||
getTaskListsUseCase: GetTaskListsUseCase,
|
||||
private val addTaskListUseCase: AddTaskListUseCase,
|
||||
private val updateTaskListUseCase: UpdateTaskListUseCase,
|
||||
private val deleteTaskListUseCase: DeleteTaskListUseCase
|
||||
private val deleteTaskListUseCase: DeleteTaskListUseCase,
|
||||
private val uiEventBus: UiEventBus
|
||||
) : ViewModel() {
|
||||
|
||||
var taskLists by mutableStateOf<List<TaskList>>(emptyList())
|
||||
@@ -51,7 +54,18 @@ class ManageListsViewModel @Inject constructor(
|
||||
}
|
||||
fun deleteTaskList(taskListId: Long) {
|
||||
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)
|
||||
}
|
||||
|
||||
fun navigateTo(route: String) {
|
||||
fun navigateTo(route: String, currentRoute: String) {
|
||||
if (route != currentRoute) {
|
||||
viewModelScope.launch {
|
||||
uiEventBus.send(UiEvent.Navigate(route))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,11 +5,14 @@ import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.wismna.geoffroy.donext.domain.model.Task
|
||||
import com.wismna.geoffroy.donext.domain.model.TaskWithListName
|
||||
import com.wismna.geoffroy.donext.domain.usecase.EmptyRecycleBinUseCase
|
||||
import com.wismna.geoffroy.donext.domain.usecase.GetDeletedTasksUseCase
|
||||
import com.wismna.geoffroy.donext.domain.usecase.PermanentlyDeleteTaskUseCase
|
||||
import com.wismna.geoffroy.donext.domain.usecase.ToggleTaskDeletedUseCase
|
||||
import com.wismna.geoffroy.donext.presentation.ui.events.UiEvent
|
||||
import com.wismna.geoffroy.donext.presentation.ui.events.UiEventBus
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
@@ -18,10 +21,11 @@ import javax.inject.Inject
|
||||
|
||||
@HiltViewModel
|
||||
class RecycleBinViewModel @Inject constructor(
|
||||
private val getDeletedTasks: GetDeletedTasksUseCase,
|
||||
private val restoreTask: ToggleTaskDeletedUseCase,
|
||||
private val permanentlyDeleteTask: PermanentlyDeleteTaskUseCase,
|
||||
private val emptyRecycleBinUseCase: EmptyRecycleBinUseCase
|
||||
private val getDeletedTasksUseCase: GetDeletedTasksUseCase,
|
||||
private val toggleTaskDeletedUseCase: ToggleTaskDeletedUseCase,
|
||||
private val permanentlyDeleteTaskUseCase: PermanentlyDeleteTaskUseCase,
|
||||
private val emptyRecycleBinUseCase: EmptyRecycleBinUseCase,
|
||||
private val uiEventBus: UiEventBus
|
||||
) : ViewModel() {
|
||||
|
||||
var deletedTasks by mutableStateOf<List<TaskWithListName>>(emptyList())
|
||||
@@ -31,8 +35,14 @@ class RecycleBinViewModel @Inject constructor(
|
||||
loadDeletedTasks()
|
||||
}
|
||||
|
||||
fun onTaskClicked(task: Task) {
|
||||
viewModelScope.launch {
|
||||
uiEventBus.send(UiEvent.EditTask(task))
|
||||
}
|
||||
}
|
||||
|
||||
fun loadDeletedTasks() {
|
||||
getDeletedTasks()
|
||||
getDeletedTasksUseCase()
|
||||
.onEach { tasks ->
|
||||
deletedTasks = tasks
|
||||
}
|
||||
@@ -41,14 +51,25 @@ class RecycleBinViewModel @Inject constructor(
|
||||
|
||||
fun restore(taskId: Long) {
|
||||
viewModelScope.launch {
|
||||
restoreTask(taskId, false)
|
||||
toggleTaskDeletedUseCase(taskId, false)
|
||||
loadDeletedTasks()
|
||||
|
||||
uiEventBus.send(
|
||||
UiEvent.ShowUndoSnackbar(
|
||||
message = "Task restored",
|
||||
undoAction = {
|
||||
viewModelScope.launch {
|
||||
toggleTaskDeletedUseCase(taskId, true)
|
||||
}
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun deleteForever(taskId: Long) {
|
||||
viewModelScope.launch {
|
||||
permanentlyDeleteTask(taskId)
|
||||
permanentlyDeleteTaskUseCase(taskId)
|
||||
loadDeletedTasks()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,8 @@ import com.wismna.geoffroy.donext.domain.model.Task
|
||||
import com.wismna.geoffroy.donext.domain.usecase.GetTasksForListUseCase
|
||||
import com.wismna.geoffroy.donext.domain.usecase.ToggleTaskDeletedUseCase
|
||||
import com.wismna.geoffroy.donext.domain.usecase.ToggleTaskDoneUseCase
|
||||
import com.wismna.geoffroy.donext.presentation.ui.events.UiEvent
|
||||
import com.wismna.geoffroy.donext.presentation.ui.events.UiEventBus
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
@@ -18,10 +20,11 @@ import javax.inject.Inject
|
||||
|
||||
@HiltViewModel
|
||||
class TaskListViewModel @Inject constructor(
|
||||
getTasks: GetTasksForListUseCase,
|
||||
savedStateHandle: SavedStateHandle,
|
||||
private val toggleTaskDone: ToggleTaskDoneUseCase,
|
||||
private val toggleTaskDeleted: ToggleTaskDeletedUseCase,
|
||||
getTasksUseCase: GetTasksForListUseCase,
|
||||
private val toggleTaskDoneUseCase: ToggleTaskDoneUseCase,
|
||||
private val toggleTaskDeletedUseCase: ToggleTaskDeletedUseCase,
|
||||
private val uiEventBus: UiEventBus
|
||||
) : ViewModel() {
|
||||
|
||||
var tasks by mutableStateOf<List<Task>>(emptyList())
|
||||
@@ -32,7 +35,7 @@ class TaskListViewModel @Inject constructor(
|
||||
private val taskListId: Long = checkNotNull(savedStateHandle["taskListId"])
|
||||
|
||||
init {
|
||||
getTasks(taskListId)
|
||||
getTasksUseCase(taskListId)
|
||||
.onEach { list ->
|
||||
tasks = list
|
||||
isLoading = false
|
||||
@@ -40,14 +43,42 @@ class TaskListViewModel @Inject constructor(
|
||||
.launchIn(viewModelScope)
|
||||
}
|
||||
|
||||
fun onTaskClicked(task: Task) {
|
||||
viewModelScope.launch {
|
||||
uiEventBus.send(UiEvent.EditTask(task))
|
||||
}
|
||||
}
|
||||
|
||||
fun updateTaskDone(taskId: Long, isDone: Boolean) {
|
||||
viewModelScope.launch {
|
||||
toggleTaskDone(taskId, isDone)
|
||||
toggleTaskDoneUseCase(taskId, isDone)
|
||||
|
||||
uiEventBus.send(
|
||||
UiEvent.ShowUndoSnackbar(
|
||||
message = "Task done",
|
||||
undoAction = {
|
||||
viewModelScope.launch {
|
||||
toggleTaskDoneUseCase(taskId, !isDone)
|
||||
}
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
fun deleteTask(taskId: Long) {
|
||||
viewModelScope.launch {
|
||||
toggleTaskDeleted(taskId, true)
|
||||
toggleTaskDeletedUseCase(taskId, true)
|
||||
|
||||
uiEventBus.send(
|
||||
UiEvent.ShowUndoSnackbar(
|
||||
message = "Task moved to recycle bin",
|
||||
undoAction = {
|
||||
viewModelScope.launch {
|
||||
toggleTaskDeletedUseCase(taskId, false)
|
||||
}
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -45,8 +45,8 @@ class TaskViewModel @Inject constructor(
|
||||
viewModelScope.launch {
|
||||
uiEventBus.events.collect { event ->
|
||||
when (event) {
|
||||
is UiEvent.EditTask -> startEditTask(event.task)
|
||||
is UiEvent.CreateNewTask -> startNewTask(event.taskListId)
|
||||
is UiEvent.EditTask -> startEditTask(event.task)
|
||||
is UiEvent.CloseTask -> reset()
|
||||
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 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 onDescriptionChanged(value: String) { description = 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
|
||||
taskListId = null
|
||||
title = ""
|
||||
|
||||
Reference in New Issue
Block a user