From 07698c798e375d8f60af4f43c273c4c50e0eafcc Mon Sep 17 00:00:00 2001 From: silverwind Date: Thu, 19 Nov 2020 21:38:23 +0100 Subject: [PATCH 01/20] Direct avatar rendering This adds new template helpers for avatar rendering which output image elements with direct links to avatars which makes them cacheable by the browsers. This should be a major performance improvment for pages with many avatars. --- models/action.go | 6 - modules/templates/helper.go | 67 +++++++++-- routers/repo/blame.go | 15 +-- templates/base/head_navbar.tmpl | 4 +- templates/explore/organizations.tmpl | 2 +- templates/explore/repo_list.tmpl | 4 +- templates/explore/users.tmpl | 2 +- templates/org/header.tmpl | 2 +- templates/org/home.tmpl | 10 +- templates/org/member/members.tmpl | 2 +- templates/org/team/members.tmpl | 2 +- templates/org/team/teams.tmpl | 2 +- templates/repo/commit_page.tmpl | 22 ++-- templates/repo/commits_list.tmpl | 5 +- templates/repo/commits_list_small.tmpl | 6 +- templates/repo/create.tmpl | 6 +- templates/repo/diff/comments.tmpl | 2 +- templates/repo/editor/commit_form.tmpl | 2 +- templates/repo/forks.tmpl | 2 +- templates/repo/graph/commits.tmpl | 6 +- templates/repo/header.tmpl | 4 +- templates/repo/header_icon.tmpl | 2 +- templates/repo/issue/list.tmpl | 6 +- templates/repo/issue/milestone_issues.tmpl | 8 +- templates/repo/issue/new_form.tmpl | 6 +- templates/repo/issue/view_content.tmpl | 6 +- .../repo/issue/view_content/comments.tmpl | 110 +++++++++--------- templates/repo/issue/view_content/pull.tmpl | 4 +- .../repo/issue/view_content/sidebar.tmpl | 14 ++- templates/repo/migrate/git.tmpl | 6 +- templates/repo/migrate/gitea.tmpl | 6 +- templates/repo/migrate/github.tmpl | 6 +- templates/repo/migrate/gitlab.tmpl | 6 +- templates/repo/projects/view.tmpl | 1 - templates/repo/pulls/fork.tmpl | 6 +- templates/repo/release/list.tmpl | 4 +- templates/repo/settings/collaboration.tmpl | 2 +- templates/repo/settings/lfs_locks.tmpl | 2 +- templates/repo/settings/protected_branch.tmpl | 6 +- templates/repo/shabox_badge.tmpl | 4 +- templates/repo/user_cards.tmpl | 2 +- templates/repo/view_list.tmpl | 4 +- templates/shared/issuelist.tmpl | 2 +- templates/user/dashboard/feeds.tmpl | 10 +- templates/user/dashboard/navbar.tmpl | 7 +- templates/user/profile.tmpl | 10 +- templates/user/project.tmpl | 8 +- templates/user/settings/organization.tmpl | 2 +- web_src/less/_base.less | 5 + web_src/less/_dashboard.less | 2 +- web_src/less/_organization.less | 4 +- web_src/less/_repository.less | 4 +- web_src/less/_user.less | 5 + web_src/less/helpers.less | 1 + 54 files changed, 260 insertions(+), 184 deletions(-) diff --git a/models/action.go b/models/action.go index f4d163afa3f5..ca186033a6bc 100644 --- a/models/action.go +++ b/models/action.go @@ -140,12 +140,6 @@ func (a *Action) GetDisplayNameTitle() string { return a.GetActFullName() } -// GetActAvatar the action's user's avatar link -func (a *Action) GetActAvatar() string { - a.loadActUser() - return a.ActUser.RelAvatarLink() -} - // GetRepoUserName returns the name of the action repository owner. func (a *Action) GetRepoUserName() string { a.loadRepo() diff --git a/modules/templates/helper.go b/modules/templates/helper.go index bb7a3476f29b..c82e70ae6892 100644 --- a/modules/templates/helper.go +++ b/modules/templates/helper.go @@ -88,7 +88,6 @@ func NewFuncMap() []template.FuncMap { "AllowedReactions": func() []string { return setting.UI.Reactions }, - "AvatarLink": models.AvatarLink, "Safe": Safe, "SafeJS": SafeJS, "Str2html": Str2html, @@ -338,7 +337,9 @@ func NewFuncMap() []template.FuncMap { } return false }, - "svg": SVG, + "svg": SVG, + "avatar": Avatar, + "avatarByEmail": AvatarByEmail, "SortArrow": func(normSort, revSort, urlSort string, isDefault bool) template.HTML { // if needed if len(normSort) == 0 || len(urlSort) == 0 { @@ -498,18 +499,38 @@ func NewTextFuncMap() []texttmpl.FuncMap { var widthRe = regexp.MustCompile(`width="[0-9]+?"`) var heightRe = regexp.MustCompile(`height="[0-9]+?"`) -// SVG render icons - arguments icon name (string), size (int), class (string) -func SVG(icon string, others ...interface{}) template.HTML { - size := 16 +func parseOthers(defaultSize int, defaultClass string, others ...interface{}) (int, string) { + size := defaultSize if len(others) > 0 && others[0].(int) != 0 { size = others[0].(int) } - class := "" + class := defaultClass if len(others) > 1 && others[1].(string) != "" { - class = others[1].(string) + if defaultClass == "" { + class = others[1].(string) + } else { + class = defaultClass + " " + others[1].(string) + } + } + + return size, class +} + +func avatarHTML(src string, size int, class string, name string) template.HTML { + sizeStr := fmt.Sprintf(`%d`, size) + + if name == "" { + name = "avatar" } + return template.HTML(`` + html.EscapeString(name) + ``) +} + +// SVG render icons - arguments icon name (string), size (int), class (string) +func SVG(icon string, others ...interface{}) template.HTML { + size, class := parseOthers(16, "", others...) + if svgStr, ok := svg.SVGs[icon]; ok { if size != 16 { svgStr = widthRe.ReplaceAllString(svgStr, fmt.Sprintf(`width="%d"`, size)) @@ -523,6 +544,38 @@ func SVG(icon string, others ...interface{}) template.HTML { return template.HTML("") } +// Avatar renders user and repo avatars. args: user/repo, size (int), class (string) +func Avatar(item interface{}, others ...interface{}) template.HTML { + size, class := parseOthers(28, "ui avatar image", others...) + if user, ok := item.(*models.User); ok { + src := user.RealSizedAvatarLink(size * 2) // request double size for finer rendering + if src != "" { + return avatarHTML(src, size, class, user.DisplayName()) + } + } + + if repo, ok := item.(*models.Repository); ok { + src := repo.RelAvatarLink() + if src != "" { + return avatarHTML(src, size, class, repo.FullName()) + } + } + + return template.HTML("") +} + +// AvatarByEmail renders avatars by email address. args: email, size (int), class (string) +func AvatarByEmail(email string, others ...interface{}) template.HTML { + size, class := parseOthers(28, "ui avatar image", others...) + src := base.SizedAvatarLink(email, size) + + if src != "" { + return avatarHTML(src, size, class, "") + } + + return template.HTML("") +} + // Safe render raw as HTML func Safe(raw string) template.HTML { return template.HTML(raw) diff --git a/routers/repo/blame.go b/routers/repo/blame.go index 812c55ea4da9..d9bc80e9ddde 100644 --- a/routers/repo/blame.go +++ b/routers/repo/blame.go @@ -10,7 +10,6 @@ import ( "fmt" "html" gotemplate "html/template" - "net/url" "strings" "code.gitea.io/gitea/models" @@ -19,7 +18,7 @@ import ( "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/highlight" "code.gitea.io/gitea/modules/log" - "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/templates" "code.gitea.io/gitea/modules/timeutil" ) @@ -209,17 +208,15 @@ func renderBlame(ctx *context.Context, blameParts []git.BlamePart, commitNames m commit := commitNames[part.Sha] if index == 0 { // User avatar image - avatar := "" commitSince := timeutil.TimeSinceUnix(timeutil.TimeStamp(commit.Author.When.Unix()), ctx.Data["Lang"].(string)) + + var avatar string if commit.User != nil { - authorName := commit.Author.Name - if len(commit.User.FullName) > 0 { - authorName = commit.User.FullName - } - avatar = fmt.Sprintf(``, setting.AppSubURL, url.PathEscape(commit.User.Name), commit.User.RelAvatarLink(), html.EscapeString(authorName)) + avatar = string(templates.Avatar(commit.User, 18)) } else { - avatar = fmt.Sprintf(``, html.EscapeString(models.AvatarLink(commit.Author.Email)), html.EscapeString(commit.Author.Name)) + avatar = string(templates.Avatar(commit.Author, 18)) } + commitInfo.WriteString(fmt.Sprintf(`
%s
%s
`, attr, avatar, repoLink, part.Sha, html.EscapeString(commit.CommitMessage), commitSince)) } else { commitInfo.WriteString(fmt.Sprintf(`
`, attr)) diff --git a/templates/base/head_navbar.tmpl b/templates/base/head_navbar.tmpl index 1b102b11d883..979e4d548872 100644 --- a/templates/base/head_navbar.tmpl +++ b/templates/base/head_navbar.tmpl @@ -47,7 +47,7 @@