mirror of
https://github.com/samsonjs/samhuri.net.git
synced 2026-03-25 09:05:47 +00:00
Fix various broken parts of posts templates
This commit is contained in:
parent
0a876c0c01
commit
5fac69542c
9 changed files with 85 additions and 57 deletions
|
|
@ -73,14 +73,16 @@ Execution, trying TDD for the first time:
|
||||||
|
|
||||||
- [x] Search for other _data.json and .ejs files and eliminate any that are found
|
- [x] Search for other _data.json and .ejs files and eliminate any that are found
|
||||||
|
|
||||||
- [ ] Link years to year indexes in the posts archive
|
- [x] Link years to year indexes in the posts archive
|
||||||
|
|
||||||
- [ ] Fix missing days on post dates in the archive and year indexes
|
- [x] Fix missing days on post dates in the archive and year indexes
|
||||||
|
|
||||||
- [ ] Find a way to add the site name to HTML titles rendered by plugins
|
- [x] Find a way to add the site name to HTML titles rendered by plugins
|
||||||
|
|
||||||
- [ ] Clean up the posts plugin
|
- [ ] Clean up the posts plugin
|
||||||
|
|
||||||
|
- [ ] Why don't plain data structures always work with Stencil? Maybe computed properties are a no-go but we can at least use structs instead of dictionaries for the actual rendering
|
||||||
|
|
||||||
- [ ] Consider using Swift for samhuri.net as well, and then making SiteGenerator a package that it uses ... then we can use Plot or pointfree.co's swift-html
|
- [ ] Consider using Swift for samhuri.net as well, and then making SiteGenerator a package that it uses ... then we can use Plot or pointfree.co's swift-html
|
||||||
|
|
||||||
- [ ] Replace remaining Ruby with Swift
|
- [ ] Replace remaining Ruby with Swift
|
||||||
|
|
|
||||||
|
|
@ -12,14 +12,6 @@ struct PageContext {
|
||||||
let body: String
|
let body: String
|
||||||
let page: Page
|
let page: Page
|
||||||
let metadata: [String: String]
|
let metadata: [String: String]
|
||||||
|
|
||||||
var title: String {
|
|
||||||
guard !page.title.isEmpty else {
|
|
||||||
return site.title
|
|
||||||
}
|
|
||||||
|
|
||||||
return "\(site.title): \(page.title)"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extension PageContext: TemplateContext {
|
extension PageContext: TemplateContext {
|
||||||
|
|
@ -30,7 +22,6 @@ extension PageContext: TemplateContext {
|
||||||
var dictionary: [String: Any] {
|
var dictionary: [String: Any] {
|
||||||
[
|
[
|
||||||
"site": site,
|
"site": site,
|
||||||
"title": title,
|
|
||||||
"body": body,
|
"body": body,
|
||||||
"page": page,
|
"page": page,
|
||||||
"metadata": metadata,
|
"metadata": metadata,
|
||||||
|
|
|
||||||
|
|
@ -17,20 +17,28 @@ struct Post {
|
||||||
let tags: [String]
|
let tags: [String]
|
||||||
let bodyMarkdown: String
|
let bodyMarkdown: String
|
||||||
|
|
||||||
var isLink: Bool {
|
var dictionary: [String: Any] {
|
||||||
link != nil
|
var result: [String: Any] = [
|
||||||
|
"slug": slug,
|
||||||
|
"title": title,
|
||||||
|
"author": author,
|
||||||
|
"day": date.day,
|
||||||
|
"month": date.month,
|
||||||
|
"year": date.year,
|
||||||
|
"formattedDate": formattedDate,
|
||||||
|
"tags": tags
|
||||||
|
]
|
||||||
|
if let link = link {
|
||||||
|
result["isLink"] = true
|
||||||
|
result["link"] = link
|
||||||
|
}
|
||||||
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
var path: String {
|
func dictionary(withPath path: String) -> [String: Any] {
|
||||||
let dateComponents = Calendar.current.dateComponents([.year, .month], from: date)
|
var dict = dictionary
|
||||||
let year = dateComponents.year!
|
dict["path"] = path
|
||||||
let month = dateComponents.month!
|
return dict
|
||||||
return "/" + [
|
|
||||||
"posts",
|
|
||||||
String(format: "%02d", year),
|
|
||||||
String(format: "%02d", month),
|
|
||||||
"\(slug)",
|
|
||||||
].joined(separator: "/")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -62,8 +62,8 @@ final class PostsPlugin: Plugin {
|
||||||
print("renderPostsByDate(postsDir: \(postsDir), templateRenderer: \(templateRenderer)")
|
print("renderPostsByDate(postsDir: \(postsDir), templateRenderer: \(templateRenderer)")
|
||||||
for post in posts.flattened() {
|
for post in posts.flattened() {
|
||||||
let monthDir = postsDir
|
let monthDir = postsDir
|
||||||
.appendingPathComponent(String(format: "%02d", post.date.year))
|
.appendingPathComponent("\(post.date.year)")
|
||||||
.appendingPathComponent(String(format: "%02d", post.date.month))
|
.appendingPathComponent(Month(post.date.month).padded)
|
||||||
try renderPost(post, monthDir: monthDir, templateRenderer: templateRenderer)
|
try renderPost(post, monthDir: monthDir, templateRenderer: templateRenderer)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -73,11 +73,10 @@ final class PostsPlugin: Plugin {
|
||||||
let recentPosts = posts.flattened().prefix(10)
|
let recentPosts = posts.flattened().prefix(10)
|
||||||
let renderedRecentPosts: [[String: Any]] = recentPosts.map { post in
|
let renderedRecentPosts: [[String: Any]] = recentPosts.map { post in
|
||||||
let html = markdownParser.html(from: post.bodyMarkdown)
|
let html = markdownParser.html(from: post.bodyMarkdown)
|
||||||
return RenderedPost(post: post, body: html).dictionary
|
let path = self.path(for: post)
|
||||||
|
return RenderedPost(path: path, post: post, body: html).dictionary
|
||||||
}
|
}
|
||||||
#warning("FIXME: get the site name out of here somehow")
|
|
||||||
let recentPostsHTML = try templateRenderer.renderTemplate(name: "recent-posts", context: [
|
let recentPostsHTML = try templateRenderer.renderTemplate(name: "recent-posts", context: [
|
||||||
"title": "samhuri.net",
|
|
||||||
"recentPosts": renderedRecentPosts,
|
"recentPosts": renderedRecentPosts,
|
||||||
])
|
])
|
||||||
let fileURL = targetURL.appendingPathComponent(recentPostsPath)
|
let fileURL = targetURL.appendingPathComponent(recentPostsPath)
|
||||||
|
|
@ -88,21 +87,23 @@ final class PostsPlugin: Plugin {
|
||||||
print("renderArchive(postsDir: \(postsDir), templateRenderer: \(templateRenderer)")
|
print("renderArchive(postsDir: \(postsDir), templateRenderer: \(templateRenderer)")
|
||||||
let allYears = posts.byYear.keys.sorted(by: >)
|
let allYears = posts.byYear.keys.sorted(by: >)
|
||||||
let allMonths = (1 ... 12).map(Month.init(_:))
|
let allMonths = (1 ... 12).map(Month.init(_:))
|
||||||
let postsByMonthByYearForContext: [[String: Any]] = allYears.map { year in
|
let yearsWithPostsByMonthForContext: [[String: Any]] = allYears.map { year in
|
||||||
[
|
[
|
||||||
"number": "\(year)",
|
"path": self.path(year: year),
|
||||||
"months": posts[year].byMonth.keys.sorted(by: >).map { month in
|
"title": "\(year)",
|
||||||
[
|
"months": posts[year].byMonth.keys.sorted(by: >).map { (month: Month) -> [String: Any] in
|
||||||
"number": month.padded,
|
let sortedPosts = posts[year][month].posts.sorted(by: { $0.date > $1.date })
|
||||||
"posts": posts[year][month].posts,
|
return [
|
||||||
|
"path": self.path(year: year, month: month),
|
||||||
|
"title": month.padded,
|
||||||
|
"posts": sortedPosts.map { $0.dictionary(withPath: self.path(for: $0)) },
|
||||||
]
|
]
|
||||||
} as Any,
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
let context: [String: Any] = [
|
let context: [String: Any] = [
|
||||||
"title": "Archive",
|
"title": "Archive",
|
||||||
"path": postsPath,
|
"years": yearsWithPostsByMonthForContext,
|
||||||
"years": postsByMonthByYearForContext,
|
|
||||||
"monthNames": allMonths.reduce(into: [String: String](), { dict, month in
|
"monthNames": allMonths.reduce(into: [String: String](), { dict, month in
|
||||||
dict[month.padded] = month.name
|
dict[month.padded] = month.name
|
||||||
}),
|
}),
|
||||||
|
|
@ -130,11 +131,10 @@ final class PostsPlugin: Plugin {
|
||||||
|
|
||||||
try fileManager.createDirectory(at: yearDir, withIntermediateDirectories: true, attributes: nil)
|
try fileManager.createDirectory(at: yearDir, withIntermediateDirectories: true, attributes: nil)
|
||||||
let months = Array(sortedPostsByMonth.keys.sorted().reversed())
|
let months = Array(sortedPostsByMonth.keys.sorted().reversed())
|
||||||
let postsByMonthForContext: [String: [Post]] = sortedPostsByMonth.reduce(into: [:]) { dict, pair in
|
let postsByMonthForContext: [String: [[String: Any]]] = sortedPostsByMonth.reduce(into: [:]) { dict, pair in
|
||||||
let (month, posts) = pair
|
let (month, posts) = pair
|
||||||
dict[month.padded] = posts
|
dict[month.padded] = posts.map { $0.dictionary(withPath: self.path(for: $0)) }
|
||||||
}
|
}
|
||||||
#warning("FIXME: get the site name in the head title but not the body title")
|
|
||||||
let context: [String: Any] = [
|
let context: [String: Any] = [
|
||||||
"title": "\(year)",
|
"title": "\(year)",
|
||||||
"path": postsPath,
|
"path": postsPath,
|
||||||
|
|
@ -166,12 +166,12 @@ final class PostsPlugin: Plugin {
|
||||||
}
|
}
|
||||||
|
|
||||||
let renderedPosts = sortedPosts.map { post -> RenderedPost in
|
let renderedPosts = sortedPosts.map { post -> RenderedPost in
|
||||||
|
let path = self.path(for: post)
|
||||||
let bodyHTML = markdownParser.html(from: post.bodyMarkdown)
|
let bodyHTML = markdownParser.html(from: post.bodyMarkdown)
|
||||||
return RenderedPost(post: post, body: bodyHTML)
|
return RenderedPost(path: path, post: post, body: bodyHTML)
|
||||||
}
|
}
|
||||||
let monthDir = yearDir.appendingPathComponent(month.padded)
|
let monthDir = yearDir.appendingPathComponent(month.padded)
|
||||||
try fileManager.createDirectory(at: monthDir, withIntermediateDirectories: true, attributes: nil)
|
try fileManager.createDirectory(at: monthDir, withIntermediateDirectories: true, attributes: nil)
|
||||||
#warning("FIXME: get the site name in the head title but not the body title")
|
|
||||||
let context: [String: Any] = [
|
let context: [String: Any] = [
|
||||||
"title": "\(month.name) \(year)",
|
"title": "\(month.name) \(year)",
|
||||||
"posts": renderedPosts.map { $0.dictionary },
|
"posts": renderedPosts.map { $0.dictionary },
|
||||||
|
|
@ -187,10 +187,10 @@ final class PostsPlugin: Plugin {
|
||||||
print("renderPost(\(post.debugDescription), monthDir: \(monthDir), templateRenderer: \(templateRenderer)")
|
print("renderPost(\(post.debugDescription), monthDir: \(monthDir), templateRenderer: \(templateRenderer)")
|
||||||
try fileManager.createDirectory(at: monthDir, withIntermediateDirectories: true, attributes: nil)
|
try fileManager.createDirectory(at: monthDir, withIntermediateDirectories: true, attributes: nil)
|
||||||
let filename = "\(post.slug).html"
|
let filename = "\(post.slug).html"
|
||||||
|
let path = self.path(for: post)
|
||||||
let postURL = monthDir.appendingPathComponent(filename)
|
let postURL = monthDir.appendingPathComponent(filename)
|
||||||
let bodyHTML = markdownParser.html(from: post.bodyMarkdown)
|
let bodyHTML = markdownParser.html(from: post.bodyMarkdown)
|
||||||
let renderedPost = RenderedPost(post: post, body: bodyHTML)
|
let renderedPost = RenderedPost(path: path, post: post, body: bodyHTML)
|
||||||
#warning("FIXME: get the site name in the head title but not the body title")
|
|
||||||
let postHTML = try templateRenderer.renderTemplate(name: "post", context: [
|
let postHTML = try templateRenderer.renderTemplate(name: "post", context: [
|
||||||
"title": "\(renderedPost.post.title)",
|
"title": "\(renderedPost.post.title)",
|
||||||
"post": renderedPost.dictionary,
|
"post": renderedPost.dictionary,
|
||||||
|
|
@ -211,4 +211,20 @@ final class PostsPlugin: Plugin {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func path(for post: Post) -> String {
|
||||||
|
path(year: post.date.year, month: Month(post.date.month), filename: "\(post.slug).html")
|
||||||
|
}
|
||||||
|
|
||||||
|
private func path(year: Int) -> String {
|
||||||
|
"/\(postsPath)/\(year)"
|
||||||
|
}
|
||||||
|
|
||||||
|
private func path(year: Int, month: Month) -> String {
|
||||||
|
path(year: year).appending("/\(month.padded)")
|
||||||
|
}
|
||||||
|
|
||||||
|
private func path(year: Int, month: Month, filename: String) -> String {
|
||||||
|
path(year: year, month: month).appending("/\(filename)")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
struct RenderedPost {
|
struct RenderedPost {
|
||||||
|
let path: String
|
||||||
let post: Post
|
let post: Post
|
||||||
let body: String
|
let body: String
|
||||||
|
|
||||||
|
|
@ -18,9 +19,8 @@ struct RenderedPost {
|
||||||
"date": post.date,
|
"date": post.date,
|
||||||
"day": post.date.day,
|
"day": post.date.day,
|
||||||
"formattedDate": post.formattedDate,
|
"formattedDate": post.formattedDate,
|
||||||
"isLink": post.isLink,
|
|
||||||
"link": post.link as Any,
|
"link": post.link as Any,
|
||||||
"path": post.path,
|
"path": path,
|
||||||
"body": body,
|
"body": body,
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -46,7 +46,6 @@ final class ProjectsPlugin: Plugin {
|
||||||
let projectsDir = targetURL.appendingPathComponent(path)
|
let projectsDir = targetURL.appendingPathComponent(path)
|
||||||
try fileManager.createDirectory(at: projectsDir, withIntermediateDirectories: true, attributes: nil)
|
try fileManager.createDirectory(at: projectsDir, withIntermediateDirectories: true, attributes: nil)
|
||||||
let projectsURL = projectsDir.appendingPathComponent("index.html")
|
let projectsURL = projectsDir.appendingPathComponent("index.html")
|
||||||
#warning("FIXME: get the site name in the head title but not the body title")
|
|
||||||
let projectsHTML = try templateRenderer.renderTemplate(name: "projects", context: [
|
let projectsHTML = try templateRenderer.renderTemplate(name: "projects", context: [
|
||||||
"title": "Projects",
|
"title": "Projects",
|
||||||
"projects": projects,
|
"projects": projects,
|
||||||
|
|
@ -56,7 +55,6 @@ final class ProjectsPlugin: Plugin {
|
||||||
for project in projects {
|
for project in projects {
|
||||||
let filename = "\(project.title).html"
|
let filename = "\(project.title).html"
|
||||||
let projectURL = projectsDir.appendingPathComponent(filename)
|
let projectURL = projectsDir.appendingPathComponent(filename)
|
||||||
#warning("FIXME: get the site name in the head title but not the body title")
|
|
||||||
let projectHTML = try templateRenderer.renderTemplate(name: "project", context: [
|
let projectHTML = try templateRenderer.renderTemplate(name: "project", context: [
|
||||||
"title": "\(project.title)",
|
"title": "\(project.title)",
|
||||||
"project": project,
|
"project": project,
|
||||||
|
|
|
||||||
|
|
@ -45,6 +45,6 @@ do {
|
||||||
exit(0)
|
exit(0)
|
||||||
}
|
}
|
||||||
catch {
|
catch {
|
||||||
fputs("error: \(error.localizedDescription)", stderr)
|
fputs("error: \(error)", stderr)
|
||||||
exit(-1)
|
exit(-1)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,24 +2,31 @@
|
||||||
|
|
||||||
{% block body %}
|
{% block body %}
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
<h1>{{ title }}</h1>
|
||||||
|
</div>
|
||||||
|
|
||||||
{% for year in years %}
|
{% for year in years %}
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<h1>{{ year.number }}</h1>
|
<h2><a href="{{ year.path }}">{{ year.title }}</a></h2>
|
||||||
|
|
||||||
{% for month in year.months %}
|
{% for month in year.months %}
|
||||||
<h2>
|
<h3>
|
||||||
<a href="/{{ path }}/{{ year.number }}/{{ month.number }}">{{ monthNames[month.number] }}</a>
|
<a href="{{ month.path }}">{{ monthNames[month.title] }}</a>
|
||||||
</h2>
|
</h3>
|
||||||
|
|
||||||
<ul class="archive">
|
<ul class="archive">
|
||||||
{% for post in month.posts %}
|
{% for post in month.posts %}
|
||||||
<li>
|
<li>
|
||||||
{% if post.isLink %}
|
{% if post.isLink %}
|
||||||
<a href="{{ post.path }}">→ {{ post.title }}</a>
|
<a href="{{ post.link }}">→ {{ post.title }}</a>
|
||||||
{% else %}
|
{% else %}
|
||||||
<a href="{{ post.path }}">{{ post.title }}</a>
|
<a href="{{ post.path }}">{{ post.title }}</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<time>{{ post.day }} {{ monthAbbreviations[month.number] }}</time>
|
<time>{{ post.day }} {{ monthAbbreviations[month.title] }}</time>
|
||||||
|
{% if post.isLink %}
|
||||||
|
<a class="permalink" href="{{ post.path }}">∞</a>
|
||||||
|
{% endif %}
|
||||||
</li>
|
</li>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</ul>
|
</ul>
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,13 @@
|
||||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
|
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
|
||||||
|
|
||||||
<title>{{ title }}</title>
|
{% if title %}
|
||||||
|
<title>{{ site.title }}: {{ title }}</title>
|
||||||
|
{% elif page.title %}
|
||||||
|
<title>{{ site.title }}: {{ page.title }}</title>
|
||||||
|
{% else %}
|
||||||
|
<title>{{ site.title }}</title>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
<link rel="icon" type="image/png" href="/images/favicon-32x32.png">
|
<link rel="icon" type="image/png" href="/images/favicon-32x32.png">
|
||||||
<link rel="shortcut icon" href="/images/favicon.ico">
|
<link rel="shortcut icon" href="/images/favicon.ico">
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue