diff --git a/src/hmnurl/hmnurl_test.go b/src/hmnurl/hmnurl_test.go
index e71f6572..1fc502b4 100644
--- a/src/hmnurl/hmnurl_test.go
+++ b/src/hmnurl/hmnurl_test.go
@@ -422,9 +422,15 @@ func TestTimeMachine(t *testing.T) {
func TestTimeMachineSubmissions(t *testing.T) {
AssertRegexMatch(t, BuildTimeMachineSubmissions(), RegexTimeMachineSubmissions, nil)
+ AssertRegexMatch(t, BuildTimeMachineSubmission(123), RegexTimeMachineSubmissions, nil)
AssertSubdomain(t, BuildTimeMachineSubmissions(), "")
}
+func TestTimeMachineAtomFeed(t *testing.T) {
+ AssertRegexMatch(t, BuildTimeMachineAtomFeed(), RegexTimeMachineAtomFeed, nil)
+ AssertSubdomain(t, BuildTimeMachineAtomFeed(), "")
+}
+
func TestTimeMachineForm(t *testing.T) {
AssertRegexMatch(t, BuildTimeMachineForm(), RegexTimeMachineForm, nil)
AssertSubdomain(t, BuildTimeMachineForm(), "")
diff --git a/src/hmnurl/urls.go b/src/hmnurl/urls.go
index 6d0eab0c..f1527223 100644
--- a/src/hmnurl/urls.go
+++ b/src/hmnurl/urls.go
@@ -126,6 +126,18 @@ func BuildTimeMachineSubmissions() string {
return Url("/timemachine/submissions", nil)
}
+func BuildTimeMachineSubmission(id int) string {
+ defer CatchPanic()
+ return UrlWithFragment("/timemachine/submissions", nil, strconv.Itoa(id))
+}
+
+var RegexTimeMachineAtomFeed = regexp.MustCompile("^/timemachine/submissions/atom$")
+
+func BuildTimeMachineAtomFeed() string {
+ defer CatchPanic()
+ return Url("/timemachine/submissions/atom", nil)
+}
+
var RegexTimeMachineForm = regexp.MustCompile("^/timemachine/submit$")
func BuildTimeMachineForm() string {
diff --git a/src/templates/src/layouts/timemachine_base.html b/src/templates/src/layouts/timemachine_base.html
index 97e2f2eb..64ee993c 100644
--- a/src/templates/src/layouts/timemachine_base.html
+++ b/src/templates/src/layouts/timemachine_base.html
@@ -177,6 +177,8 @@
margin: 0.2rem 0;
}
+
+ {{ block "extrahead" . }}{{ end }}
diff --git a/src/templates/src/timemachine_atom.xml b/src/templates/src/timemachine_atom.xml
new file mode 100644
index 00000000..fe3dd750
--- /dev/null
+++ b/src/templates/src/timemachine_atom.xml
@@ -0,0 +1,36 @@
+{{ noescape "" }}
+
diff --git a/src/templates/templates.go b/src/templates/templates.go
index 4d037622..c399f2c2 100644
--- a/src/templates/templates.go
+++ b/src/templates/templates.go
@@ -255,6 +255,9 @@ var HMNTemplateFuncs = template.FuncMap{
"noescape": func(str string) template.HTML {
return template.HTML(str)
},
+ "yesescape": func(html template.HTML) string {
+ return string(html)
+ },
"filesize": func(numBytes int) string {
scales := []string{
" bytes",
diff --git a/src/website/routes.go b/src/website/routes.go
index 966d4f29..395e2e4e 100644
--- a/src/website/routes.go
+++ b/src/website/routes.go
@@ -72,6 +72,7 @@ func NewWebsiteRoutes(conn *pgxpool.Pool) http.Handler {
hmnOnly.GET(hmnurl.RegexTimeMachine, TimeMachine)
hmnOnly.GET(hmnurl.RegexTimeMachineSubmissions, TimeMachineSubmissions)
+ hmnOnly.GET(hmnurl.RegexTimeMachineAtomFeed, TimeMachineAtomFeed)
hmnOnly.GET(hmnurl.RegexTimeMachineForm, needsAuth(TimeMachineForm))
hmnOnly.GET(hmnurl.RegexTimeMachineFormDone, needsAuth(TimeMachineFormDone))
hmnOnly.POST(hmnurl.RegexTimeMachineForm, needsAuth(csrfMiddleware(TimeMachineFormSubmit)))
diff --git a/src/website/time_machine.go b/src/website/time_machine.go
index 74fa354a..e322ff5b 100644
--- a/src/website/time_machine.go
+++ b/src/website/time_machine.go
@@ -4,6 +4,7 @@ import (
"html/template"
"net/http"
"strings"
+ "time"
"git.handmade.network/hmn/hmn/src/email"
"git.handmade.network/hmn/hmn/src/hmnurl"
@@ -67,12 +68,14 @@ func TimeMachineSubmissions(c *RequestContext) ResponseData {
templates.BaseData
MainUrl string
SubmitUrl string
+ AtomFeedUrl string
Submissions []TimeMachineSubmission
}
tmpl := TemplateData{
BaseData: baseData,
MainUrl: hmnurl.BuildTimeMachine(),
SubmitUrl: hmnurl.BuildTimeMachineForm(),
+ AtomFeedUrl: hmnurl.BuildTimeMachineAtomFeed(),
Submissions: tmSubmissions,
}
@@ -139,10 +142,39 @@ func TimeMachineFormDone(c *RequestContext) ResponseData {
return res
}
+func TimeMachineAtomFeed(c *RequestContext) ResponseData {
+ type TemplateData struct {
+ Updated time.Time
+ TimeMachineUrl string
+ SubmissionsUrl string
+ AtomFeedUrl string
+ LogoUrl string
+ Submissions []TimeMachineSubmission
+ }
+ tmpl := TemplateData{
+ Updated: tmSubmissions[0].Date,
+ TimeMachineUrl: hmnurl.BuildTimeMachine(),
+ SubmissionsUrl: hmnurl.BuildTimeMachineSubmissions(),
+ AtomFeedUrl: hmnurl.BuildTimeMachineAtomFeed(),
+ LogoUrl: hmnurl.BuildPublic("timemachine/twittercard.png", true),
+ Submissions: tmSubmissions,
+ }
+
+ var res ResponseData
+ res.MustWriteTemplate(
+ "timemachine_atom.xml",
+ tmpl,
+ c.Perf,
+ )
+ return res
+}
+
type TimeMachineSubmission struct {
+ Date time.Time
Title string
Url string
Thumbnail string
+ Permalink string // generated for feed
Details []TimeMachineSubmissionDetail
Description template.HTML
}
@@ -154,6 +186,7 @@ type TimeMachineSubmissionDetail struct {
var tmSubmissions = []TimeMachineSubmission{
{
+ Date: time.Date(2023, 6, 6, 0, 0, 0, 0, time.UTC),
Title: "2009 iPod Touch",
Url: "https://youtu.be/2eBFk1yV6mE",
Thumbnail: "timemachine/ipodtouch-dither.gif",
@@ -188,3 +221,9 @@ var tmSubmissions = []TimeMachineSubmission{
`,
},
}
+
+func init() {
+ for i := range tmSubmissions {
+ tmSubmissions[i].Permalink = hmnurl.BuildTimeMachineSubmission(len(tmSubmissions) - i)
+ }
+}