278 lines
6.2 kB
1
package pages
2
3
import (
4
"embed"
5
"fmt"
6
"html/template"
7
"io"
8
"io/fs"
9
"log"
10
"net/http"
11
"path"
12
"strings"
13
14
"github.com/sotangled/tangled/appview/auth"
15
"github.com/sotangled/tangled/appview/db"
16
"github.com/sotangled/tangled/types"
17
)
18
19
//go:embed templates/* static/*
20
var files embed.FS
21
22
type Pages struct {
23
t map[string]*template.Template
24
}
25
26
func funcMap() template.FuncMap {
27
return template.FuncMap{
28
"split": func(s string) []string {
29
return strings.Split(s, "\n")
30
},
31
"add": func(a, b int) int {
32
return a + b
33
},
34
"didOrHandle": func(did, handle string) string {
35
if handle != "" {
36
return fmt.Sprintf("@%s", handle)
37
} else {
38
return did
39
}
40
},
41
}
42
}
43
44
func NewPages() *Pages {
45
templates := make(map[string]*template.Template)
46
47
// Walk through embedded templates directory and parse all .html files
48
err := fs.WalkDir(files, "templates", func(path string, d fs.DirEntry, err error) error {
49
if err != nil {
50
return err
51
}
52
53
if !d.IsDir() && strings.HasSuffix(path, ".html") {
54
name := strings.TrimPrefix(path, "templates/")
55
name = strings.TrimSuffix(name, ".html")
56
57
if !strings.HasPrefix(path, "templates/layouts/") {
58
// Add the page template on top of the base
59
tmpl, err := template.New(name).
60
Funcs(funcMap()).
61
ParseFS(files, "templates/layouts/*.html", path)
62
if err != nil {
63
return fmt.Errorf("setting up template: %w", err)
64
}
65
66
templates[name] = tmpl
67
log.Printf("loaded template: %s", name)
68
}
69
70
return nil
71
}
72
return nil
73
})
74
if err != nil {
75
log.Fatalf("walking template dir: %v", err)
76
}
77
78
log.Printf("total templates loaded: %d", len(templates))
79
80
return &Pages{
81
t: templates,
82
}
83
}
84
85
type LoginParams struct {
86
}
87
88
func (p *Pages) execute(name string, w io.Writer, params any) error {
89
return p.t[name].ExecuteTemplate(w, "layouts/base", params)
90
}
91
92
func (p *Pages) executePlain(name string, w io.Writer, params any) error {
93
return p.t[name].Execute(w, params)
94
}
95
96
func (p *Pages) executeRepo(name string, w io.Writer, params any) error {
97
return p.t[name].ExecuteTemplate(w, "layouts/repobase", params)
98
}
99
100
func (p *Pages) Login(w io.Writer, params LoginParams) error {
101
return p.executePlain("user/login", w, params)
102
}
103
104
type TimelineParams struct {
105
LoggedInUser *auth.User
106
}
107
108
func (p *Pages) Timeline(w io.Writer, params TimelineParams) error {
109
return p.execute("timeline", w, params)
110
}
111
112
type SettingsParams struct {
113
LoggedInUser *auth.User
114
PubKeys []db.PublicKey
115
}
116
117
func (p *Pages) Settings(w io.Writer, params SettingsParams) error {
118
return p.execute("settings/keys", w, params)
119
}
120
121
type KnotsParams struct {
122
LoggedInUser *auth.User
123
Registrations []db.Registration
124
}
125
126
func (p *Pages) Knots(w io.Writer, params KnotsParams) error {
127
return p.execute("knots", w, params)
128
}
129
130
type KnotParams struct {
131
LoggedInUser *auth.User
132
Registration *db.Registration
133
Members []string
134
IsOwner bool
135
}
136
137
func (p *Pages) Knot(w io.Writer, params KnotParams) error {
138
return p.execute("knot", w, params)
139
}
140
141
type NewRepoParams struct {
142
LoggedInUser *auth.User
143
Knots []string
144
}
145
146
func (p *Pages) NewRepo(w io.Writer, params NewRepoParams) error {
147
return p.execute("repo/new", w, params)
148
}
149
150
type ProfilePageParams struct {
151
LoggedInUser *auth.User
152
UserDid string
153
UserHandle string
154
Repos []db.Repo
155
}
156
157
func (p *Pages) ProfilePage(w io.Writer, params ProfilePageParams) error {
158
return p.execute("user/profile", w, params)
159
}
160
161
type RepoInfo struct {
162
Name string
163
OwnerDid string
164
OwnerHandle string
165
Description string
166
SettingsAllowed bool
167
}
168
169
func (r RepoInfo) OwnerWithAt() string {
170
if r.OwnerHandle != "" {
171
return fmt.Sprintf("@%s", r.OwnerHandle)
172
} else {
173
return r.OwnerDid
174
}
175
}
176
177
func (r RepoInfo) FullName() string {
178
return path.Join(r.OwnerWithAt(), r.Name)
179
}
180
181
type RepoIndexParams struct {
182
LoggedInUser *auth.User
183
RepoInfo RepoInfo
184
Active string
185
types.RepoIndexResponse
186
}
187
188
func (p *Pages) RepoIndexPage(w io.Writer, params RepoIndexParams) error {
189
params.Active = "index"
190
return p.executeRepo("repo/index", w, params)
191
}
192
193
type RepoLogParams struct {
194
LoggedInUser *auth.User
195
RepoInfo RepoInfo
196
types.RepoLogResponse
197
}
198
199
func (p *Pages) RepoLog(w io.Writer, params RepoLogParams) error {
200
return p.execute("repo/log", w, params)
201
}
202
203
type RepoCommitParams struct {
204
LoggedInUser *auth.User
205
RepoInfo RepoInfo
206
types.RepoCommitResponse
207
}
208
209
func (p *Pages) RepoCommit(w io.Writer, params RepoCommitParams) error {
210
return p.executeRepo("repo/commit", w, params)
211
}
212
213
type RepoTreeParams struct {
214
LoggedInUser *auth.User
215
RepoInfo RepoInfo
216
types.RepoTreeResponse
217
}
218
219
func (p *Pages) RepoTree(w io.Writer, params RepoTreeParams) error {
220
return p.execute("repo/tree", w, params)
221
}
222
223
type RepoBranchesParams struct {
224
LoggedInUser *auth.User
225
RepoInfo RepoInfo
226
types.RepoBranchesResponse
227
}
228
229
func (p *Pages) RepoBranches(w io.Writer, params RepoBranchesParams) error {
230
return p.executeRepo("repo/branches", w, params)
231
}
232
233
type RepoTagsParams struct {
234
LoggedInUser *auth.User
235
RepoInfo RepoInfo
236
types.RepoTagsResponse
237
}
238
239
func (p *Pages) RepoTags(w io.Writer, params RepoTagsParams) error {
240
return p.executeRepo("repo/tags", w, params)
241
}
242
243
type RepoBlobParams struct {
244
LoggedInUser *auth.User
245
RepoInfo RepoInfo
246
types.RepoBlobResponse
247
}
248
249
func (p *Pages) RepoBlob(w io.Writer, params RepoBlobParams) error {
250
return p.executeRepo("repo/blob", w, params)
251
}
252
253
type RepoSettingsParams struct {
254
LoggedInUser *auth.User
255
RepoInfo RepoInfo
256
Collaborators [][]string
257
IsCollaboratorInviteAllowed bool
258
}
259
260
func (p *Pages) RepoSettings(w io.Writer, params RepoSettingsParams) error {
261
return p.executeRepo("repo/settings", w, params)
262
}
263
264
func (p *Pages) Static() http.Handler {
265
sub, err := fs.Sub(files, "static")
266
if err != nil {
267
log.Fatalf("no static dir found? that's crazy: %v", err)
268
}
269
return http.StripPrefix("/static/", http.FileServer(http.FS(sub)))
270
}
271
272
func (p *Pages) Error500(w io.Writer) error {
273
return p.execute("errors/500", w, nil)
274
}
275
276
func (p *Pages) Error404(w io.Writer) error {
277
return p.execute("errors/404", w, nil)
278
}
279