Skip to content

Commit 6a41f16

Browse files
committed
fix
1 parent 9bf821a commit 6a41f16

File tree

4 files changed

+75
-68
lines changed

4 files changed

+75
-68
lines changed

web_src/js/features/repo-diff.ts

+38-39
Original file line numberDiff line numberDiff line change
@@ -7,30 +7,19 @@ import {validateTextareaNonEmpty} from './comp/ComboMarkdownEditor.ts';
77
import {initViewedCheckboxListenerFor, countAndUpdateViewedFiles, initExpandAndCollapseFilesButton} from './pull-view-file.ts';
88
import {initImageDiff} from './imagediff.ts';
99
import {showErrorToast} from '../modules/toast.ts';
10-
import {submitEventSubmitter, queryElemSiblings, hideElem, showElem, animateOnce} from '../utils/dom.ts';
10+
import {
11+
submitEventSubmitter,
12+
queryElemSiblings,
13+
hideElem,
14+
showElem,
15+
animateOnce,
16+
addElemsEventListener, createElementFromHTML,
17+
} from '../utils/dom.ts';
1118
import {POST, GET} from '../modules/fetch.ts';
19+
import {fomanticQuery} from '../modules/fomantic/base.ts';
1220

1321
const {pageData, i18n} = window.config;
1422

