Skip to content

Commit f2a6df0

Browse files
wxiaoguangGiteaBot
andauthored
Refactor repo legacy (#32404)
Only move code, no unnecessary logic change. (There are many problems in old code, but changing them is not in this PR's scope) Co-authored-by: Giteabot <[email protected]>
1 parent 2598116 commit f2a6df0

9 files changed

+383
-362
lines changed

Diff for: web_src/js/features/repo-common.ts

+11
Original file line numberDiff line numberDiff line change
@@ -90,3 +90,14 @@ export function initRepoCommonFilterSearchDropdown(selector) {
9090
message: {noResults: $dropdown[0].getAttribute('data-no-results')},
9191
});
9292
}
93+
94+
export async function updateIssuesMeta(url, action, issue_ids, id) {
95+
try {
96+
const response = await POST(url, {data: new URLSearchParams({action, issue_ids, id})});
97+
if (!response.ok) {
98+
throw new Error('Failed to update issues meta');
99+
}
100+
} catch (error) {
101+
console.error(error);
102+
}
103+
}

Diff for: web_src/js/features/repo-issue-list.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import $ from 'jquery';
2-
import {updateIssuesMeta} from './repo-issue.ts';
2+
import {updateIssuesMeta} from './repo-common.ts';
33
import {toggleElem, hideElem, isElemHidden} from '../utils/dom.ts';
44
import {htmlEscape} from 'escape-goat';
55
import {confirmModal} from './comp/ConfirmModal.ts';

Diff for: web_src/js/features/repo-issue-sidebar.ts

