summaryrefslogtreecommitdiff
path: root/android/app
diff options
context:
space:
mode:
Diffstat (limited to 'android/app')
-rw-r--r--android/app/src/main/java/org/terst/doot/widget/data/WidgetRepository.kt16
-rw-r--r--android/app/src/main/java/org/terst/doot/widget/ui/TaskDetailActivity.kt70
2 files changed, 84 insertions, 2 deletions
diff --git a/android/app/src/main/java/org/terst/doot/widget/data/WidgetRepository.kt b/android/app/src/main/java/org/terst/doot/widget/data/WidgetRepository.kt
index 6f3d2b2..a54910d 100644
--- a/android/app/src/main/java/org/terst/doot/widget/data/WidgetRepository.kt
+++ b/android/app/src/main/java/org/terst/doot/widget/data/WidgetRepository.kt
@@ -43,6 +43,22 @@ class WidgetRepository(
}
}
+ /** POSTs a due-date update to /api/widget/reschedule. */
+ suspend fun reschedule(id: String, source: String, dateISO: String): Result<Unit> =
+ withContext(Dispatchers.IO) {
+ val body = """{"id":"$id","source":"$source","date":"$dateISO"}"""
+ .toRequestBody("application/json".toMediaType())
+ val request = Request.Builder()
+ .url("$serverUrl/api/widget/reschedule")
+ .header("Authorization", "Bearer $token")
+ .post(body)
+ .build()
+ runCatching {
+ val response = client.newCall(request).execute()
+ check(response.isSuccessful) { "HTTP ${response.code}" }
+ }
+ }
+
/** POSTs a task completion to /api/widget/complete. */
suspend fun complete(context: Context, id: String, source: String): Result<Unit> =
withContext(Dispatchers.IO) {
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 2a42871..0dac1cc 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
@@ -7,13 +7,23 @@ import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.*
-import androidx.compose.runtime.Composable
+import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
+import androidx.glance.appwidget.updateAll
+import androidx.lifecycle.lifecycleScope
+import kotlinx.coroutines.flow.first
+import kotlinx.coroutines.launch
+import okhttp3.OkHttpClient
+import org.terst.doot.widget.data.Keys
+import org.terst.doot.widget.data.WidgetRepository
+import org.terst.doot.widget.data.dataStore
import org.terst.doot.widget.work.CompleteWorker
+import java.util.Calendar
+import java.util.TimeZone
class TaskDetailActivity : ComponentActivity() {
@@ -35,6 +45,19 @@ class TaskDetailActivity : ComponentActivity() {
CompleteWorker.enqueue(this, id, source)
finish()
},
+ onReschedule = { dateISO ->
+ lifecycleScope.launch {
+ val prefs = this@TaskDetailActivity.dataStore.data.first()
+ val url = prefs[Keys.SERVER_URL]?.trimEnd('/') ?: return@launch
+ val token = prefs[Keys.TOKEN] ?: return@launch
+ val repo = WidgetRepository(OkHttpClient(), url, token)
+ repo.reschedule(id, source, dateISO).onSuccess {
+ repo.fetchAndPersist(this@TaskDetailActivity)
+ DootWidget().updateAll(this@TaskDetailActivity)
+ finish()
+ }
+ }
+ },
onDismiss = ::finish
)
}
@@ -56,8 +79,40 @@ fun TaskDetailSheet(
source: String,
completable: Boolean,
onComplete: () -> Unit,
+ onReschedule: (String) -> Unit,
onDismiss: () -> Unit
) {
+ var showDatePicker by remember { mutableStateOf(false) }
+ val datePickerState = rememberDatePickerState(
+ initialSelectedDateMillis = System.currentTimeMillis()
+ )
+
+ if (showDatePicker) {
+ DatePickerDialog(
+ onDismissRequest = { showDatePicker = false },
+ confirmButton = {
+ TextButton(onClick = {
+ datePickerState.selectedDateMillis?.let { millis ->
+ val cal = Calendar.getInstance(TimeZone.getTimeZone("UTC"))
+ cal.timeInMillis = millis
+ val dateISO = "%04d-%02d-%02d".format(
+ cal.get(Calendar.YEAR),
+ cal.get(Calendar.MONTH) + 1,
+ cal.get(Calendar.DAY_OF_MONTH)
+ )
+ onReschedule(dateISO)
+ }
+ showDatePicker = false
+ }) { Text("Set date") }
+ },
+ dismissButton = {
+ TextButton(onClick = { showDatePicker = false }) { Text("Cancel") }
+ }
+ ) {
+ DatePicker(state = datePickerState)
+ }
+ }
+
ModalBottomSheet(
onDismissRequest = onDismiss,
sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true),
@@ -94,7 +149,7 @@ fun TaskDetailSheet(
modifier = Modifier.weight(1f)
)
}
- Spacer(Modifier.height(24.dp))
+ Spacer(Modifier.height(20.dp))
if (completable) {
Button(
onClick = onComplete,
@@ -103,6 +158,17 @@ fun TaskDetailSheet(
) {
Text("Mark Complete", fontSize = 15.sp)
}
+ Spacer(Modifier.height(8.dp))
+ }
+ if (source == "doot") {
+ OutlinedButton(
+ onClick = { showDatePicker = true },
+ modifier = Modifier.fillMaxWidth(),
+ colors = ButtonDefaults.outlinedButtonColors(contentColor = Color.White),
+ border = androidx.compose.foundation.BorderStroke(1.dp, Color.White.copy(alpha = 0.3f))
+ ) {
+ Text("Reschedule", fontSize = 15.sp)
+ }
}
Spacer(Modifier.height(20.dp))
}