summaryrefslogtreecommitdiff
path: root/internal/models/timeline.go
diff options
context:
space:
mode:
authorPeter Stone <thepeterstone@gmail.com>2026-08-10 03:16:39 +0000
committerPeter Stone <thepeterstone@gmail.com>2026-08-10 03:16:39 +0000
commit9a3ece4fef431c2c35af239f3df6eeaa10ddeaf4 (patch)
treed36c63e17810183fd8ee250dcb0296f42be9f6b3 /internal/models/timeline.go
parentb4bb981d98b0c2d4758f5acc0e1deb1f2a651c79 (diff)
Fix widget showing tomorrow's dated tasks under today
Root cause: doot-native and Google Tasks due dates are always midnight-anchored (even when a task genuinely has a due date), which trips TimelineItem.ComputeDaySection's "midnight means no specific time" heuristic and sets IsAllDay=true on them. TimelineItemToWidgetItem then left wi.Start nil for any Task/GTask with IsAllDay=true, and the Android client's undated/floating pool (DootWidget.kt's `floating` list) has zero per-day awareness -- it just packs items forward from "now" -- so a task due tomorrow rendered as if due today. This was previously diagnosed and deliberately set aside (see project_doot_isallday_midnight_bug memory) with a simpler proposed fix (gate ComputeDaySection's heuristic to Event/Meal types); re-verifying that plan against the actual code before implementing it surfaced a real problem with it: it would also flip IsAllDay to false for same-day dated tasks, moving them from the web's untimed-item strip into the hourly grid with a fabricated "12:00 AM" time label -- fixing the widget by breaking the web's currently-correct rendering. Actual fix: added TimelineItem.Undated, a signal genuinely independent of IsAllDay -- true only for the caller's two genuinely-dateless constructions (nativeUndated tasks, gtasks with no due date), both of which already use Time=now as a layout placeholder rather than a real date. ComputeDaySection and IsAllDay are untouched, so web rendering is unaffected. This gate.go change routes on that new signal instead: dated Task/GTask items now get wi.Start populated (so the Android client's existing, already-correct Start-based day bucketing runs for them) while genuinely undated ones keep today's nil-Start floating treatment exactly as before. Also fixed the same latent bug in wi.DueDate's guard while in the same code path: it only checked !Time.IsZero(), but nativeUndated's Time=now placeholder is non-zero, so an undated task's Android detail popup would have shown a fabricated due date of whatever moment the request happened to run. Now guards on !Undated too. Verified: added TestTimelineItemToWidgetItem_DatedGTaskGetsStart and TestTimelineItemToWidgetItem_UndatedTaskWithPlaceholderTime_NilDueDate, and updated the three existing tests that had encoded the bug's old "Start stays nil for any IsAllDay task" assumption as expected behavior (TestTimelineItemToWidgetItem_Task, _DootTaskGetsDueDate, _AllDayTask_KeepsFloatingBehavior -- the last one needed Undated: true added since it no longer implies undated on its own). Proved the fix by reverting internal/handlers/widget.go to the pre-fix condition against a real backup and confirming exactly the three tests exercising the new behavior fail, then restored from that backup and confirmed byte-identical. go build/vet/test all clean. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EZ7ikw2ukGJFTHE3bJS7zL
Diffstat (limited to 'internal/models/timeline.go')
-rw-r--r--internal/models/timeline.go17
1 files changed, 17 insertions, 0 deletions
diff --git a/internal/models/timeline.go b/internal/models/timeline.go
index 5224b72..b740c42 100644
--- a/internal/models/timeline.go
+++ b/internal/models/timeline.go
@@ -41,6 +41,23 @@ type TimelineItem struct {
DaySection DaySection `json:"day_section"`
Source string `json:"source"` // "trello", "plantoeat", "calendar", "gtasks"
+ // Undated is true only for genuinely dateless Task/GTask items (the
+ // caller uses Time=now as a layout placeholder for them -- see
+ // timeline_logic.go's nativeUndated/gtask-no-due-date branches).
+ // Deliberately NOT the same thing as IsAllDay: doot-native and Google
+ // Tasks due dates are ALWAYS midnight-anchored even when a task has a
+ // real due date (see reference_google_tasks_due_date_utc_quirk), so
+ // IsAllDay ends up true for both "no due date" and "has a due date, but
+ // it's date-only" -- a distinction the WEB UI doesn't need (it buckets
+ // by DaySection, computed from Time regardless of IsAllDay, and only
+ // uses IsAllDay for a same-day visual choice that's fine either way for
+ // tasks) but the ANDROID WIDGET does: TimelineItemToWidgetItem uses
+ // Undated, not IsAllDay, to decide whether to populate wi.Start, because
+ // the widget's undated/floating pool has no per-day awareness at all --
+ // conflating "no date" with "dated but time-only-midnight" there means
+ // a task due tomorrow renders as if due today (2026-08-10 report).
+ Undated bool `json:"-"`
+
// Source-specific metadata
ListID string `json:"list_id,omitempty"` // For Google Tasks
RecurringEventID string `json:"recurring_event_id,omitempty"` // For calendar events