diff options
| author | Peter Stone <thepeterstone@gmail.com> | 2026-07-12 06:21:40 +0000 |
|---|---|---|
| committer | Peter Stone <thepeterstone@gmail.com> | 2026-07-12 06:21:40 +0000 |
| commit | 84252756c687044d73a0ea5cebf8088c7c9ed3e8 (patch) | |
| tree | 0f76a627ee9fb4037d83a57270a3426694b69c2f /internal/handlers/widget.go | |
| parent | a43cc7b8f007a86742756bd4b67f9ba1c204bbd0 (diff) | |
fix(widget): pin all-day calendar events to the top instead of losing them
WidgetItem.isAllDay was carried by the client's data model but nothing
ever read it. All-day events had no Start at all, so they fell into the
same floating-task queue as ordinary untimed tasks; if enough tasks were
ahead of one in the queue, SlotPacker could assign it an hour slot past
the visible grid range entirely -- not merely unpinned, actually
invisible.
Server: TimelineItemToWidgetItem now populates Start for all-day
CALENDAR EVENTS specifically (their real event date), while leaving
undated doot/gtask tasks -- also flagged IsAllDay as a "no specific
time" fallback, a different concept -- on the existing nil-Start
floating behavior.
Client: all-day events are filtered out of the hourly grid/floating-task
pipeline entirely, bucketed by day using the new Start date, and
rendered in a new pinned AllDayRow section right after the TODAY/TOMORROW
headers.
Diffstat (limited to 'internal/handlers/widget.go')
| -rw-r--r-- | internal/handlers/widget.go | 25 |
1 files changed, 18 insertions, 7 deletions
diff --git a/internal/handlers/widget.go b/internal/handlers/widget.go index 6c3b455..dc10db6 100644 --- a/internal/handlers/widget.go +++ b/internal/handlers/widget.go @@ -52,15 +52,26 @@ func TimelineItemToWidgetItem(item models.TimelineItem) models.WidgetItem { wi.Completable = item.Source == "doot" } - // Only populate Start/End for items with a real time (non-all-day, non-zero) - if !item.IsAllDay && !item.Time.IsZero() { + // Only populate Start/End for items with a real time. All-day CALENDAR + // EVENTS (not undated doot/gtask tasks, which are also flagged IsAllDay + // as a "no specific time" fallback -- see TimelineItem.ComputeDaySection) + // get Start populated too, using their real event date, so the widget + // client can pin them to the top of the correct day's section instead of + // losing them in the floating-task hourly-slot packer, which has no + // concept of "all day" and can push a slot past the visible grid range + // entirely. Tasks keep the existing nil-Start "floating" treatment + // regardless of IsAllDay -- only Start is set for them (never End), and + // only when they have a real time. + if !item.Time.IsZero() && (!item.IsAllDay || item.Type == models.TimelineItemTypeEvent) { t := item.Time wi.Start = &t - if item.EndTime != nil { - wi.End = item.EndTime - } else { - end := item.Time.Add(time.Hour) - wi.End = &end + if !item.IsAllDay { + if item.EndTime != nil { + wi.End = item.EndTime + } else { + end := item.Time.Add(time.Hour) + wi.End = &end + } } } |
