diff options
| author | Peter Stone <thepeterstone@gmail.com> | 2026-07-13 07:11:04 +0000 |
|---|---|---|
| committer | Peter Stone <thepeterstone@gmail.com> | 2026-07-13 07:11:04 +0000 |
| commit | 44abf42ed45aa8f285e7ce031cbb9ef1ade667ea (patch) | |
| tree | dbc21cadc2750c6e39e7f7613be0b3f73790c445 /internal/models | |
| parent | 8310f802dd9fc6ef5dff0be7f640f79c5b39987f (diff) | |
| parent | 4126fe4f56a6eb9703a084d4793a597f37bf2867 (diff) | |
Merge github/master: reconcile with parallel widget work
Another session pushed 27 commits in parallel covering quick-add, event
detail popups, recurrence display, overdue badges, a manual refresh button,
and its own fix for the same overdue-tasks bug (via a separate
GetOverdueNativeTasks fetch folded into BuildTimeline, rather than widening
GetNativeTasksByDateRange's bound directly). Reconciled rather than blindly
taking one side:
- Reverted GetNativeTasksByDateRange to its original bounded query and kept
upstream's GetOverdueNativeTasks + BuildTimeline fold-in as the sole
overdue mechanism for native tasks, to avoid double-counting overdue
items (my widened query + their separate fetch would have both returned
them). Re-pointed the regression test at the now-correct contract and
added a store-level test for GetOverdueNativeTasks directly.
- Kept my GetGoogleTasksByDateRange fix as-is (single unbounded query) --
upstream never touched Google Tasks overdue handling, so there's no
duplication risk there.
- Rewove WidgetRoot's LazyColumn structure (added for scrolling) around
upstream's new header buttons, pinned all-day event rows, and the
enhanced TomorrowSection, none of which were written LazyColumn-aware
since that work landed on this side only.
- Combined both sides' additions to TaskDetailActivity/TaskDetailSheet
(description-edit detail popup + due-date reschedule label) and
WidgetRepository/Actions (optimistic local removal + refresh button
wiring) -- these were independent, non-overlapping features that both
needed to survive.
- Renumbered the migration collision: both sides independently added a
migration numbered 022. Card-description was already applied to the live
production DB under that filename earlier this session (migrations are
tracked by filename), so it keeps 022; the recurring-event-id migration,
never deployed under any name here, moves to 023.
Verified: go build clean, full test suite passes (only the two
pre-existing agent-handler failures and the pre-existing models package
build error remain, both confirmed unrelated via git stash before this
session began), and a dry run against a copy of the live production
database applies both migrations cleanly with no re-run conflicts.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EZ7ikw2ukGJFTHE3bJS7zL
Diffstat (limited to 'internal/models')
| -rw-r--r-- | internal/models/timeline.go | 13 | ||||
| -rw-r--r-- | internal/models/types.go | 25 | ||||
| -rw-r--r-- | internal/models/widget.go | 21 |
3 files changed, 32 insertions, 27 deletions
diff --git a/internal/models/timeline.go b/internal/models/timeline.go index ab486a8..6e48f91 100644 --- a/internal/models/timeline.go +++ b/internal/models/timeline.go @@ -9,11 +9,11 @@ import ( type TimelineItemType string const ( - TimelineItemTypeTask TimelineItemType = "task" - TimelineItemTypeMeal TimelineItemType = "meal" - TimelineItemTypeCard TimelineItemType = "card" - TimelineItemTypeEvent TimelineItemType = "event" - TimelineItemTypeGTask TimelineItemType = "gtask" // Google Tasks + TimelineItemTypeTask TimelineItemType = "task" + TimelineItemTypeMeal TimelineItemType = "meal" + TimelineItemTypeCard TimelineItemType = "card" + TimelineItemTypeEvent TimelineItemType = "event" + TimelineItemTypeGTask TimelineItemType = "gtask" // Google Tasks ) type DaySection string @@ -42,7 +42,8 @@ type TimelineItem struct { Source string `json:"source"` // "trello", "plantoeat", "calendar", "gtasks" // Source-specific metadata - ListID string `json:"list_id,omitempty"` // For Google Tasks + ListID string `json:"list_id,omitempty"` // For Google Tasks + RecurringEventID string `json:"recurring_event_id,omitempty"` // For calendar events } // ComputeDaySection sets the DaySection, IsOverdue, and IsAllDay based on the item's time diff --git a/internal/models/types.go b/internal/models/types.go index 58d3888..6f8c405 100644 --- a/internal/models/types.go +++ b/internal/models/types.go @@ -127,12 +127,13 @@ type Project struct { // CalendarEvent represents a Google Calendar event type CalendarEvent struct { - ID string `json:"id"` - Summary string `json:"summary"` - Description string `json:"description"` - Start time.Time `json:"start"` - End time.Time `json:"end"` - HTMLLink string `json:"html_link"` + ID string `json:"id"` + Summary string `json:"summary"` + Description string `json:"description"` + Start time.Time `json:"start"` + End time.Time `json:"end"` + HTMLLink string `json:"html_link"` + RecurringEventID string `json:"recurring_event_id,omitempty"` // empty = not a recurring instance } // GoogleTask represents a task from Google Tasks @@ -249,12 +250,12 @@ type CompletedTask struct { // SourceConfig represents a configurable item from a data source type SourceConfig struct { - ID int64 `json:"id"` - Source string `json:"source"` // trello, gcal, gtasks - ItemType string `json:"item_type"` // board, project, calendar, tasklist - ItemID string `json:"item_id"` - ItemName string `json:"item_name"` - Enabled bool `json:"enabled"` + ID int64 `json:"id"` + Source string `json:"source"` // trello, gcal, gtasks + ItemType string `json:"item_type"` // board, project, calendar, tasklist + ItemID string `json:"item_id"` + ItemName string `json:"item_name"` + Enabled bool `json:"enabled"` } // FeatureToggle represents a feature flag diff --git a/internal/models/widget.go b/internal/models/widget.go index 8d0bdd6..cbaf19c 100644 --- a/internal/models/widget.go +++ b/internal/models/widget.go @@ -6,15 +6,18 @@ import "time" // Source values: "doot", "trello", "plantoeat", "calendar", "gtasks" // Type values: "task", "event" type WidgetItem struct { - ID string `json:"id"` - Title string `json:"title"` - Source string `json:"source"` - Type string `json:"type"` - Start *time.Time `json:"start,omitempty"` // nil = no specific time (floating task) - End *time.Time `json:"end,omitempty"` - IsAllDay bool `json:"is_all_day"` - URL string `json:"url,omitempty"` - Completable bool `json:"completable"` // true = doot task (checkbox shown) + ID string `json:"id"` + Title string `json:"title"` + Source string `json:"source"` + Type string `json:"type"` + Start *time.Time `json:"start,omitempty"` // nil = no specific time (floating task) + End *time.Time `json:"end,omitempty"` + IsAllDay bool `json:"is_all_day"` + IsOverdue bool `json:"is_overdue"` + DueDate *time.Time `json:"due_date,omitempty"` // doot tasks only -- see TimelineItemToWidgetItem + URL string `json:"url,omitempty"` + Completable bool `json:"completable"` // true = doot task (checkbox shown) + RecurringEventID string `json:"recurring_event_id,omitempty"` } // WidgetResponse is the full /api/widget response body. |
