Skip to content

Change form actions to fetch for submit review box #25219

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 33 commits into from
Jun 14, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
f774943
save
HesterG Jun 12, 2023
3c56833
change to fetch
HesterG Jun 12, 2023
984a30b
update
HesterG Jun 12, 2023
2b71734
change to post
HesterG Jun 12, 2023
7d3683b
Merge branch 'go-gitea:main' into change-actions-fetch
HesterG Jun 12, 2023
2318bb3
change redirect
HesterG Jun 12, 2023
183dc8e
update
HesterG Jun 12, 2023
ed54590
add loading
HesterG Jun 12, 2023
8f57fc1
add comments
HesterG Jun 12, 2023
0dc11e1
update js
HesterG Jun 12, 2023
4602642
update
HesterG Jun 12, 2023
7fa9e1e
Merge branch 'go-gitea:main' into change-actions-fetch
HesterG Jun 13, 2023
9db8855
comment
HesterG Jun 13, 2023
acdd3cd
comment
HesterG Jun 13, 2023
f029cfb
change to resirect
HesterG Jun 13, 2023
8f382f2
Merge branch 'main' into change-actions-fetch
HesterG Jun 13, 2023
cf540cd
Merge branch 'main' into change-actions-fetch
HesterG Jun 13, 2023
bfe1c6b
update to dataform
HesterG Jun 13, 2023
1a8472f
add method
HesterG Jun 13, 2023
c3d5bfb
Merge branch 'main' into change-actions-fetch
HesterG Jun 13, 2023
2d7d5d0
rename and append submitter value
HesterG Jun 13, 2023
f4da606
comment
HesterG Jun 13, 2023
b37a7db
comment
HesterG Jun 13, 2023
74e330d
fix lint
HesterG Jun 13, 2023
a5c5145
Merge branch 'main' into change-actions-fetch
HesterG Jun 13, 2023
c45a1e1
fine tune (wip)
wxiaoguang Jun 13, 2023
3b271d7
quick fix for submitterName and value
HesterG Jun 13, 2023
6023a45
Merge branch 'main' into change-actions-fetch
wxiaoguang Jun 13, 2023
b8f64ca
improve
wxiaoguang Jun 13, 2023
722d0ed
improve
wxiaoguang Jun 14, 2023
9f428a3
improve
wxiaoguang Jun 14, 2023
f43cdc6
Merge branch 'main' into change-actions-fetch
HesterG Jun 14, 2023
713feda
Merge branch 'main' into change-actions-fetch
GiteaBot Jun 14, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions modules/context/base.go
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,10 @@ func (b *Base) JSON(status int, content interface{}) {
}
}

func (b *Base) JSONRedirect(redirect string) {
b.JSON(http.StatusOK, map[string]any{"redirect": redirect})
}

