c3d35265abe0ee80ba22c954ab4e2415e50689e0
[bloat] / renderer / renderer.go
1 package renderer
2
3 import (
4         "context"
5         "io"
6         "strconv"
7         "strings"
8         "text/template"
9         "time"
10
11         "mastodon"
12 )
13
14 type Renderer interface {
15         RenderErrorPage(ctx context.Context, writer io.Writer, err error)
16         RenderHomePage(ctx context.Context, writer io.Writer) (err error)
17         RenderSigninPage(ctx context.Context, writer io.Writer) (err error)
18         RenderTimelinePage(ctx context.Context, writer io.Writer, data *TimelinePageTemplateData) (err error)
19         RenderThreadPage(ctx context.Context, writer io.Writer, data *ThreadPageTemplateData) (err error)
20 }
21
22 type renderer struct {
23         template *template.Template
24 }
25
26 func NewRenderer(templateGlobPattern string) (r *renderer, err error) {
27         t := template.New("default")
28         t, err = t.Funcs(template.FuncMap{
29                 "WithEmojis":              WithEmojis,
30                 "DisplayInteractionCount": DisplayInteractionCount,
31                 "TimeSince":               TimeSince,
32                 "FormatTimeRFC3339":       FormatTimeRFC3339,
33         }).ParseGlob(templateGlobPattern)
34         if err != nil {
35                 return
36         }
37         return &renderer{
38                 template: t,
39         }, nil
40 }
41
42 func (r *renderer) RenderErrorPage(ctx context.Context, writer io.Writer, err error) {
43         r.template.ExecuteTemplate(writer, "error.tmpl", err)
44         return
45 }
46
47 func (r *renderer) RenderHomePage(ctx context.Context, writer io.Writer) (err error) {
48         return r.template.ExecuteTemplate(writer, "homepage.tmpl", nil)
49 }
50
51 func (r *renderer) RenderSigninPage(ctx context.Context, writer io.Writer) (err error) {
52         return r.template.ExecuteTemplate(writer, "signin.tmpl", nil)
53 }
54
55 func (r *renderer) RenderTimelinePage(ctx context.Context, writer io.Writer, data *TimelinePageTemplateData) (err error) {
56         return r.template.ExecuteTemplate(writer, "timeline.tmpl", data)
57 }
58
59 func (r *renderer) RenderThreadPage(ctx context.Context, writer io.Writer, data *ThreadPageTemplateData) (err error) {
60         return r.template.ExecuteTemplate(writer, "thread.tmpl", data)
61 }
62
63 func WithEmojis(content string, emojis []mastodon.Emoji) string {
64         var emojiNameContentPair []string
65         for _, e := range emojis {
66                 emojiNameContentPair = append(emojiNameContentPair, ":"+e.ShortCode+":", "<img class=\"status-emoji\" src=\""+e.URL+"\" alt=\""+e.ShortCode+"\" />")
67         }
68         return strings.NewReplacer(emojiNameContentPair...).Replace(content)
69 }
70
71 func DisplayInteractionCount(c int64) string {
72         if c > 0 {
73                 return strconv.Itoa(int(c))
74         }
75         return ""
76 }
77
78 func TimeSince(t time.Time) string {
79         dur := time.Since(t)
80
81         s := dur.Seconds()
82         if s < 60 {
83                 return strconv.Itoa(int(s)) + "s"
84         }
85
86         m := dur.Minutes()
87         if m < 60 {
88                 return strconv.Itoa(int(m)) + "m"
89         }
90
91         h := dur.Hours()
92         if h < 24 {
93                 return strconv.Itoa(int(h)) + "h"
94         }
95
96         d := h / 24
97         if d < 30 {
98                 return strconv.Itoa(int(d)) + "d"
99         }
100
101         mo := d / 30
102         if mo < 12 {
103                 return strconv.Itoa(int(mo)) + "mo"
104         }
105
106         y := m / 12
107         return strconv.Itoa(int(y)) + "y"
108 }
109
110 func FormatTimeRFC3339(t time.Time) string {
111         return t.Format(time.RFC3339)
112 }