Add a timeline page grouping photos by day
New /timeline/ page renders photos grouped into consecutive same-day runs (ISO date heading plus per-day count), reusing the square grid. groupByDay collapses the newest-first photo list; the page is not paginated. Linked from the profile header.
This commit is contained in:
@@ -43,6 +43,14 @@ type Photo struct {
|
||||
Date time.Time
|
||||
}
|
||||
|
||||
// DayGroup is a run of photos sharing the same calendar day, for the timeline.
|
||||
type DayGroup struct {
|
||||
day string // "2006-01-02", grouping key (unexported: not for templates)
|
||||
Label string // "14 June 2026"
|
||||
Count int
|
||||
Photos []Photo
|
||||
}
|
||||
|
||||
type Generator struct {
|
||||
contentDir string
|
||||
outputDir string
|
||||
@@ -120,6 +128,9 @@ func (g *Generator) Build() error {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err := g.renderTimeline(photos); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := g.renderFeed(photos); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -385,6 +396,48 @@ func (g *Generator) renderPhotoPage(p Photo, newer, older *Photo) error {
|
||||
})
|
||||
}
|
||||
|
||||
// groupByDay collapses photos (already sorted newest first) into per-day runs,
|
||||
// preserving order. Two photos land in the same group iff their dates share a
|
||||
// calendar day.
|
||||
func groupByDay(photos []Photo) []DayGroup {
|
||||
var groups []DayGroup
|
||||
for _, p := range photos {
|
||||
day := p.Date.Format("2006-01-02")
|
||||
if n := len(groups); n > 0 && groups[n-1].day == day {
|
||||
groups[n-1].Photos = append(groups[n-1].Photos, p)
|
||||
groups[n-1].Count++
|
||||
continue
|
||||
}
|
||||
groups = append(groups, DayGroup{
|
||||
day: day,
|
||||
Label: day, // ISO 2006-01-02
|
||||
Count: 1,
|
||||
Photos: []Photo{p},
|
||||
})
|
||||
}
|
||||
return groups
|
||||
}
|
||||
|
||||
// renderTimeline writes public/timeline/index.html, photos grouped by day.
|
||||
func (g *Generator) renderTimeline(photos []Photo) error {
|
||||
dir := filepath.Join(g.outputDir, "timeline")
|
||||
if err := os.MkdirAll(dir, 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
return g.render("timeline.html", filepath.Join(dir, "index.html"), map[string]any{
|
||||
"Groups": groupByDay(photos),
|
||||
"Total": len(photos),
|
||||
"BaseURL": g.baseURL,
|
||||
"Year": time.Now().Year(),
|
||||
"Author": g.author,
|
||||
"Handle": g.handle,
|
||||
"Initial": g.initial(),
|
||||
"FontsURL": g.fontsURL,
|
||||
"SerifFamily": g.serifFamily,
|
||||
"MlFamily": g.mlFamily,
|
||||
})
|
||||
}
|
||||
|
||||
func (g *Generator) render(tmpl, dst string, data any) error {
|
||||
f, err := os.Create(dst)
|
||||
if err != nil {
|
||||
|
||||
Reference in New Issue
Block a user