1
1
mirror of https://github.com/go-gitea/gitea synced 2025-07-22 18:28:37 +00:00

Use fetch to send requests to create issues/comments (#25258)

Follow #23290

Network error won't make content lost. And this is a much better
approach than "loading-button".

The UI is not perfect and there are still some TODOs, they can be done
in following PRs, not a must in this PR's scope.

<details>


![image](https://github.com/go-gitea/gitea/assets/2114189/c94ba958-aa46-4747-8ddf-6584deeed25c)

</details>
This commit is contained in:
wxiaoguang
2023-06-16 14:32:43 +08:00
committed by GitHub
parent a305c37e62
commit b71cb7acdc
14 changed files with 163 additions and 54 deletions

View File

@@ -0,0 +1,26 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package common
import (
"net/http"
"code.gitea.io/gitea/modules/httplib"
)
// FetchRedirectDelegate helps the "fetch" requests to redirect to the correct location
func FetchRedirectDelegate(resp http.ResponseWriter, req *http.Request) {
// When use "fetch" to post requests and the response is a redirect, browser's "location.href = uri" has limitations.
// 1. change "location" from old "/foo" to new "/foo#hash", the browser will not reload the page.
// 2. when use "window.reload()", the hash is not respected, the newly loaded page won't scroll to the hash target.
// The typical page is "issue comment" page. The backend responds "/owner/repo/issues/1#comment-2",
// then frontend needs this delegate to redirect to the new location with hash correctly.
redirect := req.PostFormValue("redirect")
if httplib.IsRiskyRedirectURL(redirect) {
resp.WriteHeader(http.StatusBadRequest)
return
}
resp.Header().Add("Location", redirect)
resp.WriteHeader(http.StatusSeeOther)
}

View File

@@ -183,6 +183,8 @@ func NormalRoutes(ctx context.Context) *web.Route {
r.Mount("/api/v1", apiv1.Routes(ctx))
r.Mount("/api/internal", private.Routes())
r.Post("/-/fetch-redirect", common.FetchRedirectDelegate)
if setting.Packages.Enabled {
// This implements package support for most package managers
r.Mount("/api/packages", packages_router.CommonRoutes(ctx))

View File

@@ -1134,12 +1134,12 @@ func NewIssuePost(ctx *context.Context) {
}
if ctx.HasError() {
ctx.HTML(http.StatusOK, tplIssueNew)
ctx.JSONError(ctx.GetErrMsg())
return
}
if util.IsEmptyString(form.Title) {
ctx.RenderWithErr(ctx.Tr("repo.issues.new.title_empty"), tplIssueNew, form)
ctx.JSONError(ctx.Tr("repo.issues.new.title_empty"))
return
}
@@ -1184,9 +1184,9 @@ func NewIssuePost(ctx *context.Context) {
log.Trace("Issue created: %d/%d", repo.ID, issue.ID)
if ctx.FormString("redirect_after_creation") == "project" && projectID > 0 {
ctx.Redirect(ctx.Repo.RepoLink + "/projects/" + strconv.FormatInt(projectID, 10))
ctx.JSONRedirect(ctx.Repo.RepoLink + "/projects/" + strconv.FormatInt(projectID, 10))
} else {
ctx.Redirect(issue.Link())
ctx.JSONRedirect(issue.Link())
}
}
@@ -2777,8 +2777,7 @@ func NewComment(ctx *context.Context) {
}
if issue.IsLocked && !ctx.Repo.CanWriteIssuesOrPulls(issue.IsPull) && !ctx.Doer.IsAdmin {
ctx.Flash.Error(ctx.Tr("repo.issues.comment_on_locked"))
ctx.Redirect(issue.Link())
ctx.JSONError(ctx.Tr("repo.issues.comment_on_locked"))
return
}
@@ -2788,8 +2787,7 @@ func NewComment(ctx *context.Context) {
}
if ctx.HasError() {
ctx.Flash.Error(ctx.Data["ErrorMsg"].(string))
ctx.Redirect(issue.Link())
ctx.JSONError(ctx.GetErrMsg())
return
}
@@ -2809,8 +2807,7 @@ func NewComment(ctx *context.Context) {
pr, err = issues_model.GetUnmergedPullRequest(ctx, pull.HeadRepoID, pull.BaseRepoID, pull.HeadBranch, pull.BaseBranch, pull.Flow)
if err != nil {
if !issues_model.IsErrPullRequestNotExist(err) {
ctx.Flash.Error(ctx.Tr("repo.issues.dependency.pr_close_blocked"))
ctx.Redirect(fmt.Sprintf("%s/pulls/%d", ctx.Repo.RepoLink, pull.Index))
ctx.JSONError(ctx.Tr("repo.issues.dependency.pr_close_blocked"))
return
}
}
@@ -2841,8 +2838,7 @@ func NewComment(ctx *context.Context) {
}
if ok := git.IsBranchExist(ctx, pull.HeadRepo.RepoPath(), pull.BaseBranch); !ok {
// todo localize
ctx.Flash.Error("The origin branch is delete, cannot reopen.")
ctx.Redirect(fmt.Sprintf("%s/pulls/%d", ctx.Repo.RepoLink, pull.Index))
ctx.JSONError("The origin branch is delete, cannot reopen.")
return
}
headBranchRef := pull.GetGitHeadBranchRefName()
@@ -2882,11 +2878,9 @@ func NewComment(ctx *context.Context) {
if issues_model.IsErrDependenciesLeft(err) {
if issue.IsPull {
ctx.Flash.Error(ctx.Tr("repo.issues.dependency.pr_close_blocked"))
ctx.Redirect(fmt.Sprintf("%s/pulls/%d", ctx.Repo.RepoLink, issue.Index))
ctx.JSONError(ctx.Tr("repo.issues.dependency.pr_close_blocked"))
} else {
ctx.Flash.Error(ctx.Tr("repo.issues.dependency.issue_close_blocked"))
ctx.Redirect(fmt.Sprintf("%s/issues/%d", ctx.Repo.RepoLink, issue.Index))
ctx.JSONError(ctx.Tr("repo.issues.dependency.issue_close_blocked"))
}
return
}
@@ -2899,7 +2893,6 @@ func NewComment(ctx *context.Context) {
log.Trace("Issue [%d] status changed to closed: %v", issue.ID, issue.IsClosed)
}
}
}
// Redirect to comment hashtag if there is any actual content.
@@ -2908,9 +2901,9 @@ func NewComment(ctx *context.Context) {
typeName = "pulls"
}
if comment != nil {
ctx.Redirect(fmt.Sprintf("%s/%s/%d#%s", ctx.Repo.RepoLink, typeName, issue.Index, comment.HashTag()))
ctx.JSONRedirect(fmt.Sprintf("%s/%s/%d#%s", ctx.Repo.RepoLink, typeName, issue.Index, comment.HashTag()))
} else {
ctx.Redirect(fmt.Sprintf("%s/%s/%d", ctx.Repo.RepoLink, typeName, issue.Index))
ctx.JSONRedirect(fmt.Sprintf("%s/%s/%d", ctx.Repo.RepoLink, typeName, issue.Index))
}
}()