package web
import (
"bytes"
"fmt"
"html/template"
"net/http"
"strings"
"time"
)
// pageNames are the templates with a full page each; "layout" wraps them. Fragments (for htmx) are defined inside
// page files and rendered on their own.
var pageNames = []string{"home", "signin", "signup", "account", "device", "online", "console", "console_people",
"console_person", "error"}
var funcs = template.FuncMap{
"ago": func(t time.Time) string {
d := time.Since(t)
switch {
case d < time.Minute:
return "just now"
case d < time.Hour:
return fmt.Sprintf("%d min ago", int(d.Minutes()))
case d < 48*time.Hour:
return fmt.Sprintf("%d h ago", int(d.Hours()))
}
return t.Format("2 Jan 2006")
},
"date": func(t time.Time) string { return t.Format("2 Jan 2006 15:04") },
"upper": strings.ToUpper,
}
func (s *Server) parseTemplates() error {
s.pages = map[string]*template.Template{}
for _, name := range pageNames {
t, err := template.New("layout.html").Funcs(funcs).ParseFS(templateFiles, "templates/layout.html", "templates/"+name+".html")
if err != nil {
return err
}
s.pages[name] = t
}
return nil
}
// render shows a page in the layout. data is the page's own values; the layout gets the person and CSRF token.
func (s *Server) render(q *request, name string, data map[string]any) {
if data == nil {
data = map[string]any{}
}
data["Person"] = q.person
data["CSRF"] = q.csrf
data["Flash"] = q.flash
data["Page"] = name
var buf bytes.Buffer
if err := s.pages[name].ExecuteTemplate(&buf, "layout.html", data); err != nil {
s.Log.Printf("rendering %s: %v", name, err)
http.Error(q.w, "Something went wrong on our side.", http.StatusInternalServerError)
return
}
q.w.Header().Set("Content-Type", "text/html; charset=utf-8")
q.w.Header().Set("Cache-Control", "no-store")
buf.WriteTo(q.w)
}
// fragment shows one template from a page's file, without the layout (htmx swaps it in).
func (s *Server) fragment(q *request, page, name string, data map[string]any) {
if data == nil {
data = map[string]any{}
}
data["Person"] = q.person
data["CSRF"] = q.csrf
var buf bytes.Buffer
if err := s.pages[page].ExecuteTemplate(&buf, name, data); err != nil {
s.Log.Printf("rendering %s/%s: %v", page, name, err)
http.Error(q.w, "Something went wrong on our side.", http.StatusInternalServerError)
return
}
q.w.Header().Set("Content-Type", "text/html; charset=utf-8")
q.w.Header().Set("Cache-Control", "no-store")
buf.WriteTo(q.w)
}