Remove icons
[bloat] / renderer / renderer.go
1 package renderer
2
3 import (
4         "fmt"
5         "io"
6         "strconv"
7         "strings"
8         "text/template"
9         "time"
10
11         "mastodon"
12 )
13
14 type TemplateData struct {
15         Data interface{}
16         Ctx  *Context
17 }
18
19 type Renderer interface {
20         RenderSigninPage(ctx *Context, writer io.Writer, data *SigninData) (err error)
21         RenderErrorPage(ctx *Context, writer io.Writer, data *ErrorData)
22         RenderTimelinePage(ctx *Context, writer io.Writer, data *TimelineData) (err error)
23         RenderThreadPage(ctx *Context, writer io.Writer, data *ThreadData) (err error)
24         RenderNotificationPage(ctx *Context, writer io.Writer, data *NotificationData) (err error)
25         RenderUserPage(ctx *Context, writer io.Writer, data *UserData) (err error)
26         RenderAboutPage(ctx *Context, writer io.Writer, data *AboutData) (err error)
27         RenderEmojiPage(ctx *Context, writer io.Writer, data *EmojiData) (err error)
28         RenderLikedByPage(ctx *Context, writer io.Writer, data *LikedByData) (err error)
29         RenderRetweetedByPage(ctx *Context, writer io.Writer, data *RetweetedByData) (err error)
30         RenderFollowingPage(ctx *Context, writer io.Writer, data *FollowingData) (err error)
31         RenderFollowersPage(ctx *Context, writer io.Writer, data *FollowersData) (err error)
32         RenderSearchPage(ctx *Context, writer io.Writer, data *SearchData) (err error)
33         RenderSettingsPage(ctx *Context, writer io.Writer, data *SettingsData) (err error)
34 }
35
36 type renderer struct {
37         template *template.Template
38 }
39
40 func NewRenderer(templateGlobPattern string) (r *renderer, err error) {
41         t := template.New("default")
42         t, err = t.Funcs(template.FuncMap{
43                 "EmojiFilter":             EmojiFilter,
44                 "StatusContentFilter":     StatusContentFilter,
45                 "DisplayInteractionCount": DisplayInteractionCount,
46                 "TimeSince":               TimeSince,
47                 "FormatTimeRFC3339":       FormatTimeRFC3339,
48                 "FormatTimeRFC822":        FormatTimeRFC822,
49                 "WithContext":             WithContext,
50         }).ParseGlob(templateGlobPattern)
51         if err != nil {
52                 return
53         }
54         return &renderer{
55                 template: t,
56         }, nil
57 }
58
59 func (r *renderer) RenderSigninPage(ctx *Context, writer io.Writer,
60         signinData *SigninData) (err error) {
61         return r.template.ExecuteTemplate(writer, "signin.tmpl", WithContext(signinData, ctx))
62 }
63
64 func (r *renderer) RenderErrorPage(ctx *Context, writer io.Writer,
65         errorData *ErrorData) {
66         r.template.ExecuteTemplate(writer, "error.tmpl", WithContext(errorData, ctx))
67         return
68 }
69
70 func (r *renderer) RenderTimelinePage(ctx *Context, writer io.Writer,
71         data *TimelineData) (err error) {
72         return r.template.ExecuteTemplate(writer, "timeline.tmpl", WithContext(data, ctx))
73 }
74
75 func (r *renderer) RenderThreadPage(ctx *Context, writer io.Writer,
76         data *ThreadData) (err error) {
77         return r.template.ExecuteTemplate(writer, "thread.tmpl", WithContext(data, ctx))
78 }
79
80 func (r *renderer) RenderNotificationPage(ctx *Context, writer io.Writer,
81         data *NotificationData) (err error) {
82         return r.template.ExecuteTemplate(writer, "notification.tmpl", WithContext(data, ctx))
83 }
84
85 func (r *renderer) RenderUserPage(ctx *Context, writer io.Writer,
86         data *UserData) (err error) {
87         return r.template.ExecuteTemplate(writer, "user.tmpl", WithContext(data, ctx))
88 }
89
90 func (r *renderer) RenderAboutPage(ctx *Context, writer io.Writer,
91         data *AboutData) (err error) {
92         return r.template.ExecuteTemplate(writer, "about.tmpl", WithContext(data, ctx))
93 }
94
95 func (r *renderer) RenderEmojiPage(ctx *Context, writer io.Writer,
96         data *EmojiData) (err error) {
97         return r.template.ExecuteTemplate(writer, "emoji.tmpl", WithContext(data, ctx))
98 }
99
100 func (r *renderer) RenderLikedByPage(ctx *Context, writer io.Writer,
101         data *LikedByData) (err error) {
102         return r.template.ExecuteTemplate(writer, "likedby.tmpl", WithContext(data, ctx))
103 }
104
105 func (r *renderer) RenderRetweetedByPage(ctx *Context, writer io.Writer,
106         data *RetweetedByData) (err error) {
107         return r.template.ExecuteTemplate(writer, "retweetedby.tmpl", WithContext(data, ctx))
108 }
109
110 func (r *renderer) RenderFollowingPage(ctx *Context, writer io.Writer,
111         data *FollowingData) (err error) {
112         return r.template.ExecuteTemplate(writer, "following.tmpl", WithContext(data, ctx))
113 }
114
115 func (r *renderer) RenderFollowersPage(ctx *Context, writer io.Writer,
116         data *FollowersData) (err error) {
117         return r.template.ExecuteTemplate(writer, "followers.tmpl", WithContext(data, ctx))
118 }
119
120 func (r *renderer) RenderSearchPage(ctx *Context, writer io.Writer,
121         data *SearchData) (err error) {
122         return r.template.ExecuteTemplate(writer, "search.tmpl", WithContext(data, ctx))
123 }
124
125 func (r *renderer) RenderSettingsPage(ctx *Context, writer io.Writer,
126         data *SettingsData) (err error) {
127         return r.template.ExecuteTemplate(writer, "settings.tmpl", WithContext(data, ctx))
128 }
129
130 func EmojiFilter(content string, emojis []mastodon.Emoji) string {
131         var replacements []string
132         var r string
133         for _, e := range emojis {
134                 r = fmt.Sprintf("<img class=\"status-emoji\" src=\"%s\" alt=\"%s\" title=\"%s\" />",
135                         e.URL, e.ShortCode, e.ShortCode)
136                 replacements = append(replacements, ":"+e.ShortCode+":", r)
137         }
138         return strings.NewReplacer(replacements...).Replace(content)
139 }
140
141 func StatusContentFilter(spoiler string, content string,
142         emojis []mastodon.Emoji, mentions []mastodon.Mention) string {
143
144         var replacements []string
145         var r string
146         if len(spoiler) > 0 {
147                 content = spoiler + "<br />" + content
148         }
149         for _, e := range emojis {
150                 r = fmt.Sprintf("<img class=\"status-emoji\" src=\"%s\" alt=\"%s\" title=\"%s\" />",
151                         e.URL, e.ShortCode, e.ShortCode)
152                 replacements = append(replacements, ":"+e.ShortCode+":", r)
153         }
154         for _, m := range mentions {
155                 replacements = append(replacements, "\""+m.URL+"\"", "\"/user/"+m.ID+"\"")
156         }
157         return strings.NewReplacer(replacements...).Replace(content)
158 }
159
160 func DisplayInteractionCount(c int64) string {
161         if c > 0 {
162                 return strconv.Itoa(int(c))
163         }
164         return ""
165 }
166
167 func TimeSince(t time.Time) string {
168         dur := time.Since(t)
169         s := dur.Seconds()
170         if s < 60 {
171                 return strconv.Itoa(int(s)) + "s"
172         }
173         m := dur.Minutes()
174         if m < 60 {
175                 return strconv.Itoa(int(m)) + "m"
176         }
177         h := dur.Hours()
178         if h < 24 {
179                 return strconv.Itoa(int(h)) + "h"
180         }
181         d := h / 24
182         if d < 30 {
183                 return strconv.Itoa(int(d)) + "d"
184         }
185         mo := d / 30
186         if mo < 12 {
187                 return strconv.Itoa(int(mo)) + "mo"
188         }
189         y := mo / 12
190         return strconv.Itoa(int(y)) + "y"
191 }
192
193 func FormatTimeRFC3339(t time.Time) string {
194         return t.Format(time.RFC3339)
195 }
196
197 func FormatTimeRFC822(t time.Time) string {
198         return t.Format(time.RFC822)
199 }
200
201 func WithContext(data interface{}, ctx *Context) TemplateData {
202         return TemplateData{data, ctx}
203 }