// RemoteAddr returns the client machine ip address
func (b *Base) RemoteAddr() string {
return b.Req.RemoteAddr
Expand Down
10 changes: 10 additions & 0 deletions routers/web/devtest/devtest.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,16 @@ func List(ctx *context.Context) {
ctx.HTML(http.StatusOK, "devtest/list")
}

func FetchActionTest(ctx *context.Context) {
_ = ctx.Req.ParseForm()
ctx.Flash.Info(ctx.Req.Method + " " + ctx.Req.RequestURI + "<br>" +
"Form: " + ctx.Req.Form.Encode() + "<br>" +
"PostForm: " + ctx.Req.PostForm.Encode(),
)
time.Sleep(2 * time.Second)
ctx.JSONRedirect("")
}

func Tmpl(ctx *context.Context) {
now := time.Now()
ctx.Data["TimeNow"] = now
Expand Down
9 changes: 4 additions & 5 deletions routers/web/repo/pull_review.go
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ func SubmitReview(ctx *context.Context) {
}
if ctx.HasError() {
ctx.Flash.Error(ctx.Data["ErrorMsg"].(string))
ctx.Redirect(fmt.Sprintf("%s/pulls/%d/files", ctx.Repo.RepoLink, issue.Index))
ctx.JSONRedirect(fmt.Sprintf("%s/pulls/%d/files", ctx.Repo.RepoLink, issue.Index))
return
}

Expand All @@ -214,7 +214,7 @@ func SubmitReview(ctx *context.Context) {
}

ctx.Flash.Error(translated)
ctx.Redirect(fmt.Sprintf("%s/pulls/%d/files", ctx.Repo.RepoLink, issue.Index))
ctx.JSONRedirect(fmt.Sprintf("%s/pulls/%d/files", ctx.Repo.RepoLink, issue.Index))
return
}
}
Expand All @@ -228,14 +228,13 @@ func SubmitReview(ctx *context.Context) {
if err != nil {
if issues_model.IsContentEmptyErr(err) {
ctx.Flash.Error(ctx.Tr("repo.issues.review.content.empty"))
ctx.Redirect(fmt.Sprintf("%s/pulls/%d/files", ctx.Repo.RepoLink, issue.Index))
ctx.JSONRedirect(fmt.Sprintf("%s/pulls/%d/files", ctx.Repo.RepoLink, issue.Index))
} else {
ctx.ServerError("SubmitReview", err)
}
return
}

ctx.Redirect(fmt.Sprintf("%s/pulls/%d#%s", ctx.Repo.RepoLink, issue.Index, comm.HashTag()))
ctx.JSONRedirect(fmt.Sprintf("%s/pulls/%d#%s", ctx.Repo.RepoLink, issue.Index, comm.HashTag()))
}

// DismissReview dismissing stale review by repo admin
Expand Down
1 change: 1 addition & 0 deletions routers/web/web.go
Original file line number Diff line number Diff line change
Expand Up @@ -1411,6 +1411,7 @@ func registerRoutes(m *web.Route) {

if !setting.IsProd {
m.Any("/devtest", devtest.List)
m.Any("/devtest/fetch-action-test", devtest.FetchActionTest)
m.Any("/devtest/{sub}", devtest.Tmpl)
}

Expand Down
42 changes: 42 additions & 0 deletions templates/devtest/fetch-action.tmpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
{{template "base/head" .}}
<div class="page-content devtest ui container">
{{template "base/alert" .}}
<div>
<h1>link-action</h1>
<div>
Use "window.fetch" to send a request to backend, the request is defined in an "A" or "BUTTON" element.
It might be renamed to "link-fetch-action" to match the "form-fetch-action".
</div>
<div>
<button class="link-action" data-url="fetch-action-test?k=1">test</button>
</div>
</div>
<div>
<h1>form-fetch-action</h1>
<div>Use "window.fetch" to send a form request to backend</div>
<div>
<form method="get" action="fetch-action-test?k=1" class="form-fetch-action">
<button name="btn">submit get</button>
</form>
<form method="post" action="fetch-action-test?k=1" class="form-fetch-action">
<div><textarea name="text" rows="3" class="js-quick-submit"></textarea></div>
<div><label><input name="check" type="checkbox"> check</label></div>
<div><button name="btn">submit post</button></div>
</form>
<form method="post" action="/no-such-uri" class="form-fetch-action">
<div class="gt-py-5">bad action url</div>
<div><button name="btn">submit test</button></div>
</form>
</div>
</div>
</div>
<style>
.ui.message.flash-message {
text-align: left;
}
.form-fetch-action {
margin-bottom: 1em;
border: 1px red dashed; /* show the border for demo purpose */
}
</style>
{{template "base/footer" .}}
11 changes: 11 additions & 0 deletions templates/devtest/gitea-ui.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,17 @@
<div><span data-tooltip-content="test tooltip" data-tooltip-interactive="true">text with interactive tooltip</span></div>
</div>

<div>
<h1>Loading</h1>
<div class="is-loading small-loading-icon gt-border-secondary gt-py-2"><span>loading ...</span></div>
<div class="is-loading gt-border-secondary gt-py-4">
<p>loading ...</p>
<p>loading ...</p>
<p>loading ...</p>
<p>loading ...</p>
</div>
</div>

<div>
<h1>GiteaOriginUrl</h1>
<div><gitea-origin-url data-url="test/url"></gitea-origin-url></div>
Expand Down
17 changes: 10 additions & 7 deletions templates/devtest/list.tmpl
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
<style>
@media (prefers-color-scheme: dark) {
:root {
color-scheme: dark;
}
}
</style>
{{template "base/head" .}}

<ul>
{{range .SubNames}}
<li><a href="{{AppSubUrl}}/devtest/{{.}}">{{.}}</a></li>
{{end}}
</ul>

<style>
ul {
line-height: 2em;
}
</style>

{{template "base/footer" .}}
2 changes: 1 addition & 1 deletion templates/repo/diff/new_review.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
</button>
<div class="review-box-panel tippy-target">
<div class="ui segment">
<form class="ui form" action="{{.Link}}/reviews/submit" method="post">
<form class="ui form form-fetch-action" action="{{.Link}}/reviews/submit" method="post">
{{.CsrfTokenHtml}}
<input type="hidden" name="commit_id" value="{{.AfterCommitID}}">
<div class="field gt-df gt-ac">
Expand Down
21 changes: 12 additions & 9 deletions web_src/css/modules/animations.css
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,22 @@
}

