summaryrefslogtreecommitdiff
path: root/internal/handlers/widget.go
diff options
context:
space:
mode:
authorPeter Stone <thepeterstone@gmail.com>2026-07-12 06:21:40 +0000
committerPeter Stone <thepeterstone@gmail.com>2026-07-12 06:21:40 +0000
commit84252756c687044d73a0ea5cebf8088c7c9ed3e8 (patch)
tree0f76a627ee9fb4037d83a57270a3426694b69c2f /internal/handlers/widget.go
parenta43cc7b8f007a86742756bd4b67f9ba1c204bbd0 (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.go25
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
+ }
}
}