Added theme to context and added empty-avatar support

This commit is contained in:
Asaf Gartner 2021-05-25 16:12:20 +03:00
parent e5beb209c0
commit 9c19484333
14 changed files with 66 additions and 49 deletions

View File

@ -72,14 +72,6 @@ func ProjectUrlWithFragment(path string, query []Q, slug string, fragment string
return url.String()
}
func StaticUrl(path string, query []Q) string {
return Url(StaticPath+"/"+trim(path), query)
}
func StaticThemeUrl(path string, theme string, query []Q) string {
return Url(StaticThemePath+"/"+theme+"/"+trim(path), query)
}
func trim(path string) string {
if len(path) > 0 && path[0] == '/' {
return path[1:]

View File

@ -329,14 +329,17 @@ func TestProjectCSS(t *testing.T) {
}
func TestPublic(t *testing.T) {
AssertRegexMatch(t, BuildPublic("test"), RegexPublic, nil)
AssertRegexMatch(t, BuildPublic("/test"), RegexPublic, nil)
AssertRegexMatch(t, BuildPublic("/test/"), RegexPublic, nil)
AssertRegexMatch(t, BuildPublic("/test/thing/image.png"), RegexPublic, nil)
assert.Panics(t, func() { BuildPublic("") })
assert.Panics(t, func() { BuildPublic("/") })
assert.Panics(t, func() { BuildPublic("/thing//image.png") })
assert.Panics(t, func() { BuildPublic("/thing/ /image.png") })
AssertRegexMatch(t, BuildPublic("test", false), RegexPublic, nil)
AssertRegexMatch(t, BuildPublic("/test", true), RegexPublic, nil)
AssertRegexMatch(t, BuildPublic("/test/", false), RegexPublic, nil)
AssertRegexMatch(t, BuildPublic("/test/thing/image.png", true), RegexPublic, nil)
assert.Panics(t, func() { BuildPublic("", false) })
assert.Panics(t, func() { BuildPublic("/", false) })
assert.Panics(t, func() { BuildPublic("/thing//image.png", false) })
assert.Panics(t, func() { BuildPublic("/thing/ /image.png", false) })
assert.Panics(t, func() { BuildPublic("/thing/image.png?hello", false) })
AssertRegexMatch(t, BuildTheme("test.css", "light", true), RegexPublic, nil)
}
func TestMarkRead(t *testing.T) {

View File

@ -1,6 +1,7 @@
package hmnurl
import (
"fmt"
"net/url"
"regexp"
"strconv"
@ -577,11 +578,14 @@ func BuildProjectCSS(color string) string {
var RegexPublic = regexp.MustCompile("^/public/.+$")
func BuildPublic(filepath string) string {
func BuildPublic(filepath string, cachebust bool) string {
filepath = strings.Trim(filepath, "/")
if len(strings.TrimSpace(filepath)) == 0 {
panic(oops.New(nil, "Attempted to build a /public url with no path"))
}
if strings.Contains(filepath, "?") {
panic(oops.New(nil, "Public url failpath must not contain query params"))
}
var builder strings.Builder
builder.WriteString("/public")
pathParts := strings.Split(filepath, "/")
@ -593,7 +597,18 @@ func BuildPublic(filepath string) string {
builder.WriteRune('/')
builder.WriteString(part)
}
return Url(builder.String(), nil)
var query []Q
if cachebust {
query = []Q{{"v", cacheBust}}
}
return Url(builder.String(), query)
}
func BuildTheme(filepath string, theme string, cachebust bool) string {
if len(theme) == 0 {
panic(oops.New(nil, "Theme can't be blank"))
}
return BuildPublic(fmt.Sprintf("themes/%s/%s", theme, strings.Trim(filepath, "/")), cachebust)
}
/*

View File

@ -8,10 +8,10 @@ import (
"git.handmade.network/hmn/hmn/src/models"
)
func PostToTemplate(p *models.Post, author *models.User) Post {
func PostToTemplate(p *models.Post, author *models.User, currentTheme string) Post {
var authorUser *User
if author != nil {
authorTmpl := UserToTemplate(author)
authorTmpl := UserToTemplate(author, currentTheme)
authorUser = &authorTmpl
}
@ -31,11 +31,11 @@ func PostToTemplate(p *models.Post, author *models.User) Post {
}
}
func (p *Post) AddContentVersion(ver models.PostVersion, editor *models.User) {
func (p *Post) AddContentVersion(ver models.PostVersion, editor *models.User, currentTheme string) {
p.Content = template.HTML(ver.TextParsed)
if editor != nil {
editorTmpl := UserToTemplate(editor)
editorTmpl := UserToTemplate(editor, currentTheme)
p.Editor = &editorTmpl
p.EditDate = ver.EditDate
p.EditIP = maybeIp(ver.EditIP)
@ -76,12 +76,14 @@ func ThreadToTemplate(t *models.Thread) Thread {
}
}
func UserToTemplate(u *models.User) User {
func UserToTemplate(u *models.User, currentTheme string) User {
// TODO: Handle deleted users. Maybe not here, but if not, at call sites of this function.
avatar := ""
if u.Avatar != nil {
avatar = hmnurl.StaticUrl(*u.Avatar, nil)
if u.Avatar != nil && len(*u.Avatar) > 0 {
avatar = hmnurl.BuildPublic(*u.Avatar, false)
} else {
avatar = hmnurl.BuildTheme("empty-avatar.svg", currentTheme, true)
}
name := u.Name
@ -99,7 +101,7 @@ func UserToTemplate(u *models.User) User {
Name: name,
Blurb: u.Blurb,
Signature: u.Signature,
AvatarUrl: avatar, // TODO
AvatarUrl: avatar,
ProfileUrl: hmnurl.Url("m/"+u.Username, nil),
DarkTheme: u.DarkTheme,

View File

@ -86,7 +86,9 @@
<span class="pl3">
Edited by
<a class="name" href="{{ .Editor.ProfileUrl }}" target="_blank">{{ coalesce .Editor.Name .Editor.Username }}</a>
{{ if $.User }}
{{ if and $.User.IsStaff .EditIP }}<span class="ip">[{{ .EditIP }}]</span>{{ end }}
{{ end }}
on {{ timehtml (absolutedate .EditDate) .EditDate }}
{{ with .EditReason }}
Reason: {{ . }}

View File

@ -149,28 +149,21 @@ var HMNTemplateFuncs = template.FuncMap{
}
},
"static": func(filepath string) string {
return hmnurl.StaticUrl(filepath, []hmnurl.Q{{"v", cachebust}})
return hmnurl.BuildPublic(filepath, true)
},
"staticnobust": func(filepath string) string {
return hmnurl.StaticUrl(filepath, nil)
return hmnurl.BuildPublic(filepath, false)
},
"statictheme": func(theme string, filepath string) string {
return hmnurl.StaticThemeUrl(filepath, theme, []hmnurl.Q{{"v", cachebust}})
return hmnurl.BuildTheme(filepath, theme, true)
},
"staticthemenobust": func(theme string, filepath string) string {
return hmnurl.StaticThemeUrl(filepath, theme, nil)
return hmnurl.BuildTheme(filepath, theme, false)
},
"timehtml": func(formatted string, t time.Time) template.HTML {
iso := t.Format(time.RFC3339)
return template.HTML(fmt.Sprintf(`<time datetime="%s">%s</time>`, iso, formatted))
},
"url": func(url string) string {
return hmnurl.Url(url, nil)
},
"urlq": func(url string, query string) string {
absUrl := hmnurl.Url(url, nil)
return fmt.Sprintf("%s?%s", absUrl, query)
},
}
type ErrInvalidHexColor struct {

View File

@ -148,6 +148,7 @@ func Feed(c *RequestContext) ResponseData {
postResult.LibraryResource,
!hasRead,
true,
c.Theme,
))
}
c.Perf.EndBlock()

View File

@ -141,9 +141,9 @@ func ForumCategory(c *RequestContext) ResponseData {
return templates.ThreadListItem{
Title: row.Thread.Title,
Url: hmnurl.BuildForumThread(c.CurrentProject.Slug, lineageBuilder.GetSubforumLineageSlugs(row.Thread.CategoryID), row.Thread.ID, row.Thread.Title, 1),
FirstUser: templates.UserToTemplate(row.FirstUser),
FirstUser: templates.UserToTemplate(row.FirstUser, c.Theme),
FirstDate: row.FirstPost.PostDate,
LastUser: templates.UserToTemplate(row.LastUser),
LastUser: templates.UserToTemplate(row.LastUser, c.Theme),
LastDate: row.LastPost.PostDate,
Unread: !hasRead,
@ -403,8 +403,8 @@ func ForumThread(c *RequestContext) ResponseData {
for _, irow := range itPosts.ToSlice() {
row := irow.(*postsQueryResult)
post := templates.PostToTemplate(&row.Post, row.Author)
post.AddContentVersion(row.Ver, row.Editor)
post := templates.PostToTemplate(&row.Post, row.Author, c.Theme)
post.AddContentVersion(row.Ver, row.Editor, c.Theme)
post.AddUrls(c.CurrentProject.Slug, currentSubforumSlugs, thread.ID, post.ID)
posts = append(posts, post)

View File

@ -183,7 +183,7 @@ func Index(c *RequestContext) ResponseData {
landingPageProject.FeaturedPost = &LandingPageFeaturedPost{
Title: projectPost.Thread.Title,
Url: hmnurl.BuildBlogPost(proj.Slug, projectPost.Thread.ID, projectPost.Post.ID),
User: templates.UserToTemplate(&projectPost.User),
User: templates.UserToTemplate(&projectPost.User, c.Theme),
Date: projectPost.Post.PostDate,
Unread: !hasRead,
Content: template.HTML(content),
@ -200,6 +200,7 @@ func Index(c *RequestContext) ResponseData {
projectPost.LibraryResource,
!hasRead,
false,
c.Theme,
),
)
}
@ -293,7 +294,7 @@ func Index(c *RequestContext) ResponseData {
NewsPost: LandingPageFeaturedPost{
Title: newsPostResult.Thread.Title,
Url: hmnurl.BuildBlogPost(models.HMNProjectSlug, newsPostResult.Thread.ID, newsPostResult.Post.ID),
User: templates.UserToTemplate(&newsPostResult.User),
User: templates.UserToTemplate(&newsPostResult.User, c.Theme),
Date: newsPostResult.Post.PostDate,
Unread: true, // TODO
Content: template.HTML(newsPostResult.PostVersion.TextParsed),

View File

@ -30,11 +30,11 @@ func UrlForGenericPost(post *models.Post, subforums []string, threadTitle string
}
// NOTE(asaf): THIS DOESN'T HANDLE WIKI EDIT ITEMS. Wiki edits are PostTextVersions, not Posts.
func MakePostListItem(lineageBuilder *models.CategoryLineageBuilder, project *models.Project, thread *models.Thread, post *models.Post, user *models.User, libraryResource *models.LibraryResource, unread bool, includeBreadcrumbs bool) templates.PostListItem {
func MakePostListItem(lineageBuilder *models.CategoryLineageBuilder, project *models.Project, thread *models.Thread, post *models.Post, user *models.User, libraryResource *models.LibraryResource, unread bool, includeBreadcrumbs bool, currentTheme string) templates.PostListItem {
var result templates.PostListItem
result.Title = thread.Title
result.User = templates.UserToTemplate(user)
result.User = templates.UserToTemplate(user, currentTheme)
result.Date = post.PostDate
result.Unread = unread
libraryResourceId := 0

View File

@ -121,6 +121,7 @@ type RequestContext struct {
Conn *pgxpool.Pool
CurrentProject *models.Project
CurrentUser *models.User
Theme string
Perf *perf.RequestPerf
}

View File

@ -133,7 +133,7 @@ func getBaseData(c *RequestContext) templates.BaseData {
Project: templates.ProjectToTemplate(c.CurrentProject),
LoginPageUrl: hmnurl.BuildLoginPage(c.FullUrl()),
User: templateUser,
Theme: "light",
Theme: c.Theme,
ProjectCSSUrl: hmnurl.BuildProjectCSS(c.CurrentProject.Color1),
Header: templates.Header{
AdminUrl: hmnurl.BuildHomepage(), // TODO(asaf)
@ -279,6 +279,13 @@ func LoadCommonWebsiteData(c *RequestContext) (bool, ResponseData) {
// http.ErrNoCookie is the only error Cookie ever returns, so no further handling to do here.
}
theme := "light"
if c.CurrentUser != nil && c.CurrentUser.DarkTheme {
theme = "dark"
}
c.Theme = theme
return true, ResponseData{}
}