summaryrefslogtreecommitdiff
path: root/docs/superpowers/specs
diff options
context:
space:
mode:
authorPeter Stone <thepeterstone@gmail.com>2026-07-12 09:51:15 +0000
committerPeter Stone <thepeterstone@gmail.com>2026-07-12 09:51:15 +0000
commit5ed9f04225e69a0d1bdc23c9da5761b663c18271 (patch)
tree6e369c22ff375f940d2ddee65ac081c9fa7cd38d /docs/superpowers/specs
parent0089863559fbbcc3bc39ecac300d7f08e9b932e1 (diff)
docs: add widget quick-add design spec
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VTUSAEKfsPc6WGDq45yPHD
Diffstat (limited to 'docs/superpowers/specs')
-rw-r--r--docs/superpowers/specs/2026-07-12-widget-quick-add-design.md35
1 files changed, 35 insertions, 0 deletions
diff --git a/docs/superpowers/specs/2026-07-12-widget-quick-add-design.md b/docs/superpowers/specs/2026-07-12-widget-quick-add-design.md
new file mode 100644
index 0000000..a025554
--- /dev/null
+++ b/docs/superpowers/specs/2026-07-12-widget-quick-add-design.md
@@ -0,0 +1,35 @@
+# Widget Quick Add — Design
+
+## Context
+
+Fourth of five widget features (refresh → overdue badge → clickable reschedule → **quick add** → recurrence). Decided without a user Q&A round (per explicit instruction to proceed autonomously).
+
+## Key constraint from grounding
+
+Jetpack Glance / RemoteViews widgets do not support a reliable in-widget text-entry field across launchers and Android versions. Every existing interactive flow in this widget that needs more than a tap (reschedule's date picker) already follows the same pattern: tap a widget element → launch a full `ComponentActivity` with a Compose `ModalBottomSheet` → do the real work there → close. Quick add follows the identical pattern rather than attempting in-widget text input.
+
+A reusable server-side creation path already exists for the web UI: `HandleUnifiedAdd` (`internal/handlers/handlers.go:608`) creates a `models.Task` via `h.store.CreateNativeTask`. It's form-encoded and behind session/cookie auth, not the widget's bearer-token JSON API, so it isn't directly reusable from the widget client — but the underlying `CreateNativeTask` call is the same one this feature will use, just from a new bearer-token-protected JSON endpoint that mirrors the existing `/api/widget/complete` and `/api/widget/reschedule` handlers.
+
+## Design
+
+**Server (Go):**
+- New handler `HandleWidgetAdd` in `internal/handlers/widget.go`, matching the shape of `HandleWidgetComplete`: decode a JSON body `{"title": "..."}`, reject empty/whitespace-only titles with 400, create an undated doot task (`models.Task{ID: newID(), Content: title, Priority: 1}` — no due date; a quick-add task starts in the same "undated/floating" bucket as any other undated doot task), call `h.store.CreateNativeTask`, return 200 on success or 500 on a store error.
+- New route in `cmd/dashboard/main.go`, alongside the other widget routes: `r.With(widgetAuth).Post("/api/widget/add", h.HandleWidgetAdd)`.
+
+**Android — data layer:**
+- `WidgetRepository` gains `suspend fun addTask(title: String): Result<Unit>`, POSTing to `/api/widget/add`.
+- Unlike `reschedule`/`complete` (which manually string-interpolate their JSON bodies from internal IDs that can't contain special characters), `addTask` takes arbitrary free-text user input, so it must not use manual string interpolation — a title containing a `"` or `\` would produce invalid or unsafe JSON. It uses a small `@Serializable data class WidgetAddRequest(val title: String)` encoded via the file's existing `json` (`kotlinx.serialization.json.Json`) instance instead.
+
+**Android — UI:**
+- New composable `QuickAddButton()` in `DootWidget.kt`, same 24dp tap-target pattern as `RefreshButton`, placed in the "TODAY" header row next to the refresh button (order: `Text("TODAY")` — `defaultWeight()` — `QuickAddButton()` — `RefreshButton(isRefreshing)`). Uses a new `ic_add.xml` drawable (24dp plus-sign vector, same stroke style as the other icons).
+- Tapping it launches a new `QuickAddActivity : ComponentActivity`, structurally a near-twin of `TaskDetailActivity`: a `ModalBottomSheet` containing a `TextField` (title input, autofocus) and an "Add" `Button`. Submitting: calls `WidgetRepository.addTask(title)`, and on success, `fetchAndPersist` + `DootWidget().updateAll()` + `finish()`, matching the existing reschedule success flow exactly. Empty/blank titles disable the "Add" button (no server round-trip for obviously-invalid input — the server still validates independently as the source of truth).
+- `QuickAddActivity` is a separate class from `TaskDetailActivity` (not a mode flag on the existing one) — they have different triggers (widget button vs. task row tap), different required inputs (no id/source/completable for creation), and keeping them separate avoids a sprawling "does five different things" activity, consistent with this codebase's existing per-purpose-Activity pattern.
+
+## Testing
+
+- Server: unit test for `HandleWidgetAdd` covering (a) success — valid title creates a task, verified via the store, (b) empty title → 400, matching the existing `TestHandleWidgetComplete_*` test style in `widget_test.go` (uses `setupTestDB`).
+- Android: no unit-test surface (consistent with the rest of this session's widget UI work) — verified by build + manual on-device check.
+
+## Out of scope
+
+Recurrence display gets its own spec (the fifth and last feature).