15-
function initRepoDiffReviewButton() {
16-
const reviewBox = document.querySelector('#review-box');
17-
if (!reviewBox) return;
18-
19-
const counter = reviewBox.querySelector('.review-comments-counter');
20-
if (!counter) return;
21-
22-
$(document).on('click', 'button[name="pending_review"]', (e) => {
23-
const $form = $(e.target).closest('form');
24-
// Watch for the form's submit event.
25-
$form.on('submit', () => {
26-
const num = parseInt(counter.getAttribute('data-pending-comment-number')) + 1 || 1;
27-
counter.setAttribute('data-pending-comment-number', num);
28-
counter.textContent = num;
29-
animateOnce(reviewBox, 'pulse-1p5-200');
30-
});
31-
});
32-
}
33-
3423
function initRepoDiffFileViewToggle() {
3524
$('.file-view-toggle').on('click', function () {
3625
for (const el of queryElemSiblings(this)) {
@@ -47,19 +36,15 @@ function initRepoDiffFileViewToggle() {
4736
}
4837

4938
function initRepoDiffConversationForm() {
50-
$(document).on('submit', '.conversation-holder form', async (e) => {
39+
addElemsEventListener<HTMLFormElement>(document, 'submit', '.conversation-holder form', async (form, e) => {
5140
e.preventDefault();
41+
const textArea = form.querySelector('textarea');
42+
if (!validateTextareaNonEmpty(textArea)) return;
43+
if (form.classList.contains('is-loading')) return;
5244

53-
const $form = $(e.target);
54-
const textArea = e.target.querySelector('textarea');
55-
if (!validateTextareaNonEmpty(textArea)) {
56-
return;
57-
}
58-
59-
if (e.target.classList.contains('is-loading')) return;
6045
try {
61-
e.target.classList.add('is-loading');
62-
const formData = new FormData($form[0]);
46+
form.classList.add('is-loading');
47+
const formData = new FormData(form);
6348

6449
// if the form is submitted by a button, append the button's name and value to the form data
6550
const submitter = submitEventSubmitter(e);
@@ -68,26 +53,41 @@ function initRepoDiffConversationForm() {
6853
formData.append(submitter.name, submitter.value);
6954
}
7055

71-
const response = await POST(e.target.getAttribute('action'), {data: formData});
72-
const $newConversationHolder = $(await response.text());
73-
const {path, side, idx} = $newConversationHolder.data();
56+
const trLineType = form.closest('tr').getAttribute('data-line-type');
57+
const response = await POST(form.getAttribute('action'), {data: formData});
58+
const newConversationHolder = createElementFromHTML(await response.text());
59+
const path = newConversationHolder.getAttribute('data-path');
60+
const side = newConversationHolder.getAttribute('data-side');
61+
const idx = newConversationHolder.getAttribute('data-idx');
62+
63+
form.closest('.conversation-holder').replaceWith(newConversationHolder);
64+
form = null; // prevent further usage of the form because it should have been replaced
7465

75-
$form.closest('.conversation-holder').replaceWith($newConversationHolder);
7666
let selector;
77-
if ($form.closest('tr').data('line-type') === 'same') {
67+
if (trLineType === 'same') {
7868
selector = `[data-path="${path}"] .add-code-comment[data-idx="${idx}"]`;
7969
} else {
8070
selector = `[data-path="${path}"] .add-code-comment[data-side="${side}"][data-idx="${idx}"]`;
8171
}
8272
for (const el of document.querySelectorAll(selector)) {
83-
el.classList.add('tw-invisible');
73+
el.classList.add('tw-invisible'); // TODO need to figure out why
74+
}
75+
fomanticQuery(newConversationHolder.querySelectorAll('.ui.dropdown')).dropdown();
76+
77+
if (submitter?.matches('button[name="pending_review"]')) {
78+
const reviewBox = document.querySelector('#review-box');
79+
const counter = reviewBox?.querySelector('.review-comments-counter');
80+
if (!counter) return;
81+
const num = parseInt(counter.getAttribute('data-pending-comment-number')) + 1 || 1;
82+
counter.setAttribute('data-pending-comment-number', String(num));
83+
counter.textContent = String(num);
84+
animateOnce(reviewBox, 'pulse-1p5-200');
8485
}
85-
$newConversationHolder.find('.dropdown').dropdown();
8686
} catch (error) {
8787
console.error('Error:', error);
8888
showErrorToast(i18n.network_error);
8989
} finally {
90-
e.target.classList.remove('is-loading');
90+
form?.classList.remove('is-loading');
9191
}
9292
});
9393

@@ -219,7 +219,6 @@ export function initRepoDiffView() {
219219
initDiffFileList();
220220
initDiffCommitSelect();
221221
initRepoDiffShowMore();
222-
initRepoDiffReviewButton();
223222
initRepoDiffFileViewToggle();
224223
initViewedCheckboxListenerFor();
225224
initExpandAndCollapseFilesButton();

web_src/js/features/repo-issue.ts

+20-28
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import $ from 'jquery';
22
import {htmlEscape} from 'escape-goat';
33
import {createTippy, showTemporaryTooltip} from '../modules/tippy.ts';
4-
import {hideElem, showElem, toggleElem} from '../utils/dom.ts';
4+
import {addElemsEventListener, createElementFromHTML, hideElem, showElem, toggleElem} from '../utils/dom.ts';
55
import {setFileFolding} from './file-fold.ts';
66
import {ComboMarkdownEditor, getComboMarkdownEditor, initComboMarkdownEditor} from './comp/ComboMarkdownEditor.ts';
77
import {parseIssuePageInfo, toAbsoluteUrl} from '../utils.ts';
@@ -443,21 +443,19 @@ export function initRepoPullRequestReview() {
443443
});
444444
}
445445

446-
$(document).on('click', '.add-code-comment', async function (e) {
447-
if (e.target.classList.contains('btn-add-single')) return; // https://github.com/go-gitea/gitea/issues/4745
446+
addElemsEventListener(document, 'click', '.add-code-comment', async (el, e) => {
448447
e.preventDefault();
449448

450-
const isSplit = this.closest('.code-diff')?.classList.contains('code-diff-split');
451-
const side = this.getAttribute('data-side');
452-
const idx = this.getAttribute('data-idx');
453-
const path = this.closest('[data-path]')?.getAttribute('data-path');
454-
const tr = this.closest('tr');
449+
const isSplit = el.closest('.code-diff')?.classList.contains('code-diff-split');
450+
const side = el.getAttribute('data-side');
451+
const idx = el.getAttribute('data-idx');
452+
const path = el.closest('[data-path]')?.getAttribute('data-path');
453+
const tr = el.closest('tr');
455454
const lineType = tr.getAttribute('data-line-type');
456455

457-
const ntr = tr.nextElementSibling;
458-
let $ntr = $(ntr);
456+
let ntr = tr.nextElementSibling;
459457
if (!ntr?.classList.contains('add-comment')) {
460-
$ntr = $(`
458+
ntr = createElementFromHTML(`
461459
<tr class="add-comment" data-line-type="${lineType}">
462460
${isSplit ? `
463461
<td class="add-comment-left" colspan="4"></td>
@@ -466,24 +464,18 @@ export function initRepoPullRequestReview() {
466464
<td class="add-comment-left add-comment-right" colspan="5"></td>
467465
`}
468466
</tr>`);
469-
$(tr).after($ntr);
467+
tr.after(ntr);
470468
}
471-
472-
const $td = $ntr.find(`.add-comment-${side}`);
473-
const $commentCloud = $td.find('.comment-code-cloud');
474-
if (!$commentCloud.length && !$ntr.find('button[name="pending_review"]').length) {
475-
try {
476-
const response = await GET(this.closest('[data-new-comment-url]')?.getAttribute('data-new-comment-url'));
477-
const html = await response.text();
478-
$td.html(html);
479-
$td.find("input[name='line']").val(idx);
480-
$td.find("input[name='side']").val(side === 'left' ? 'previous' : 'proposed');
481-
$td.find("input[name='path']").val(path);
482-
const editor = await initComboMarkdownEditor($td[0].querySelector('.combo-markdown-editor'));
483-
editor.focus();
484-
} catch (error) {
485-
console.error(error);
486-
}
469+
const td = ntr.querySelector(`.add-comment-${side}`);
470+
const commentCloud = td.querySelector('.comment-code-cloud');
471+
if (!commentCloud && !ntr.querySelector('button[name="pending_review"]')) {
472+
const response = await GET(el.closest('[data-new-comment-url]')?.getAttribute('data-new-comment-url'));
473+
td.innerHTML = await response.text();
474+
td.querySelector<HTMLInputElement>("input[name='line']").value = idx;
475+
td.querySelector<HTMLInputElement>("input[name='side']").value = (side === 'left' ? 'previous' : 'proposed');
476+
td.querySelector<HTMLInputElement>("input[name='path']").value = path;
477+
const editor = await initComboMarkdownEditor(td.querySelector<HTMLElement>('.combo-markdown-editor'));
478+
editor.focus();
487479
}
488480
});
489481
}

web_src/js/utils/dom.test.ts

+1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import {createElementFromAttrs, createElementFromHTML, querySingleVisibleElem} f
22

33
test('createElementFromHTML', () => {
44
expect(createElementFromHTML('<a>foo<span>bar</span></a>').outerHTML).toEqual('<a>foo<span>bar</span></a>');
5+
expect(createElementFromHTML('<tr data-x="1"><td>foo</td></tr>').outerHTML).toEqual('<tr data-x="1"><td>foo</td></tr>');
56
});
67

78
test('createElementFromAttrs', () => {

web_src/js/utils/dom.ts

+16-1
Original file line numberDiff line numberDiff line change
@@ -302,8 +302,15 @@ export function replaceTextareaSelection(textarea: HTMLTextAreaElement, text: st
302302

303303
// Warning: Do not enter any unsanitized variables here
304304
export function createElementFromHTML(htmlString: string): HTMLElement {
305+
htmlString = htmlString.trim();
306+
// some tags like "tr" are special, it must use a correct parent container to create
307+
if (htmlString.startsWith('<tr')) {
308+
const container = document.createElement('table');
309+
container.innerHTML = htmlString;
310+
return container.querySelector('tr') as HTMLElement;
311+
}
305312
const div = document.createElement('div');
306-
div.innerHTML = htmlString.trim();
313+
div.innerHTML = htmlString;
307314
return div.firstChild as HTMLElement;
308315
}
309316

@@ -340,3 +347,11 @@ export function querySingleVisibleElem<T extends HTMLElement>(parent: Element, s
340347
if (candidates.length > 1) throw new Error(`Expected exactly one visible element matching selector "${selector}", but found ${candidates.length}`);
341348
return candidates.length ? candidates[0] as T : null;
342349
}
350+
351+
export function addElemsEventListener<T extends HTMLElement>(parent: Node, type: string, selector: string, listener: (elem: T, e: Event) => void | Promise<any>, options?: boolean | AddEventListenerOptions) {
352+
parent.addEventListener(type, (e: Event) => {
353+
const elem = (e.target as HTMLElement).closest(selector);
354+
if (!elem) return;
355+
listener(elem as T, e);
356+
}, options);
357+
}

0 commit comments

Comments
 (0)