From 96518adb706b65670508b170b50f4bd5e1aeeed8 Mon Sep 17 00:00:00 2001 From: Peter Stone Date: Thu, 3 Sep 2026 00:42:09 +0000 Subject: Revert "Show one row per recurring chore in the flat task list" This reverts commit 10f9fcd271bbac4ff26f32880db212b5bd3b7f00. --- internal/models/recurrence.go | 40 -------------------------------------- internal/models/recurrence_test.go | 40 -------------------------------------- 2 files changed, 80 deletions(-) (limited to 'internal/models') diff --git a/internal/models/recurrence.go b/internal/models/recurrence.go index f407688..f42183c 100644 --- a/internal/models/recurrence.go +++ b/internal/models/recurrence.go @@ -55,43 +55,3 @@ 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 -} diff --git a/internal/models/recurrence_test.go b/internal/models/recurrence_test.go index affba56..8d18996 100644 --- a/internal/models/recurrence_test.go +++ b/internal/models/recurrence_test.go @@ -49,43 +49,3 @@ func TestComputeNextOccurrence(t *testing.T) { }) } } - -func TestCollapseRecurringSeries_KeepsOnlyEarliestOpenInstance(t *testing.T) { - sep1 := time.Date(2026, 9, 1, 0, 0, 0, 0, time.UTC) - oct1 := time.Date(2026, 10, 1, 0, 0, 0, 0, time.UTC) - - tasks := []Task{ - {ID: "bills-sep", Content: "Bills", DueDate: &sep1, RecurrenceSeriesID: "bills"}, - {ID: "bills-oct", Content: "Bills", DueDate: &oct1, RecurrenceSeriesID: "bills"}, - {ID: "one-off", Content: "Fix curtain holder"}, - } - - got := CollapseRecurringSeries(tasks) - - var ids []string - for _, task := range got { - ids = append(ids, task.ID) - } - if len(got) != 2 { - t.Fatalf("got %d tasks (%v), want 2: one row per recurring series plus the one-off", len(got), ids) - } - if got[0].ID != "bills-sep" { - t.Errorf("kept %q for the Bills series, want bills-sep (the earliest open instance is the one you actually owe)", got[0].ID) - } - if got[1].ID != "one-off" { - t.Errorf("non-recurring task was dropped or reordered: got %q", got[1].ID) - } -} - -func TestCollapseRecurringSeries_UndatedAndDistinctSeriesUntouched(t *testing.T) { - due := time.Date(2026, 9, 1, 0, 0, 0, 0, time.UTC) - tasks := []Task{ - {ID: "a", RecurrenceSeriesID: "series-a", DueDate: &due}, - {ID: "b", RecurrenceSeriesID: "series-b", DueDate: &due}, - {ID: "c", RecurrenceSeriesID: "series-c"}, // recurring but undated - } - - if got := CollapseRecurringSeries(tasks); len(got) != 3 { - t.Errorf("len = %d, want 3 -- distinct series must not collapse into each other", len(got)) - } -} -- cgit v1.2.3