+274
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,274 @@
1+
import $ from 'jquery';
2+
import {POST} from '../modules/fetch.ts';
3+
import {updateIssuesMeta} from './repo-common.ts';
4+
import {svg} from '../svg.ts';
5+
import {htmlEscape} from 'escape-goat';
6+
7+
// if there are draft comments, confirm before reloading, to avoid losing comments
8+
function reloadConfirmDraftComment() {
9+
const commentTextareas = [
10+
document.querySelector('.edit-content-zone:not(.tw-hidden) textarea'),
11+
document.querySelector('#comment-form textarea'),
12+
];
13+
for (const textarea of commentTextareas) {
14+
// Most users won't feel too sad if they lose a comment with 10 chars, they can re-type these in seconds.
15+
// But if they have typed more (like 50) chars and the comment is lost, they will be very unhappy.
16+
if (textarea && textarea.value.trim().length > 10) {
17+
textarea.parentElement.scrollIntoView();
18+
if (!window.confirm('Page will be reloaded, but there are draft comments. Continuing to reload will discard the comments. Continue?')) {
19+
return;
20+
}
21+
break;
22+
}
23+
}
24+
window.location.reload();
25+
}
26+
27+
function initBranchSelector() {
28+
const elSelectBranch = document.querySelector('.ui.dropdown.select-branch');
29+
if (!elSelectBranch) return;
30+
31+
const urlUpdateIssueRef = elSelectBranch.getAttribute('data-url-update-issueref');
32+
const $selectBranch = $(elSelectBranch);
33+
const $branchMenu = $selectBranch.find('.reference-list-menu');
34+
$branchMenu.find('.item:not(.no-select)').on('click', async function (e) {
35+
e.preventDefault();
36+
const selectedValue = this.getAttribute('data-id'); // eg: "refs/heads/my-branch"
37+
const selectedText = this.getAttribute('data-name'); // eg: "my-branch"
38+
if (urlUpdateIssueRef) {
39+
// for existing issue, send request to update issue ref, and reload page
40+
try {
41+
await POST(urlUpdateIssueRef, {data: new URLSearchParams({ref: selectedValue})});
42+
window.location.reload();
43+
} catch (error) {
44+
console.error(error);
45+
}
46+
} else {
47+
// for new issue, only update UI&form, do not send request/reload
48+
const selectedHiddenSelector = this.getAttribute('data-id-selector');
49+
document.querySelector(selectedHiddenSelector).value = selectedValue;
50+
elSelectBranch.querySelector('.text-branch-name').textContent = selectedText;
51+
}
52+
});
53+
}
54+
55+
// List submits
56+
function initListSubmits(selector, outerSelector) {
57+
const $list = $(`.ui.${outerSelector}.list`);
58+
const $noSelect = $list.find('.no-select');
59+
const $listMenu = $(`.${selector} .menu`);
60+
let hasUpdateAction = $listMenu.data('action') === 'update';
61+
const items = {};
62+
63+
$(`.${selector}`).dropdown({
64+
'action': 'nothing', // do not hide the menu if user presses Enter
65+
fullTextSearch: 'exact',
66+
async onHide() {
67+
hasUpdateAction = $listMenu.data('action') === 'update'; // Update the var
68+
if (hasUpdateAction) {
69+
// TODO: Add batch functionality and make this 1 network request.
70+
const itemEntries = Object.entries(items);
71+
for (const [elementId, item] of itemEntries) {
72+
await updateIssuesMeta(
73+
item['update-url'],
74+
item.action,
75+
item['issue-id'],
76+
elementId,
77+
);
78+
}
79+
if (itemEntries.length) {
80+
reloadConfirmDraftComment();
81+
}
82+
}
83+
},
84+
});
85+
86+
$listMenu.find('.item:not(.no-select)').on('click', function (e) {
87+
e.preventDefault();
88+
if (this.classList.contains('ban-change')) {
89+
return false;
90+
}
91+
92+
hasUpdateAction = $listMenu.data('action') === 'update'; // Update the var
93+
94+
const clickedItem = this; // eslint-disable-line unicorn/no-this-assignment
95+
const scope = this.getAttribute('data-scope');
96+
97+
$(this).parent().find('.item').each(function () {
98+
if (scope) {
99+
// Enable only clicked item for scoped labels
100+
if (this.getAttribute('data-scope') !== scope) {
101+
return true;
102+
}
103+
if (this !== clickedItem && !this.classList.contains('checked')) {
104+
return true;
105+
}
106+
} else if (this !== clickedItem) {
107+
// Toggle for other labels
108+
return true;
109+
}
110+
111+
if (this.classList.contains('checked')) {
112+
$(this).removeClass('checked');
113+
$(this).find('.octicon-check').addClass('tw-invisible');
114+
if (hasUpdateAction) {
115+
if (!($(this).data('id') in items)) {
116+
items[$(this).data('id')] = {
117+
'update-url': $listMenu.data('update-url'),
118+
action: 'detach',
119+
'issue-id': $listMenu.data('issue-id'),
120+
};
121+
} else {
122+
delete items[$(this).data('id')];
123+
}
124+
}
125+
} else {
126+
$(this).addClass('checked');
127+
$(this).find('.octicon-check').removeClass('tw-invisible');
128+
if (hasUpdateAction) {
129+
if (!($(this).data('id') in items)) {
130+
items[$(this).data('id')] = {
131+
'update-url': $listMenu.data('update-url'),
132+
action: 'attach',
133+
'issue-id': $listMenu.data('issue-id'),
134+
};
135+
} else {
136+
delete items[$(this).data('id')];
137+
}
138+
}
139+
}
140+
});
141+
142+
// TODO: Which thing should be done for choosing review requests
143+
// to make chosen items be shown on time here?
144+
if (selector === 'select-reviewers-modify' || selector === 'select-assignees-modify') {
145+
return false;
146+
}
147+
148+
const listIds = [];
149+
$(this).parent().find('.item').each(function () {
150+
if (this.classList.contains('checked')) {
151+
listIds.push($(this).data('id'));
152+
$($(this).data('id-selector')).removeClass('tw-hidden');
153+
} else {
154+
$($(this).data('id-selector')).addClass('tw-hidden');
155+
}
156+
});
157+
if (!listIds.length) {
158+
$noSelect.removeClass('tw-hidden');
159+
} else {
160+
$noSelect.addClass('tw-hidden');
161+
}
162+
$($(this).parent().data('id')).val(listIds.join(','));
163+
return false;
164+
});
165+
$listMenu.find('.no-select.item').on('click', function (e) {
166+
e.preventDefault();
167+
if (hasUpdateAction) {
168+
(async () => {
169+
await updateIssuesMeta(
170+
$listMenu.data('update-url'),
171+
'clear',
172+
$listMenu.data('issue-id'),
173+
'',
174+
);
175+
reloadConfirmDraftComment();
176+
})();
177+
}
178+
179+
$(this).parent().find('.item').each(function () {
180+
$(this).removeClass('checked');
181+
$(this).find('.octicon-check').addClass('tw-invisible');
182+
});
183+
184+
if (selector === 'select-reviewers-modify' || selector === 'select-assignees-modify') {
185+
return false;
186+
}
187+
188+
$list.find('.item').each(function () {
189+
$(this).addClass('tw-hidden');
190+
});
191+
$noSelect.removeClass('tw-hidden');
192+
$($(this).parent().data('id')).val('');
193+
});
194+
}
195+
196+
function selectItem(select_id, input_id) {
197+
const $menu = $(`${select_id} .menu`);
198+
const $list = $(`.ui${select_id}.list`);
199+
const hasUpdateAction = $menu.data('action') === 'update';
200+
201+
$menu.find('.item:not(.no-select)').on('click', function () {
202+
$(this).parent().find('.item').each(function () {
203+
$(this).removeClass('selected active');
204+
});
205+
206+
$(this).addClass('selected active');
207+
if (hasUpdateAction) {
208+
(async () => {
209+
await updateIssuesMeta(
210+
$menu.data('update-url'),
211+
'',
212+
$menu.data('issue-id'),
213+
$(this).data('id'),
214+
);
215+
reloadConfirmDraftComment();
216+
})();
217+
}
218+
219+
let icon = '';
220+
if (input_id === '#milestone_id') {
221+
icon = svg('octicon-milestone', 18, 'tw-mr-2');
222+
} else if (input_id === '#project_id') {
223+
icon = svg('octicon-project', 18, 'tw-mr-2');
224+
} else if (input_id === '#assignee_id') {
225+
icon = `<img class="ui avatar image tw-mr-2" alt="avatar" src=${$(this).data('avatar')}>`;
226+
}
227+
228+
$list.find('.selected').html(`
229+
<a class="item muted sidebar-item-link" href="${htmlEscape(this.getAttribute('data-href'))}">
230+
${icon}
231+
${htmlEscape(this.textContent)}
232+
</a>
233+
`);
234+
235+
$(`.ui${select_id}.list .no-select`).addClass('tw-hidden');
236+
$(input_id).val($(this).data('id'));
237+
});
238+
$menu.find('.no-select.item').on('click', function () {
239+
$(this).parent().find('.item:not(.no-select)').each(function () {
240+
$(this).removeClass('selected active');
241+
});
242+
243+
if (hasUpdateAction) {
244+
(async () => {
245+
await updateIssuesMeta(
246+
$menu.data('update-url'),
247+
'',
248+
$menu.data('issue-id'),
249+
$(this).data('id'),
250+
);
251+
reloadConfirmDraftComment();
252+
})();
253+
}
254+
255+
$list.find('.selected').html('');
256+
$list.find('.no-select').removeClass('tw-hidden');
257+
$(input_id).val('');
258+
});
259+
}
260+
261+
export function initRepoIssueSidebar() {
262+
initBranchSelector();
263+
264+
// Init labels and assignees
265+
initListSubmits('select-label', 'labels');
266+
initListSubmits('select-assignees', 'assignees');
267+
initListSubmits('select-assignees-modify', 'assignees');
268+
initListSubmits('select-reviewers-modify', 'assignees');
269+
270+
// Milestone, Assignee, Project
271+
selectItem('.select-project', '#project_id');
272+
selectItem('.select-milestone', '#milestone_id');
273+
selectItem('.select-assignee', '#assignee_id');
274+
}

