mirror of
https://github.com/wismna/DoNext.git
synced 2025-10-03 07:30:13 -04:00
Theme is now working properly
Fix overdue use case Allow selecting today as due date Dates are now real badges Animate done tasks
This commit is contained in:
@@ -7,7 +7,7 @@
|
||||
<activity
|
||||
android:name="com.wismna.geoffroy.donext.presentation.MainActivity"
|
||||
android:exported="true"
|
||||
android:label="@string/title_activity_main"
|
||||
android:label="DoNext"
|
||||
android:theme="@style/Theme.DoNext">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
|
@@ -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.repository.TaskRepository
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import java.time.Instant
|
||||
import java.time.LocalDate
|
||||
import java.time.ZoneOffset
|
||||
import javax.inject.Inject
|
||||
|
||||
class GetTaskListsWithOverdueUseCase @Inject constructor(
|
||||
private val taskRepository: TaskRepository
|
||||
) {
|
||||
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.compose.setContent
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.compose.foundation.isSystemInDarkTheme
|
||||
import com.wismna.geoffroy.donext.presentation.screen.MainScreen
|
||||
import com.wismna.geoffroy.donext.presentation.ui.theme.DoNextTheme
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
@@ -14,7 +15,7 @@ class MainActivity : ComponentActivity() {
|
||||
super.onCreate(savedInstanceState)
|
||||
enableEdgeToEdge()
|
||||
setContent {
|
||||
DoNextTheme { MainScreen() }
|
||||
DoNextTheme(darkTheme = isSystemInDarkTheme(), dynamicColor = false) { MainScreen() }
|
||||
}
|
||||
}
|
||||
}
|
@@ -107,7 +107,6 @@ fun MainScreen(
|
||||
onNavigate = { route ->
|
||||
scope.launch { drawerState.close() }
|
||||
navController.navigate(route) {
|
||||
//launchSingleTop = true
|
||||
restoreState = true
|
||||
}
|
||||
}
|
||||
|
@@ -16,6 +16,7 @@ import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
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.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
@@ -88,23 +89,19 @@ fun TaskItemScreen(
|
||||
|
||||
// Due date badge
|
||||
viewModel.dueDateText?.let { dueMillis ->
|
||||
Box(
|
||||
Badge(
|
||||
modifier = Modifier
|
||||
.align(
|
||||
if (viewModel.description.isNullOrBlank()) Alignment.CenterEnd
|
||||
else Alignment.TopEnd
|
||||
)
|
||||
.background(
|
||||
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)
|
||||
),
|
||||
containerColor = if (viewModel.isOverdue) MaterialTheme.colorScheme.error else MaterialTheme.colorScheme.primaryContainer
|
||||
) {
|
||||
Text(
|
||||
modifier = Modifier.padding(start = 1.dp, end = 1.dp),
|
||||
text = viewModel.dueDateText,
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
color = if (viewModel.isOverdue) Color.White else MaterialTheme.colorScheme.primary
|
||||
color = if (viewModel.isOverdue) Color.White else MaterialTheme.colorScheme.onPrimaryContainer,
|
||||
style = MaterialTheme.typography.bodySmall
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@@ -1,19 +1,20 @@
|
||||
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.height
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
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.filled.Add
|
||||
import androidx.compose.material3.ExtendedFloatingActionButton
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||
@@ -29,36 +30,53 @@ fun TaskListScreen(
|
||||
onTaskClick: (Task) -> Unit) {
|
||||
val tasks = viewModel.tasks
|
||||
|
||||
// Split tasks into active and done
|
||||
val (active, done) = remember(tasks) {
|
||||
tasks.partition { !it.isDone }
|
||||
}
|
||||
|
||||
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 ->
|
||||
if (index > 0) {
|
||||
val prev = tasks[index - 1]
|
||||
|
||||
when {
|
||||
// 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))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Active tasks section
|
||||
items(
|
||||
items = active,
|
||||
key = { it.id!! }
|
||||
) { task ->
|
||||
TaskItemScreen(
|
||||
modifier = Modifier.animateItem(),
|
||||
viewModel = TaskItemViewModel(task),
|
||||
onClick = { onTaskClick(task) },
|
||||
onToggleDone = { isChecked ->
|
||||
viewModel.updateTaskDone(task.id!!, isChecked)
|
||||
onToggleDone = { checked ->
|
||||
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)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
@@ -143,8 +143,8 @@ fun TaskBottomSheet(
|
||||
initialSelectedDateMillis = viewModel.dueDate,
|
||||
selectableDates = object: SelectableDates {
|
||||
override fun isSelectableDate(utcTimeMillis: Long): Boolean {
|
||||
val todayStartMillis = LocalDate.now()
|
||||
.atStartOfDay(ZoneId.systemDefault())
|
||||
val todayStartMillis = LocalDate.now(ZoneOffset.UTC)
|
||||
.atStartOfDay(ZoneOffset.UTC)
|
||||
.toInstant()
|
||||
.toEpochMilli()
|
||||
return utcTimeMillis >= todayStartMillis
|
||||
@@ -173,6 +173,7 @@ fun TaskBottomSheet(
|
||||
Row (
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = if (viewModel.isEditing()) Arrangement.SpaceBetween else Arrangement.End) {
|
||||
|
||||
// --- Delete Button ---
|
||||
if (viewModel.isEditing()) {
|
||||
Button(
|
||||
|
@@ -2,10 +2,26 @@ package com.wismna.geoffroy.donext.presentation.ui.theme
|
||||
|
||||
import androidx.compose.ui.graphics.Color
|
||||
|
||||
val Purple80 = Color(0xFFD0BCFF)
|
||||
val PurpleGrey80 = Color(0xFFCCC2DC)
|
||||
val Pink80 = Color(0xFFEFB8C8)
|
||||
// Primary shades
|
||||
val Purple80 = Color(0xFFD0BCFF) // Light theme primary
|
||||
val Purple40 = Color(0xFF6650A4) // Dark theme primary
|
||||
|
||||
val Purple40 = Color(0xFF6650a4)
|
||||
val PurpleGrey40 = Color(0xFF625b71)
|
||||
val PurpleGrey80 = Color(0xFFCCC2DC)
|
||||
val PurpleGrey40 = Color(0xFF625B71)
|
||||
|
||||
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.lightColorScheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.graphics.Color
|
||||
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
|
||||
fun DoNextTheme(
|
||||
darkTheme: Boolean = isSystemInDarkTheme(),
|
||||
// Dynamic color is available on Android 12+
|
||||
dynamicColor: Boolean = true,
|
||||
content: @Composable () -> Unit
|
||||
) {
|
||||
@@ -45,8 +23,51 @@ fun DoNextTheme(
|
||||
if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
|
||||
}
|
||||
|
||||
darkTheme -> DarkColorScheme
|
||||
else -> LightColorScheme
|
||||
darkTheme -> darkColorScheme(
|
||||
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(
|
||||
|
Reference in New Issue
Block a user