summaryrefslogtreecommitdiff
path: root/internal/handlers
diff options
context:
space:
mode:
Diffstat (limited to 'internal/handlers')
-rw-r--r--internal/handlers/widget.go10
-rw-r--r--internal/handlers/widget_test.go33
2 files changed, 40 insertions, 3 deletions
diff --git a/internal/handlers/widget.go b/internal/handlers/widget.go
index 0092b6b..f0fd101 100644
--- a/internal/handlers/widget.go
+++ b/internal/handlers/widget.go
@@ -70,7 +70,15 @@ func TimelineItemToWidgetItem(item models.TimelineItem) models.WidgetItem {
if !item.Time.IsZero() && (!item.IsAllDay || item.Type == models.TimelineItemTypeEvent) {
t := item.Time
wi.Start = &t
- if !item.IsAllDay {
+ if item.Type == models.TimelineItemTypeEvent {
+ // Events always forward End when known, even when IsAllDay --
+ // a multi-day all-day event (e.g. a 3-day conference) needs its
+ // End date reaching the client so it can detect the span and
+ // render "starts"/"ends"/spans-through labels per rendered day.
+ if item.EndTime != nil {
+ wi.End = item.EndTime
+ }
+ } else if !item.IsAllDay {
if item.EndTime != nil {
wi.End = item.EndTime
} else {
diff --git a/internal/handlers/widget_test.go b/internal/handlers/widget_test.go
index 4e08d2a..2a6967e 100644
--- a/internal/handlers/widget_test.go
+++ b/internal/handlers/widget_test.go
@@ -174,8 +174,9 @@ func TestTimelineItemToWidgetItem_Event(t *testing.T) {
// IsAllDay item regardless of type, which meant the widget client's
// hourly-slot packer had no date information to pin all-day events to the
// top of the correct day and they could silently fall outside the visible
-// grid range instead. End stays nil since there's no meaningful end time to
-// show for an all-day item.
+// grid range instead. This fixture's End stays nil only because it has no
+// EndTime set -- see TestTimelineItemToWidgetItem_MultiDayAllDayEvent_ForwardsEnd
+// for the case where a multi-day all-day event has a real EndTime.
func TestTimelineItemToWidgetItem_AllDayEvent(t *testing.T) {
day := time.Date(2026, 7, 12, 0, 0, 0, 0, time.Local)
item := models.TimelineItem{
@@ -206,6 +207,34 @@ func TestTimelineItemToWidgetItem_AllDayEvent(t *testing.T) {
}
}
+// TestTimelineItemToWidgetItem_MultiDayAllDayEvent_ForwardsEnd proves that a
+// genuine all-day CALENDAR EVENT with a real EndTime (e.g. a 3-day
+// conference with no specific time) still gets End forwarded to the widget
+// client, even though IsAllDay is true -- needed so the client can detect
+// the multi-day span and render "starts"/"ends" labels per day.
+func TestTimelineItemToWidgetItem_MultiDayAllDayEvent_ForwardsEnd(t *testing.T) {
+ start := time.Date(2026, 7, 12, 0, 0, 0, 0, time.Local)
+ end := time.Date(2026, 7, 15, 0, 0, 0, 0, time.Local)
+ item := models.TimelineItem{
+ ID: "cal-conference",
+ Title: "Offsite",
+ Source: "calendar",
+ Type: models.TimelineItemTypeEvent,
+ Time: start,
+ EndTime: &end,
+ IsAllDay: true,
+ }
+
+ wi := TimelineItemToWidgetItem(item)
+
+ if wi.End == nil {
+ t.Fatal("expected End to be forwarded for a multi-day all-day event")
+ }
+ if !wi.End.Equal(end) {
+ t.Errorf("End = %v, want %v", *wi.End, end)
+ }
+}
+
// TestTimelineItemToWidgetItem_AllDayTask_KeepsFloatingBehavior proves the
// same fix does NOT change behavior for undated doot/gtask tasks, which are
// also flagged IsAllDay as a "no specific time" fallback (see