aboutsummaryrefslogtreecommitdiffstats
path: root/web_src
diff options
context:
space:
mode:
authorCalvin K <70356237+CalK16@users.noreply.github.com>2024-11-09 12:48:31 +0800
committerGitHub <noreply@github.com>2024-11-09 04:48:31 +0000
commit18aeca53203adba7b4fb3b7311f0e77bef92e266 (patch)
tree17a55e9f9615b74a9b43f5920b35cc7a8f5e5833 /web_src
parentd80f99ef0441da135ba2857840687e0cf921ac52 (diff)
downloadgitea-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.css1
-rw-r--r--web_src/css/repo.css9
-rw-r--r--web_src/js/features/repo-issue-sidebar-combolist.ts89
-rw-r--r--web_src/js/features/repo-issue-sidebar.ts41
-rw-r--r--web_src/js/features/repo-issue.ts12
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;