Use local url for for mentioned users
[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         RenderNotificationPage(ctx context.Context, writer io.Writer, data *NotificationPageTemplateData) (err error)
21         RenderUserPage(ctx context.Context, writer io.Writer, data *UserPageTemplateData) (err error)
22 }
23
24 type renderer struct {
25         template *template.Template
26 }
27
28 func NewRenderer(templateGlobPattern string) (r *renderer, err error) {
29         t := template.New("default")
30         t, err = t.Funcs(template.FuncMap{
31                 "EmojiFilter":             EmojiFilter,
32                 "StatusContentFilter":     StatusContentFilter,
33                 "DisplayInteractionCount": DisplayInteractionCount,
34                 "TimeSince":               TimeSince,
35                 "FormatTimeRFC3339":       FormatTimeRFC3339,
36         }).ParseGlob(templateGlobPattern)
37         if err != nil {
38                 return
39         }
40         return &renderer{
41                 template: t,
42         }, nil
43 }
44
45 func (r *renderer) RenderErrorPage(ctx context.Context, writer io.Writer, err error) {
46         r.template.ExecuteTemplate(writer, "error.tmpl", err)
47         return
48 }
49
50 func (r *renderer) RenderHomePage(ctx context.Context, writer io.Writer) (err error) {
51         return r.template.ExecuteTemplate(writer, "homepage.tmpl", nil)
52 }
53
54 func (r *renderer) RenderSigninPage(ctx context.Context, writer io.Writer) (err error) {
55         return r.template.ExecuteTemplate(writer, "signin.tmpl", nil)
56 }
57
58 func (r *renderer) RenderTimelinePage(ctx context.Context, writer io.Writer, data *TimelinePageTemplateData) (err error) {
59         return r.template.ExecuteTemplate(writer, "timeline.tmpl", data)
60 }
61
62 func (r *renderer) RenderThreadPage(ctx context.Context, writer io.Writer, data *ThreadPageTemplateData) (err error) {
63         return r.template.ExecuteTemplate(writer, "thread.tmpl", data)
64 }
65
66 func (r *renderer) RenderNotificationPage(ctx context.Context, writer io.Writer, data *NotificationPageTemplateData) (err error) {
67         return r.template.ExecuteTemplate(writer, "notification.tmpl", data)
68 }
69
70 func (r *renderer) RenderUserPage(ctx context.Context, writer io.Writer, data *UserPageTemplateData) (err error) {
71         return r.template.ExecuteTemplate(writer, "user.tmpl", data)
72 }
73
74
75 func EmojiFilter(content string, emojis []mastodon.Emoji) string {
76         var replacements []string
77         for _, e := range emojis {
78                 replacements = append(replacements, ":"+e.ShortCode+":", "<img class=\"status-emoji\" src=\""+e.URL+"\" alt=\""+e.ShortCode+"\" />")
79         }
80         return strings.NewReplacer(replacements...).Replace(content)
81 }
82
83 func StatusContentFilter(content string, emojis []mastodon.Emoji, mentions []mastodon.Mention) string {
84         var replacements []string
85         for _, e := range emojis {
86                 replacements = append(replacements, ":"+e.ShortCode+":", "<img class=\"status-emoji\" src=\""+e.URL+"\" alt=\""+e.ShortCode+"\" />")
87         }
88         for _, m := range mentions {
89                 replacements = append(replacements, "\""+m.URL+"\"", "\"/user/"+m.ID+"\"")
90         }
91         return strings.NewReplacer(replacements...).Replace(content)
92 }
93
94 func DisplayInteractionCount(c int64) string {
95         if c > 0 {
96                 return strconv.Itoa(int(c))
97         }
98         return ""
99 }
100
101 func TimeSince(t time.Time) string {
102         dur := time.Since(t)
103
104         s := dur.Seconds()
105         if s < 60 {
106                 return strconv.Itoa(int(s)) + "s"
107         }
108
109         m := dur.Minutes()
110         if m < 60 {
111                 return strconv.Itoa(int(m)) + "m"
112         }
113
114         h := dur.Hours()
115         if h < 24 {
116                 return strconv.Itoa(int(h)) + "h"
117         }
118
119         d := h / 24
120         if d < 30 {
121                 return strconv.Itoa(int(d)) + "d"
122         }
123
124         mo := d / 30
125         if mo < 12 {
126                 return strconv.Itoa(int(mo)) + "mo"
127         }
128
129         y := mo / 12
130         return strconv.Itoa(int(y)) + "y"
131 }
132
133 func FormatTimeRFC3339(t time.Time) string {
134         return t.Format(time.RFC3339)
135 }