diff options
Diffstat (limited to 'internal/models/recurrence.go')
| -rw-r--r-- | internal/models/recurrence.go | 40 |
1 files changed, 40 insertions, 0 deletions
diff --git a/internal/models/recurrence.go b/internal/models/recurrence.go index f42183c..f407688 100644 --- a/internal/models/recurrence.go +++ b/internal/models/recurrence.go @@ -55,3 +55,43 @@ func nextWeeklyOccurrence(due time.Time, interval int, weekdays []int) time.Time daysToNextWeekStart := 7 - dueWeekday return due.AddDate(0, 0, daysToNextWeekStart+sorted[0]+7*(interval-1)) } + +// CollapseRecurringSeries reduces each recurrence series to its single +// earliest-due instance, preserving input order otherwise. +// +// The scheduler deliberately materializes a series' next instance once the +// current one comes due, whether or not it was completed, so that ignoring one +// occurrence doesn't stall the series (see +// GetSeriesNeedingNextIteration). The side effect is that an unfinished +// recurring chore and its already-generated successor both sit in the flat task +// list at once -- "Bills" appearing twice, once overdue from Sep 1 and once for +// Oct 1. Only the earliest open one is a thing you actually owe right now. +// +// This is for flat "what's open" views only. Date-anchored views (timeline, +// widget) should keep every instance, since there each one legitimately belongs +// to its own day. +func CollapseRecurringSeries(tasks []Task) []Task { + earliest := make(map[string]*Task) + for i := range tasks { + t := &tasks[i] + if t.RecurrenceSeriesID == "" || t.DueDate == nil { + continue + } + cur, ok := earliest[t.RecurrenceSeriesID] + if !ok || t.DueDate.Before(*cur.DueDate) { + earliest[t.RecurrenceSeriesID] = t + } + } + + out := make([]Task, 0, len(tasks)) + for i := range tasks { + t := tasks[i] + // An undated recurring row has no position in the series ordering, so it + // is never collapsed away -- only dated instances compete. + if keep, ok := earliest[t.RecurrenceSeriesID]; ok && t.DueDate != nil && keep.ID != t.ID { + continue + } + out = append(out, t) + } + return out +} |
