bloat/renderer/renderer.go

180 lines
4.2 KiB
Go
Raw Normal View History

2019-12-13 18:08:26 +00:00
package renderer
import (
2022-03-30 15:52:30 +00:00
"html/template"
2019-12-13 18:08:26 +00:00
"io"
2022-01-27 10:53:18 +00:00
"regexp"
2019-12-13 18:08:26 +00:00
"strconv"
"strings"
"time"
2020-02-01 11:31:44 +00:00
"bloat/mastodon"
2019-12-13 18:08:26 +00:00
)
2020-02-23 19:51:42 +00:00
type Page string
const (
SigninPage = "signin.tmpl"
ErrorPage = "error.tmpl"
NavPage = "nav.tmpl"
RootPage = "root.tmpl"
TimelinePage = "timeline.tmpl"
2022-02-11 11:18:02 +00:00
ListsPage = "lists.tmpl"
ListPage = "list.tmpl"
2020-02-23 19:51:42 +00:00
ThreadPage = "thread.tmpl"
2021-09-05 17:17:59 +00:00
QuickReplyPage = "quickreply.tmpl"
2020-02-23 19:51:42 +00:00
NotificationPage = "notification.tmpl"
UserPage = "user.tmpl"
UserSearchPage = "usersearch.tmpl"
AboutPage = "about.tmpl"
2022-10-12 10:34:41 +00:00
AboutInstance = "aboutinstance.tmpl"
2020-02-23 19:51:42 +00:00
EmojiPage = "emoji.tmpl"
LikedByPage = "likedby.tmpl"
RetweetedByPage = "retweetedby.tmpl"
SearchPage = "search.tmpl"
SettingsPage = "settings.tmpl"
2021-01-30 16:51:09 +00:00
FiltersPage = "filters.tmpl"
2020-02-23 19:51:42 +00:00
)
2020-01-14 16:57:16 +00:00
type TemplateData struct {
Data interface{}
Ctx *Context
}
func allowed_emoji_page(emoj string, codes ...string) bool {
for _, code := range codes {
if strings.Contains(emoj, code) {
return true
}
}
return false
}
2022-01-27 11:41:31 +00:00
func emojiHTML(e mastodon.Emoji, height string) string {
return `<img class="emoji" src="` + e.URL + `" alt=":` + e.ShortCode + `:" title=":` + e.ShortCode + `:" height="` + height + `"/>`
}
2020-02-23 19:51:42 +00:00
func emojiFilter(content string, emojis []mastodon.Emoji) string {
2019-12-21 05:48:06 +00:00
var replacements []string
for _, e := range emojis {
2022-01-27 11:41:31 +00:00
replacements = append(replacements, ":"+e.ShortCode+":", emojiHTML(e, "24"))
2019-12-21 05:48:06 +00:00
}
return strings.NewReplacer(replacements...).Replace(content)
}
2022-01-27 10:53:18 +00:00
var quoteRE = regexp.MustCompile("(?mU)(^|> *|\n)(&gt;.*)(<br|$)")
2022-03-30 15:52:30 +00:00
func statusContentFilter(content string, emojis []mastodon.Emoji, mentions []mastodon.Mention) string {
2022-01-27 11:41:31 +00:00
content = quoteRE.ReplaceAllString(content, `$1<span class="quote">$2</span>$3`)
var replacements []string
2019-12-13 18:08:26 +00:00
for _, e := range emojis {
2022-01-27 11:41:31 +00:00
replacements = append(replacements, ":"+e.ShortCode+":", emojiHTML(e, "32"))
2019-12-21 05:48:06 +00:00
}
for _, m := range mentions {
2020-10-30 16:07:11 +00:00
replacements = append(replacements, `"`+m.URL+`"`, `"/user/`+m.ID+`" title="@`+m.Acct+`"`)
2019-12-13 18:08:26 +00:00
}
2019-12-21 05:48:06 +00:00
return strings.NewReplacer(replacements...).Replace(content)
2019-12-13 18:08:26 +00:00
}
2020-02-23 19:51:42 +00:00
func displayInteractionCount(c int64) string {
2019-12-13 18:08:26 +00:00
if c > 0 {
return strconv.Itoa(int(c))
}
return ""
}
2022-05-10 17:27:33 +00:00
func durUnit(s int64) (dur int64, unit string) {
2019-12-13 18:08:26 +00:00
if s < 60 {
2022-05-10 17:27:33 +00:00
if s < 0 {
s = 0
}
return s, "s"
2019-12-13 18:08:26 +00:00
}
m := s / 60
h := m / 60
2022-05-10 17:27:33 +00:00
if h < 2 {
return m, "m"
2019-12-13 18:08:26 +00:00
}
d := h / 24
2022-05-10 17:27:33 +00:00
if d < 2 {
return h, "h"
2019-12-13 18:08:26 +00:00
}
mo := d / 30
2022-05-10 17:27:33 +00:00
if mo < 2 {
return d, "d"
2019-12-13 18:08:26 +00:00
}
y := d / 365
2022-05-10 17:27:33 +00:00
if y < 2 {
return mo, "mo"
}
return y, "y"
2019-12-13 18:08:26 +00:00
}
2020-02-23 19:51:42 +00:00
func timeSince(t time.Time) string {
2022-05-10 17:27:33 +00:00
d, u := durUnit(time.Now().Unix() - t.Unix())
return strconv.FormatInt(d, 10) + u
}
2020-02-23 19:51:42 +00:00
func timeUntil(t time.Time) string {
2022-05-10 17:27:33 +00:00
d, u := durUnit(t.Unix() - time.Now().Unix())
return strconv.FormatInt(d, 10) + u
}
2020-02-23 19:51:42 +00:00
func formatTimeRFC3339(t time.Time) string {
2019-12-13 18:08:26 +00:00
return t.Format(time.RFC3339)
}
2020-01-10 04:05:01 +00:00
2020-02-23 19:51:42 +00:00
func formatTimeRFC822(t time.Time) string {
2020-01-10 04:05:01 +00:00
return t.Format(time.RFC822)
}
2020-01-12 17:16:57 +00:00
2020-02-23 19:51:42 +00:00
func withContext(data interface{}, ctx *Context) TemplateData {
2020-01-14 16:57:16 +00:00
return TemplateData{data, ctx}
}
2020-02-23 19:51:42 +00:00
2022-03-30 15:52:30 +00:00
func raw(s string) template.HTML {
return template.HTML(s)
}
2022-05-10 17:25:58 +00:00
func rawCSS(s string) template.CSS {
return template.CSS(s)
}
2020-02-23 19:51:42 +00:00
type Renderer interface {
Render(ctx *Context, writer io.Writer, page string, data interface{}) (err error)
}
type renderer struct {
template *template.Template
}
func NewRenderer(templateGlobPattern string) (r *renderer, err error) {
t := template.New("default")
t, err = t.Funcs(template.FuncMap{
"EmojiFilter": emojiFilter,
"Allowed_emoji_page": allowed_emoji_page,
2020-02-23 19:51:42 +00:00
"StatusContentFilter": statusContentFilter,
"DisplayInteractionCount": displayInteractionCount,
"TimeSince": timeSince,
"TimeUntil": timeUntil,
"FormatTimeRFC3339": formatTimeRFC3339,
"FormatTimeRFC822": formatTimeRFC822,
"WithContext": withContext,
2022-03-30 15:52:30 +00:00
"HTML": template.HTMLEscapeString,
"Raw": raw,
2022-05-10 17:25:58 +00:00
"RawCSS": rawCSS,
2020-02-23 19:51:42 +00:00
}).ParseGlob(templateGlobPattern)
if err != nil {
return
}
return &renderer{
template: t,
}, nil
}
func (r *renderer) Render(ctx *Context, writer io.Writer,
page string, data interface{}) (err error) {
return r.template.ExecuteTemplate(writer, page, withContext(data, ctx))
}