diff options
| author | Peter Stone <thepeterstone@gmail.com> | 2026-06-29 08:53:48 +0000 |
|---|---|---|
| committer | Peter Stone <thepeterstone@gmail.com> | 2026-06-29 08:53:48 +0000 |
| commit | 3fbfdd1f9441299eb146bf215449f238977deb66 (patch) | |
| tree | a01d0cd35309afbab6cc8989e26e2e824442c898 /internal/handlers/widget.go | |
| parent | de3e37cca4f34a30f9c0beef05f67c3d515d64c2 (diff) | |
feat: native task management + Todoist migration
Adds doot-owned task storage (native_tasks table) so tasks can be
managed without Todoist. CompleteTask for 'doot' source just updates
the DB — no external API call, no token dependency.
Migration path:
POST /settings/import-from-todoist — copies Todoist cache → native_tasks
Then remove TODOIST_TOKEN from .env to disable Todoist
Changes:
- migration 020: native_tasks table
- store: GetNativeTasks, GetNativeTasksByDateRange, GetUndatedNativeTasks,
CreateNativeTask, CompleteNativeTask, UncompleteNativeTask,
UpdateNativeTask, ImportFromTodoist
- timeline: native tasks appear as source="doot" (teal)
- handleAtomToggle: "doot" case — no external API needed
- HandleWidgetComplete: method on Handler, handles "doot" natively
- HandleUnifiedAdd: "doot" source creates in native_tasks
- widget: "doot" tasks are completable, teal color indicator
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Diffstat (limited to 'internal/handlers/widget.go')
| -rw-r--r-- | internal/handlers/widget.go | 37 |
1 files changed, 22 insertions, 15 deletions
diff --git a/internal/handlers/widget.go b/internal/handlers/widget.go index 00efc75..360ccc5 100644 --- a/internal/handlers/widget.go +++ b/internal/handlers/widget.go @@ -6,7 +6,6 @@ import ( "strings" "time" - "task-dashboard/internal/api" "task-dashboard/internal/config" "task-dashboard/internal/models" ) @@ -48,7 +47,7 @@ func TimelineItemToWidgetItem(item models.TimelineItem) models.WidgetItem { // not completable via widget API default: wi.Type = "task" - wi.Completable = item.Source == "todoist" + wi.Completable = item.Source == "todoist" || item.Source == "doot" } // Only populate Start/End for items with a real time (non-all-day, non-zero) @@ -102,26 +101,34 @@ type widgetCompleteRequest struct { Source string `json:"source"` } -// HandleWidgetComplete proxies a task completion to the source API. -func HandleWidgetComplete(todoistClient api.TodoistAPI) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - var req widgetCompleteRequest - if err := json.NewDecoder(r.Body).Decode(&req); err != nil { - http.Error(w, "bad request", http.StatusBadRequest) - return - } - if req.Source != "todoist" { - http.Error(w, "source not completable", http.StatusBadRequest) +// HandleWidgetComplete proxies a task completion to the source API or native store. +func (h *Handler) HandleWidgetComplete(w http.ResponseWriter, r *http.Request) { + var req widgetCompleteRequest + if err := json.NewDecoder(r.Body).Decode(&req); err != nil { + http.Error(w, "bad request", http.StatusBadRequest) + return + } + + switch req.Source { + case "doot": + if err := h.store.CompleteNativeTask(req.ID); err != nil { + http.Error(w, "failed to complete task", http.StatusInternalServerError) return } - if todoistClient == nil { + _ = h.store.SaveCompletedTask("doot", req.ID, "", nil) + case "todoist": + if h.todoistClient == nil { http.Error(w, "todoist not configured", http.StatusServiceUnavailable) return } - if err := todoistClient.CompleteTask(r.Context(), req.ID); err != nil { + if err := h.todoistClient.CompleteTask(r.Context(), req.ID); err != nil { http.Error(w, "failed to complete task", http.StatusInternalServerError) return } - w.WriteHeader(http.StatusOK) + default: + http.Error(w, "source not completable", http.StatusBadRequest) + return } + + w.WriteHeader(http.StatusOK) } |
