Load mentionValues asynchronously (#36739)
Eliminate a few database queries on all issue and pull request pages by moving mention autocomplete data to async JSON endpoints fetched on-demand when the user types `@`. See https://github.com/go-gitea/gitea/pull/36739#issuecomment-3963184858 for the full table of affected pages. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
This commit is contained in:
@@ -14,7 +14,6 @@ import (
|
||||
|
||||
"code.gitea.io/gitea/models/db"
|
||||
issues_model "code.gitea.io/gitea/models/issues"
|
||||
"code.gitea.io/gitea/models/organization"
|
||||
access_model "code.gitea.io/gitea/models/perm/access"
|
||||
project_model "code.gitea.io/gitea/models/project"
|
||||
"code.gitea.io/gitea/models/renderhelper"
|
||||
@@ -649,47 +648,3 @@ func attachmentsHTML(ctx *context.Context, attachments []*repo_model.Attachment,
|
||||
}
|
||||
return attachHTML
|
||||
}
|
||||
|
||||
// handleMentionableAssigneesAndTeams gets all teams that current user can mention, and fills the assignee users to the context data
|
||||
func handleMentionableAssigneesAndTeams(ctx *context.Context, assignees []*user_model.User) {
|
||||
// TODO: need to figure out how many places this is really used, and rename it to "MentionableAssignees"
|
||||
// at the moment it is used on the issue list page, for the markdown editor mention
|
||||
ctx.Data["Assignees"] = assignees
|
||||
|
||||
if ctx.Doer == nil || !ctx.Repo.Owner.IsOrganization() {
|
||||
return
|
||||
}
|
||||
|
||||
var isAdmin bool
|
||||
var err error
|
||||
var teams []*organization.Team
|
||||
org := organization.OrgFromUser(ctx.Repo.Owner)
|
||||
// Admin has super access.
|
||||
if ctx.Doer.IsAdmin {
|
||||
isAdmin = true
|
||||
} else {
|
||||
isAdmin, err = org.IsOwnedBy(ctx, ctx.Doer.ID)
|
||||
if err != nil {
|
||||
ctx.ServerError("IsOwnedBy", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if isAdmin {
|
||||
teams, err = org.LoadTeams(ctx)
|
||||
if err != nil {
|
||||
ctx.ServerError("LoadTeams", err)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
teams, err = org.GetUserTeams(ctx, ctx.Doer.ID)
|
||||
if err != nil {
|
||||
ctx.ServerError("GetUserTeams", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
ctx.Data["MentionableTeams"] = teams
|
||||
ctx.Data["MentionableTeamsOrg"] = ctx.Repo.Owner.Name
|
||||
ctx.Data["MentionableTeamsOrgAvatar"] = ctx.Repo.Owner.AvatarLink(ctx)
|
||||
}
|
||||
|
||||
@@ -662,10 +662,7 @@ func prepareIssueFilterAndList(ctx *context.Context, milestoneID, projectID int6
|
||||
ctx.ServerError("GetRepoAssignees", err)
|
||||
return
|
||||
}
|
||||
handleMentionableAssigneesAndTeams(ctx, shared_user.MakeSelfOnTop(ctx.Doer, assigneeUsers))
|
||||
if ctx.Written() {
|
||||
return
|
||||
}
|
||||
ctx.Data["Assignees"] = shared_user.MakeSelfOnTop(ctx.Doer, assigneeUsers)
|
||||
|
||||
ctx.Data["IssueRefEndNames"], ctx.Data["IssueRefURLs"] = issue_service.GetRefEndNamesAndURLs(issues, ctx.Repo.RepoLink)
|
||||
|
||||
|
||||
@@ -155,8 +155,7 @@ func (d *IssuePageMetaData) retrieveAssigneesData(ctx *context.Context) {
|
||||
}
|
||||
d.AssigneesData.SelectedAssigneeIDs = strings.Join(ids, ",")
|
||||
}
|
||||
// FIXME: this is a tricky part which writes ctx.Data["Mentionable*"]
|
||||
handleMentionableAssigneesAndTeams(ctx, d.AssigneesData.CandidateAssignees)
|
||||
ctx.Data["Assignees"] = d.AssigneesData.CandidateAssignees
|
||||
}
|
||||
|
||||
func (d *IssuePageMetaData) retrieveProjectsDataForIssueWriter(ctx *context.Context) {
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
// Copyright 2026 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package repo
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
|
||||
issues_model "code.gitea.io/gitea/models/issues"
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
shared_mention "code.gitea.io/gitea/routers/web/shared/mention"
|
||||
"code.gitea.io/gitea/services/context"
|
||||
)
|
||||
|
||||
// GetMentionsInRepo returns JSON data for mention autocomplete (assignees, participants, mentionable teams).
|
||||
func GetMentionsInRepo(ctx *context.Context) {
|
||||
c := shared_mention.NewCollector()
|
||||
|
||||
// Get participants if issue_index is provided
|
||||
if issueIndex := ctx.FormInt64("issue_index"); issueIndex > 0 {
|
||||
issue, err := issues_model.GetIssueByIndex(ctx, ctx.Repo.Repository.ID, issueIndex)
|
||||
if err != nil && !errors.Is(err, util.ErrNotExist) {
|
||||
ctx.ServerError("GetIssueByIndex", err)
|
||||
return
|
||||
}
|
||||
if issue != nil {
|
||||
userIDs, err := issue.GetParticipantIDsByIssue(ctx)
|
||||
if err != nil {
|
||||
ctx.ServerError("GetParticipantIDsByIssue", err)
|
||||
return
|
||||
}
|
||||
users, err := user_model.GetUsersByIDs(ctx, userIDs)
|
||||
if err != nil {
|
||||
ctx.ServerError("GetUsersByIDs", err)
|
||||
return
|
||||
}
|
||||
c.AddUsers(ctx, users)
|
||||
}
|
||||
}
|
||||
|
||||
// Get repo assignees
|
||||
assignees, err := repo_model.GetRepoAssignees(ctx, ctx.Repo.Repository)
|
||||
if err != nil {
|
||||
ctx.ServerError("GetRepoAssignees", err)
|
||||
return
|
||||
}
|
||||
c.AddUsers(ctx, assignees)
|
||||
|
||||
// Get mentionable teams for org repos
|
||||
if err := c.AddMentionableTeams(ctx, ctx.Doer, ctx.Repo.Owner); err != nil {
|
||||
ctx.ServerError("AddMentionableTeams", err)
|
||||
return
|
||||
}
|
||||
|
||||
ctx.JSON(http.StatusOK, util.SliceNilAsEmpty(c.Result))
|
||||
}
|
||||
@@ -913,10 +913,7 @@ func viewPullFiles(ctx *context.Context, beforeCommitID, afterCommitID string) {
|
||||
ctx.ServerError("GetRepoAssignees", err)
|
||||
return
|
||||
}
|
||||
handleMentionableAssigneesAndTeams(ctx, shared_user.MakeSelfOnTop(ctx.Doer, assigneeUsers))
|
||||
if ctx.Written() {
|
||||
return
|
||||
}
|
||||
ctx.Data["Assignees"] = shared_user.MakeSelfOnTop(ctx.Doer, assigneeUsers)
|
||||
|
||||
currentReview, err := issues_model.GetCurrentReview(ctx, ctx.Doer, issue)
|
||||
if err != nil && !issues_model.IsErrReviewNotExist(err) {
|
||||
|
||||
Reference in New Issue
Block a user