Diff for: web_src/js/features/repo-issue.ts

+19-13
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import {ComboMarkdownEditor, getComboMarkdownEditor, initComboMarkdownEditor} fr
77
import {toAbsoluteUrl} from '../utils.ts';
88
import {GET, POST} from '../modules/fetch.ts';
99
import {showErrorToast} from '../modules/toast.ts';
10+
import {initRepoIssueSidebar} from './repo-issue-sidebar.ts';
11+
import {updateIssuesMeta} from './repo-common.ts';
1012

1113
const {appSubUrl} = window.config;
1214

@@ -369,17 +371,6 @@ export function initRepoIssueWipTitle() {
369371
});
370372
}
371373

372-
export async function updateIssuesMeta(url, action, issue_ids, id) {
373-
try {
374-
const response = await POST(url, {data: new URLSearchParams({action, issue_ids, id})});
375-
if (!response.ok) {
376-
throw new Error('Failed to update issues meta');
377-
}
378-
} catch (error) {
379-
console.error(error);
380-
}
381-
}
382-
383374
export function initRepoIssueComments() {
384375
if (!$('.repository.view.issue .timeline').length) return;
385376

@@ -665,7 +656,7 @@ export function initRepoIssueBranchSelect() {
665656
});
666657
}
667658

668-
export async function initSingleCommentEditor($commentForm) {
659+
async function initSingleCommentEditor($commentForm) {
669660
// pages:
670661
// * normal new issue/pr page: no status-button, no comment-button (there is only a normal submit button which can submit empty content)
671662
// * issue/pr view page: with comment form, has status-button and comment-button
@@ -687,7 +678,7 @@ export async function initSingleCommentEditor($commentForm) {
687678
syncUiState();
688679
}
689680

690-
export function initIssueTemplateCommentEditors($commentForm) {
681+
function initIssueTemplateCommentEditors($commentForm) {
691682
// pages:
692683
// * new issue with issue template
693684
const $comboFields = $commentForm.find('.combo-editor-dropzone');
@@ -733,3 +724,18 @@ export function initArchivedLabelHandler() {
733724
toggleElem(label, label.classList.contains('checked'));
734725
}
735726
}
727+
728+
export function initRepoCommentFormAndSidebar() {
729+
const $commentForm = $('.comment.form');
730+
if (!$commentForm.length) return;
731+
732+
if ($commentForm.find('.field.combo-editor-dropzone').length) {
733+
// at the moment, if a form has multiple combo-markdown-editors, it must be an issue template form
734+
initIssueTemplateCommentEditors($commentForm);
735+
} else if ($commentForm.find('.combo-markdown-editor').length) {
736+
// it's quite unclear about the "comment form" elements, sometimes it's for issue comment, sometimes it's for file editor/uploader message
737+
initSingleCommentEditor($commentForm);
738+
}
739+
740+
initRepoIssueSidebar();
741+
}

0 commit comments

Comments
 (0)