.is-loading {
background: transparent !important;
color: transparent !important;
border: transparent !important;
pointer-events: none !important;
position: relative !important;
overflow: hidden !important;
}

.is-loading > * {
opacity: 0.3;
}

.is-loading::after {
content: "";
position: absolute;
display: block;
width: 4rem;
height: 4rem;
max-height: 50%;
aspect-ratio: 1 / 1;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
Expand All @@ -28,30 +30,31 @@
border-radius: 100%;
}

.is-loading.small-loading-icon::after {
border-width: 2px;
}

.markup pre.is-loading,
.editor-loading.is-loading,
.pdf-content.is-loading {
height: var(--height-loading);
}

/* TODO: not needed, use "is-loading small-loading-icon" instead */
.btn-octicon.is-loading::after {
border-width: 2px;
height: 1.25rem;
width: 1.25rem;
}

/* TODO: not needed, use "is-loading small-loading-icon" instead */
code.language-math.is-loading::after {
padding: 0;
border-width: 2px;
width: 1.25rem;
height: 1.25rem;
}

#oauth2-login-navigator.is-loading::after {
width: 40px;
height: 40px;
}

@keyframes fadein {
0% {
opacity: 0;
Expand Down
6 changes: 6 additions & 0 deletions web_src/css/modules/tippy.css
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,12 @@
color: var(--color-text);
}

.tippy-box[data-theme="form-fetch-error"] {
border-color: var(--color-error-border);
background-color: var(--color-error-bg);
color: var(--color-error-text);
}

.tippy-content {
position: relative;
padding: 1rem;
Expand Down
84 changes: 81 additions & 3 deletions web_src/js/features/common-global.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {handleGlobalEnterQuickSubmit} from './comp/QuickSubmit.js';
import {svg} from '../svg.js';
import {hideElem, showElem, toggleElem} from '../utils/dom.js';
import {htmlEscape} from 'escape-goat';
import {createTippy} from '../modules/tippy.js';

const {appUrl, csrfToken, i18n} = window.config;

Expand Down Expand Up @@ -60,6 +61,81 @@ export function initGlobalButtonClickOnEnter() {
});
}

