diff options
author | delvh <leon@kske.dev> | 2022-05-07 20:28:10 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-05-07 20:28:10 +0200 |
commit | 5ca224a789394d00cc1efb5afcae7b87aa7d1e49 (patch) | |
tree | d88bddc7a08449a2c8f2b4e5ae47d3ef067a1cc0 /web_src | |
parent | 59b30f060a840cde305952ef7bc344fa4101c0d5 (diff) | |
download | gitea-5ca224a789394d00cc1efb5afcae7b87aa7d1e49.tar.gz gitea-5ca224a789394d00cc1efb5afcae7b87aa7d1e49.zip |
Allow to mark files in a PR as viewed (#19007)
Users can now mark files in PRs as viewed, resulting in them not being shown again by default when they reopen the PR again.
Diffstat (limited to 'web_src')
-rw-r--r-- | web_src/js/features/file-fold.js | 18 | ||||
-rw-r--r-- | web_src/js/features/pull-view-file.js | 71 | ||||
-rw-r--r-- | web_src/js/features/repo-code.js | 6 | ||||
-rw-r--r-- | web_src/js/features/repo-diff.js | 12 | ||||
-rw-r--r-- | web_src/js/index.js | 3 | ||||
-rw-r--r-- | web_src/less/_review.less | 18 |
6 files changed, 121 insertions, 7 deletions
diff --git a/web_src/js/features/file-fold.js b/web_src/js/features/file-fold.js new file mode 100644 index 0000000000..5e714a1de8 --- /dev/null +++ b/web_src/js/features/file-fold.js @@ -0,0 +1,18 @@ +import {svg} from '../svg.js'; + + +// Hides the file if newFold is true, and shows it otherwise. The actual hiding is performed using CSS. +// +// The fold arrow is the icon displayed on the upper left of the file box, especially intended for components having the 'fold-file' class. +// The file content box is the box that should be hidden or shown, especially intended for components having the 'file-content' class. +// +export function setFileFolding(fileContentBox, foldArrow, newFold) { + foldArrow.innerHTML = svg(`octicon-chevron-${newFold ? 'right' : 'down'}`, 18); + fileContentBox.setAttribute('data-folded', newFold); +} + +// Like `setFileFolding`, except that it automatically inverts the current file folding state. +export function invertFileFolding(fileContentBox, foldArrow) { + setFileFolding(fileContentBox, foldArrow, fileContentBox.getAttribute('data-folded') !== 'true'); +} + diff --git a/web_src/js/features/pull-view-file.js b/web_src/js/features/pull-view-file.js new file mode 100644 index 0000000000..57e9d5e047 --- /dev/null +++ b/web_src/js/features/pull-view-file.js @@ -0,0 +1,71 @@ +import {setFileFolding} from './file-fold.js'; + +const {csrfToken, pageData} = window.config; +const prReview = pageData.prReview || {}; +const viewedStyleClass = 'viewed-file-checked-form'; +const viewedCheckboxSelector = '.viewed-file-form'; // Selector under which all "Viewed" checkbox forms can be found + + +// Refreshes the summary of viewed files if present +// The data used will be window.config.pageData.prReview.numberOf{Viewed}Files +function refreshViewedFilesSummary() { + const viewedFilesMeter = document.getElementById('viewed-files-summary'); + viewedFilesMeter?.setAttribute('value', prReview.numberOfViewedFiles); + const summaryLabel = document.getElementById('viewed-files-summary-label'); + if (summaryLabel) summaryLabel.innerHTML = summaryLabel.getAttribute('data-text-changed-template') + .replace('%[1]d', prReview.numberOfViewedFiles) + .replace('%[2]d', prReview.numberOfFiles); +} + +// Explicitly recounts how many files the user has currently reviewed by counting the number of checked "viewed" checkboxes +// Additionally, the viewed files summary will be updated if it exists +export function countAndUpdateViewedFiles() { + // The number of files is constant, but the number of viewed files can change because files can be loaded dynamically + prReview.numberOfViewedFiles = document.querySelectorAll(`${viewedCheckboxSelector} > input[type=checkbox][checked]`).length; + refreshViewedFilesSummary(); +} + +// Initializes a listener for all children of the given html element +// (for example 'document' in the most basic case) +// to watch for changes of viewed-file checkboxes +export function initViewedCheckboxListenerFor() { + for (const form of document.querySelectorAll(`${viewedCheckboxSelector}:not([data-has-viewed-checkbox-listener="true"])`)) { + // To prevent double addition of listeners + form.setAttribute('data-has-viewed-checkbox-listener', true); + + // The checkbox consists of a div containing the real checkbox with its label and the CSRF token, + // hence the actual checkbox first has to be found + const checkbox = form.querySelector('input[type=checkbox]'); + checkbox.addEventListener('change', function() { + // Mark the file as viewed visually - will especially change the background + if (this.checked) { + form.classList.add(viewedStyleClass); + prReview.numberOfViewedFiles++; + } else { + form.classList.remove(viewedStyleClass); + prReview.numberOfViewedFiles--; + } + + // Update viewed-files summary and remove "has changed" label if present + refreshViewedFilesSummary(); + const hasChangedLabel = form.parentNode.querySelector('.changed-since-last-review'); + hasChangedLabel?.parentNode.removeChild(hasChangedLabel); + + // Unfortunately, actual forms cause too many problems, hence another approach is needed + const files = {}; + files[checkbox.getAttribute('name')] = this.checked; + const data = {files}; + const headCommitSHA = form.getAttribute('data-headcommit'); + if (headCommitSHA) data.headCommitSHA = headCommitSHA; + fetch(form.getAttribute('data-link'), { + method: 'POST', + headers: {'X-Csrf-Token': csrfToken}, + body: JSON.stringify(data), + }); + + // Fold the file accordingly + const parentBox = form.closest('.diff-file-header'); + setFileFolding(parentBox.closest('.file-content'), parentBox.querySelector('.fold-file'), this.checked); + }); + } +} diff --git a/web_src/js/features/repo-code.js b/web_src/js/features/repo-code.js index d7b4baac83..8562ba0072 100644 --- a/web_src/js/features/repo-code.js +++ b/web_src/js/features/repo-code.js @@ -1,5 +1,6 @@ import $ from 'jquery'; import {svg} from '../svg.js'; +import {invertFileFolding} from './file-fold.js'; function changeHash(hash) { if (window.history.pushState) { @@ -148,10 +149,7 @@ export function initRepoCodeView() { }).trigger('hashchange'); } $(document).on('click', '.fold-file', ({currentTarget}) => { - const box = currentTarget.closest('.file-content'); - const folded = box.getAttribute('data-folded') !== 'true'; - currentTarget.innerHTML = svg(`octicon-chevron-${folded ? 'right' : 'down'}`, 18); - box.setAttribute('data-folded', String(folded)); + invertFileFolding(currentTarget.closest('.file-content'), currentTarget); }); $(document).on('click', '.blob-excerpt', async ({currentTarget}) => { const url = currentTarget.getAttribute('data-url'); diff --git a/web_src/js/features/repo-diff.js b/web_src/js/features/repo-diff.js index 013b8d3fa0..8403a2fd42 100644 --- a/web_src/js/features/repo-diff.js +++ b/web_src/js/features/repo-diff.js @@ -2,6 +2,7 @@ import $ from 'jquery'; import {initCompReactionSelector} from './comp/ReactionSelector.js'; import {initRepoIssueContentHistory} from './repo-issue-content.js'; import {validateTextareaNonEmpty} from './comp/EasyMDE.js'; +import {initViewedCheckboxListenerFor, countAndUpdateViewedFiles} from './pull-view-file.js'; const {csrfToken} = window.config; @@ -104,6 +105,13 @@ export function initRepoDiffConversationNav() { }); } +// Will be called when the show more (files) button has been pressed +function onShowMoreFiles() { + initRepoIssueContentHistory(); + initViewedCheckboxListenerFor(); + countAndUpdateViewedFiles(); +} + export function initRepoDiffShowMore() { $('#diff-files, #diff-file-boxes').on('click', '#diff-show-more-files, #diff-show-more-files-stats', (e) => { e.preventDefault(); @@ -125,7 +133,7 @@ export function initRepoDiffShowMore() { $('#diff-too-many-files-stats').remove(); $('#diff-files').append($(resp).find('#diff-files li')); $('#diff-incomplete').replaceWith($(resp).find('#diff-file-boxes').children()); - initRepoIssueContentHistory(); + onShowMoreFiles(); }).fail(() => { $('#diff-show-more-files, #diff-show-more-files-stats').removeClass('disabled'); }); @@ -151,7 +159,7 @@ export function initRepoDiffShowMore() { } $target.parent().replaceWith($(resp).find('#diff-file-boxes .diff-file-body .file-body').children()); - initRepoIssueContentHistory(); + onShowMoreFiles(); }).fail(() => { $target.removeClass('disabled'); }); diff --git a/web_src/js/index.js b/web_src/js/index.js index 5b95a0d8ef..4343c2d965 100644 --- a/web_src/js/index.js +++ b/web_src/js/index.js @@ -70,6 +70,7 @@ import { initRepoSettingsCollaboration, initRepoSettingSearchTeamBox, } from './features/repo-settings.js'; +import {initViewedCheckboxListenerFor} from './features/pull-view-file.js'; import {initOrgTeamSearchRepoBox, initOrgTeamSettings} from './features/org-team.js'; import {initUserAuthWebAuthn, initUserAuthWebAuthnRegister} from './features/user-auth-webauthn.js'; import {initRepoRelease, initRepoReleaseEditor} from './features/repo-release.js'; @@ -178,6 +179,6 @@ $(document).ready(() => { initUserAuthWebAuthn(); initUserAuthWebAuthnRegister(); initUserSettings(); - + initViewedCheckboxListenerFor(); checkAppUrl(); }); diff --git a/web_src/less/_review.less b/web_src/less/_review.less index 8bc41b9736..1bcb3663c2 100644 --- a/web_src/less/_review.less +++ b/web_src/less/_review.less @@ -262,3 +262,21 @@ a.blob-excerpt:hover { scroll-margin-top: 130px; } } + +.changed-since-last-review { + margin: 0 5px; + padding: 0 3px; + border: 2px var(--color-primary-light-3) solid; + background-color: var(--color-primary-alpha-30); + border-radius: 7px; +} + +.viewed-file-form { + margin: 0 3px; + padding: 0 3px; + border-radius: 3px; +} + +.viewed-file-checked-form { + background-color: var(--color-primary-light-4); +} |