Repos / s4g / c57827b375
commit c57827b3751625d7f00fbc8bcdfb423da3c03125
Author: Nhân <hi@imnhan.com>
Date:   Wed Aug 23 20:45:41 2023 +0700

    implement Series (poorly)
    
    Probably grossly inefficient, but it hasn't been a problem yet on my
    blog (34 articles took around 80-90ms). Maybe think of something
    better... sometime.

diff --git a/docs/_s4g/theme/navbar.css b/docs/_s4g/theme/navbar.css
index 3a7107b..71bba65 100644
--- a/docs/_s4g/theme/navbar.css
+++ b/docs/_s4g/theme/navbar.css
@@ -5,6 +5,7 @@ nav > a {
 nav > .posted-on {
   float: right;
   font-style: italic;
+  margin-bottom: 0.5rem;
 }
 
 .nav-hr {
diff --git a/docs/_s4g/theme/post.tmpl b/docs/_s4g/theme/post.tmpl
index 6878886..15fa7c6 100644
--- a/docs/_s4g/theme/post.tmpl
+++ b/docs/_s4g/theme/post.tmpl
@@ -5,8 +5,47 @@
 {{- template "navbar" .}}
 
 <main>
+
+{{- if .Post.Parent }}
+<em>
+  This post is part of
+  <a href="{{.Post.Parent.WebPath}}">{{.Post.Parent.Title}}</a>
+</em>
+<hr>
+{{- end }}
+
 <h1>{{.Post.Title}}</h1>
+
 {{.Content}}
+{{if .Post.Parent }}
+  <div class="series-container">
+    <p>
+      Here's every post in
+      <a href="{{.Post.Parent.WebPath}}">{{.Post.Parent.Title}}</a>,
+      in chronological order:
+    </p>
+
+    <ol>
+    {{- range .Post.Parent.Children }}
+      {{ if not .IsDraft -}}
+      <li><a href="{{.WebPath}}">{{.Title}}</a>
+          {{- if eq $.Post.WebPath .WebPath }} (you are here) {{- end -}}
+      </li>
+      {{- end -}}
+    {{ end }}
+    </ol>
+  </div>
+
+  <style>
+    .series-container {
+      margin: 2rem 0;
+      padding: 0 1rem;
+      border: 1px dashed #aaa;
+      background-color: #eee;
+    }
+  </style>
+{{ end -}}
+
 </main>
 
 {{template "footer" .}}
diff --git a/docs/_s4g/theme/series-index.tmpl b/docs/_s4g/theme/series-index.tmpl
new file mode 100644
index 0000000..aee5b53
--- /dev/null
+++ b/docs/_s4g/theme/series-index.tmpl
@@ -0,0 +1,27 @@
+{{- define "head"}}{{- end}}
+
+{{define "body"}}
+
+{{- template "navbar" .}}
+
+<main>
+
+<h1>{{.Post.Title}}</h1>
+{{.Content}}
+
+<ol>
+{{ range .Post.Children }}
+{{- if not .IsDraft}}
+  <li style="margin-bottom: 1rem;">
+    <a href="{{.WebPath}}">{{.Title}}</a>
+    <br>
+    <span>{{.PostedAt.Local.Format "January 1, 2006"}}</span>
+  </li>
+{{- end}}
+{{ end }}
+</ol>
+
+</main>
+
+{{template "footer" .}}
+{{- end}}
diff --git a/docs/about/index.html b/docs/about/index.html
index d761c95..ee026a6 100644
--- a/docs/about/index.html
+++ b/docs/about/index.html
@@ -25,7 +25,9 @@
 
 
 <main>
+
 <h1>About</h1>
+
 <p>This is a sample website to demonstrate some features of <a href="https://github.com/nhanb/s4g">s4g</a>.
 It also doubles as a lazy end-to-end test suite until the core design
 stablizes. Its source code, as well as output, is in the <a href="https://github.com/nhanb/s4g/tree/master/docs">/docs/</a> dir.</p>
diff --git a/docs/mfws.html b/docs/mfws.html
index 3bb6415..af1fd44 100644
--- a/docs/mfws.html
+++ b/docs/mfws.html
@@ -31,7 +31,9 @@
 
 
 <main>
+
 <h1>This is a motherfucking website.</h1>
+
 <p>And it’s fucking perfect.</p>
 <section id="Seriously-what-the-fuck-else-do-you-want">
 <h2>Seriously, what the fuck else do you want?</h2>
diff --git a/feed.go b/feed.go
index c49dff7..f902ae1 100644
--- a/feed.go
+++ b/feed.go
@@ -9,7 +9,7 @@
 
 // TODO: Use Article's updated date instead of PostedAt.
 // I need to implement Article.UpdatedAt first though.
-func generateFeed(site *SiteMetadata, posts []Article, path string) []byte {
+func generateFeed(site *SiteMetadata, posts []*Article, path string) []byte {
 	siteAddr := site.Address
 	if !strings.HasSuffix(siteAddr, "/") {
 		siteAddr += "/"
diff --git a/main.go b/main.go
index 56a35a1..7077598 100644
--- a/main.go
+++ b/main.go
@@ -193,7 +193,7 @@ func regenerate(fsys writablefs.FS) (site *SiteMetadata, err error) {
 
 	generatedFiles := make(map[string]bool)
 
-	var articlesInNav []Article
+	var articlesInNav []*Article
 	for _, link := range site.NavbarLinks {
 		a, ok := articles[link]
 		if !ok {
@@ -206,7 +206,7 @@ func regenerate(fsys writablefs.FS) (site *SiteMetadata, err error) {
 		articlesInNav = append(articlesInNav, a)
 	}
 
-	var articlesInFeed []Article
+	var articlesInFeed []*Article
 	startYear := time.Now().Year()
 	for _, a := range articles {
 		if a.ShowInFeed {
@@ -222,6 +222,16 @@ func regenerate(fsys writablefs.FS) (site *SiteMetadata, err error) {
 		return articlesInFeed[i].PostedAt.Compare(articlesInFeed[j].PostedAt) > 0
 	})
 
+	// TODO: fix wasteful loop?
+	for _, a := range articles {
+		// Sort articles in series, oldest first
+		if len(a.Children) > 0 {
+			sort.Slice(a.Children, func(i int, j int) bool {
+				return a.Children[i].PostedAt.Compare(a.Children[j].PostedAt) < 0
+			})
+		}
+	}
+
 	for _, a := range articles {
 		err := a.WriteHtmlFile(site, articlesInNav, articlesInFeed, startYear)
 		if err != nil {
@@ -264,6 +274,12 @@ type Article struct {
 	WebPath        string
 	TemplatePaths  []string
 	OpenGraphImage string
+	Parent         *Article
+	Children       []*Article
+}
+
+func (a *Article) IsSeriesIndex() bool {
+	return a.PageType == PTSeriesIndex
 }
 
 func (a *Article) ComputeDerivedFields(addr, root string) {
@@ -332,8 +348,8 @@ func (a *Article) computeTemplatePaths() {
 
 func (a *Article) WriteHtmlFile(
 	site *SiteMetadata,
-	articlesInNav []Article,
-	articlesInFeed []Article,
+	articlesInNav []*Article,
+	articlesInFeed []*Article,
 	startYear int,
 ) error {
 	contentHtml := djot.ToHtml(a.DjotBody)
@@ -352,8 +368,8 @@ func (a *Article) WriteHtmlFile(
 		Content        template.HTML
 		Title          string
 		Post           *Article
-		ArticlesInNav  []Article
-		ArticlesInFeed []Article
+		ArticlesInNav  []*Article
+		ArticlesInFeed []*Article
 		Feed           string
 		Now            time.Time
 		StartYear      int
@@ -384,10 +400,12 @@ func (a *Article) WriteHtmlFile(
 	return nil
 }
 
-func findArticles(fsys writablefs.FS, site *SiteMetadata) (map[string]Article, error) {
-	result := make(map[string]Article)
+func findArticles(fsys writablefs.FS, site *SiteMetadata) (map[string]*Article, error) {
+	articles := make(map[string]*Article)
+	var seriesPaths []string
 
 	err := fs.WalkDir(fsys, ".", func(path string, d fs.DirEntry, err error) error {
+		fmt.Println("DIR:", path)
 		if d.IsDir() || !strings.HasSuffix(d.Name(), DjotExt) {
 			return nil
 		}
@@ -438,12 +456,31 @@ func findArticles(fsys writablefs.FS, site *SiteMetadata) (map[string]Article, e
 			ArticleMetadata: meta,
 		}
 		article.ComputeDerivedFields(site.Address, site.Root)
-		result[article.Path] = article
+
+		if article.PageType == PTSeriesIndex {
+			seriesPaths = append(seriesPaths, article.Path)
+		}
+
+		articles[article.Path] = &article
 		return nil
 	})
 
 	if err != nil {
 		return nil, err
 	}
-	return result, nil
+
+	// TODO: there must be a more... elegant way?
+	for _, sPath := range seriesPaths {
+		for aPath, a := range articles {
+			if a.PageType != PTSeriesIndex &&
+				filepath.Dir(sPath) == filepath.Dir(filepath.Dir(aPath)) {
+				child := a
+				parent := articles[sPath]
+				parent.Children = append(parent.Children, child)
+				child.Parent = parent
+			}
+		}
+	}
+
+	return articles, nil
 }
diff --git a/theme/navbar.css b/theme/navbar.css
index 3a7107b..71bba65 100644
--- a/theme/navbar.css
+++ b/theme/navbar.css
@@ -5,6 +5,7 @@ nav > a {
 nav > .posted-on {
   float: right;
   font-style: italic;
+  margin-bottom: 0.5rem;
 }
 
 .nav-hr {
diff --git a/theme/post.tmpl b/theme/post.tmpl
index 6878886..15fa7c6 100644
--- a/theme/post.tmpl
+++ b/theme/post.tmpl
@@ -5,8 +5,47 @@
 {{- template "navbar" .}}
 
 <main>
+
+{{- if .Post.Parent }}
+<em>
+  This post is part of
+  <a href="{{.Post.Parent.WebPath}}">{{.Post.Parent.Title}}</a>
+</em>
+<hr>
+{{- end }}
+
 <h1>{{.Post.Title}}</h1>
+
 {{.Content}}
+{{if .Post.Parent }}
+  <div class="series-container">
+    <p>
+      Here's every post in
+      <a href="{{.Post.Parent.WebPath}}">{{.Post.Parent.Title}}</a>,
+      in chronological order:
+    </p>
+
+    <ol>
+    {{- range .Post.Parent.Children }}
+      {{ if not .IsDraft -}}
+      <li><a href="{{.WebPath}}">{{.Title}}</a>
+          {{- if eq $.Post.WebPath .WebPath }} (you are here) {{- end -}}
+      </li>
+      {{- end -}}
+    {{ end }}
+    </ol>
+  </div>
+
+  <style>
+    .series-container {
+      margin: 2rem 0;
+      padding: 0 1rem;
+      border: 1px dashed #aaa;
+      background-color: #eee;
+    }
+  </style>
+{{ end -}}
+
 </main>
 
 {{template "footer" .}}
diff --git a/theme/series-index.tmpl b/theme/series-index.tmpl
new file mode 100644
index 0000000..aee5b53
--- /dev/null
+++ b/theme/series-index.tmpl
@@ -0,0 +1,27 @@
+{{- define "head"}}{{- end}}
+
+{{define "body"}}
+
+{{- template "navbar" .}}
+
+<main>
+
+<h1>{{.Post.Title}}</h1>
+{{.Content}}
+
+<ol>
+{{ range .Post.Children }}
+{{- if not .IsDraft}}
+  <li style="margin-bottom: 1rem;">
+    <a href="{{.WebPath}}">{{.Title}}</a>
+    <br>
+    <span>{{.PostedAt.Local.Format "January 1, 2006"}}</span>
+  </li>
+{{- end}}
+{{ end }}
+</ol>
+
+</main>
+
+{{template "footer" .}}
+{{- end}}