Improve AJAX link and modal confirm dialog (#25210)

Clarify the "link-action" behavior:

>  // A "link-action" can post AJAX request to its "data-url"
> // Then the browser is redirect to: the "redirect" in response, or
"data-redirect" attribute, or current URL by reloading.

And enhance the "link-action" to support showing a modal dialog for
confirm. A similar general approach could also help PRs like
https://github.com/go-gitea/gitea/pull/22344#discussion_r1062883436

> // If the "link-action" has "data-modal-confirm(-html)" attribute, a
confirm modal dialog will be shown before taking action.


And a lot of duplicate code can be removed now. A good framework design
can help to avoid code copying&pasting.

---------

Co-authored-by: silverwind <me@silverwind.io>
This commit is contained in:
wxiaoguang 2023-06-13 20:10:10 +08:00 committed by GitHub
parent a51b115b0a
commit 6bbccdd177
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 86 additions and 157 deletions

View file

@ -8,7 +8,7 @@ import {svg} from '../svg.js';
import {hideElem, showElem, toggleElem} from '../utils/dom.js';
import {htmlEscape} from 'escape-goat';
const {appUrl, csrfToken} = window.config;
const {appUrl, csrfToken, i18n} = window.config;
export function initGlobalFormDirtyLeaveConfirm() {
// Warn users that try to leave a page after entering data into a form.
@ -172,6 +172,62 @@ export function initGlobalDropzone() {
}
}
function linkAction(e) {
e.preventDefault();
// A "link-action" can post AJAX request to its "data-url"
// Then the browser is redirect to: the "redirect" in response, or "data-redirect" attribute, or current URL by reloading.
// If the "link-action" has "data-modal-confirm(-html)" attribute, a confirm modal dialog will be shown before taking action.
const $this = $(e.target);
const redirect = $this.attr('data-redirect');
const request = () => {
$this.prop('disabled', true);
$.post($this.attr('data-url'), {
_csrf: csrfToken
}).done((data) => {
if (data && data.redirect) {
window.location.href = data.redirect;
} else if (redirect) {
window.location.href = redirect;
} else {
window.location.reload();
}
}).always(() => {
$this.prop('disabled', false);
});
};
const modalConfirmHtml = htmlEscape($this.attr('data-modal-confirm') || '');
if (!modalConfirmHtml) {
request();
return;
}
const okButtonColor = $this.hasClass('red') || $this.hasClass('yellow') || $this.hasClass('orange') || $this.hasClass('negative') ? 'orange' : 'green';
const $modal = $(`
<div class="ui g-modal-confirm modal">
<div class="content">${modalConfirmHtml}</div>
<div class="actions">
<button class="ui basic cancel button">${svg('octicon-x')} ${i18n.modal_cancel}</button>
<button class="ui ${okButtonColor} ok button">${svg('octicon-check')} ${i18n.modal_confirm}</button>
</div>
</div>
`);
$modal.appendTo(document.body);
$modal.modal({
onApprove() {
request();
},
onHidden() {
$modal.remove();
},
}).modal('show');
}
export function initGlobalLinkActions() {
function showDeletePopup(e) {
e.preventDefault();
@ -217,75 +273,9 @@ export function initGlobalLinkActions() {
}).modal('show');
}
function showAddAllPopup(e) {
e.preventDefault();
const $this = $(this);
let filter = '';
if ($this.attr('data-modal-id')) {
filter += `#${$this.attr('data-modal-id')}`;
}
const dialog = $(`.addall.modal${filter}`);
dialog.find('.name').text($this.data('name'));
dialog.modal({
closable: false,
onApprove() {
if ($this.data('type') === 'form') {
$($this.data('form')).trigger('submit');
return;
}
$.post($this.data('url'), {
_csrf: csrfToken,
id: $this.data('id')
}).done((data) => {
window.location.href = data.redirect;
});
}
}).modal('show');
}
function linkAction(e) {
e.preventDefault();
const $this = $(this);
const redirect = $this.data('redirect');
$this.prop('disabled', true);
$.post($this.data('url'), {
_csrf: csrfToken
}).done((data) => {
if (data.redirect) {
window.location.href = data.redirect;
} else if (redirect) {
window.location.href = redirect;
} else {
window.location.reload();
}
}).always(() => {
$this.prop('disabled', false);
});
}
// Helpers.
$('.delete-button').on('click', showDeletePopup);
$('.link-action').on('click', linkAction);
// FIXME: this function is only used once, and not common, not well designed. should be refactored later
$('.add-all-button').on('click', showAddAllPopup);
// FIXME: this is only used once, and should be replace with `link-action` instead
$('.undo-button').on('click', function () {
const $this = $(this);
$this.prop('disabled', true);
$.post($this.data('url'), {
_csrf: csrfToken,
id: $this.data('id')
}).done((data) => {
window.location.href = data.redirect;
}).always(() => {
$this.prop('disabled', false);
});
});
}
export function initGlobalButtons() {
@ -346,16 +336,6 @@ export function initGlobalButtons() {
initCompColorPicker();
}
});
$('.delete-post.button').on('click', function (e) {
e.preventDefault();
const $this = $(this);
$.post($this.attr('data-request-url'), {
_csrf: csrfToken
}).done(() => {
window.location.href = $this.attr('data-done-url');
});
});
}
/**