Monorepo for Tangled tangled.org

appview: proper nested templates

This now allows for a more organized template structure. All templates
can now be accessed via "parent/child" paths, and are templated atop
the layouts/*.html files.

The layout files themselves are modular and can be included in each
other. The only caveat is layout/*.html NEED to define their own
template names like so:

{{ define "base" }}

The other page templates do not (and musn't) define their own template.

+1 -1
.air/appview.toml
··· 4 4 root = "." 5 5 6 6 exclude_regex = [".*_templ.go"] 7 - include_ext = ["go", "templ"] 7 + include_ext = ["go", "templ", "html"] 8 8 exclude_dir = ["target", "atrium"]
appview/pages/knot.html appview/pages/templates/knot.html
appview/pages/knots.html appview/pages/templates/knots.html
-20
appview/pages/layout.html
··· 1 - <!DOCTYPE html> 2 - <html lang="en"> 3 - <head> 4 - <meta charset="UTF-8"> 5 - <meta name="viewport" content="width=device-width, initial-scale=1.0"> 6 - <script src="https://unpkg.com/htmx.org@2.0.4" integrity="sha384-HGfztofotfshcF7+8n44JQL2oJmowVChPTg48S+jvZoztPfvwD79OC/LTtG6dMp+" crossorigin="anonymous"></script> 7 - <title>{{block "title" .}}tangled{{end}}</title> 8 - </head> 9 - <body> 10 - 11 - <div class="navbar"> 12 - <a href="/" style="color: white; text-decoration: none;">Home</a> 13 - </div> 14 - 15 - <div class="content"> 16 - {{block "content" .}}{{end}} 17 - </div> 18 - 19 - </body> 20 - </html>
-14
appview/pages/login.html
··· 1 - {{define "title"}}login{{end}} 2 - 3 - {{define "content"}} 4 - <h1>login</h1> 5 - <form method="POST" action="/login"> 6 - <label for="handle">handle</label> 7 - <input type="text" id="handle" name="handle" required> 8 - 9 - <label for="app_password">app password</label> 10 - <input type="password" id="app_password" name="app_password" required> 11 - 12 - <button type="submit">login</button> 13 - </form> 14 - {{end}}
appview/pages/new-repo.html appview/pages/templates/new-repo.html
+59 -30
appview/pages/pages.go
··· 2 2 3 3 import ( 4 4 "embed" 5 + "fmt" 5 6 "html/template" 6 7 "io" 7 - "sync" 8 + "io/fs" 9 + "log" 10 + "strings" 8 11 9 12 "github.com/sotangled/tangled/appview/auth" 10 13 "github.com/sotangled/tangled/appview/db" 11 14 ) 12 15 13 - //go:embed *.html 16 + //go:embed templates/* 14 17 var files embed.FS 15 18 16 - var ( 17 - cache = make(map[string]*template.Template) 18 - mutex sync.Mutex 19 - ) 19 + type Pages struct { 20 + t map[string]*template.Template 21 + } 22 + 23 + func NewPages() *Pages { 24 + templates := make(map[string]*template.Template) 25 + 26 + // Walk through embedded templates directory and parse all .html files 27 + err := fs.WalkDir(files, "templates", func(path string, d fs.DirEntry, err error) error { 28 + if err != nil { 29 + return err 30 + } 31 + 32 + if !d.IsDir() && strings.HasSuffix(path, ".html") { 33 + name := strings.TrimPrefix(path, "templates/") 34 + name = strings.TrimSuffix(name, ".html") 20 35 21 - func parse(file string) *template.Template { 22 - mutex.Lock() 23 - defer mutex.Unlock() 36 + if !strings.HasPrefix(path, "templates/layouts/") { 37 + // Add the page template on top of the base 38 + tmpl, err := template.New(name).ParseFS(files, path, "templates/layouts/*.html") 39 + if err != nil { 40 + return fmt.Errorf("setting up template: %w", err) 41 + } 24 42 25 - if tmpl, found := cache[file]; found { 26 - return tmpl 43 + templates[name] = tmpl 44 + log.Printf("loaded template: %s", name) 45 + } 46 + 47 + return nil 48 + } 49 + return nil 50 + }) 51 + if err != nil { 52 + log.Fatalf("walking template dir: %v", err) 27 53 } 28 54 29 - tmpl := template.Must( 30 - template.New("layout.html").ParseFS(files, "layout.html", file), 31 - ) 55 + log.Printf("total templates loaded: %d", len(templates)) 32 56 33 - cache[file] = tmpl 34 - return tmpl 57 + return &Pages{ 58 + t: templates, 59 + } 35 60 } 36 61 37 62 type LoginParams struct { 38 63 } 39 64 40 - func Login(w io.Writer, p LoginParams) error { 41 - return parse("login.html").Execute(w, p) 65 + func (p *Pages) execute(name string, w io.Writer, params any) error { 66 + return p.t[name].ExecuteTemplate(w, "layouts/base", params) 67 + } 68 + 69 + func (p *Pages) Login(w io.Writer, params LoginParams) error { 70 + return p.t["user/login"].ExecuteTemplate(w, "layouts/base", params) 42 71 } 43 72 44 73 type TimelineParams struct { 45 74 User *auth.User 46 75 } 47 76 48 - func Timeline(w io.Writer, p TimelineParams) error { 49 - return parse("timeline.html").Execute(w, p) 77 + func (p *Pages) Timeline(w io.Writer, params TimelineParams) error { 78 + return p.execute("timeline", w, params) 50 79 } 51 80 52 81 type SettingsParams struct { ··· 54 83 PubKeys []db.PublicKey 55 84 } 56 85 57 - func Settings(w io.Writer, p SettingsParams) error { 58 - return parse("settings.html").Execute(w, p) 86 + func (p *Pages) Settings(w io.Writer, params SettingsParams) error { 87 + return p.execute("settings/keys", w, params) 59 88 } 60 89 61 90 type KnotsParams struct { ··· 63 92 Registrations []db.Registration 64 93 } 65 94 66 - func Knots(w io.Writer, p KnotsParams) error { 67 - return parse("knots.html").Execute(w, p) 95 + func (p *Pages) Knots(w io.Writer, params KnotsParams) error { 96 + return p.execute("knots", w, params) 68 97 } 69 98 70 99 type KnotParams struct { ··· 74 103 IsOwner bool 75 104 } 76 105 77 - func Knot(w io.Writer, p KnotParams) error { 78 - return parse("knot.html").Execute(w, p) 106 + func (p *Pages) Knot(w io.Writer, params KnotParams) error { 107 + return p.execute("knot", w, params) 79 108 } 80 109 81 110 type NewRepoParams struct { 82 111 User *auth.User 83 112 } 84 113 85 - func NewRepo(w io.Writer, p NewRepoParams) error { 86 - return parse("new-repo.html").Execute(w, p) 114 + func (p *Pages) NewRepo(w io.Writer, params NewRepoParams) error { 115 + return p.execute("repo/new", w, params) 87 116 } 88 117 89 118 type ProfilePageParams struct { ··· 93 122 Repos []db.Repo 94 123 } 95 124 96 - func ProfilePage(w io.Writer, p ProfilePageParams) error { 97 - return parse("profile.html").Execute(w, p) 125 + func (p *Pages) ProfilePage(w io.Writer, params ProfilePageParams) error { 126 + return p.execute("user/profile", w, params) 98 127 }
appview/pages/profile.html appview/pages/templates/user/profile.html
appview/pages/settings.html appview/pages/templates/settings/keys.html
+10
appview/pages/templates/errors/404.html
··· 1 + <html> 2 + <title>404</title> 3 + {{ template "layouts/head" . }} 4 + <body> 5 + {{ template "layouts/nav" . }} 6 + <main> 7 + <h3>404 &mdash; nothing like that here.</h3> 8 + </main> 9 + </body> 10 + </html>
+10
appview/pages/templates/errors/500.html
··· 1 + <html> 2 + <title>500</title> 3 + {{ template "layouts/head" . }} 4 + <body> 5 + {{ template "layouts/nav" . }} 6 + <main> 7 + <h3>500 &mdash; something broke!</h3> 8 + </main> 9 + </body> 10 + </html>
+21
appview/pages/templates/index.html
··· 1 + <html> 2 + {{ template "layouts/head" . }} 3 + 4 + <header> 5 + <h1>{{ .meta.Title }}</h1> 6 + <h2>{{ .meta.Description }}</h2> 7 + </header> 8 + <body> 9 + <main> 10 + <div class="index"> 11 + {{ range .info }} 12 + <div class="index-name"> 13 + <a href="/{{ .Name }}">{{ .DisplayName }}</a> 14 + </div> 15 + <div class="desc">{{ .Desc }}</div> 16 + <div>{{ .Idle }}</div> 17 + {{ end }} 18 + </div> 19 + </main> 20 + </body> 21 + </html>
+18
appview/pages/templates/layouts/base.html
··· 1 + {{ define "layouts/base" }} 2 + <!doctype html> 3 + <html lang="en"> 4 + <head> 5 + <meta charset="UTF-8" /> 6 + <meta name="viewport" content="width=device-width, initial-scale=1.0" /> 7 + <script 8 + src="https://unpkg.com/htmx.org@2.0.4" 9 + integrity="sha384-HGfztofotfshcF7+8n44JQL2oJmowVChPTg48S+jvZoztPfvwD79OC/LTtG6dMp+" 10 + crossorigin="anonymous" 11 + ></script> 12 + <title>{{block "title" .}}{{end}}</title> 13 + </head> 14 + <body> 15 + <main class="content">{{block "content" .}}{{end}}</main> 16 + </body> 17 + </html> 18 + {{ end }}
+37
appview/pages/templates/layouts/head.html
··· 1 + <head> 2 + <meta charset="utf-8" /> 3 + <meta name="viewport" content="width=device-width, initial-scale=1" /> 4 + <link rel="stylesheet" href="/static/style.css" type="text/css" /> 5 + <link rel="icon" type="image/png" size="32x32" href="/static/legit.png" /> 6 + <script src="https://unpkg.com/htmx.org@2.0.4"></script> 7 + <meta name="htmx-config" content='{"selfRequestsOnly":false}' /> 8 + 9 + {{ if .parent }} 10 + <title> 11 + {{ .meta.Title }} &mdash; {{ .name }} ({{ .ref }}): {{ .parent }}/ 12 + </title> 13 + 14 + {{ else if .path }} 15 + <title> 16 + {{ .meta.Title }} &mdash; {{ .name }} ({{ .ref }}): {{ .path }} 17 + </title> 18 + {{ else if .files }} 19 + <title>{{ .meta.Title }} &mdash; {{ .name }} ({{ .ref }})</title> 20 + {{ else if .commit }} 21 + <title>{{ .meta.Title }} &mdash; {{ .name }}: {{ .commit.This }}</title> 22 + {{ else if .branches }} 23 + <title>{{ .meta.Title }} &mdash; {{ .name }}: refs</title> 24 + {{ else if .commits }} {{ if .log }} 25 + <title>{{ .meta.Title }} &mdash; {{ .name }}: log</title> 26 + {{ else }} 27 + <title>{{ .meta.Title }} &mdash; {{ .name }}</title> 28 + {{ end }} {{ else }} 29 + <title>{{ .meta.Title }}</title> 30 + {{ end }} {{ if and .servername .gomod }} 31 + <meta 32 + name="go-import" 33 + content="{{ .servername}}/{{ .name }} git https://{{ .servername }}/{{ .name }}" 34 + /> 35 + {{ end }} 36 + <!-- other meta tags here --> 37 + </head>
+13
appview/pages/templates/layouts/nav.html
··· 1 + {{ define "nav" }} 2 + <nav> 3 + <ul> 4 + {{ if .name }} 5 + <li><a href="/{{ .name }}">summary</a></li> 6 + <li><a href="/{{ .name }}/refs">refs</a> {{ if .ref }}</li> 7 + 8 + <li><a href="/{{ .name }}/tree/{{ .ref }}/">tree</a></li> 9 + <li><a href="/{{ .name }}/log/{{ .ref }}">log</a> {{ end }}</li> 10 + {{ end }} 11 + </ul> 12 + </nav> 13 + {{ end }}
+11
appview/pages/templates/layouts/repo-header.html
··· 1 + {{ define "repo-header" }} 2 + <header> 3 + <h2> 4 + <a href="/">all repos</a> 5 + &mdash; {{ .displayname }} {{ if .ref }} 6 + <span class="ref">@ {{ .ref }}</span> 7 + {{ end }} 8 + </h2> 9 + <h3 class="desc">{{ .desc }}</h3> 10 + </header> 11 + {{ end }}
+5
appview/pages/templates/layouts/test.html
··· 1 + <p>Hello world!</p> 2 + <div class="example"> 3 + <h1>Welcome</h1> 4 + <p>This is a simple HTML example</p> 5 + </div>
+100
appview/pages/templates/repo/commit.html
··· 1 + <html> 2 + {{ template "layouts/head" . }} 3 + 4 + {{ template "layouts/repo-header" . }} 5 + <body> 6 + {{ template "layouts/nav" . }} 7 + <main> 8 + <section class="commit"> 9 + <pre>{{- .commit.Message -}}</pre> 10 + <div class="commit-info"> 11 + {{ .commit.Author.Name }} <a href="mailto:{{ .commit.Author.Email }}" class="commit-email">{{ .commit.Author.Email}}</a> 12 + <div>{{ .commit.Author.When.Format "Mon, 02 Jan 2006 15:04:05 -0700" }}</div> 13 + </div> 14 + 15 + <div> 16 + <strong>commit</strong> 17 + <p><a href="/{{ .name }}/commit/{{ .commit.This }}" class="commit-hash"> 18 + {{ .commit.This }} 19 + </a> 20 + </p> 21 + </div> 22 + 23 + {{ if .commit.Parent }} 24 + <div> 25 + <strong>parent</strong> 26 + <p><a href="/{{ .name }}/commit/{{ .commit.Parent }}" class="commit-hash"> 27 + {{ .commit.Parent }} 28 + </a></p> 29 + </div> 30 + 31 + {{ end }} 32 + <div class="diff-stat"> 33 + <div> 34 + {{ .stat.FilesChanged }} files changed, 35 + {{ .stat.Insertions }} insertions(+), 36 + {{ .stat.Deletions }} deletions(-) 37 + </div> 38 + <div> 39 + <br> 40 + <strong>jump to</strong> 41 + {{ range .diff }} 42 + <ul> 43 + <li><a href="#{{ .Name.New }}">{{ .Name.New }}</a></li> 44 + </ul> 45 + {{ end }} 46 + </div> 47 + </div> 48 + </section> 49 + <section> 50 + {{ $repo := .name }} 51 + {{ $this := .commit.This }} 52 + {{ $parent := .commit.Parent }} 53 + {{ range .diff }} 54 + <div id="{{ .Name.New }}"> 55 + <div class="diff"> 56 + {{ if .IsNew }} 57 + <span class="diff-type">A</span> 58 + {{ end }} 59 + {{ if .IsDelete }} 60 + <span class="diff-type">D</span> 61 + {{ end }} 62 + {{ if not (or .IsNew .IsDelete) }} 63 + <span class="diff-type">M</span> 64 + {{ end }} 65 + {{ if .Name.Old }} 66 + <a href="/{{ $repo }}/blob/{{ $parent }}/{{ .Name.Old }}">{{ .Name.Old }}</a> 67 + {{ if .Name.New }} 68 + &#8594; 69 + <a href="/{{ $repo }}/blob/{{ $this }}/{{ .Name.New }}">{{ .Name.New }}</a> 70 + {{ end }} 71 + {{ else }} 72 + <a href="/{{ $repo }}/blob/{{ $this }}/{{ .Name.New }}">{{ .Name.New }}</a> 73 + {{- end -}} 74 + {{ if .IsBinary }} 75 + <p>Not showing binary file.</p> 76 + {{ else }} 77 + <pre> 78 + {{- range .TextFragments -}} 79 + <p>{{- .Header -}}</p> 80 + {{- range .Lines -}} 81 + {{- if eq .Op.String "+" -}} 82 + <span class="diff-add">{{ .String }}</span> 83 + {{- end -}} 84 + {{- if eq .Op.String "-" -}} 85 + <span class="diff-del">{{ .String }}</span> 86 + {{- end -}} 87 + {{- if eq .Op.String " " -}} 88 + <span class="diff-noop">{{ .String }}</span> 89 + {{- end -}} 90 + {{- end -}} 91 + {{- end -}} 92 + {{- end -}} 93 + </pre> 94 + </div> 95 + </div> 96 + {{ end }} 97 + </section> 98 + </main> 99 + </body> 100 + </html>
+9
appview/pages/templates/repo/empty.html
··· 1 + <html> 2 + {{ template "layouts/head" . }} 3 + 4 + <body> 5 + <main> 6 + <p>This is an empty Git repository. Push some commits here.</p> 7 + </main> 8 + </body> 9 + </html>
+34
appview/pages/templates/repo/file.html
··· 1 + <html> 2 + {{ template "layouts/head" . }} 3 + {{ template "layouts/repo-header" . }} 4 + <body> 5 + {{ template "layouts/nav" . }} 6 + <main> 7 + <p>{{ .path }} (<a style="color: gray" href="?raw=true">view raw</a>)</p> 8 + {{if .chroma }} 9 + <div class="chroma-file-wrapper"> 10 + {{ .content }} 11 + </div> 12 + {{else}} 13 + <div class="file-wrapper"> 14 + <table> 15 + <tbody><tr> 16 + <td class="line-numbers"> 17 + <pre> 18 + {{- range .linecount }} 19 + <a id="L{{ . }}" href="#L{{ . }}">{{ . }}</a> 20 + {{- end -}} 21 + </pre> 22 + </td> 23 + <td class="file-content"> 24 + <pre> 25 + {{- .content -}} 26 + </pre> 27 + </td> 28 + </tbody></tr> 29 + </table> 30 + </div> 31 + {{end}} 32 + </main> 33 + </body> 34 + </html>
+23
appview/pages/templates/repo/log.html
··· 1 + <html> 2 + {{ template "layouts/head" . }} 3 + 4 + {{ template "layouts/repo-header" . }} 5 + <body> 6 + {{ template "layouts/nav" . }} 7 + <main> 8 + {{ $repo := .name }} 9 + <div class="log"> 10 + {{ range .commits }} 11 + <div> 12 + <div><a href="/{{ $repo }}/commit/{{ .Hash.String }}" class="commit-hash">{{ slice .Hash.String 0 8 }}</a></div> 13 + <pre>{{ .Message }}</pre> 14 + </div> 15 + <div class="commit-info"> 16 + {{ .Author.Name }} <a href="mailto:{{ .Author.Email }}" class="commit-email">{{ .Author.Email }}</a> 17 + <div>{{ .Author.When.Format "Mon, 02 Jan 2006 15:04:05 -0700" }}</div> 18 + </div> 19 + {{ end }} 20 + </div> 21 + </main> 22 + </body> 23 + </html>
+14
appview/pages/templates/repo/new.html
··· 1 + {{define "title"}}new repo{{end}} 2 + 3 + {{define "content"}} 4 + <h1>new repo</h1> 5 + <form method="POST" action="/repo/new"> 6 + <label for="name">repo name</label> 7 + <input type="text" id="name" name="name" required /> 8 + 9 + <label for="domain">domain</label> 10 + <input type="domain" id="domain" name="domain" required /> 11 + 12 + <button type="submit">create repo</button> 13 + </form> 14 + {{end}}
+38
appview/pages/templates/repo/refs.html
··· 1 + <html> 2 + {{ template "layouts/head" . }} 3 + 4 + {{ template "layouts/repo-header" . }} 5 + <body> 6 + {{ template "layouts/nav" . }} 7 + <main> 8 + {{ $name := .name }} 9 + <h3>branches</h3> 10 + <div class="refs"> 11 + {{ range .branches }} 12 + <div> 13 + <strong>{{ .Name.Short }}</strong> 14 + <a href="/{{ $name }}/tree/{{ .Name.Short }}/">browse</a> 15 + <a href="/{{ $name }}/log/{{ .Name.Short }}">log</a> 16 + <a href="/{{ $name }}/archive/{{ .Name.Short }}.tar.gz">tar.gz</a> 17 + </div> 18 + {{ end }} 19 + </div> 20 + {{ if .tags }} 21 + <h3>tags</h3> 22 + <div class="refs"> 23 + {{ range .tags }} 24 + <div> 25 + <strong>{{ .Name }}</strong> 26 + <a href="/{{ $name }}/tree/{{ .Name }}/">browse</a> 27 + <a href="/{{ $name }}/log/{{ .Name }}">log</a> 28 + <a href="/{{ $name }}/archive/{{ .Name }}.tar.gz">tar.gz</a> 29 + {{ if .Message }} 30 + <pre>{{ .Message }}</pre> 31 + </div> 32 + {{ end }} 33 + {{ end }} 34 + </div> 35 + {{ end }} 36 + </main> 37 + </body> 38 + </html>
+36
appview/pages/templates/repo/repo.html
··· 1 + <html> 2 + {{ template "layouts/head" . }} 3 + 4 + {{ template "layouts/repo-header" . }} 5 + 6 + <body> 7 + {{ template "layouts/nav" . }} 8 + <main> 9 + {{ $repo := .name }} 10 + <div class="log"> 11 + {{ range .commits }} 12 + <div> 13 + <div><a href="/{{ $repo }}/commit/{{ .Hash.String }}" class="commit-hash">{{ slice .Hash.String 0 8 }}</a></div> 14 + <pre>{{ .Message }}</pre> 15 + </div> 16 + <div class="commit-info"> 17 + {{ .Author.Name }} <a href="mailto:{{ .Author.Email }}" class="commit-email">{{ .Author.Email }}</a> 18 + <div>{{ .Author.When.Format "Mon, 02 Jan 2006 15:04:05 -0700" }}</div> 19 + </div> 20 + {{ end }} 21 + </div> 22 + {{- if .readme }} 23 + <article class="readme"> 24 + {{- .readme -}} 25 + </article> 26 + {{- end -}} 27 + 28 + <div class="clone-url"> 29 + <strong>clone</strong> 30 + <pre> 31 + git clone https://{{ .servername }}/{{ .name }} 32 + </pre> 33 + </div> 34 + </main> 35 + </body> 36 + </html>
+53
appview/pages/templates/repo/tree.html
··· 1 + <html> 2 + 3 + {{ template "layouts/head" . }} 4 + 5 + {{ template "layouts/repo-header" . }} 6 + <body> 7 + {{ template "layouts/nav" . }} 8 + <main> 9 + {{ $repo := .name }} 10 + {{ $ref := .ref }} 11 + {{ $parent := .parent }} 12 + 13 + <div class="tree"> 14 + {{ if $parent }} 15 + <div></div> 16 + <div></div> 17 + <div><a href="/{{ $repo }}/tree/{{ $ref }}/{{ .dotdot }}">..</a></div> 18 + {{ end }} 19 + {{ range .files }} 20 + {{ if not .IsFile }} 21 + <div class="mode">{{ .Mode }}</div> 22 + <div class="size">{{ .Size }}</div> 23 + <div> 24 + {{ if $parent }} 25 + <a href="/{{ $repo }}/tree/{{ $ref }}/{{ $parent }}/{{ .Name }}">{{ .Name }}/</a> 26 + {{ else }} 27 + <a href="/{{ $repo }}/tree/{{ $ref }}/{{ .Name }}">{{ .Name }}/</a> 28 + {{ end }} 29 + </div> 30 + {{ end }} 31 + {{ end }} 32 + {{ range .files }} 33 + {{ if .IsFile }} 34 + <div class="mode">{{ .Mode }}</div> 35 + <div class="size">{{ .Size }}</div> 36 + <div> 37 + {{ if $parent }} 38 + <a href="/{{ $repo }}/blob/{{ $ref }}/{{ $parent }}/{{ .Name }}">{{ .Name }}</a> 39 + {{ else }} 40 + <a href="/{{ $repo }}/blob/{{ $ref }}/{{ .Name }}">{{ .Name }}</a> 41 + {{ end }} 42 + </div> 43 + {{ end }} 44 + {{ end }} 45 + </div> 46 + <article> 47 + <pre> 48 + {{- if .readme }}{{ .readme }}{{- end -}} 49 + </pre> 50 + </article> 51 + </main> 52 + </body> 53 + </html>
+12
appview/pages/templates/user/login.html
··· 1 + {{ define "title" }}login{{end}} {{define "content"}} 2 + <h1>login</h1> 3 + <form method="POST" action="/login"> 4 + <label for="handle">handle</label> 5 + <input type="text" id="handle" name="handle" required /> 6 + 7 + <label for="app_password">app password</label> 8 + <input type="password" id="app_password" name="app_password" required /> 9 + 10 + <button type="submit">login</button> 11 + </form> 12 + {{end}}
appview/pages/timeline.html appview/pages/templates/timeline.html
+14 -9
appview/state/state.go
··· 29 29 auth *auth.Auth 30 30 enforcer *rbac.Enforcer 31 31 tidClock *syntax.TIDClock 32 + pages *pages.Pages 32 33 } 33 34 34 35 func Make() (*State, error) { 35 - 36 36 db, err := db.Make(appview.SqliteDbPath) 37 37 if err != nil { 38 38 return nil, err ··· 50 50 51 51 clock := syntax.NewTIDClock(0) 52 52 53 - return &State{db, auth, enforcer, clock}, nil 53 + pgs := pages.NewPages() 54 + 55 + return &State{db, auth, enforcer, clock, pgs}, nil 54 56 } 55 57 56 58 func (s *State) TID() string { ··· 62 64 63 65 switch r.Method { 64 66 case http.MethodGet: 65 - pages.Login(w, pages.LoginParams{}) 67 + err := s.pages.Login(w, pages.LoginParams{}) 68 + if err != nil { 69 + log.Printf("rendering login page: %s", err) 70 + } 66 71 return 67 72 case http.MethodPost: 68 73 handle := r.FormValue("handle") ··· 99 104 100 105 func (s *State) Timeline(w http.ResponseWriter, r *http.Request) { 101 106 user := s.auth.GetUser(r) 102 - pages.Timeline(w, pages.TimelineParams{ 107 + s.pages.Timeline(w, pages.TimelineParams{ 103 108 User: user, 104 109 }) 105 110 return ··· 149 154 log.Println(err) 150 155 } 151 156 152 - pages.Settings(w, pages.SettingsParams{ 157 + s.pages.Settings(w, pages.SettingsParams{ 153 158 User: user, 154 159 PubKeys: pubKeys, 155 160 }) ··· 358 363 IsOwner: isOwner, 359 364 } 360 365 361 - pages.Knot(w, p) 366 + s.pages.Knot(w, p) 362 367 } 363 368 364 369 // get knots registered by this user ··· 370 375 log.Println(err) 371 376 } 372 377 373 - pages.Knots(w, pages.KnotsParams{ 378 + s.pages.Knots(w, pages.KnotsParams{ 374 379 User: user, 375 380 Registrations: registrations, 376 381 }) ··· 475 480 func (s *State) AddRepo(w http.ResponseWriter, r *http.Request) { 476 481 switch r.Method { 477 482 case http.MethodGet: 478 - pages.NewRepo(w, pages.NewRepoParams{ 483 + s.pages.NewRepo(w, pages.NewRepoParams{ 479 484 User: s.auth.GetUser(r), 480 485 }) 481 486 case http.MethodPost: ··· 562 567 log.Printf("getting repos for %s: %s", ident.DID.String(), err) 563 568 } 564 569 565 - pages.ProfilePage(w, pages.ProfilePageParams{ 570 + s.pages.ProfilePage(w, pages.ProfilePageParams{ 566 571 LoggedInUser: s.auth.GetUser(r), 567 572 UserDid: ident.DID.String(), 568 573 UserHandle: ident.Handle.String(),