async function formFetchAction(e) {
if (!e.target.classList.contains('form-fetch-action')) return;

e.preventDefault();
const formEl = e.target;
if (formEl.classList.contains('is-loading')) return;

formEl.classList.add('is-loading');
if (formEl.clientHeight < 50) {
formEl.classList.add('small-loading-icon');
}

const formMethod = formEl.getAttribute('method') || 'get';
const formActionUrl = formEl.getAttribute('action');
const formData = new FormData(formEl);
const [submitterName, submitterValue] = [e.submitter?.getAttribute('name'), e.submitter?.getAttribute('value')];
if (submitterName) {
formData.append(submitterName, submitterValue || '');
}

let reqUrl = formActionUrl;
const reqOpt = {method: formMethod.toUpperCase(), headers: {'X-Csrf-Token': csrfToken}};
if (formMethod.toLowerCase() === 'get') {
const params = new URLSearchParams();
for (const [key, value] of formData) {
params.append(key, value.toString());
}
const pos = reqUrl.indexOf('?');
if (pos !== -1) {
reqUrl = reqUrl.slice(0, pos);
}
reqUrl += `?${params.toString()}`;
} else {
reqOpt.body = formData;
}

let errorTippy;
const onError = (msg) => {
formEl.classList.remove('is-loading', 'small-loading-icon');
if (errorTippy) errorTippy.destroy();
errorTippy = createTippy(formEl, {
content: msg,
interactive: true,
showOnCreate: true,
hideOnClick: true,
role: 'alert',
theme: 'form-fetch-error',
trigger: 'manual',
arrow: false,
});
};

const doRequest = async () => {
try {
const resp = await fetch(reqUrl, reqOpt);
if (resp.status === 200) {
const {redirect} = await resp.json();
formEl.classList.remove('dirty'); // remove the areYouSure check before reloading
if (redirect) {
window.location.href = redirect;
} else {
window.location.reload();
}
} else {
onError(`server error: ${resp.status}`);
}
} catch (e) {
onError(e.error);
}
};

// TODO: add "confirm" support like "link-action" in the future
await doRequest();
}

export function initGlobalCommon() {
// Semantic UI modules.
const $uiDropdowns = $('.ui.dropdown');
Expand Down Expand Up @@ -114,6 +190,8 @@ export function initGlobalCommon() {
if (btn.classList.contains('loading')) return e.preventDefault();
btn.classList.add('loading');
});

document.addEventListener('submit', formFetchAction);
}

export function initGlobalDropzone() {
Expand Down Expand Up @@ -182,7 +260,7 @@ function linkAction(e) {
const $this = $(e.target);
const redirect = $this.attr('data-redirect');

const request = () => {
const doRequest = () => {
$this.prop('disabled', true);
$.post($this.attr('data-url'), {
_csrf: csrfToken
Expand All @@ -201,7 +279,7 @@ function linkAction(e) {

const modalConfirmHtml = htmlEscape($this.attr('data-modal-confirm') || '');
if (!modalConfirmHtml) {
request();
doRequest();
return;
}

Expand All @@ -220,7 +298,7 @@ function linkAction(e) {
$modal.appendTo(document.body);
$modal.modal({
onApprove() {
request();
doRequest();
},
onHidden() {
$modal.remove();
Expand Down
21 changes: 14 additions & 7 deletions web_src/js/features/comp/QuickSubmit.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,24 @@
import $ from 'jquery';

export function handleGlobalEnterQuickSubmit(target) {
const $target = $(target);
const $form = $(target).closest('form');
if ($form.length) {
const form = target.closest('form');
if (form) {
if (!form.checkValidity()) {
form.reportValidity();
return;
}

if (form.classList.contains('form-fetch-action')) {
form.dispatchEvent(new SubmitEvent('submit', {bubbles: true, cancelable: true}));
return;
}

// here use the event to trigger the submit event (instead of calling `submit()` method directly)
// otherwise the `areYouSure` handler won't be executed, then there will be an annoying "confirm to leave" dialog
if ($form[0].checkValidity()) {
$form.trigger('submit');
}
$(form).trigger('submit');
} else {
// if no form, then the editor is for an AJAX request, dispatch an event to the target, let the target's event handler to do the AJAX request.
// the 'ce-' prefix means this is a CustomEvent
$target.trigger('ce-quick-submit');
target.dispatchEvent(new CustomEvent('ce-quick-submit', {bubbles: true}));
}
}
Loading