summaryrefslogtreecommitdiff
path: root/android
diff options
context:
space:
mode:
Diffstat (limited to 'android')
-rw-r--r--android/app/src/main/java/org/terst/doot/widget/ui/TaskDetailActivity.kt37
-rw-r--r--android/app/src/test/java/org/terst/doot/widget/ui/TaskDetailActivityTest.kt39
2 files changed, 76 insertions, 0 deletions
diff --git a/android/app/src/main/java/org/terst/doot/widget/ui/TaskDetailActivity.kt b/android/app/src/main/java/org/terst/doot/widget/ui/TaskDetailActivity.kt
index f486dec..effb535 100644
--- a/android/app/src/main/java/org/terst/doot/widget/ui/TaskDetailActivity.kt
+++ b/android/app/src/main/java/org/terst/doot/widget/ui/TaskDetailActivity.kt
@@ -441,6 +441,31 @@ fun TaskDetailSheet(
border = androidx.compose.foundation.BorderStroke(1.dp, Color.White.copy(alpha = 0.3f))
) { Text("Edit") }
}
+ if (isDoot && completable) {
+ Box(modifier = Modifier.weight(1f)) {
+ var showPostponeMenu by remember { mutableStateOf(false) }
+ OutlinedButton(
+ onClick = { showPostponeMenu = true },
+ modifier = Modifier.fillMaxWidth(),
+ colors = ButtonDefaults.outlinedButtonColors(contentColor = Color.White),
+ border = androidx.compose.foundation.BorderStroke(1.dp, Color.White.copy(alpha = 0.3f))
+ ) { Text("Postpone") }
+ DropdownMenu(expanded = showPostponeMenu, onDismissRequest = { showPostponeMenu = false }) {
+ DropdownMenuItem(
+ text = { Text("Tomorrow") },
+ onClick = { showPostponeMenu = false; onReschedule(postponeDate(PostponePeriod.TOMORROW)) }
+ )
+ DropdownMenuItem(
+ text = { Text("Next week") },
+ onClick = { showPostponeMenu = false; onReschedule(postponeDate(PostponePeriod.NEXT_WEEK)) }
+ )
+ DropdownMenuItem(
+ text = { Text("Next month") },
+ onClick = { showPostponeMenu = false; onReschedule(postponeDate(PostponePeriod.NEXT_MONTH)) }
+ )
+ }
+ }
+ }
if (completable) {
Button(
onClick = onComplete,
@@ -493,6 +518,18 @@ internal fun isoDateFromMillis(millis: Long): String {
return "%04d-%02d-%02d".format(cal.get(Calendar.YEAR), cal.get(Calendar.MONTH) + 1, cal.get(Calendar.DAY_OF_MONTH))
}
+internal enum class PostponePeriod { TOMORROW, NEXT_WEEK, NEXT_MONTH }
+
+/** ISO date (yyyy-MM-dd) for the postpone menu, relative to the device's current date. */
+internal fun postponeDate(period: PostponePeriod, today: LocalDate = LocalDate.now()): String {
+ val target = when (period) {
+ PostponePeriod.TOMORROW -> today.plusDays(1)
+ PostponePeriod.NEXT_WEEK -> today.plusWeeks(1)
+ PostponePeriod.NEXT_MONTH -> today.plusMonths(1)
+ }
+ return target.toString()
+}
+
private val LABEL_COLOR_PALETTE = listOf(
"#3B82F6", "#EF4444", "#F59E0B", "#10B981", "#8B5CF6", "#EC4899", "#6B7280"
)
diff --git a/android/app/src/test/java/org/terst/doot/widget/ui/TaskDetailActivityTest.kt b/android/app/src/test/java/org/terst/doot/widget/ui/TaskDetailActivityTest.kt
new file mode 100644
index 0000000..94a5ce9
--- /dev/null
+++ b/android/app/src/test/java/org/terst/doot/widget/ui/TaskDetailActivityTest.kt
@@ -0,0 +1,39 @@
+package org.terst.doot.widget.ui
+
+import org.junit.Assert.assertEquals
+import org.junit.Test
+import java.time.LocalDate
+
+class TaskDetailActivityTest {
+
+ private val anchor = LocalDate.of(2026, 8, 6) // Thursday
+
+ @Test
+ fun `postponeDate tomorrow is a single day ahead`() {
+ assertEquals("2026-08-07", postponeDate(PostponePeriod.TOMORROW, anchor))
+ }
+
+ @Test
+ fun `postponeDate next week is seven days ahead`() {
+ assertEquals("2026-08-13", postponeDate(PostponePeriod.NEXT_WEEK, anchor))
+ }
+
+ @Test
+ fun `postponeDate next month is a calendar month ahead`() {
+ assertEquals("2026-09-06", postponeDate(PostponePeriod.NEXT_MONTH, anchor))
+ }
+
+ @Test
+ fun `postponeDate next month across a short month clamps -- NOT the same as the Go server's overflow behavior`() {
+ // Verified, not assumed: java.time.LocalDate.plusMonths CLAMPS to the target
+ // month's last valid day (Jan 31 -> Feb 28), unlike
+ // ComputeNextOccurrence (internal/models/recurrence.go)'s Go time.AddDate,
+ // which OVERFLOWS instead (Jan 31 + 1 month -> Mar 3). This is a real,
+ // known divergence between this client-side postpone helper and the
+ // server's recurrence date math for day-of-month 29-31 -- not something
+ // this fix reconciles, just documenting it accurately instead of
+ // asserting the wrong behavior.
+ val jan31 = LocalDate.of(2026, 1, 31)
+ assertEquals("2026-02-28", postponeDate(PostponePeriod.NEXT_MONTH, jan31))
+ }
+}