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