mirror of
https://github.com/wismna/DoNext.git
synced 2025-10-03 07:30:13 -04:00
Compare commits
3 Commits
926a9bf66b
...
f8fd041f8e
Author | SHA1 | Date | |
---|---|---|---|
![]() |
f8fd041f8e | ||
![]() |
0c5bf77b4d | ||
![]() |
78ce584900 |
@@ -7,7 +7,7 @@
|
|||||||
<activity
|
<activity
|
||||||
android:name="com.wismna.geoffroy.donext.presentation.MainActivity"
|
android:name="com.wismna.geoffroy.donext.presentation.MainActivity"
|
||||||
android:exported="true"
|
android:exported="true"
|
||||||
android:label="@string/title_activity_main"
|
android:label="DoNext"
|
||||||
android:theme="@style/Theme.DoNext">
|
android:theme="@style/Theme.DoNext">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN" />
|
||||||
|
@@ -0,0 +1,18 @@
|
|||||||
|
package com.wismna.geoffroy.donext.domain.model
|
||||||
|
|
||||||
|
sealed class AppDestination(
|
||||||
|
val route: String,
|
||||||
|
val title: String,
|
||||||
|
val showBackButton: Boolean = false,
|
||||||
|
) {
|
||||||
|
data class TaskList(val taskListId: Long, val name: String) : AppDestination(
|
||||||
|
route = "taskList/$taskListId",
|
||||||
|
title = name,
|
||||||
|
)
|
||||||
|
|
||||||
|
object ManageLists : AppDestination(
|
||||||
|
route = "manageLists",
|
||||||
|
title = "Manage Lists",
|
||||||
|
showBackButton = true,
|
||||||
|
)
|
||||||
|
}
|
@@ -3,13 +3,19 @@ package com.wismna.geoffroy.donext.domain.usecase
|
|||||||
import com.wismna.geoffroy.donext.domain.model.TaskListWithOverdue
|
import com.wismna.geoffroy.donext.domain.model.TaskListWithOverdue
|
||||||
import com.wismna.geoffroy.donext.domain.repository.TaskRepository
|
import com.wismna.geoffroy.donext.domain.repository.TaskRepository
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import java.time.Instant
|
import java.time.LocalDate
|
||||||
|
import java.time.ZoneOffset
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
class GetTaskListsWithOverdueUseCase @Inject constructor(
|
class GetTaskListsWithOverdueUseCase @Inject constructor(
|
||||||
private val taskRepository: TaskRepository
|
private val taskRepository: TaskRepository
|
||||||
) {
|
) {
|
||||||
operator fun invoke(): Flow<List<TaskListWithOverdue>> {
|
operator fun invoke(): Flow<List<TaskListWithOverdue>> {
|
||||||
return taskRepository.getTaskListsWithOverdue(Instant.parse("2025-09-15T12:00:00Z").toEpochMilli())
|
return taskRepository.getTaskListsWithOverdue(
|
||||||
|
LocalDate.now()
|
||||||
|
.atStartOfDay(ZoneOffset.UTC) // or system default
|
||||||
|
.toInstant()
|
||||||
|
.toEpochMilli()
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -4,6 +4,7 @@ import android.os.Bundle
|
|||||||
import androidx.activity.ComponentActivity
|
import androidx.activity.ComponentActivity
|
||||||
import androidx.activity.compose.setContent
|
import androidx.activity.compose.setContent
|
||||||
import androidx.activity.enableEdgeToEdge
|
import androidx.activity.enableEdgeToEdge
|
||||||
|
import androidx.compose.foundation.isSystemInDarkTheme
|
||||||
import com.wismna.geoffroy.donext.presentation.screen.MainScreen
|
import com.wismna.geoffroy.donext.presentation.screen.MainScreen
|
||||||
import com.wismna.geoffroy.donext.presentation.ui.theme.DoNextTheme
|
import com.wismna.geoffroy.donext.presentation.ui.theme.DoNextTheme
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
@@ -14,7 +15,7 @@ class MainActivity : ComponentActivity() {
|
|||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
enableEdgeToEdge()
|
enableEdgeToEdge()
|
||||||
setContent {
|
setContent {
|
||||||
DoNextTheme { MainScreen() }
|
DoNextTheme(darkTheme = isSystemInDarkTheme(), dynamicColor = false) { MainScreen() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -11,6 +11,7 @@ import androidx.compose.foundation.layout.fillMaxSize
|
|||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.filled.Add
|
||||||
import androidx.compose.material.icons.filled.ArrowBack
|
import androidx.compose.material.icons.filled.ArrowBack
|
||||||
import androidx.compose.material.icons.filled.Menu
|
import androidx.compose.material.icons.filled.Menu
|
||||||
import androidx.compose.material3.CircularProgressIndicator
|
import androidx.compose.material3.CircularProgressIndicator
|
||||||
@@ -39,45 +40,26 @@ import androidx.compose.ui.Modifier
|
|||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
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 androidx.navigation.NavBackStackEntry
|
|
||||||
import androidx.navigation.NavType
|
import androidx.navigation.NavType
|
||||||
import androidx.navigation.compose.NavHost
|
import androidx.navigation.compose.NavHost
|
||||||
import androidx.navigation.compose.composable
|
import androidx.navigation.compose.composable
|
||||||
import androidx.navigation.compose.currentBackStackEntryAsState
|
import androidx.navigation.compose.currentBackStackEntryAsState
|
||||||
import androidx.navigation.compose.rememberNavController
|
import androidx.navigation.compose.rememberNavController
|
||||||
import androidx.navigation.navArgument
|
import androidx.navigation.navArgument
|
||||||
import com.wismna.geoffroy.donext.domain.model.TaskListWithOverdue
|
import com.wismna.geoffroy.donext.domain.model.AppDestination
|
||||||
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 com.wismna.geoffroy.donext.presentation.viewmodel.TaskViewModel
|
import com.wismna.geoffroy.donext.presentation.viewmodel.TaskViewModel
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
sealed class AppDestination(
|
|
||||||
val route: String,
|
|
||||||
val title: String,
|
|
||||||
val showBackButton: Boolean = false,
|
|
||||||
val actions: @Composable (() -> Unit)? = null
|
|
||||||
) {
|
|
||||||
data class TaskList(val taskListId: Long, val name: String) : AppDestination(
|
|
||||||
route = "taskList/$taskListId",
|
|
||||||
title = name,
|
|
||||||
)
|
|
||||||
|
|
||||||
object ManageLists : AppDestination(
|
|
||||||
route = "manageLists",
|
|
||||||
title = "Manage Lists",
|
|
||||||
showBackButton = true,
|
|
||||||
actions = { ManageListsActions() }
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun MainScreen(
|
fun MainScreen(
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
viewModel: MainViewModel = hiltViewModel()
|
viewModel: MainViewModel = hiltViewModel()
|
||||||
) {
|
) {
|
||||||
val navController = rememberNavController()
|
val navController = rememberNavController()
|
||||||
var showBottomSheet by remember { mutableStateOf(false) }
|
var showTaskSheet by remember { mutableStateOf(false) }
|
||||||
|
var showAddListSheet by remember { mutableStateOf(false) }
|
||||||
val drawerState = rememberDrawerState(DrawerValue.Closed)
|
val drawerState = rememberDrawerState(DrawerValue.Closed)
|
||||||
val scope = rememberCoroutineScope()
|
val scope = rememberCoroutineScope()
|
||||||
val taskViewModel: TaskViewModel = hiltViewModel()
|
val taskViewModel: TaskViewModel = hiltViewModel()
|
||||||
@@ -89,25 +71,26 @@ fun MainScreen(
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
val firstListId = viewModel.taskLists.firstOrNull()?.id
|
val startDestination = viewModel.destinations.firstOrNull() ?: AppDestination.ManageLists
|
||||||
if (showBottomSheet) {
|
if (showTaskSheet) {
|
||||||
TaskBottomSheet(taskViewModel) { showBottomSheet = false }
|
TaskBottomSheet(taskViewModel) { showTaskSheet = false }
|
||||||
|
}
|
||||||
|
if (showAddListSheet) {
|
||||||
|
AddListBottomSheet { showAddListSheet = false }
|
||||||
}
|
}
|
||||||
|
|
||||||
val navBackStackEntry by navController.currentBackStackEntryAsState()
|
val navBackStackEntry by navController.currentBackStackEntryAsState()
|
||||||
val currentDestination = remember(navBackStackEntry, viewModel.taskLists) {
|
val currentDestination =
|
||||||
deriveDestination(navBackStackEntry, viewModel.taskLists)
|
viewModel.deriveDestination(navBackStackEntry?.destination?.route)
|
||||||
}
|
?: startDestination
|
||||||
|
|
||||||
ModalNavigationDrawer(
|
ModalNavigationDrawer(
|
||||||
drawerContent = {
|
drawerContent = {
|
||||||
MenuScreen (
|
MenuScreen (
|
||||||
taskLists = viewModel.taskLists,
|
|
||||||
currentDestination = currentDestination,
|
currentDestination = currentDestination,
|
||||||
onNavigate = { route ->
|
onNavigate = { route ->
|
||||||
scope.launch { drawerState.close() }
|
scope.launch { drawerState.close() }
|
||||||
navController.navigate(route) {
|
navController.navigate(route) {
|
||||||
//launchSingleTop = true
|
|
||||||
restoreState = true
|
restoreState = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -120,7 +103,7 @@ fun MainScreen(
|
|||||||
containerColor = Color.Transparent,
|
containerColor = Color.Transparent,
|
||||||
topBar = {
|
topBar = {
|
||||||
TopAppBar(
|
TopAppBar(
|
||||||
title = { Text(currentDestination.title) },
|
title = { Text(currentDestination!!.title) },
|
||||||
colors = TopAppBarDefaults.topAppBarColors(
|
colors = TopAppBarDefaults.topAppBarColors(
|
||||||
containerColor = MaterialTheme.colorScheme.primaryContainer,
|
containerColor = MaterialTheme.colorScheme.primaryContainer,
|
||||||
titleContentColor = MaterialTheme.colorScheme.onPrimaryContainer,
|
titleContentColor = MaterialTheme.colorScheme.onPrimaryContainer,
|
||||||
@@ -128,7 +111,7 @@ fun MainScreen(
|
|||||||
),
|
),
|
||||||
navigationIcon = {
|
navigationIcon = {
|
||||||
CompositionLocalProvider(LocalContentColor provides MaterialTheme.colorScheme.onPrimaryContainer) {
|
CompositionLocalProvider(LocalContentColor provides MaterialTheme.colorScheme.onPrimaryContainer) {
|
||||||
if (currentDestination.showBackButton) {
|
if (currentDestination!!.showBackButton) {
|
||||||
IconButton(onClick = { navController.popBackStack() }) {
|
IconButton(onClick = { navController.popBackStack() }) {
|
||||||
Icon(Icons.Default.ArrowBack, contentDescription = "Back")
|
Icon(Icons.Default.ArrowBack, contentDescription = "Back")
|
||||||
}
|
}
|
||||||
@@ -142,7 +125,13 @@ fun MainScreen(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
actions = { currentDestination.actions?.invoke() }
|
actions = {
|
||||||
|
if (currentDestination is AppDestination.ManageLists) {
|
||||||
|
IconButton(onClick = { showAddListSheet = true }) {
|
||||||
|
Icon(Icons.Default.Add, contentDescription = "Add List")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
floatingActionButton = {
|
floatingActionButton = {
|
||||||
@@ -150,7 +139,7 @@ fun MainScreen(
|
|||||||
is AppDestination.TaskList -> {
|
is AppDestination.TaskList -> {
|
||||||
TaskListFab(
|
TaskListFab(
|
||||||
taskListId = dest.taskListId,
|
taskListId = dest.taskListId,
|
||||||
showBottomSheet = { showBottomSheet = it }
|
showBottomSheet = { showTaskSheet = it }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
else -> null
|
else -> null
|
||||||
@@ -166,8 +155,7 @@ fun MainScreen(
|
|||||||
) {
|
) {
|
||||||
NavHost(
|
NavHost(
|
||||||
navController = navController,
|
navController = navController,
|
||||||
startDestination = firstListId?.let { "taskList/$it" }
|
startDestination = startDestination.route,
|
||||||
?: AppDestination.ManageLists.route,
|
|
||||||
enterTransition = {
|
enterTransition = {
|
||||||
slideInHorizontally(initialOffsetX = { fullWidth -> fullWidth }, animationSpec = tween(300))
|
slideInHorizontally(initialOffsetX = { fullWidth -> fullWidth }, animationSpec = tween(300))
|
||||||
},
|
},
|
||||||
@@ -181,7 +169,7 @@ fun MainScreen(
|
|||||||
slideOutHorizontally(targetOffsetX = { fullWidth -> fullWidth }, animationSpec = tween(300))
|
slideOutHorizontally(targetOffsetX = { fullWidth -> fullWidth }, animationSpec = tween(300))
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
viewModel.taskLists.forEach { list ->
|
viewModel.destinations.forEach { destination ->
|
||||||
composable(
|
composable(
|
||||||
route = "taskList/{taskListId}",
|
route = "taskList/{taskListId}",
|
||||||
arguments = listOf(navArgument("taskListId") {
|
arguments = listOf(navArgument("taskListId") {
|
||||||
@@ -193,40 +181,20 @@ fun MainScreen(
|
|||||||
viewModel = viewModel,
|
viewModel = viewModel,
|
||||||
onTaskClick = { task ->
|
onTaskClick = { task ->
|
||||||
taskViewModel.startEditTask(task)
|
taskViewModel.startEditTask(task)
|
||||||
showBottomSheet = true
|
showTaskSheet = true
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
composable(AppDestination.ManageLists.route) {
|
composable(AppDestination.ManageLists.route) {
|
||||||
ManageListsScreen(modifier = Modifier)
|
ManageListsScreen(
|
||||||
|
modifier = Modifier,
|
||||||
|
showAddListSheet = {showAddListSheet = true}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
fun deriveDestination(
|
|
||||||
navBackStackEntry: NavBackStackEntry?,
|
|
||||||
taskLists: List<TaskListWithOverdue>
|
|
||||||
): AppDestination {
|
|
||||||
val route = navBackStackEntry?.destination?.route
|
|
||||||
|
|
||||||
return when {
|
|
||||||
route == AppDestination.ManageLists.route -> AppDestination.ManageLists
|
|
||||||
route?.startsWith("taskList/") == true || route == "taskList/{taskListId}" -> {
|
|
||||||
val idArg = navBackStackEntry.arguments?.getLong("taskListId")
|
|
||||||
val taskListId = idArg ?: route.substringAfter("taskList/", "").toLongOrNull()
|
|
||||||
val matching = taskLists.find { it.id == taskListId }
|
|
||||||
matching?.let { AppDestination.TaskList(it.id, it.name) }
|
|
||||||
?: taskLists.firstOrNull()?.let { AppDestination.TaskList(it.id, it.name) }
|
|
||||||
?: AppDestination.ManageLists
|
|
||||||
}
|
|
||||||
else -> {
|
|
||||||
taskLists.firstOrNull()?.let { AppDestination.TaskList(it.id, it.name) }
|
|
||||||
?: AppDestination.ManageLists
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
@@ -1,14 +1,20 @@
|
|||||||
package com.wismna.geoffroy.donext.presentation.screen
|
package com.wismna.geoffroy.donext.presentation.screen
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.Spacer
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.height
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.layout.width
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
import androidx.compose.foundation.lazy.itemsIndexed
|
import androidx.compose.foundation.lazy.itemsIndexed
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.Add
|
import androidx.compose.material.icons.filled.Check
|
||||||
import androidx.compose.material.icons.filled.Delete
|
import androidx.compose.material.icons.filled.Delete
|
||||||
import androidx.compose.material.icons.filled.Edit
|
import androidx.compose.material.icons.filled.Edit
|
||||||
|
import androidx.compose.material3.Button
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.HorizontalDivider
|
import androidx.compose.material3.HorizontalDivider
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
@@ -16,24 +22,38 @@ import androidx.compose.material3.IconButton
|
|||||||
import androidx.compose.material3.ListItem
|
import androidx.compose.material3.ListItem
|
||||||
import androidx.compose.material3.LocalContentColor
|
import androidx.compose.material3.LocalContentColor
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.ModalBottomSheet
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.material3.TextButton
|
||||||
|
import androidx.compose.material3.TextField
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.CompositionLocalProvider
|
import androidx.compose.runtime.CompositionLocalProvider
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.focus.FocusRequester
|
||||||
|
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.TaskList
|
||||||
import com.wismna.geoffroy.donext.presentation.viewmodel.ManageListsViewModel
|
import com.wismna.geoffroy.donext.presentation.viewmodel.ManageListsViewModel
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun ManageListsScreen(
|
fun ManageListsScreen(
|
||||||
modifier: Modifier,
|
modifier: Modifier,
|
||||||
viewModel: ManageListsViewModel = hiltViewModel()
|
viewModel: ManageListsViewModel = hiltViewModel(),
|
||||||
|
showAddListSheet: () -> Unit
|
||||||
) {
|
) {
|
||||||
val lists = viewModel.taskLists
|
val lists = viewModel.taskLists
|
||||||
|
|
||||||
LazyColumn(modifier = modifier.fillMaxWidth().padding()) {
|
LazyColumn(modifier = modifier.fillMaxWidth().padding()) {
|
||||||
itemsIndexed(lists, key = { _, list -> list.id!! }) { index, list ->
|
itemsIndexed(lists, key = { _, list -> list.id!! }) { index, list ->
|
||||||
ListItem(
|
ListItem(
|
||||||
|
modifier = Modifier.animateItem(),
|
||||||
headlineContent = { Text(list.name) },
|
headlineContent = { Text(list.name) },
|
||||||
trailingContent = {
|
trailingContent = {
|
||||||
Row {
|
Row {
|
||||||
@@ -54,10 +74,83 @@ fun ManageListsScreen(
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun ManageListsActions(
|
fun EditableListRow(
|
||||||
viewModel: ManageListsViewModel = hiltViewModel()
|
list: TaskList,
|
||||||
|
onNameChange: (String) -> Unit,
|
||||||
|
//onTypeChange: (ListType) -> Unit,
|
||||||
|
onDone: () -> Unit
|
||||||
) {
|
) {
|
||||||
IconButton(onClick = { viewModel.createTaskList("Test", 1) }) {
|
var name by remember { mutableStateOf(list.name) }
|
||||||
Icon(Icons.Default.Add, contentDescription = "Add List")
|
//var type by remember { mutableStateOf(list.type) }
|
||||||
|
|
||||||
|
Row(
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
modifier = Modifier.fillMaxWidth().padding(8.dp)
|
||||||
|
) {
|
||||||
|
TextField(
|
||||||
|
value = name,
|
||||||
|
onValueChange = { name = it },
|
||||||
|
modifier = Modifier.weight(1f),
|
||||||
|
singleLine = true
|
||||||
|
)
|
||||||
|
// TODO: implement type
|
||||||
|
//DropdownSelector(selected = type, onSelect = { type = it; onTypeChange(it) })
|
||||||
|
IconButton(onClick = onDone) {
|
||||||
|
Icon(Icons.Default.Check, contentDescription = "Save")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
|
@Composable
|
||||||
|
fun AddListBottomSheet(
|
||||||
|
viewModel: ManageListsViewModel = hiltViewModel(),
|
||||||
|
onDismiss: () -> Unit
|
||||||
|
) {
|
||||||
|
val titleFocusRequester = remember { FocusRequester() }
|
||||||
|
|
||||||
|
LaunchedEffect(Unit) {
|
||||||
|
titleFocusRequester.requestFocus()
|
||||||
|
}
|
||||||
|
|
||||||
|
ModalBottomSheet(onDismissRequest = onDismiss) {
|
||||||
|
var name by remember { mutableStateOf("") }
|
||||||
|
//var type by remember { mutableStateOf(ListType.Default) }
|
||||||
|
//var description by remember { mutableStateOf("") }
|
||||||
|
|
||||||
|
Column(modifier = Modifier.padding(16.dp)) {
|
||||||
|
Text("Create New List", style = MaterialTheme.typography.titleMedium)
|
||||||
|
|
||||||
|
Spacer(Modifier.height(8.dp))
|
||||||
|
TextField(
|
||||||
|
value = name,
|
||||||
|
onValueChange = { name = it },
|
||||||
|
label = { Text("List Name") },
|
||||||
|
singleLine = true
|
||||||
|
)
|
||||||
|
|
||||||
|
Spacer(Modifier.height(8.dp))
|
||||||
|
//DropdownSelector(selected = type, onSelect = { type = it })
|
||||||
|
|
||||||
|
/*Spacer(Modifier.height(8.dp))
|
||||||
|
TextField(
|
||||||
|
value = description,
|
||||||
|
onValueChange = { description = it },
|
||||||
|
label = { Text("Description") },
|
||||||
|
maxLines = 3
|
||||||
|
)*/
|
||||||
|
|
||||||
|
Spacer(Modifier.height(16.dp))
|
||||||
|
Row(horizontalArrangement = Arrangement.End, modifier = Modifier.fillMaxWidth()) {
|
||||||
|
TextButton(onClick = onDismiss) { Text("Cancel") }
|
||||||
|
Spacer(Modifier.width(8.dp))
|
||||||
|
Button(onClick = {
|
||||||
|
viewModel.createTaskList(name/*, type, description*/, 1)
|
||||||
|
onDismiss()
|
||||||
|
}) {
|
||||||
|
Text("Add")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -18,11 +18,13 @@ import androidx.compose.material3.Text
|
|||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import com.wismna.geoffroy.donext.domain.model.TaskListWithOverdue
|
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||||
|
import com.wismna.geoffroy.donext.domain.model.AppDestination
|
||||||
|
import com.wismna.geoffroy.donext.presentation.viewmodel.MenuViewModel
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun MenuScreen(
|
fun MenuScreen(
|
||||||
taskLists: List<TaskListWithOverdue>,
|
viewModel: MenuViewModel = hiltViewModel(),
|
||||||
currentDestination: AppDestination,
|
currentDestination: AppDestination,
|
||||||
onNavigate: (String) -> Unit
|
onNavigate: (String) -> Unit
|
||||||
) {
|
) {
|
||||||
@@ -42,7 +44,7 @@ fun MenuScreen(
|
|||||||
style = MaterialTheme.typography.titleMedium,
|
style = MaterialTheme.typography.titleMedium,
|
||||||
modifier = Modifier.padding(16.dp)
|
modifier = Modifier.padding(16.dp)
|
||||||
)
|
)
|
||||||
taskLists.forEach { list ->
|
viewModel.taskLists.forEach { list ->
|
||||||
NavigationDrawerItem(
|
NavigationDrawerItem(
|
||||||
label = { Text(list.name) },
|
label = { Text(list.name) },
|
||||||
icon = { Icon(Icons.Default.List, contentDescription = list.name) },
|
icon = { Icon(Icons.Default.List, contentDescription = list.name) },
|
||||||
|
@@ -5,7 +5,6 @@ import androidx.compose.animation.expandVertically
|
|||||||
import androidx.compose.animation.fadeIn
|
import androidx.compose.animation.fadeIn
|
||||||
import androidx.compose.animation.fadeOut
|
import androidx.compose.animation.fadeOut
|
||||||
import androidx.compose.animation.shrinkVertically
|
import androidx.compose.animation.shrinkVertically
|
||||||
import androidx.compose.foundation.background
|
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.IntrinsicSize
|
import androidx.compose.foundation.layout.IntrinsicSize
|
||||||
@@ -15,7 +14,7 @@ import androidx.compose.foundation.layout.height
|
|||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.layout.size
|
import androidx.compose.foundation.layout.size
|
||||||
import androidx.compose.foundation.shape.CircleShape
|
import androidx.compose.foundation.shape.CircleShape
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.material3.Badge
|
||||||
import androidx.compose.material3.Checkbox
|
import androidx.compose.material3.Checkbox
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
@@ -88,23 +87,19 @@ fun TaskItemScreen(
|
|||||||
|
|
||||||
// Due date badge
|
// Due date badge
|
||||||
viewModel.dueDateText?.let { dueMillis ->
|
viewModel.dueDateText?.let { dueMillis ->
|
||||||
Box(
|
Badge(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.align(
|
.align(
|
||||||
if (viewModel.description.isNullOrBlank()) Alignment.CenterEnd
|
if (viewModel.description.isNullOrBlank()) Alignment.CenterEnd
|
||||||
else Alignment.TopEnd
|
else Alignment.TopEnd
|
||||||
)
|
),
|
||||||
.background(
|
containerColor = if (viewModel.isOverdue) MaterialTheme.colorScheme.error else MaterialTheme.colorScheme.primaryContainer
|
||||||
color = if (viewModel.isOverdue) MaterialTheme.colorScheme.error
|
|
||||||
else MaterialTheme.colorScheme.primary.copy(alpha = 0.1f),
|
|
||||||
shape = RoundedCornerShape(12.dp)
|
|
||||||
)
|
|
||||||
.padding(horizontal = 8.dp, vertical = 2.dp)
|
|
||||||
) {
|
) {
|
||||||
Text(
|
Text(
|
||||||
|
modifier = Modifier.padding(start = 1.dp, end = 1.dp),
|
||||||
text = viewModel.dueDateText,
|
text = viewModel.dueDateText,
|
||||||
style = MaterialTheme.typography.bodySmall,
|
color = if (viewModel.isOverdue) Color.White else MaterialTheme.colorScheme.onPrimaryContainer,
|
||||||
color = if (viewModel.isOverdue) Color.White else MaterialTheme.colorScheme.primary
|
style = MaterialTheme.typography.bodySmall
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,19 +1,20 @@
|
|||||||
package com.wismna.geoffroy.donext.presentation.screen
|
package com.wismna.geoffroy.donext.presentation.screen
|
||||||
|
|
||||||
import androidx.compose.foundation.layout.Spacer
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.height
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
import androidx.compose.foundation.lazy.itemsIndexed
|
import androidx.compose.foundation.lazy.items
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.Add
|
import androidx.compose.material.icons.filled.Add
|
||||||
import androidx.compose.material3.ExtendedFloatingActionButton
|
import androidx.compose.material3.ExtendedFloatingActionButton
|
||||||
import androidx.compose.material3.HorizontalDivider
|
import androidx.compose.material3.HorizontalDivider
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.MaterialTheme
|
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
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
|
||||||
@@ -29,36 +30,53 @@ fun TaskListScreen(
|
|||||||
onTaskClick: (Task) -> Unit) {
|
onTaskClick: (Task) -> Unit) {
|
||||||
val tasks = viewModel.tasks
|
val tasks = viewModel.tasks
|
||||||
|
|
||||||
|
// Split tasks into active and done
|
||||||
|
val (active, done) = remember(tasks) {
|
||||||
|
tasks.partition { !it.isDone }
|
||||||
|
}
|
||||||
|
|
||||||
LazyColumn(
|
LazyColumn(
|
||||||
modifier = modifier.fillMaxSize().padding()
|
modifier = Modifier.fillMaxSize(),
|
||||||
|
contentPadding = PaddingValues(8.dp),
|
||||||
|
verticalArrangement = Arrangement.spacedBy(8.dp)
|
||||||
) {
|
) {
|
||||||
itemsIndexed(tasks, key = { id, task -> task.id!! }) { index, task ->
|
// Active tasks section
|
||||||
if (index > 0) {
|
items(
|
||||||
val prev = tasks[index - 1]
|
items = active,
|
||||||
|
key = { it.id!! }
|
||||||
when {
|
) { task ->
|
||||||
// Divider between non-done and done tasks
|
|
||||||
!prev.isDone && task.isDone -> {
|
|
||||||
HorizontalDivider(
|
|
||||||
thickness = 1.dp,
|
|
||||||
color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.2f),
|
|
||||||
modifier = Modifier.padding(vertical = 8.dp)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Extra spacing between different priorities (only if done status is same)
|
|
||||||
prev.priority != task.priority && prev.isDone == task.isDone -> {
|
|
||||||
Spacer(modifier = Modifier.height(20.dp))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TaskItemScreen(
|
TaskItemScreen(
|
||||||
modifier = Modifier.animateItem(),
|
modifier = Modifier.animateItem(),
|
||||||
viewModel = TaskItemViewModel(task),
|
viewModel = TaskItemViewModel(task),
|
||||||
onClick = { onTaskClick(task) },
|
onClick = { onTaskClick(task) },
|
||||||
onToggleDone = { isChecked ->
|
onToggleDone = { checked ->
|
||||||
viewModel.updateTaskDone(task.id!!, isChecked)
|
viewModel.updateTaskDone(task.id!!, checked)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Divider between active and done (optional)
|
||||||
|
if (done.isNotEmpty() && active.isNotEmpty()) {
|
||||||
|
item {
|
||||||
|
HorizontalDivider(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(vertical = 4.dp)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Done tasks section
|
||||||
|
items(
|
||||||
|
items = done,
|
||||||
|
key = { it.id!! }
|
||||||
|
) { task ->
|
||||||
|
TaskItemScreen(
|
||||||
|
modifier = Modifier.animateItem(),
|
||||||
|
viewModel = TaskItemViewModel(task),
|
||||||
|
onClick = { onTaskClick(task) },
|
||||||
|
onToggleDone = { checked ->
|
||||||
|
viewModel.updateTaskDone(task.id!!, checked)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@@ -42,7 +42,6 @@ import com.wismna.geoffroy.donext.domain.model.Priority
|
|||||||
import com.wismna.geoffroy.donext.presentation.viewmodel.TaskViewModel
|
import com.wismna.geoffroy.donext.presentation.viewmodel.TaskViewModel
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
import java.time.LocalDate
|
import java.time.LocalDate
|
||||||
import java.time.ZoneId
|
|
||||||
import java.time.ZoneOffset
|
import java.time.ZoneOffset
|
||||||
import java.time.format.DateTimeFormatter
|
import java.time.format.DateTimeFormatter
|
||||||
import java.time.format.FormatStyle
|
import java.time.format.FormatStyle
|
||||||
@@ -143,8 +142,8 @@ fun TaskBottomSheet(
|
|||||||
initialSelectedDateMillis = viewModel.dueDate,
|
initialSelectedDateMillis = viewModel.dueDate,
|
||||||
selectableDates = object: SelectableDates {
|
selectableDates = object: SelectableDates {
|
||||||
override fun isSelectableDate(utcTimeMillis: Long): Boolean {
|
override fun isSelectableDate(utcTimeMillis: Long): Boolean {
|
||||||
val todayStartMillis = LocalDate.now()
|
val todayStartMillis = LocalDate.now(ZoneOffset.UTC)
|
||||||
.atStartOfDay(ZoneId.systemDefault())
|
.atStartOfDay(ZoneOffset.UTC)
|
||||||
.toInstant()
|
.toInstant()
|
||||||
.toEpochMilli()
|
.toEpochMilli()
|
||||||
return utcTimeMillis >= todayStartMillis
|
return utcTimeMillis >= todayStartMillis
|
||||||
@@ -173,6 +172,7 @@ fun TaskBottomSheet(
|
|||||||
Row (
|
Row (
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth(),
|
||||||
horizontalArrangement = if (viewModel.isEditing()) Arrangement.SpaceBetween else Arrangement.End) {
|
horizontalArrangement = if (viewModel.isEditing()) Arrangement.SpaceBetween else Arrangement.End) {
|
||||||
|
|
||||||
// --- Delete Button ---
|
// --- Delete Button ---
|
||||||
if (viewModel.isEditing()) {
|
if (viewModel.isEditing()) {
|
||||||
Button(
|
Button(
|
||||||
|
@@ -2,10 +2,26 @@ package com.wismna.geoffroy.donext.presentation.ui.theme
|
|||||||
|
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
|
|
||||||
val Purple80 = Color(0xFFD0BCFF)
|
// Primary shades
|
||||||
val PurpleGrey80 = Color(0xFFCCC2DC)
|
val Purple80 = Color(0xFFD0BCFF) // Light theme primary
|
||||||
val Pink80 = Color(0xFFEFB8C8)
|
val Purple40 = Color(0xFF6650A4) // Dark theme primary
|
||||||
|
|
||||||
val Purple40 = Color(0xFF6650a4)
|
val PurpleGrey80 = Color(0xFFCCC2DC)
|
||||||
val PurpleGrey40 = Color(0xFF625b71)
|
val PurpleGrey40 = Color(0xFF625B71)
|
||||||
val Pink40 = Color(0xFF7D5260)
|
|
||||||
|
val Pink80 = Color(0xFFEFB8C8)
|
||||||
|
val Pink40 = Color(0xFF7D5260)
|
||||||
|
|
||||||
|
// Container variants
|
||||||
|
val Purple80Container = Color(0xFFEADDFF)
|
||||||
|
val Purple40Container = Color(0xFF381E72)
|
||||||
|
|
||||||
|
val PurpleGrey80Container = Color(0xFFE8DEF8)
|
||||||
|
val PurpleGrey40Container = Color(0xFF4A4458)
|
||||||
|
|
||||||
|
val Pink80Container = Color(0xFFFFD8E4)
|
||||||
|
val Pink40Container = Color(0xFF633B48)
|
||||||
|
|
||||||
|
// Surface container
|
||||||
|
val LightSurfaceContainer = Color(0xFFF5F3FF)
|
||||||
|
val DarkSurfaceContainer = Color(0xFF1E1B2C)
|
||||||
|
@@ -8,34 +8,12 @@ import androidx.compose.material3.dynamicDarkColorScheme
|
|||||||
import androidx.compose.material3.dynamicLightColorScheme
|
import androidx.compose.material3.dynamicLightColorScheme
|
||||||
import androidx.compose.material3.lightColorScheme
|
import androidx.compose.material3.lightColorScheme
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
|
||||||
private val DarkColorScheme = darkColorScheme(
|
|
||||||
primary = Purple80,
|
|
||||||
secondary = PurpleGrey80,
|
|
||||||
tertiary = Pink80
|
|
||||||
)
|
|
||||||
|
|
||||||
private val LightColorScheme = lightColorScheme(
|
|
||||||
primary = Purple40,
|
|
||||||
secondary = PurpleGrey40,
|
|
||||||
tertiary = Pink40
|
|
||||||
|
|
||||||
/* Other default colors to override
|
|
||||||
background = Color(0xFFFFFBFE),
|
|
||||||
surface = Color(0xFFFFFBFE),
|
|
||||||
onPrimary = Color.White,
|
|
||||||
onSecondary = Color.White,
|
|
||||||
onTertiary = Color.White,
|
|
||||||
onBackground = Color(0xFF1C1B1F),
|
|
||||||
onSurface = Color(0xFF1C1B1F),
|
|
||||||
*/
|
|
||||||
)
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun DoNextTheme(
|
fun DoNextTheme(
|
||||||
darkTheme: Boolean = isSystemInDarkTheme(),
|
darkTheme: Boolean = isSystemInDarkTheme(),
|
||||||
// Dynamic color is available on Android 12+
|
|
||||||
dynamicColor: Boolean = true,
|
dynamicColor: Boolean = true,
|
||||||
content: @Composable () -> Unit
|
content: @Composable () -> Unit
|
||||||
) {
|
) {
|
||||||
@@ -45,8 +23,51 @@ fun DoNextTheme(
|
|||||||
if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
|
if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
|
||||||
}
|
}
|
||||||
|
|
||||||
darkTheme -> DarkColorScheme
|
darkTheme -> darkColorScheme(
|
||||||
else -> LightColorScheme
|
primary = Purple40,
|
||||||
|
onPrimary = Color.White,
|
||||||
|
primaryContainer = Purple40Container,
|
||||||
|
onPrimaryContainer = Color.White,
|
||||||
|
secondary = PurpleGrey40,
|
||||||
|
onSecondary = Color.White,
|
||||||
|
secondaryContainer = PurpleGrey40Container,
|
||||||
|
onSecondaryContainer = Color.White,
|
||||||
|
tertiary = Pink40,
|
||||||
|
onTertiary = Color.White,
|
||||||
|
tertiaryContainer = Pink40Container,
|
||||||
|
onTertiaryContainer = Color.White,
|
||||||
|
background = Color(0xFF121212),
|
||||||
|
onBackground = Color.White,
|
||||||
|
surface = Color(0xFF121212),
|
||||||
|
onSurface = Color.White,
|
||||||
|
surfaceVariant = DarkSurfaceContainer,
|
||||||
|
onSurfaceVariant = Color.White,
|
||||||
|
error = Color(0xFFCF6679),
|
||||||
|
onError = Color.Black
|
||||||
|
)
|
||||||
|
|
||||||
|
else -> lightColorScheme(
|
||||||
|
primary = Purple80,
|
||||||
|
onPrimary = Color.Black,
|
||||||
|
primaryContainer = Purple80Container,
|
||||||
|
onPrimaryContainer = Color.Black,
|
||||||
|
secondary = PurpleGrey80,
|
||||||
|
onSecondary = Color.Black,
|
||||||
|
secondaryContainer = PurpleGrey80Container,
|
||||||
|
onSecondaryContainer = Color.Black,
|
||||||
|
tertiary = Pink80,
|
||||||
|
onTertiary = Color.Black,
|
||||||
|
tertiaryContainer = Pink80Container,
|
||||||
|
onTertiaryContainer = Color.Black,
|
||||||
|
background = Color(0xFFFFFBFE),
|
||||||
|
onBackground = Color.Black,
|
||||||
|
surface = Color(0xFFFFFBFE),
|
||||||
|
onSurface = Color.Black,
|
||||||
|
surfaceVariant = LightSurfaceContainer,
|
||||||
|
onSurfaceVariant = Color.Black,
|
||||||
|
error = Color(0xFFB00020),
|
||||||
|
onError = Color.White
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
MaterialTheme(
|
MaterialTheme(
|
||||||
@@ -54,4 +75,4 @@ fun DoNextTheme(
|
|||||||
typography = Typography,
|
typography = Typography,
|
||||||
content = content
|
content = content
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@@ -5,8 +5,8 @@ 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.TaskListWithOverdue
|
import com.wismna.geoffroy.donext.domain.model.AppDestination
|
||||||
import com.wismna.geoffroy.donext.domain.usecase.GetTaskListsWithOverdueUseCase
|
import com.wismna.geoffroy.donext.domain.usecase.GetTaskListsUseCase
|
||||||
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
|
||||||
@@ -14,23 +14,33 @@ import javax.inject.Inject
|
|||||||
|
|
||||||
@HiltViewModel
|
@HiltViewModel
|
||||||
class MainViewModel @Inject constructor(
|
class MainViewModel @Inject constructor(
|
||||||
getTaskListsWithOverdue: GetTaskListsWithOverdueUseCase
|
getTaskLists: GetTaskListsUseCase
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
|
|
||||||
var taskLists by mutableStateOf<List<TaskListWithOverdue>>(emptyList())
|
|
||||||
private set
|
|
||||||
/*val destinations: List<AppDestination>
|
|
||||||
get() = taskLists.map { AppDestination.TaskList(it.id, it.name) } +
|
|
||||||
AppDestination.ManageLists*/
|
|
||||||
var isLoading by mutableStateOf(true)
|
var isLoading by mutableStateOf(true)
|
||||||
private set
|
private set
|
||||||
|
|
||||||
|
var destinations by mutableStateOf<List<AppDestination>>(emptyList())
|
||||||
|
private set
|
||||||
|
|
||||||
init {
|
init {
|
||||||
getTaskListsWithOverdue()
|
getTaskLists()
|
||||||
.onEach { lists ->
|
.onEach { lists ->
|
||||||
taskLists = lists
|
destinations = lists.map { taskList ->
|
||||||
|
AppDestination.TaskList(taskList.id!!, taskList.name)
|
||||||
|
} + AppDestination.ManageLists
|
||||||
isLoading = false
|
isLoading = false
|
||||||
}
|
}
|
||||||
.launchIn(viewModelScope)
|
.launchIn(viewModelScope)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun deriveDestination(route: String?): AppDestination? {
|
||||||
|
if (route == null) return null
|
||||||
|
return destinations.firstOrNull { dest ->
|
||||||
|
when (dest) {
|
||||||
|
is AppDestination.TaskList -> route.startsWith("tasklist/")
|
||||||
|
else -> dest.route == route
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
@@ -0,0 +1,30 @@
|
|||||||
|
package com.wismna.geoffroy.donext.presentation.viewmodel
|
||||||
|
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
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.TaskListWithOverdue
|
||||||
|
import com.wismna.geoffroy.donext.domain.usecase.GetTaskListsWithOverdueUseCase
|
||||||
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
|
import kotlinx.coroutines.flow.launchIn
|
||||||
|
import kotlinx.coroutines.flow.onEach
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@HiltViewModel
|
||||||
|
class MenuViewModel @Inject constructor(
|
||||||
|
getTaskListsWithOverdue: GetTaskListsWithOverdueUseCase
|
||||||
|
) : ViewModel() {
|
||||||
|
|
||||||
|
var taskLists by mutableStateOf<List<TaskListWithOverdue>>(emptyList())
|
||||||
|
private set
|
||||||
|
|
||||||
|
init {
|
||||||
|
getTaskListsWithOverdue()
|
||||||
|
.onEach { lists ->
|
||||||
|
taskLists = lists
|
||||||
|
}
|
||||||
|
.launchIn(viewModelScope)
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user