diff options
author | Calvin K <70356237+CalK16@users.noreply.github.com> | 2024-11-09 12:48:31 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-11-09 04:48:31 +0000 |
commit | 18aeca53203adba7b4fb3b7311f0e77bef92e266 (patch) | |
tree | 17a55e9f9615b74a9b43f5920b35cc7a8f5e5833 /web_src | |
parent | d80f99ef0441da135ba2857840687e0cf921ac52 (diff) | |
download | gitea-18aeca53203adba7b4fb3b7311f0e77bef92e266.tar.gz gitea-18aeca53203adba7b4fb3b7311f0e77bef92e266.zip |
Add reviewers selection to new pull request (#32403)
Users could add reviewers when creating new PRs.
---------
Co-authored-by: splitt3r <splitt3r@users.noreply.github.com>
Co-authored-by: Sebastian Sauer <sauer.sebastian@gmail.com>
Co-authored-by: bb-ben <70356237+bboerben@users.noreply.github.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Diffstat (limited to 'web_src')
-rw-r--r-- | web_src/css/base.css | 1 | ||||
-rw-r--r-- | web_src/css/repo.css | 9 | ||||
-rw-r--r-- | web_src/js/features/repo-issue-sidebar-combolist.ts | 89 | ||||
-rw-r--r-- | web_src/js/features/repo-issue-sidebar.ts | 41 | ||||
-rw-r--r-- | web_src/js/features/repo-issue.ts | 12 |
5 files changed, 110 insertions, 42 deletions
diff --git a/web_src/css/base.css b/web_src/css/base.css index 8d9f810ef8..b5a39c7af6 100644 --- a/web_src/css/base.css +++ b/web_src/css/base.css @@ -1381,6 +1381,7 @@ table th[data-sortt-desc] .svg { align-items: stretch; } +.ui.list.flex-items-block > .item, .flex-items-block > .item, .flex-text-block { display: flex; diff --git a/web_src/css/repo.css b/web_src/css/repo.css index 61aa99d531..185a5f6f55 100644 --- a/web_src/css/repo.css +++ b/web_src/css/repo.css @@ -50,6 +50,15 @@ width: 300px; } +.issue-sidebar-combo .ui.dropdown .item:not(.checked) svg.octicon-check { + visibility: hidden; +} +/* ideally, we should move these styles to ".ui.dropdown .menu.flex-items-menu > .item ...", could be done later */ +.issue-sidebar-combo .ui.dropdown .menu > .item > img, +.issue-sidebar-combo .ui.dropdown .menu > .item > svg { + margin: 0; +} + .issue-content-right .dropdown > .menu { max-width: 270px; min-width: 0; diff --git a/web_src/js/features/repo-issue-sidebar-combolist.ts b/web_src/js/features/repo-issue-sidebar-combolist.ts new file mode 100644 index 0000000000..d541615988 --- /dev/null +++ b/web_src/js/features/repo-issue-sidebar-combolist.ts @@ -0,0 +1,89 @@ +import {fomanticQuery} from '../modules/fomantic/base.ts'; +import {POST} from '../modules/fetch.ts'; +import {queryElemChildren, toggleElem} from '../utils/dom.ts'; + +// if there are draft comments, confirm before reloading, to avoid losing comments +export function issueSidebarReloadConfirmDraftComment() { + const commentTextareas = [ + document.querySelector<HTMLTextAreaElement>('.edit-content-zone:not(.tw-hidden) textarea'), + document.querySelector<HTMLTextAreaElement>('#comment-form textarea'), + ]; + for (const textarea of commentTextareas) { + // Most users won't feel too sad if they lose a comment with 10 chars, they can re-type these in seconds. + // But if they have typed more (like 50) chars and the comment is lost, they will be very unhappy. + if (textarea && textarea.value.trim().length > 10) { + textarea.parentElement.scrollIntoView(); + if (!window.confirm('Page will be reloaded, but there are draft comments. Continuing to reload will discard the comments. Continue?')) { + return; + } + break; + } + } + window.location.reload(); +} + +function collectCheckedValues(elDropdown: HTMLElement) { + return Array.from(elDropdown.querySelectorAll('.menu > .item.checked'), (el) => el.getAttribute('data-value')); +} + +export function initIssueSidebarComboList(container: HTMLElement) { + if (!container) return; + + const updateUrl = container.getAttribute('data-update-url'); + const elDropdown = container.querySelector<HTMLElement>(':scope > .ui.dropdown'); + const elList = container.querySelector<HTMLElement>(':scope > .ui.list'); + const elComboValue = container.querySelector<HTMLInputElement>(':scope > .combo-value'); + const initialValues = collectCheckedValues(elDropdown); + + elDropdown.addEventListener('click', (e) => { + const elItem = (e.target as HTMLElement).closest('.item'); + if (!elItem) return; + e.preventDefault(); + if (elItem.getAttribute('data-can-change') !== 'true') return; + elItem.classList.toggle('checked'); + elComboValue.value = collectCheckedValues(elDropdown).join(','); + }); + + const updateToBackend = async (changedValues) => { + let changed = false; + for (const value of initialValues) { + if (!changedValues.includes(value)) { + await POST(updateUrl, {data: new URLSearchParams({action: 'detach', id: value})}); + changed = true; + } + } + for (const value of changedValues) { + if (!initialValues.includes(value)) { + await POST(updateUrl, {data: new URLSearchParams({action: 'attach', id: value})}); + changed = true; + } + } + if (changed) issueSidebarReloadConfirmDraftComment(); + }; + + const syncList = (changedValues) => { + const elEmptyTip = elList.querySelector('.item.empty-list'); + queryElemChildren(elList, '.item:not(.empty-list)', (el) => el.remove()); + for (const value of changedValues) { + const el = elDropdown.querySelector<HTMLElement>(`.menu > .item[data-value="${value}"]`); + const listItem = el.cloneNode(true) as HTMLElement; + listItem.querySelector('svg.octicon-check')?.remove(); + elList.append(listItem); + } + const hasItems = Boolean(elList.querySelector('.item:not(.empty-list)')); + toggleElem(elEmptyTip, !hasItems); + }; + + fomanticQuery(elDropdown).dropdown({ + action: 'nothing', // do not hide the menu if user presses Enter + fullTextSearch: 'exact', + async onHide() { + const changedValues = collectCheckedValues(elDropdown); + if (updateUrl) { + await updateToBackend(changedValues); // send requests to backend and reload the page + } else { + syncList(changedValues); // only update the list in the sidebar + } + }, + }); +} diff --git a/web_src/js/features/repo-issue-sidebar.ts b/web_src/js/features/repo-issue-sidebar.ts index 0d30d8103c..4a1ef02aab 100644 --- a/web_src/js/features/repo-issue-sidebar.ts +++ b/web_src/js/features/repo-issue-sidebar.ts @@ -4,26 +4,7 @@ import {updateIssuesMeta} from './repo-common.ts'; import {svg} from '../svg.ts'; import {htmlEscape} from 'escape-goat'; import {toggleElem} from '../utils/dom.ts'; - -// if there are draft comments, confirm before reloading, to avoid losing comments -function reloadConfirmDraftComment() { - const commentTextareas = [ - document.querySelector('.edit-content-zone:not(.tw-hidden) textarea'), - document.querySelector('#comment-form textarea'), - ]; - for (const textarea of commentTextareas) { - // Most users won't feel too sad if they lose a comment with 10 chars, they can re-type these in seconds. - // But if they have typed more (like 50) chars and the comment is lost, they will be very unhappy. - if (textarea && textarea.value.trim().length > 10) { - textarea.parentElement.scrollIntoView(); - if (!window.confirm('Page will be reloaded, but there are draft comments. Continuing to reload will discard the comments. Continue?')) { - return; - } - break; - } - } - window.location.reload(); -} +import {initIssueSidebarComboList, issueSidebarReloadConfirmDraftComment} from './repo-issue-sidebar-combolist.ts'; function initBranchSelector() { const elSelectBranch = document.querySelector('.ui.dropdown.select-branch'); @@ -78,7 +59,7 @@ function initListSubmits(selector, outerSelector) { ); } if (itemEntries.length) { - reloadConfirmDraftComment(); + issueSidebarReloadConfirmDraftComment(); } } }, @@ -142,7 +123,7 @@ function initListSubmits(selector, outerSelector) { // TODO: Which thing should be done for choosing review requests // to make chosen items be shown on time here? - if (selector === 'select-reviewers-modify' || selector === 'select-assignees-modify') { + if (selector === 'select-assignees-modify') { return false; } @@ -173,7 +154,7 @@ function initListSubmits(selector, outerSelector) { $listMenu.data('issue-id'), '', ); - reloadConfirmDraftComment(); + issueSidebarReloadConfirmDraftComment(); })(); } @@ -182,7 +163,7 @@ function initListSubmits(selector, outerSelector) { $(this).find('.octicon-check').addClass('tw-invisible'); }); - if (selector === 'select-reviewers-modify' || selector === 'select-assignees-modify') { + if (selector === 'select-assignees-modify') { return false; } @@ -213,7 +194,7 @@ function selectItem(select_id, input_id) { $menu.data('issue-id'), $(this).data('id'), ); - reloadConfirmDraftComment(); + issueSidebarReloadConfirmDraftComment(); })(); } @@ -249,7 +230,7 @@ function selectItem(select_id, input_id) { $menu.data('issue-id'), $(this).data('id'), ); - reloadConfirmDraftComment(); + issueSidebarReloadConfirmDraftComment(); })(); } @@ -276,14 +257,14 @@ export function initRepoIssueSidebar() { initBranchSelector(); initRepoIssueDue(); - // Init labels and assignees + // TODO: refactor the legacy initListSubmits&selectItem to initIssueSidebarComboList initListSubmits('select-label', 'labels'); initListSubmits('select-assignees', 'assignees'); initListSubmits('select-assignees-modify', 'assignees'); - initListSubmits('select-reviewers-modify', 'assignees'); - - // Milestone, Assignee, Project selectItem('.select-project', '#project_id'); selectItem('.select-milestone', '#milestone_id'); selectItem('.select-assignee', '#assignee_id'); + + // init the combo list: a dropdown for selecting reviewers, and a list for showing selected reviewers and related actions + initIssueSidebarComboList(document.querySelector('.issue-sidebar-combo[data-sidebar-combo-for="reviewers"]')); } diff --git a/web_src/js/features/repo-issue.ts b/web_src/js/features/repo-issue.ts index 721a746aa2..92916ec8d7 100644 --- a/web_src/js/features/repo-issue.ts +++ b/web_src/js/features/repo-issue.ts @@ -8,7 +8,6 @@ import {parseIssuePageInfo, toAbsoluteUrl} from '../utils.ts'; import {GET, POST} from '../modules/fetch.ts'; import {showErrorToast} from '../modules/toast.ts'; import {initRepoIssueSidebar} from './repo-issue-sidebar.ts'; -import {updateIssuesMeta} from './repo-common.ts'; const {appSubUrl} = window.config; @@ -326,17 +325,6 @@ export function initRepoIssueWipTitle() { export function initRepoIssueComments() { if (!$('.repository.view.issue .timeline').length) return; - $('.re-request-review').on('click', async function (e) { - e.preventDefault(); - const url = this.getAttribute('data-update-url'); - const issueId = this.getAttribute('data-issue-id'); - const id = this.getAttribute('data-id'); - const isChecked = this.classList.contains('checked'); - - await updateIssuesMeta(url, isChecked ? 'detach' : 'attach', issueId, id); - window.location.reload(); - }); - document.addEventListener('click', (e) => { const urlTarget = document.querySelector(':target'); if (!urlTarget) return; |