From ff0c9a04a5b21d970b3f6996bcac674308fc2612 Mon Sep 17 00:00:00 2001 From: silverwind Date: Mon, 15 Apr 2024 22:16:05 +0200 Subject: [PATCH 01/19] Remove jQuery `.text()` --- .eslintrc.yaml | 4 ++-- web_src/js/features/common-global.js | 6 +++--- web_src/js/features/imagediff.js | 2 +- web_src/js/features/notification.js | 13 +++++++------ web_src/js/features/repo-editor.js | 8 ++++---- web_src/js/features/repo-issue-edit.js | 2 +- web_src/js/features/repo-issue.js | 14 +++++++------- web_src/js/features/repo-legacy.js | 6 +++--- web_src/js/features/repo-settings.js | 6 +++--- 9 files changed, 31 insertions(+), 30 deletions(-) diff --git a/.eslintrc.yaml b/.eslintrc.yaml index 3e4c6ea50b722..24a0aa1907a6c 100644 --- a/.eslintrc.yaml +++ b/.eslintrc.yaml @@ -321,7 +321,7 @@ rules: jquery/no-sizzle: [2] jquery/no-slide: [0] jquery/no-submit: [0] - jquery/no-text: [0] + jquery/no-text: [2] jquery/no-toggle: [2] jquery/no-trigger: [0] jquery/no-trim: [2] @@ -474,7 +474,7 @@ rules: no-jquery/no-slide: [2] no-jquery/no-sub: [2] no-jquery/no-support: [2] - no-jquery/no-text: [0] + no-jquery/no-text: [2] no-jquery/no-trigger: [0] no-jquery/no-trim: [2] no-jquery/no-type: [2] diff --git a/web_src/js/features/common-global.js b/web_src/js/features/common-global.js index e7db9b23364bb..71c1500d67088 100644 --- a/web_src/js/features/common-global.js +++ b/web_src/js/features/common-global.js @@ -303,10 +303,10 @@ export function initGlobalLinkActions() { } const $dialog = $(`.delete.modal${filter}`); - $dialog.find('.name').text($this.data('name')); + $dialog.find('.name')[0].textContent = $this.data('name'); for (const [key, value] of Object.entries(dataArray)) { if (key && key.startsWith('data')) { - $dialog.find(`.${key}`).text(value); + $dialog.find(`.${key}`)[0].textContent = value; } } @@ -373,7 +373,7 @@ function initGlobalShowModal() { } else if ($attrTarget[0].matches('input, textarea')) { $attrTarget.val(attrib.value); // FIXME: add more supports like checkbox } else { - $attrTarget.text(attrib.value); // FIXME: it should be more strict here, only handle div/span/p + $attrTarget[0].textContent = attrib.value; // FIXME: it should be more strict here, only handle div/span/p } } diff --git a/web_src/js/features/imagediff.js b/web_src/js/features/imagediff.js index 192a64283473b..28ae4a50b78b4 100644 --- a/web_src/js/features/imagediff.js +++ b/web_src/js/features/imagediff.js @@ -92,7 +92,7 @@ export function initImageDiff() { return loadElem(img, info.path); })); // only the first images is associated with $boundsInfo - if (!success) info.$boundsInfo.text('(image error)'); + if (!success) info.$boundsInfo[0].textContent = '(image error)'; if (info.mime === 'image/svg+xml') { const resp = await GET(info.path); const text = await resp.text(); diff --git a/web_src/js/features/notification.js b/web_src/js/features/notification.js index 2de640e6742c0..ce56de9604308 100644 --- a/web_src/js/features/notification.js +++ b/web_src/js/features/notification.js @@ -47,16 +47,13 @@ async function receiveUpdateCount(event) { export function initNotificationCount() { const $notificationCount = $('.notification_count'); - - if (!$notificationCount.length) { - return; - } + if (!$notificationCount.length) return; let usingPeriodicPoller = false; const startPeriodicPoller = (timeout, lastCount) => { if (timeout <= 0 || !Number.isFinite(timeout)) return; usingPeriodicPoller = true; - lastCount = lastCount ?? $notificationCount.text(); + lastCount = lastCount ?? getCurrentCount(); setTimeout(async () => { await updateNotificationCountWithCallback(startPeriodicPoller, timeout, lastCount); }, timeout); @@ -120,8 +117,12 @@ export function initNotificationCount() { startPeriodicPoller(notificationSettings.MinTimeout); } +function getCurrentCount() { + return document.querySelector('.notification_count').textContent; +} + async function updateNotificationCountWithCallback(callback, timeout, lastCount) { - const currentCount = $('.notification_count').text(); + const currentCount = getCurrentCount(); if (lastCount !== currentCount) { callback(notificationSettings.MinTimeout, currentCount); return; diff --git a/web_src/js/features/repo-editor.js b/web_src/js/features/repo-editor.js index 01dc4b95aaa47..eade8aaea579e 100644 --- a/web_src/js/features/repo-editor.js +++ b/web_src/js/features/repo-editor.js @@ -70,7 +70,7 @@ export function initRepoEditor() { hideElem('.quick-pull-branch-name'); document.querySelector('.quick-pull-branch-name input').required = false; } - $('#commit-button').text(this.getAttribute('button_text')); + $('#commit-button')[0].textContent = this.getAttribute('button_text'); }); const joinTreePath = ($fileNameEl) => { @@ -78,9 +78,9 @@ export function initRepoEditor() { $('.breadcrumb span.section').each(function () { const $element = $(this); if ($element.find('a').length) { - parts.push($element.find('a').text()); + parts.push($element.find('a')[0].textContent); } else { - parts.push($element.text()); + parts.push($element[0].textContent); } }); if ($fileNameEl.val()) parts.push($fileNameEl.val()); @@ -116,7 +116,7 @@ export function initRepoEditor() { if (e.code === 'Backspace' && getCursorPosition($(this)) === 0 && $section.length > 0) { e.preventDefault(); const $divider = $('.breadcrumb .breadcrumb-divider'); - const value = $section.last().find('a').text(); + const value = $section.last().find('a')[0].textContent; $(this).val(value + $(this).val()); this.setSelectionRange(value.length, value.length); $section.last().remove(); diff --git a/web_src/js/features/repo-issue-edit.js b/web_src/js/features/repo-issue-edit.js index 4c03325c7a5f6..ef5a277c6b8a9 100644 --- a/web_src/js/features/repo-issue-edit.js +++ b/web_src/js/features/repo-issue-edit.js @@ -183,7 +183,7 @@ export function initRepoIssueCommentEdit() { $(document).on('click', '.quote-reply', async function (event) { event.preventDefault(); const target = $(this).data('target'); - const quote = $(`#${target}`).text().replace(/\n/g, '\n> '); + const quote = $(`#${target}`)[0].textContent.replace(/\n/g, '\n> '); const content = `> ${quote}\n\n`; let editor; if ($(this).hasClass('quote-reply-diff')) { diff --git a/web_src/js/features/repo-issue.js b/web_src/js/features/repo-issue.js index 2b2eed58bbfb3..9cd623b77e382 100644 --- a/web_src/js/features/repo-issue.js +++ b/web_src/js/features/repo-issue.js @@ -281,7 +281,7 @@ export function initRepoPullRequestUpdate() { if (url) { const buttonText = pullUpdateButton.querySelector('.button-text'); if (buttonText) { - buttonText.textContent = $choice.text(); + buttonText.textContent = $choice[0].textContent; } pullUpdateButton.setAttribute('data-do', url); } @@ -566,7 +566,7 @@ export function initRepoIssueReferenceIssue() { // Reference issue $(document).on('click', '.reference-issue', function (event) { const $this = $(this); - const content = $(`#${$this.data('target')}`).text(); + const content = $(`#${$this.data('target')}`)[0].textContent; const poster = $this.data('poster-username'); const reference = toAbsoluteUrl($this.data('reference')); const $modal = $($this.data('modal')); @@ -603,8 +603,7 @@ export function initRepoIssueWipToggle() { async function pullrequest_targetbranch_change(update_url) { const targetBranch = $('#pull-target-branch').data('branch'); - const $branchTarget = $('#branch_target'); - if (targetBranch === $branchTarget.text()) { + if (targetBranch === $('#branch_target')[0].textContent) { window.location.reload(); return false; } @@ -640,8 +639,9 @@ export function initRepoIssueTitleEdit() { $('#cancel-edit-title').on('click', editTitleToggle); $('#save-edit-title').on('click', editTitleToggle).on('click', async function () { const pullrequest_target_update_url = this.getAttribute('data-target-update-url'); - if (!$editInput.val().length || $editInput.val() === $issueTitle.text()) { - $editInput.val($issueTitle.text()); + const titleText = $issueTitle[0].textContent; + if (!$editInput.val().length || $editInput.val() === titleText) { + $editInput.val(titleText); await pullrequest_targetbranch_change(pullrequest_target_update_url); } else { try { @@ -650,7 +650,7 @@ export function initRepoIssueTitleEdit() { const response = await POST(this.getAttribute('data-update-url'), {data: params}); const data = await response.json(); $editInput.val(data.title); - $issueTitle.text(data.title); + $issueTitle[0].textContent = data.title; if (pullrequest_target_update_url) { await pullrequest_targetbranch_change(pullrequest_target_update_url); // it will reload the window } else { diff --git a/web_src/js/features/repo-legacy.js b/web_src/js/features/repo-legacy.js index 18d98c891d066..e855901f7a8a6 100644 --- a/web_src/js/features/repo-legacy.js +++ b/web_src/js/features/repo-legacy.js @@ -64,7 +64,7 @@ export function initRepoCommentForm() { const editMode = $('#editing_mode').val(); $($(this).data('id-selector')).val(selectedValue); if ($isNewIssue) { - $selectBranch.find('.ui .branch-name').text($(this).data('name')); + $selectBranch.find('.ui .branch-name')[0].textContent = $(this).data('name'); return; } @@ -79,7 +79,7 @@ export function initRepoCommentForm() { console.error(error); } } else if (editMode === '') { - $selectBranch.find('.ui .branch-name').text(selectedValue); + $selectBranch.find('.ui .branch-name')[0].textContent = selectedValue; } }); $selectBranch.find('.reference.column').on('click', function () { @@ -275,7 +275,7 @@ export function initRepoCommentForm() { $list.find('.selected').html(` ${icon} - ${htmlEscape($(this).text())} + ${htmlEscape(this.textContent)} `); diff --git a/web_src/js/features/repo-settings.js b/web_src/js/features/repo-settings.js index 52c5de2bfaea2..f4fd94670b1ca 100644 --- a/web_src/js/features/repo-settings.js +++ b/web_src/js/features/repo-settings.js @@ -22,12 +22,12 @@ export function initRepoSettingsCollaboration() { data.append('mode', value); await POST(el.getAttribute('data-url'), {data}); } catch { - $text.text('(error)'); // prevent from misleading users when error occurs + $text[0].textContent = '(error)'; // prevent from misleading users when error occurs el.setAttribute('data-last-value', lastValue); } }, onChange(_value, text, _$choice) { - $text.text(text); // update the text when using keyboard navigating + $text[0].textContent = text; // update the text when using keyboard navigating }, onHide() { // set to the really selected value, defer to next tick to make sure `action` has finished its work because the calling order might be onHide -> action @@ -36,7 +36,7 @@ export function initRepoSettingsCollaboration() { if ($item) { $dropdown.dropdown('set selected', el.getAttribute('data-last-value')); } else { - $text.text('(none)'); // prevent from misleading users when the access mode is undefined + $text[0].textContent = '(none)'; // prevent from misleading users when the access mode is undefined } }, 0); }, From d3476cf848522a5e4b4862f362409e5889322f7d Mon Sep 17 00:00:00 2001 From: silverwind Date: Thu, 6 Jun 2024 19:14:23 +0200 Subject: [PATCH 02/19] refactor and fix imagediff --- web_src/js/features/imagediff.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/web_src/js/features/imagediff.js b/web_src/js/features/imagediff.js index 2addb91d27d9f..2d28b4b526aff 100644 --- a/web_src/js/features/imagediff.js +++ b/web_src/js/features/imagediff.js @@ -79,20 +79,20 @@ export function initImageDiff() { path: this.getAttribute('data-path-after'), mime: this.getAttribute('data-mime-after'), $images: $container.find('img.image-after'), // matches 3 - $boundsInfo: $container.find('.bounds-info-after'), + boundsInfo: this.querySelector('.bounds-info-after'), }, { path: this.getAttribute('data-path-before'), mime: this.getAttribute('data-mime-before'), $images: $container.find('img.image-before'), // matches 3 - $boundsInfo: $container.find('.bounds-info-before'), + boundsInfo: this.querySelector('.bounds-info-before'), }]; await Promise.all(imageInfos.map(async (info) => { const [success] = await Promise.all(Array.from(info.$images, (img) => { return loadElem(img, info.path); })); - // only the first images is associated with $boundsInfo - if (!success) info.$boundsInfo[0].textContent = '(image error)'; + // only the first images is associated with boundsInfo + if (!success && info.boundsInfo) info.boundsInfo.textContent = '(image error)'; if (info.mime === 'image/svg+xml') { const resp = await GET(info.path); const text = await resp.text(); @@ -102,7 +102,7 @@ export function initImageDiff() { this.setAttribute('width', bounds.width); this.setAttribute('height', bounds.height); }); - hideElem(info.$boundsInfo); + hideElem(info.boundsInfo); } } })); From b5b0455b8e1f5b8efb9e19f9a61bc8a0eea01176 Mon Sep 17 00:00:00 2001 From: silverwind Date: Thu, 6 Jun 2024 19:18:11 +0200 Subject: [PATCH 03/19] refactor initNotificationCount --- web_src/js/features/notification.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/web_src/js/features/notification.js b/web_src/js/features/notification.js index 87582dbd91dbc..f045879dec1aa 100644 --- a/web_src/js/features/notification.js +++ b/web_src/js/features/notification.js @@ -47,8 +47,7 @@ async function receiveUpdateCount(event) { } export function initNotificationCount() { - const $notificationCount = $('.notification_count'); - if (!$notificationCount.length) return; + if (!document.querySelector('.notification_count')) return; let usingPeriodicPoller = false; const startPeriodicPoller = (timeout, lastCount) => { From d3c1299cc95bcd65fdd046c569a886a3b5c60394 Mon Sep 17 00:00:00 2001 From: silverwind Date: Thu, 6 Jun 2024 19:23:40 +0200 Subject: [PATCH 04/19] refactor joinTreePath --- web_src/js/features/repo-editor.js | 33 +++++++++++++++--------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/web_src/js/features/repo-editor.js b/web_src/js/features/repo-editor.js index 75d98583cd187..971bfb509fee8 100644 --- a/web_src/js/features/repo-editor.js +++ b/web_src/js/features/repo-editor.js @@ -61,6 +61,21 @@ function getCursorPosition($e) { return pos; } +function joinTreePath(fileNameEl) { + const parts = []; + + for (const el of document.querySelectorAll('.breadcrumb span.section')) { + const link = el.querySelector('a'); + parts.push(link ? link.textContent : el.textContent); + } + + if (fileNameEl.value) { + parts.push(fileNameEl.value); + } + + document.querySelector('#tree_path').value = parts.join('/'); +} + export function initRepoEditor() { initEditorForm(); @@ -75,20 +90,6 @@ export function initRepoEditor() { $('#commit-button')[0].textContent = this.getAttribute('button_text'); }); - const joinTreePath = ($fileNameEl) => { - const parts = []; - $('.breadcrumb span.section').each(function () { - const $element = $(this); - if ($element.find('a').length) { - parts.push($element.find('a')[0].textContent); - } else { - parts.push($element[0].textContent); - } - }); - if ($fileNameEl.val()) parts.push($fileNameEl.val()); - $('#tree_path').val(parts.join('/')); - }; - const $editFilename = $('#file-name'); $editFilename.on('input', function () { const parts = $(this).val().split('/'); @@ -108,7 +109,7 @@ export function initRepoEditor() { } } - joinTreePath($(this)); + joinTreePath(this); }); $editFilename.on('keydown', function (e) { @@ -123,7 +124,7 @@ export function initRepoEditor() { this.setSelectionRange(value.length, value.length); $section.last().remove(); $divider.last().remove(); - joinTreePath($(this)); + joinTreePath(this); } }); From 068b2751c6b50f31aa6bd2f9e26f431d04c9e736 Mon Sep 17 00:00:00 2001 From: silverwind Date: Thu, 6 Jun 2024 19:29:24 +0200 Subject: [PATCH 05/19] refactor quote reply --- web_src/js/features/repo-issue-edit.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/web_src/js/features/repo-issue-edit.js b/web_src/js/features/repo-issue-edit.js index cad5cfe9e0bbc..29b96f51276e9 100644 --- a/web_src/js/features/repo-issue-edit.js +++ b/web_src/js/features/repo-issue-edit.js @@ -189,11 +189,12 @@ export function initRepoIssueCommentEdit() { // Quote reply $(document).on('click', '.quote-reply', async function (event) { event.preventDefault(); - const target = $(this).data('target'); - const quote = $(`#${target}`)[0].textContent.replace(/\n/g, '\n> '); + const target = this.getAttribute('data-target'); + const quote = document.querySelector(`#${target}`).textContent.replace(/\n/g, '\n> '); const content = `> ${quote}\n\n`; + let editor; - if ($(this).hasClass('quote-reply-diff')) { + if (this.classList.contains('quote-reply-diff')) { const $replyBtn = $(this).closest('.comment-code-cloud').find('button.comment-form-reply'); editor = await handleReply($replyBtn); } else { From 70eb298ec91f231579848852ea55876e77664eca Mon Sep 17 00:00:00 2001 From: silverwind Date: Thu, 6 Jun 2024 19:32:20 +0200 Subject: [PATCH 06/19] refactor pull update button --- web_src/js/features/repo-issue.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/web_src/js/features/repo-issue.js b/web_src/js/features/repo-issue.js index 983862685f3d7..dde4a01d88fff 100644 --- a/web_src/js/features/repo-issue.js +++ b/web_src/js/features/repo-issue.js @@ -278,11 +278,12 @@ export function initRepoPullRequestUpdate() { $('.update-button > .dropdown').dropdown({ onChange(_text, _value, $choice) { - const url = $choice[0].getAttribute('data-do'); + const choiceEl = $choice[0]; + const url = choiceEl.getAttribute('data-do'); if (url) { const buttonText = pullUpdateButton.querySelector('.button-text'); if (buttonText) { - buttonText.textContent = $choice[0].textContent ?? ''; + buttonText.textContent = choiceEl.textContent ?? ''; } pullUpdateButton.setAttribute('data-do', url); } From 8717f0596aa152beec3cde2bcf835f538b536ea2 Mon Sep 17 00:00:00 2001 From: silverwind Date: Thu, 6 Jun 2024 19:39:10 +0200 Subject: [PATCH 07/19] refactor issue reference --- web_src/js/features/repo-issue.js | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/web_src/js/features/repo-issue.js b/web_src/js/features/repo-issue.js index dde4a01d88fff..91b134a85eb3c 100644 --- a/web_src/js/features/repo-issue.js +++ b/web_src/js/features/repo-issue.js @@ -568,14 +568,15 @@ export function initRepoPullRequestReview() { export function initRepoIssueReferenceIssue() { // Reference issue $(document).on('click', '.reference-issue', function (event) { - const $this = $(this); - const content = document.querySelector(`#${$this.data('target')}`)?.textContent ?? ''; - const poster = $this.data('poster-username'); - const reference = toAbsoluteUrl($this.data('reference')); - const $modal = $($this.data('modal')); - $modal.find('textarea[name="content"]').val(`${content}\n\n_Originally posted by @${poster} in ${reference}_`); - $modal.modal('show'); - + const target = this.getAttribute('data-target'); + const content = document.querySelector(`#${target}`)?.textContent ?? ''; + const poster = this.getAttribute('data-poster-username'); + const reference = toAbsoluteUrl(this.getAttribute('data-reference')); + const modalSelector = this.getAttribute('data-modal'); + const modal = document.querySelector(modalSelector); + const textarea = modal.querySelector('textarea[name="content"]'); + textarea.value = `${content}\n\n_Originally posted by @${poster} in ${reference}_`; + $(modal).modal('show'); event.preventDefault(); }); } From 607de374358843e28fcffa19233189be581ce8c2 Mon Sep 17 00:00:00 2001 From: silverwind Date: Thu, 6 Jun 2024 19:46:07 +0200 Subject: [PATCH 08/19] refactor collaborator access dropdown --- web_src/js/features/repo-settings.js | 32 ++++++++++++++-------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/web_src/js/features/repo-settings.js b/web_src/js/features/repo-settings.js index f4fd94670b1ca..fff9996d8b146 100644 --- a/web_src/js/features/repo-settings.js +++ b/web_src/js/features/repo-settings.js @@ -8,35 +8,35 @@ const {appSubUrl, csrfToken} = window.config; export function initRepoSettingsCollaboration() { // Change collaborator access mode - $('.page-content.repository .ui.dropdown.access-mode').each((_, el) => { - const $dropdown = $(el); - const $text = $dropdown.find('> .text'); - $dropdown.dropdown({ + $('.page-content.repository .ui.dropdown.access-mode').each((_, dropdownEl) => { + const textEl = dropdownEl.querySelector(':scope > .text'); + $(dropdownEl).dropdown({ async action(_text, value) { - const lastValue = el.getAttribute('data-last-value'); + const lastValue = dropdownEl.getAttribute('data-last-value'); try { - el.setAttribute('data-last-value', value); - $dropdown.dropdown('hide'); + dropdownEl.setAttribute('data-last-value', value); + $(dropdownEl).dropdown('hide'); const data = new FormData(); - data.append('uid', el.getAttribute('data-uid')); + data.append('uid', dropdownEl.getAttribute('data-uid')); data.append('mode', value); - await POST(el.getAttribute('data-url'), {data}); + await POST(dropdownEl.getAttribute('data-url'), {data}); } catch { - $text[0].textContent = '(error)'; // prevent from misleading users when error occurs - el.setAttribute('data-last-value', lastValue); + textEl.textContent = '(error)'; // prevent from misleading users when error occurs + dropdownEl.setAttribute('data-last-value', lastValue); } }, onChange(_value, text, _$choice) { - $text[0].textContent = text; // update the text when using keyboard navigating + textEl.textContent = text; // update the text when using keyboard navigating }, onHide() { - // set to the really selected value, defer to next tick to make sure `action` has finished its work because the calling order might be onHide -> action + // set to the really selected value, defer to next tick to make sure `action` has finished + // its work because the calling order might be onHide -> action setTimeout(() => { - const $item = $dropdown.dropdown('get item', el.getAttribute('data-last-value')); + const $item = $(dropdownEl).dropdown('get item', dropdownEl.getAttribute('data-last-value')); if ($item) { - $dropdown.dropdown('set selected', el.getAttribute('data-last-value')); + $(dropdownEl).dropdown('set selected', dropdownEl.getAttribute('data-last-value')); } else { - $text[0].textContent = '(none)'; // prevent from misleading users when the access mode is undefined + textEl.textContent = '(none)'; // prevent from misleading users when the access mode is undefined } }, 0); }, From c449f5b3ce20ac36e10d3a700a185ef70b37859e Mon Sep 17 00:00:00 2001 From: silverwind Date: Thu, 6 Jun 2024 19:47:33 +0200 Subject: [PATCH 09/19] remove unused arg --- web_src/js/features/repo-settings.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web_src/js/features/repo-settings.js b/web_src/js/features/repo-settings.js index fff9996d8b146..9204e903b8680 100644 --- a/web_src/js/features/repo-settings.js +++ b/web_src/js/features/repo-settings.js @@ -25,7 +25,7 @@ export function initRepoSettingsCollaboration() { dropdownEl.setAttribute('data-last-value', lastValue); } }, - onChange(_value, text, _$choice) { + onChange(_value, text) { textEl.textContent = text; // update the text when using keyboard navigating }, onHide() { From 4d0ada03f53566cee200b4524a0f30d591734a77 Mon Sep 17 00:00:00 2001 From: silverwind Date: Thu, 6 Jun 2024 20:07:26 +0200 Subject: [PATCH 10/19] wip on showDeletePopup --- web_src/js/features/common-global.js | 43 ++++++++++++++++------------ 1 file changed, 25 insertions(+), 18 deletions(-) diff --git a/web_src/js/features/common-global.js b/web_src/js/features/common-global.js index 1d477a87b84bd..d62b8e470e281 100644 --- a/web_src/js/features/common-global.js +++ b/web_src/js/features/common-global.js @@ -304,39 +304,46 @@ async function linkAction(e) { export function initGlobalLinkActions() { function showDeletePopup(e) { e.preventDefault(); - const $this = $(this); - const dataArray = $this.data(); - let filter = ''; - if (this.getAttribute('data-modal-id')) { - filter += `#${this.getAttribute('data-modal-id')}`; + const modalId = this.getAttribute('data-modal-id'); + const modal = document.querySelector(`.delete.modal${modalId ?? ''}`); + const modalNameEl = modal.querySelector('.name'); + if (modalNameEl) { + modalNameEl.textContent = this.getAttribute('name'); } - const $dialog = $(`.delete.modal${filter}`); - $dialog.find('.name')[0].textContent = $this.data('name'); - for (const [key, value] of Object.entries(dataArray)) { - if (key && key.startsWith('data')) { - $dialog.find(`.${key}`)[0].textContent = value; + for (let i = 0; i < this.attributes.length; i++) { + const {name, value} = this.attributes[i]; + if (name?.startsWith('data-')) { + const nameEl = modal.querySelector(`.${name}`); + if (nameEl) { + nameEl.textContent = value; + } } } - $dialog.modal({ + $(modal).modal({ closable: false, onApprove: async () => { - if ($this.data('type') === 'form') { - $($this.data('form')).trigger('submit'); + if (modal.getAttribute('data-type') === 'form') { + const formSelector = this.getAttribute('data-form'); + const form = document.querySelector(formSelector); + if (form) { + form.submit(); + } return; } const postData = new FormData(); - for (const [key, value] of Object.entries(dataArray)) { - if (key && key.startsWith('data')) { - postData.append(key.slice(4), value); + for (let i = 0; i < this.attributes.length; i++) { + const {name, value} = this.attributes[i]; + if (name?.startsWith('data-')) { + postData.append(name.slice(4), value); } - if (key === 'id') { + if (name === 'id') { postData.append('id', value); } } - const response = await POST($this.data('url'), {data: postData}); + const response = await POST(this.getAttribute('data-url'), {data: postData}); if (response.ok) { const data = await response.json(); window.location.href = data.redirect; From a9ccad7cb477b0d01104c44e17948ee553e225eb Mon Sep 17 00:00:00 2001 From: silverwind Date: Fri, 7 Jun 2024 00:52:01 +0200 Subject: [PATCH 11/19] fix up showDeletePopup --- web_src/js/features/common-global.js | 86 ++++++++++++++-------------- 1 file changed, 42 insertions(+), 44 deletions(-) diff --git a/web_src/js/features/common-global.js b/web_src/js/features/common-global.js index d62b8e470e281..060ec68811bb2 100644 --- a/web_src/js/features/common-global.js +++ b/web_src/js/features/common-global.js @@ -302,58 +302,56 @@ async function linkAction(e) { } export function initGlobalLinkActions() { - function showDeletePopup(e) { - e.preventDefault(); - const modalId = this.getAttribute('data-modal-id'); - const modal = document.querySelector(`.delete.modal${modalId ?? ''}`); - const modalNameEl = modal.querySelector('.name'); - if (modalNameEl) { - modalNameEl.textContent = this.getAttribute('name'); - } + for (const el of document.querySelectorAll('.delete-button')) { + el.addEventListener('click', (e) => { + e.preventDefault(); + const btn = e.currentTarget; + + // eslint-disable-next-line github/no-dataset -- code depends on the camel-casing + const dataObj = btn.dataset; + console.log(dataObj); - for (let i = 0; i < this.attributes.length; i++) { - const {name, value} = this.attributes[i]; - if (name?.startsWith('data-')) { - const nameEl = modal.querySelector(`.${name}`); - if (nameEl) { - nameEl.textContent = value; + const modalId = btn.getAttribute('data-modal-id'); + const modal = document.querySelector(`.delete.modal${modalId ? `#${modalId}` : ''}`); + const modalNameEl = modal.querySelector('.name'); + if (modalNameEl) modalNameEl.textContent = btn.getAttribute('name'); + + for (const [key, value] of Object.entries(dataObj)) { + if (key?.startsWith('data')) { + const nameEl = modal.querySelector(`.${key}`); + if (nameEl) nameEl.textContent = value; } } - } - $(modal).modal({ - closable: false, - onApprove: async () => { - if (modal.getAttribute('data-type') === 'form') { - const formSelector = this.getAttribute('data-form'); - const form = document.querySelector(formSelector); - if (form) { - form.submit(); - } - return; - } - const postData = new FormData(); - for (let i = 0; i < this.attributes.length; i++) { - const {name, value} = this.attributes[i]; - if (name?.startsWith('data-')) { - postData.append(name.slice(4), value); + $(modal).modal({ + closable: false, + onApprove: async () => { + if (modal.getAttribute('data-type') === 'form') { + const form = document.querySelector(btn.getAttribute('data-form')); + if (form) form.submit(); + return; } - if (name === 'id') { - postData.append('id', value); + const postData = new FormData(); + for (const [key, value] of Object.entries(dataObj)) { + // data-dataxxx (HTML) -> dataxxx + // data-data-xxx (HTML) -> dataXxx + if (key?.startsWith('data')) { + postData.append(key.slice(4), value); + } + if (key === 'id') { + postData.append('id', value); + } } - } - const response = await POST(this.getAttribute('data-url'), {data: postData}); - if (response.ok) { - const data = await response.json(); - window.location.href = data.redirect; - } - }, - }).modal('show'); + const response = await POST(btn.getAttribute('data-url'), {data: postData}); + if (response.ok) { + const data = await response.json(); + window.location.href = data.redirect; + } + }, + }).modal('show'); + }); } - - // Helpers. - $('.delete-button').on('click', showDeletePopup); } function initGlobalShowModal() { From c201bf8548fd1c531bfc1e5b2e7f2e5ebaae1790 Mon Sep 17 00:00:00 2001 From: silverwind Date: Fri, 7 Jun 2024 00:53:55 +0200 Subject: [PATCH 12/19] remove debug --- web_src/js/features/common-global.js | 1 - 1 file changed, 1 deletion(-) diff --git a/web_src/js/features/common-global.js b/web_src/js/features/common-global.js index 060ec68811bb2..141a2dd867fea 100644 --- a/web_src/js/features/common-global.js +++ b/web_src/js/features/common-global.js @@ -309,7 +309,6 @@ export function initGlobalLinkActions() { // eslint-disable-next-line github/no-dataset -- code depends on the camel-casing const dataObj = btn.dataset; - console.log(dataObj); const modalId = btn.getAttribute('data-modal-id'); const modal = document.querySelector(`.delete.modal${modalId ? `#${modalId}` : ''}`); From 3617a8f0c2092162d40fd5f9fdc3b393e639f184 Mon Sep 17 00:00:00 2001 From: silverwind Date: Fri, 7 Jun 2024 01:06:09 +0200 Subject: [PATCH 13/19] test the last case --- web_src/js/features/repo-legacy.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/web_src/js/features/repo-legacy.js b/web_src/js/features/repo-legacy.js index 54170bfd6233d..e53d86cca07c7 100644 --- a/web_src/js/features/repo-legacy.js +++ b/web_src/js/features/repo-legacy.js @@ -272,9 +272,9 @@ export function initRepoCommentForm() { } $list.find('.selected').html(` - + ${icon} - ${htmlEscape(this.textContent ?? '')} + ${htmlEscape(this.textContent)} `); From 0083413aa1352a9ff3bd95c4552c755407def133 Mon Sep 17 00:00:00 2001 From: silverwind Date: Fri, 7 Jun 2024 08:54:44 +0200 Subject: [PATCH 14/19] Update web_src/js/features/repo-issue.js --- web_src/js/features/repo-issue.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web_src/js/features/repo-issue.js b/web_src/js/features/repo-issue.js index 91b134a85eb3c..3cbbdc41fc1d1 100644 --- a/web_src/js/features/repo-issue.js +++ b/web_src/js/features/repo-issue.js @@ -283,7 +283,7 @@ export function initRepoPullRequestUpdate() { if (url) { const buttonText = pullUpdateButton.querySelector('.button-text'); if (buttonText) { - buttonText.textContent = choiceEl.textContent ?? ''; + buttonText.textContent = choiceEl.textContent; } pullUpdateButton.setAttribute('data-do', url); } From 23a64803fc921e2873df3093ce3f2e607e8a9e30 Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Mon, 10 Jun 2024 13:06:14 +0800 Subject: [PATCH 15/19] fine tune "access mode" dropdown --- templates/repo/settings/collaboration.tmpl | 8 ++++---- web_src/js/features/repo-settings.js | 23 +++++++++++----------- 2 files changed, 15 insertions(+), 16 deletions(-) diff --git a/templates/repo/settings/collaboration.tmpl b/templates/repo/settings/collaboration.tmpl index ed4d5e7eb3e8e..cc923acd7965c 100644 --- a/templates/repo/settings/collaboration.tmpl +++ b/templates/repo/settings/collaboration.tmpl @@ -19,13 +19,13 @@
{{svg "octicon-shield-lock"}} - diff --git a/web_src/js/features/repo-settings.js b/web_src/js/features/repo-settings.js index 9204e903b8680..cdda48fa895e3 100644 --- a/web_src/js/features/repo-settings.js +++ b/web_src/js/features/repo-settings.js @@ -1,33 +1,32 @@ import $ from 'jquery'; import {minimatch} from 'minimatch'; import {createMonaco} from './codeeditor.js'; -import {onInputDebounce, toggleElem} from '../utils/dom.js'; +import {onInputDebounce, queryElemChildren, queryElems, toggleElem} from '../utils/dom.js'; import {POST} from '../modules/fetch.js'; const {appSubUrl, csrfToken} = window.config; export function initRepoSettingsCollaboration() { // Change collaborator access mode - $('.page-content.repository .ui.dropdown.access-mode').each((_, dropdownEl) => { + for (const dropdownEl of queryElems('.page-content.repository .ui.dropdown.access-mode')) { const textEl = dropdownEl.querySelector(':scope > .text'); $(dropdownEl).dropdown({ - async action(_text, value) { + async action(text, value) { + dropdownEl.classList.add('is-loading', 'loading-icon-2px'); const lastValue = dropdownEl.getAttribute('data-last-value'); + $(dropdownEl).dropdown('hide'); try { + const uid = dropdownEl.getAttribute('data-uid'); + await POST(dropdownEl.getAttribute('data-url'), {data: new URLSearchParams({uid, 'mode': value})}); + textEl.textContent = text; dropdownEl.setAttribute('data-last-value', value); - $(dropdownEl).dropdown('hide'); - const data = new FormData(); - data.append('uid', dropdownEl.getAttribute('data-uid')); - data.append('mode', value); - await POST(dropdownEl.getAttribute('data-url'), {data}); } catch { textEl.textContent = '(error)'; // prevent from misleading users when error occurs dropdownEl.setAttribute('data-last-value', lastValue); + } finally { + dropdownEl.classList.remove('is-loading'); } }, - onChange(_value, text) { - textEl.textContent = text; // update the text when using keyboard navigating - }, onHide() { // set to the really selected value, defer to next tick to make sure `action` has finished // its work because the calling order might be onHide -> action @@ -41,7 +40,7 @@ export function initRepoSettingsCollaboration() { }, 0); }, }); - }); + } } export function initRepoSettingSearchTeamBox() { From c850b3df4a146895b41967cee1d1717ad9571b16 Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Mon, 10 Jun 2024 13:26:29 +0800 Subject: [PATCH 16/19] fix delete-button --- web_src/js/features/common-global.js | 32 ++++++++++++++++++---------- web_src/js/features/repo-settings.js | 2 +- web_src/js/index.js | 4 ++-- 3 files changed, 24 insertions(+), 14 deletions(-) diff --git a/web_src/js/features/common-global.js b/web_src/js/features/common-global.js index 90f824741d15f..26503cb378cb1 100644 --- a/web_src/js/features/common-global.js +++ b/web_src/js/features/common-global.js @@ -301,7 +301,12 @@ async function linkAction(e) { } } -export function initGlobalLinkActions() { +export function initGlobalDeleteButton() { + // ".delete-button" shows a confirmation modal defined by `data-modal-id` attribute. + // Some model/form elements will be filled by `data-id` / `data-name` / `data-data-xxx` attributes. + // If there is a form defined by `data-form`, then the form will be submitted as-is (without any modification). + // If there is no form, then the data will be posted to `data-url`. + // TODO: it's not encouraged to use this method. `show-modal` does far better than this. for (const el of document.querySelectorAll('.delete-button')) { el.addEventListener('click', (e) => { e.preventDefault(); @@ -312,32 +317,37 @@ export function initGlobalLinkActions() { const modalId = btn.getAttribute('data-modal-id'); const modal = document.querySelector(`.delete.modal${modalId ? `#${modalId}` : ''}`); + + // set the modal name by `data-name` const modalNameEl = modal.querySelector('.name'); - if (modalNameEl) modalNameEl.textContent = btn.getAttribute('name'); + if (modalNameEl) modalNameEl.textContent = btn.getAttribute('data-name'); + // fill the modal elements with data-xxx attributes: `data-data-organization-name="..."` => `...` for (const [key, value] of Object.entries(dataObj)) { if (key?.startsWith('data')) { - const nameEl = modal.querySelector(`.${key}`); - if (nameEl) nameEl.textContent = value; + const textEl = modal.querySelector(`.${key}`); + if (textEl) textEl.textContent = value; } } $(modal).modal({ closable: false, onApprove: async () => { + // if `data-type="form"` exists, then submit the form by the selector provided by `data-form="..."` if (modal.getAttribute('data-type') === 'form') { - const form = document.querySelector(btn.getAttribute('data-form')); - if (form) form.submit(); - return; + const formSelector = btn.getAttribute('data-form'); + const form = document.querySelector(formSelector); + if (!form) throw new Error(`no form named ${formSelector} found`); + form.submit(); } + + // prepare an AJAX form by data attributes const postData = new FormData(); for (const [key, value] of Object.entries(dataObj)) { - // data-dataxxx (HTML) -> dataxxx - // data-data-xxx (HTML) -> dataXxx - if (key?.startsWith('data')) { + if (key.startsWith('data')) { // for data-data-xxx (HTML) -> dataXxx (form) postData.append(key.slice(4), value); } - if (key === 'id') { + if (key === 'id') { // for data-id="..." postData.append('id', value); } } diff --git a/web_src/js/features/repo-settings.js b/web_src/js/features/repo-settings.js index cdda48fa895e3..652f8ac290e21 100644 --- a/web_src/js/features/repo-settings.js +++ b/web_src/js/features/repo-settings.js @@ -1,7 +1,7 @@ import $ from 'jquery'; import {minimatch} from 'minimatch'; import {createMonaco} from './codeeditor.js'; -import {onInputDebounce, queryElemChildren, queryElems, toggleElem} from '../utils/dom.js'; +import {onInputDebounce, queryElems, toggleElem} from '../utils/dom.js'; import {POST} from '../modules/fetch.js'; const {appSubUrl, csrfToken} = window.config; diff --git a/web_src/js/index.js b/web_src/js/index.js index 1867556eeed21..12cd0ee15a2ad 100644 --- a/web_src/js/index.js +++ b/web_src/js/index.js @@ -43,7 +43,7 @@ import { initGlobalDropzone, initGlobalEnterQuickSubmit, initGlobalFormDirtyLeaveConfirm, - initGlobalLinkActions, + initGlobalDeleteButton, initHeadNavbarContentToggle, } from './features/common-global.js'; import {initRepoTopicBar} from './features/repo-home.js'; @@ -103,7 +103,7 @@ onDomReady(() => { initGlobalDropzone(); initGlobalEnterQuickSubmit(); initGlobalFormDirtyLeaveConfirm(); - initGlobalLinkActions(); + initGlobalDeleteButton(); initCommonOrganization(); initCommonIssueListQuickGoto(); From 62c4b64741e040c0498b061ad7817e7eb1c8a867 Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Mon, 10 Jun 2024 13:56:47 +0800 Subject: [PATCH 17/19] improve code filename editor --- templates/repo/editor/commit_form.tmpl | 6 +- templates/repo/settings/collaboration.tmpl | 2 +- web_src/js/features/repo-editor.js | 104 ++++++++++----------- 3 files changed, 52 insertions(+), 60 deletions(-) diff --git a/templates/repo/editor/commit_form.tmpl b/templates/repo/editor/commit_form.tmpl index 21ef63288fd84..61122417d260f 100644 --- a/templates/repo/editor/commit_form.tmpl +++ b/templates/repo/editor/commit_form.tmpl @@ -23,7 +23,7 @@
- +