mirror of
https://github.com/go-gitea/gitea
synced 2025-07-04 17:47:19 +00:00
Fix modal + form abuse (#34921)
See the comment. And due to the abuse, there is a regression: when the modal is hidden, the form will be reset and it can't submit. This PR fixes all problems: keep the modal with form open, and add "loading" indicator.
This commit is contained in:
@ -43,13 +43,16 @@ export function initGlobalDeleteButton(): void {
|
|||||||
|
|
||||||
fomanticQuery(modal).modal({
|
fomanticQuery(modal).modal({
|
||||||
closable: false,
|
closable: false,
|
||||||
onApprove: async () => {
|
onApprove: () => {
|
||||||
// if `data-type="form"` exists, then submit the form by the selector provided by `data-form="..."`
|
// if `data-type="form"` exists, then submit the form by the selector provided by `data-form="..."`
|
||||||
if (btn.getAttribute('data-type') === 'form') {
|
if (btn.getAttribute('data-type') === 'form') {
|
||||||
const formSelector = btn.getAttribute('data-form');
|
const formSelector = btn.getAttribute('data-form');
|
||||||
const form = document.querySelector<HTMLFormElement>(formSelector);
|
const form = document.querySelector<HTMLFormElement>(formSelector);
|
||||||
if (!form) throw new Error(`no form named ${formSelector} found`);
|
if (!form) throw new Error(`no form named ${formSelector} found`);
|
||||||
|
modal.classList.add('is-loading'); // the form is not in the modal, so also add loading indicator to the modal
|
||||||
|
form.classList.add('is-loading');
|
||||||
form.submit();
|
form.submit();
|
||||||
|
return false; // prevent modal from closing automatically
|
||||||
}
|
}
|
||||||
|
|
||||||
// prepare an AJAX form by data attributes
|
// prepare an AJAX form by data attributes
|
||||||
@ -62,12 +65,15 @@ export function initGlobalDeleteButton(): void {
|
|||||||
postData.append('id', value);
|
postData.append('id', value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
(async () => {
|
||||||
const response = await POST(btn.getAttribute('data-url'), {data: postData});
|
const response = await POST(btn.getAttribute('data-url'), {data: postData});
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
window.location.href = data.redirect;
|
window.location.href = data.redirect;
|
||||||
}
|
}
|
||||||
|
})();
|
||||||
|
modal.classList.add('is-loading'); // the request is in progress, so also add loading indicator to the modal
|
||||||
|
return false; // prevent modal from closing automatically
|
||||||
},
|
},
|
||||||
}).modal('show');
|
}).modal('show');
|
||||||
});
|
});
|
||||||
@ -158,13 +164,7 @@ function onShowModalClick(el: HTMLElement, e: MouseEvent) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fomanticQuery(elModal).modal('setting', {
|
fomanticQuery(elModal).modal('show');
|
||||||
onApprove: () => {
|
|
||||||
// "form-fetch-action" can handle network errors gracefully,
|
|
||||||
// so keep the modal dialog to make users can re-submit the form if anything wrong happens.
|
|
||||||
if (elModal.querySelector('.form-fetch-action')) return false;
|
|
||||||
},
|
|
||||||
}).modal('show');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function initGlobalButtons(): void {
|
export function initGlobalButtons(): void {
|
||||||
|
@ -72,6 +72,7 @@ export function initCompLabelEdit(pageSelector: string) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
submitFormFetchAction(form);
|
submitFormFetchAction(form);
|
||||||
|
return false;
|
||||||
},
|
},
|
||||||
}).modal('show');
|
}).modal('show');
|
||||||
};
|
};
|
||||||
|
@ -9,8 +9,9 @@ const fomanticModalFn = $.fn.modal;
|
|||||||
export function initAriaModalPatch() {
|
export function initAriaModalPatch() {
|
||||||
if ($.fn.modal === ariaModalFn) throw new Error('initAriaModalPatch could only be called once');
|
if ($.fn.modal === ariaModalFn) throw new Error('initAriaModalPatch could only be called once');
|
||||||
$.fn.modal = ariaModalFn;
|
$.fn.modal = ariaModalFn;
|
||||||
$.fn.fomanticExt.onModalBeforeHidden = onModalBeforeHidden;
|
|
||||||
(ariaModalFn as FomanticInitFunction).settings = fomanticModalFn.settings;
|
(ariaModalFn as FomanticInitFunction).settings = fomanticModalFn.settings;
|
||||||
|
$.fn.fomanticExt.onModalBeforeHidden = onModalBeforeHidden;
|
||||||
|
$.fn.modal.settings.onApprove = onModalApproveDefault;
|
||||||
}
|
}
|
||||||
|
|
||||||
// the patched `$.fn.modal` modal function
|
// the patched `$.fn.modal` modal function
|
||||||
@ -34,6 +35,29 @@ function ariaModalFn(this: any, ...args: Parameters<FomanticInitFunction>) {
|
|||||||
function onModalBeforeHidden(this: any) {
|
function onModalBeforeHidden(this: any) {
|
||||||
const $modal = $(this);
|
const $modal = $(this);
|
||||||
const elModal = $modal[0];
|
const elModal = $modal[0];
|
||||||
queryElems(elModal, 'form', (form: HTMLFormElement) => form.reset());
|
|
||||||
hideToastsFrom(elModal.closest('.ui.dimmer') ?? document.body);
|
hideToastsFrom(elModal.closest('.ui.dimmer') ?? document.body);
|
||||||
|
|
||||||
|
// reset the form after the modal is hidden, after other modal events and handlers (e.g. "onApprove", form submit)
|
||||||
|
setTimeout(() => {
|
||||||
|
queryElems(elModal, 'form', (form: HTMLFormElement) => form.reset());
|
||||||
|
}, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
function onModalApproveDefault(this: any) {
|
||||||
|
const $modal = $(this);
|
||||||
|
const selectors = $modal.modal('setting', 'selector');
|
||||||
|
const elModal = $modal[0];
|
||||||
|
const elApprove = elModal.querySelector(selectors.approve);
|
||||||
|
const elForm = elApprove?.closest('form');
|
||||||
|
if (!elForm) return true; // no form, just allow closing the modal
|
||||||
|
|
||||||
|
// "form-fetch-action" can handle network errors gracefully,
|
||||||
|
// so keep the modal dialog to make users can re-submit the form if anything wrong happens.
|
||||||
|
if (elForm.matches('.form-fetch-action')) return false;
|
||||||
|
|
||||||
|
// There is an abuse for the "modal" + "form" combination, the "Approve" button is a traditional form submit button in the form.
|
||||||
|
// Then "approve" and "submit" occur at the same time, the modal will be closed immediately before the form is submitted.
|
||||||
|
// So here we prevent the modal from closing automatically by returning false, add the "is-loading" class to the form element.
|
||||||
|
elForm.classList.add('is-loading');
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user