mirror of
https://github.com/go-gitea/gitea.git
synced 2026-08-20 05:48:36 +00:00
feat: Add avatar stacks (#37594)
Parse `Co-authored-by:` trailers from commit messages and surface contributors as an avatar stack across the commit page, commits list, PR commits tab, latest-commit row, blame, graph, and dashboard feed. - Up to 10 visible 20px avatars, GitHub-style overlap (6px first stride, 4px between subsequent), `+N` chip for the rest. - Label: 1 → name; 2 → `<a> and <b>`; 3+ → `<N> people` opens a Tippy popup with all participants. - Names and avatars link to the repo's commits-by-author search; fall back to profile or `mailto:`. - Trailer parsing uses `net/mail.ParseAddress`, scans only the trailing paragraph, filters out the commit's own author/committer. - Drops the non-standard `Co-committed-by:` emission on squash merge and web edits. Devtest: `/devtest/coauthor-avatars`. Fixes #25521 ---- <img width="353" height="277" alt="image" src="https://github.com/user-attachments/assets/72092ceb-97ca-4b09-9557-0b72d3c5458e" /> <img width="533" height="328" src="https://github.com/user-attachments/assets/11d0c8f8-8b3f-4f2e-9993-879f1c06bcc5" /> --------- Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com> Co-authored-by: silverwind <me@silverwind.io> Co-authored-by: wxiaoguang <wxiaoguang@gmail.com> Co-authored-by: Giteabot <teabot@gitea.io>
This commit is contained in:
@@ -9,19 +9,15 @@ import (
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"gitea.dev/models/avatars"
|
||||
repo_model "gitea.dev/models/repo"
|
||||
user_model "gitea.dev/models/user"
|
||||
"gitea.dev/modules/cache"
|
||||
"gitea.dev/modules/cachegroup"
|
||||
"gitea.dev/modules/git"
|
||||
"gitea.dev/modules/gitrepo"
|
||||
"gitea.dev/modules/log"
|
||||
"gitea.dev/modules/setting"
|
||||
api "gitea.dev/modules/structs"
|
||||
)
|
||||
|
||||
// PushCommit represents a commit in a push operation.
|
||||
// This struct is marshaled as JSON (see ActionContent2Commits)
|
||||
type PushCommit struct {
|
||||
Sha1 string
|
||||
Message string
|
||||
@@ -33,6 +29,7 @@ type PushCommit struct {
|
||||
}
|
||||
|
||||
// PushCommits represents list of commits in a push operation.
|
||||
// This struct is marshaled as JSON (see ActionContent2Commits)
|
||||
type PushCommits struct {
|
||||
Commits []*PushCommit
|
||||
HeadCommit *PushCommit
|
||||
@@ -128,26 +125,6 @@ func (pc *PushCommits) ToAPIPayloadCommits(ctx context.Context, repo *repo_model
|
||||
return commits, headCommit, nil
|
||||
}
|
||||
|
||||
// AvatarLink tries to match user in database with e-mail
|
||||
// in order to show custom avatar, and falls back to general avatar link.
|
||||
func (pc *PushCommits) AvatarLink(ctx context.Context, email string) string {
|
||||
size := avatars.DefaultAvatarPixelSize * setting.Avatar.RenderedSizeFactor
|
||||
|
||||
v, _ := cache.GetWithContextCache(ctx, cachegroup.EmailAvatarLink, email, func(ctx context.Context, email string) (string, error) {
|
||||
u, err := user_model.GetUserByEmail(ctx, email)
|
||||
if err != nil {
|
||||
if !user_model.IsErrUserNotExist(err) {
|
||||
log.Error("GetUserByEmail: %v", err)
|
||||
return "", err
|
||||
}
|
||||
return avatars.GenerateEmailAvatarFastLink(ctx, email, size), nil
|
||||
}
|
||||
return u.AvatarLinkWithSize(ctx, size), nil
|
||||
})
|
||||
|
||||
return v
|
||||
}
|
||||
|
||||
// CommitToPushCommit transforms a git.Commit to PushCommit type.
|
||||
func CommitToPushCommit(commit *git.Commit) *PushCommit {
|
||||
return &PushCommit{
|
||||
|
||||
Reference in New Issue
Block a user