mirror of
https://codeberg.org/forgejo/forgejo
synced 2024-11-27 12:16:10 +01:00
a43ea22479
Co-author: @wxiaoguang Close #25096 The way to fix it in this PR is to change form submit to fetch using formData, and add flags to avoid post repeatedly. Should be able to apply to more forms that have the same issue after this PR. In the demo below, 'approve' is clicked several times, and then 'comment' is clicked several time after 'request changes' clicked. After: https://github.com/go-gitea/gitea/assets/17645053/beabeb1d-fe66-4b76-b048-4f022b4e83a0 Update: screenshots from /devtest > ![image](https://user-images.githubusercontent.com/2114189/245680011-ee4231e0-a53d-4c2a-a9c2-71ccd98005cc.png) > > ![image](https://user-images.githubusercontent.com/2114189/245680057-9215d348-63d8-406d-8828-17e171163aaa.png) > > ![image](https://user-images.githubusercontent.com/2114189/245680148-89d7b3d1-d7b6-442f-b69e-eadaee112482.png) --------- Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
201 lines
6.1 KiB
JavaScript
201 lines
6.1 KiB
JavaScript
import $ from 'jquery';
|
|
import {svg} from '../svg.js';
|
|
import {invertFileFolding} from './file-fold.js';
|
|
import {createTippy} from '../modules/tippy.js';
|
|
import {clippie} from 'clippie';
|
|
import {toAbsoluteUrl} from '../utils.js';
|
|
|
|
export const singleAnchorRegex = /^#(L|n)([1-9][0-9]*)$/;
|
|
export const rangeAnchorRegex = /^#(L[1-9][0-9]*)-(L[1-9][0-9]*)$/;
|
|
|
|
function changeHash(hash) {
|
|
if (window.history.pushState) {
|
|
window.history.pushState(null, null, hash);
|
|
} else {
|
|
window.location.hash = hash;
|
|
}
|
|
}
|
|
|
|
function selectRange($list, $select, $from) {
|
|
$list.removeClass('active');
|
|
|
|
// add hashchange to permalink
|
|
const $refInNewIssue = $('a.ref-in-new-issue');
|
|
const $copyPermalink = $('a.copy-line-permalink');
|
|
const $viewGitBlame = $('a.view_git_blame');
|
|
|
|
const updateIssueHref = function (anchor) {
|
|
if ($refInNewIssue.length === 0) {
|
|
return;
|
|
}
|
|
const urlIssueNew = $refInNewIssue.attr('data-url-issue-new');
|
|
const urlParamBodyLink = $refInNewIssue.attr('data-url-param-body-link');
|
|
const issueContent = `${toAbsoluteUrl(urlParamBodyLink)}#${anchor}`; // the default content for issue body
|
|
$refInNewIssue.attr('href', `${urlIssueNew}?body=${encodeURIComponent(issueContent)}`);
|
|
};
|
|
|
|
const updateViewGitBlameFragment = function (anchor) {
|
|
if ($viewGitBlame.length === 0) {
|
|
return;
|
|
}
|
|
let href = $viewGitBlame.attr('href');
|
|
href = `${href.replace(/#L\d+$|#L\d+-L\d+$/, '')}`;
|
|
if (anchor.length !== 0) {
|
|
href = `${href}#${anchor}`;
|
|
}
|
|
$viewGitBlame.attr('href', href);
|
|
};
|
|
|
|
const updateCopyPermalinkUrl = function(anchor) {
|
|
if ($copyPermalink.length === 0) {
|
|
return;
|
|
}
|
|
let link = $copyPermalink.attr('data-url');
|
|
link = `${link.replace(/#L\d+$|#L\d+-L\d+$/, '')}#${anchor}`;
|
|
$copyPermalink.attr('data-url', link);
|
|
};
|
|
|
|
if ($from) {
|
|
let a = parseInt($select.attr('rel').slice(1));
|
|
let b = parseInt($from.attr('rel').slice(1));
|
|
let c;
|
|
if (a !== b) {
|
|
if (a > b) {
|
|
c = a;
|
|
a = b;
|
|
b = c;
|
|
}
|
|
const classes = [];
|
|
for (let i = a; i <= b; i++) {
|
|
classes.push(`[rel=L${i}]`);
|
|
}
|
|
$list.filter(classes.join(',')).addClass('active');
|
|
changeHash(`#L${a}-L${b}`);
|
|
|
|
updateIssueHref(`L${a}-L${b}`);
|
|
updateViewGitBlameFragment(`L${a}-L${b}`);
|
|
updateCopyPermalinkUrl(`L${a}-L${b}`);
|
|
return;
|
|
}
|
|
}
|
|
$select.addClass('active');
|
|
changeHash(`#${$select.attr('rel')}`);
|
|
|
|
updateIssueHref($select.attr('rel'));
|
|
updateViewGitBlameFragment($select.attr('rel'));
|
|
updateCopyPermalinkUrl($select.attr('rel'));
|
|
}
|
|
|
|
function showLineButton() {
|
|
const menu = document.querySelector('.code-line-menu');
|
|
if (!menu) return;
|
|
|
|
// remove all other line buttons
|
|
for (const el of document.querySelectorAll('.code-line-button')) {
|
|
el.remove();
|
|
}
|
|
|
|
// find active row and add button
|
|
const tr = document.querySelector('.code-view td.lines-code.active').closest('tr');
|
|
const td = tr.querySelector('td');
|
|
const btn = document.createElement('button');
|
|
btn.classList.add('code-line-button');
|
|
btn.innerHTML = svg('octicon-kebab-horizontal');
|
|
td.prepend(btn);
|
|
|
|
// put a copy of the menu back into DOM for the next click
|
|
btn.closest('.code-view').append(menu.cloneNode(true));
|
|
|
|
createTippy(btn, {
|
|
trigger: 'click',
|
|
hideOnClick: true,
|
|
content: menu,
|
|
placement: 'right-start',
|
|
interactive: true,
|
|
onShow: (tippy) => {
|
|
tippy.popper.addEventListener('click', () => {
|
|
tippy.hide();
|
|
}, {once: true});
|
|
}
|
|
});
|
|
}
|
|
|
|
export function initRepoCodeView() {
|
|
if ($('.code-view .lines-num').length > 0) {
|
|
$(document).on('click', '.lines-num span', function (e) {
|
|
const $select = $(this);
|
|
let $list;
|
|
if ($('div.blame').length) {
|
|
$list = $('.code-view td.lines-code.blame-code');
|
|
} else {
|
|
$list = $('.code-view td.lines-code');
|
|
}
|
|
selectRange($list, $list.filter(`[rel=${$select.attr('id')}]`), (e.shiftKey ? $list.filter('.active').eq(0) : null));
|
|
|
|
if (window.getSelection) {
|
|
window.getSelection().removeAllRanges();
|
|
} else {
|
|
document.selection.empty();
|
|
}
|
|
|
|
// show code view menu marker (don't show in blame page)
|
|
if ($('div.blame').length === 0) {
|
|
showLineButton();
|
|
}
|
|
});
|
|
|
|
$(window).on('hashchange', () => {
|
|
let m = window.location.hash.match(rangeAnchorRegex);
|
|
let $list;
|
|
if ($('div.blame').length) {
|
|
$list = $('.code-view td.lines-code.blame-code');
|
|
} else {
|
|
$list = $('.code-view td.lines-code');
|
|
}
|
|
let $first;
|
|
if (m) {
|
|
$first = $list.filter(`[rel=${m[1]}]`);
|
|
if ($first.length) {
|
|
selectRange($list, $first, $list.filter(`[rel=${m[2]}]`));
|
|
|
|
// show code view menu marker (don't show in blame page)
|
|
if ($('div.blame').length === 0) {
|
|
showLineButton();
|
|
}
|
|
|
|
$('html, body').scrollTop($first.offset().top - 200);
|
|
return;
|
|
}
|
|
}
|
|
m = window.location.hash.match(singleAnchorRegex);
|
|
if (m) {
|
|
$first = $list.filter(`[rel=L${m[2]}]`);
|
|
if ($first.length) {
|
|
selectRange($list, $first);
|
|
|
|
// show code view menu marker (don't show in blame page)
|
|
if ($('div.blame').length === 0) {
|
|
showLineButton();
|
|
}
|
|
|
|
$('html, body').scrollTop($first.offset().top - 200);
|
|
}
|
|
}
|
|
}).trigger('hashchange');
|
|
}
|
|
$(document).on('click', '.fold-file', ({currentTarget}) => {
|
|
invertFileFolding(currentTarget.closest('.file-content'), currentTarget);
|
|
});
|
|
$(document).on('click', '.code-expander-button', async ({currentTarget}) => {
|
|
const url = currentTarget.getAttribute('data-url');
|
|
const query = currentTarget.getAttribute('data-query');
|
|
const anchor = currentTarget.getAttribute('data-anchor');
|
|
if (!url) return;
|
|
const blob = await $.get(`${url}?${query}&anchor=${anchor}`);
|
|
currentTarget.closest('tr').outerHTML = blob;
|
|
});
|
|
$(document).on('click', '.copy-line-permalink', async (e) => {
|
|
await clippie(toAbsoluteUrl(e.currentTarget.getAttribute('data-url')));
|
|
});
|
|
}
|