2022-01-28 21:00:11 +00:00
|
|
|
import $ from 'jquery';
|
2021-10-16 17:28:04 +00:00
|
|
|
import {htmlEscape} from 'escape-goat';
|
2024-07-07 15:32:30 +00:00
|
|
|
import {createTippy, showTemporaryTooltip} from '../modules/tippy.ts';
|
Refactor issue filter (labels, poster, assignee) (#32771)
Rewrite a lot of legacy strange code, remove duplicate code, remove
jquery, and make these filters reusable.
Let's forget the old code, new code affects:
* issue list open/close switch
* issue list filter (label, author, assignee)
* milestone list open/close switch
* milestone issue list filter (label, author, assignee)
* project view (label, assignee)
2024-12-10 03:38:22 +00:00
|
|
|
import {
|
|
|
|
addDelegatedEventListener,
|
|
|
|
createElementFromHTML,
|
|
|
|
hideElem,
|
|
|
|
queryElems,
|
|
|
|
showElem,
|
|
|
|
toggleElem,
|
|
|
|
} from '../utils/dom.ts';
|
2024-07-07 15:32:30 +00:00
|
|
|
import {setFileFolding} from './file-fold.ts';
|
2024-10-23 02:48:04 +00:00
|
|
|
import {ComboMarkdownEditor, getComboMarkdownEditor, initComboMarkdownEditor} from './comp/ComboMarkdownEditor.ts';
|
2024-11-08 02:21:13 +00:00
|
|
|
import {parseIssuePageInfo, toAbsoluteUrl} from '../utils.ts';
|
2024-07-07 15:32:30 +00:00
|
|
|
import {GET, POST} from '../modules/fetch.ts';
|
|
|
|
import {showErrorToast} from '../modules/toast.ts';
|
2024-11-03 11:00:12 +00:00
|
|
|
import {initRepoIssueSidebar} from './repo-issue-sidebar.ts';
|
2024-12-09 07:54:59 +00:00
|
|
|
import {fomanticQuery} from '../modules/fomantic/base.ts';
|
2021-10-16 17:28:04 +00:00
|
|
|
|
2024-03-14 21:36:17 +00:00
|
|
|
const {appSubUrl} = window.config;
|
2021-10-16 17:28:04 +00:00
|
|
|
|
2023-04-07 00:11:02 +00:00
|
|
|
export function initRepoIssueSidebarList() {
|
2024-11-08 02:21:13 +00:00
|
|
|
const issuePageInfo = parseIssuePageInfo();
|
2021-10-16 17:28:04 +00:00
|
|
|
const crossRepoSearch = $('#crossRepoSearch').val();
|
2024-11-08 02:21:13 +00:00
|
|
|
let issueSearchUrl = `${issuePageInfo.repoLink}/issues/search?q={query}&type=${issuePageInfo.issueDependencySearchType}`;
|
2021-10-16 17:28:04 +00:00
|
|
|
if (crossRepoSearch === 'true') {
|
2024-11-08 02:21:13 +00:00
|
|
|
issueSearchUrl = `${appSubUrl}/issues/search?q={query}&priority_repo_id=${issuePageInfo.repoId}&type=${issuePageInfo.issueDependencySearchType}`;
|
2021-10-16 17:28:04 +00:00
|
|
|
}
|
2024-12-09 07:54:59 +00:00
|
|
|
fomanticQuery('#new-dependency-drop-list').dropdown({
|
|
|
|
fullTextSearch: true,
|
|
|
|
apiSettings: {
|
|
|
|
url: issueSearchUrl,
|
|
|
|
onResponse(response) {
|
|
|
|
const filteredResponse = {success: true, results: []};
|
|
|
|
const currIssueId = $('#new-dependency-drop-list').data('issue-id');
|
|
|
|
// Parse the response from the api to work with our dropdown
|
|
|
|
$.each(response, (_i, issue) => {
|
|
|
|
// Don't list current issue in the dependency list.
|
|
|
|
if (issue.id === currIssueId) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
filteredResponse.results.push({
|
|
|
|
name: `<div class="gt-ellipsis">#${issue.number} ${htmlEscape(issue.title)}</div>
|
2024-06-04 13:57:11 +00:00
|
|
|
<div class="text small tw-break-anywhere">${htmlEscape(issue.repository.full_name)}</div>`,
|
2024-12-09 07:54:59 +00:00
|
|
|
value: issue.id,
|
2021-10-16 17:28:04 +00:00
|
|
|
});
|
2024-12-09 07:54:59 +00:00
|
|
|
});
|
|
|
|
return filteredResponse;
|
2021-10-16 17:28:04 +00:00
|
|
|
},
|
2024-12-09 07:54:59 +00:00
|
|
|
cache: false,
|
|
|
|
},
|
|
|
|
});
|
|
|
|
}
|
2021-10-16 17:28:04 +00:00
|
|
|
|
Refactor issue filter (labels, poster, assignee) (#32771)
Rewrite a lot of legacy strange code, remove duplicate code, remove
jquery, and make these filters reusable.
Let's forget the old code, new code affects:
* issue list open/close switch
* issue list filter (label, author, assignee)
* milestone list open/close switch
* milestone issue list filter (label, author, assignee)
* project view (label, assignee)
2024-12-10 03:38:22 +00:00
|
|
|
function initRepoIssueLabelFilter(elDropdown: Element) {
|
|
|
|
const url = new URL(window.location.href);
|
|
|
|
const showArchivedLabels = url.searchParams.get('archived_labels') === 'true';
|
|
|
|
const queryLabels = url.searchParams.get('labels') || '';
|
|
|
|
const selectedLabelIds = new Set<string>();
|
|
|
|
for (const id of queryLabels ? queryLabels.split(',') : []) {
|
|
|
|
selectedLabelIds.add(`${Math.abs(parseInt(id))}`); // "labels" contains negative ids, which are excluded
|
|
|
|
}
|
|
|
|
|
|
|
|
const excludeLabel = (e: MouseEvent|KeyboardEvent, item: Element) => {
|
|
|
|
e.preventDefault();
|
|
|
|
e.stopPropagation();
|
|
|
|
const labelId = item.getAttribute('data-label-id');
|
|
|
|
let labelIds: string[] = queryLabels ? queryLabels.split(',') : [];
|
|
|
|
labelIds = labelIds.filter((id) => Math.abs(parseInt(id)) !== Math.abs(parseInt(labelId)));
|
|
|
|
labelIds.push(`-${labelId}`);
|
|
|
|
url.searchParams.set('labels', labelIds.join(','));
|
|
|
|
window.location.assign(url);
|
|
|
|
};
|
|
|
|
|
|
|
|
// alt(or option) + click to exclude label
|
|
|
|
queryElems(elDropdown, '.label-filter-query-item', (el) => {
|
|
|
|
el.addEventListener('click', (e: MouseEvent) => {
|
|
|
|
if (e.altKey) excludeLabel(e, el);
|
2021-10-16 17:28:04 +00:00
|
|
|
});
|
|
|
|
});
|
Refactor issue filter (labels, poster, assignee) (#32771)
Rewrite a lot of legacy strange code, remove duplicate code, remove
jquery, and make these filters reusable.
Let's forget the old code, new code affects:
* issue list open/close switch
* issue list filter (label, author, assignee)
* milestone list open/close switch
* milestone issue list filter (label, author, assignee)
* project view (label, assignee)
2024-12-10 03:38:22 +00:00
|
|
|
// alt(or option) + enter to exclude selected label
|
|
|
|
elDropdown.addEventListener('keydown', (e: KeyboardEvent) => {
|
2024-07-07 15:32:30 +00:00
|
|
|
if (e.altKey && e.key === 'Enter') {
|
Refactor issue filter (labels, poster, assignee) (#32771)
Rewrite a lot of legacy strange code, remove duplicate code, remove
jquery, and make these filters reusable.
Let's forget the old code, new code affects:
* issue list open/close switch
* issue list filter (label, author, assignee)
* milestone list open/close switch
* milestone issue list filter (label, author, assignee)
* project view (label, assignee)
2024-12-10 03:38:22 +00:00
|
|
|
const selectedItem = elDropdown.querySelector('.label-filter-query-item.selected');
|
|
|
|
if (selectedItem) excludeLabel(e, selectedItem);
|
2021-10-16 17:28:04 +00:00
|
|
|
}
|
|
|
|
});
|
Refactor issue filter (labels, poster, assignee) (#32771)
Rewrite a lot of legacy strange code, remove duplicate code, remove
jquery, and make these filters reusable.
Let's forget the old code, new code affects:
* issue list open/close switch
* issue list filter (label, author, assignee)
* milestone list open/close switch
* milestone issue list filter (label, author, assignee)
* project view (label, assignee)
2024-12-10 03:38:22 +00:00
|
|
|
// no "labels" query parameter means "all issues"
|
|
|
|
elDropdown.querySelector('.label-filter-query-default').classList.toggle('selected', queryLabels === '');
|
|
|
|
// "labels=0" query parameter means "issues without label"
|
|
|
|
elDropdown.querySelector('.label-filter-query-not-set').classList.toggle('selected', queryLabels === '0');
|
|
|
|
|
|
|
|
// prepare to process "archived" labels
|
|
|
|
const elShowArchivedLabel = elDropdown.querySelector('.label-filter-archived-toggle');
|
|
|
|
if (!elShowArchivedLabel) return;
|
|
|
|
const elShowArchivedInput = elShowArchivedLabel.querySelector<HTMLInputElement>('input');
|
|
|
|
elShowArchivedInput.checked = showArchivedLabels;
|
|
|
|
const archivedLabels = elDropdown.querySelectorAll('.item[data-is-archived]');
|
|
|
|
// if no archived labels, hide the toggle and return
|
|
|
|
if (!archivedLabels.length) {
|
|
|
|
hideElem(elShowArchivedLabel);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// show the archived labels if the toggle is checked or the label is selected
|
|
|
|
for (const label of archivedLabels) {
|
|
|
|
toggleElem(label, showArchivedLabels || selectedLabelIds.has(label.getAttribute('data-label-id')));
|
|
|
|
}
|
|
|
|
// update the url when the toggle is changed and reload
|
|
|
|
elShowArchivedInput.addEventListener('input', () => {
|
|
|
|
if (elShowArchivedInput.checked) {
|
|
|
|
url.searchParams.set('archived_labels', 'true');
|
|
|
|
} else {
|
|
|
|
url.searchParams.delete('archived_labels');
|
|
|
|
}
|
|
|
|
window.location.assign(url);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
export function initRepoIssueFilterItemLabel() {
|
|
|
|
// the "label-filter" is used in 2 templates: projects/view, issue/filter_list (issue list page including the milestone page)
|
|
|
|
queryElems(document, '.ui.dropdown.label-filter', initRepoIssueLabelFilter);
|
2021-10-16 17:28:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
export function initRepoIssueCommentDelete() {
|
|
|
|
// Delete comment
|
2024-12-11 08:29:04 +00:00
|
|
|
document.addEventListener('click', async (e: MouseEvent & {target: HTMLElement}) => {
|
2024-03-31 12:01:21 +00:00
|
|
|
if (!e.target.matches('.delete-comment')) return;
|
|
|
|
e.preventDefault();
|
|
|
|
|
|
|
|
const deleteButton = e.target;
|
|
|
|
if (window.confirm(deleteButton.getAttribute('data-locale'))) {
|
2024-03-14 21:36:17 +00:00
|
|
|
try {
|
2024-03-31 12:01:21 +00:00
|
|
|
const response = await POST(deleteButton.getAttribute('data-url'));
|
2024-03-14 21:36:17 +00:00
|
|
|
if (!response.ok) throw new Error('Failed to delete comment');
|
2024-03-31 12:01:21 +00:00
|
|
|
|
|
|
|
const conversationHolder = deleteButton.closest('.conversation-holder');
|
|
|
|
const parentTimelineItem = deleteButton.closest('.timeline-item');
|
|
|
|
const parentTimelineGroup = deleteButton.closest('.timeline-item-group');
|
|
|
|
|
2022-05-07 05:35:12 +00:00
|
|
|
// Check if this was a pending comment.
|
2024-03-31 12:01:21 +00:00
|
|
|
if (conversationHolder?.querySelector('.pending-label')) {
|
2024-03-26 19:33:32 +00:00
|
|
|
const counter = document.querySelector('#review-box .review-comments-counter');
|
|
|
|
let num = parseInt(counter?.getAttribute('data-pending-comment-number')) - 1 || 0;
|
2022-05-07 05:35:12 +00:00
|
|
|
num = Math.max(num, 0);
|
2024-12-11 08:29:04 +00:00
|
|
|
counter.setAttribute('data-pending-comment-number', String(num));
|
2024-03-26 19:33:32 +00:00
|
|
|
counter.textContent = String(num);
|
2022-05-07 05:35:12 +00:00
|
|
|
}
|
|
|
|
|
2024-06-10 20:49:33 +00:00
|
|
|
document.querySelector(`#${deleteButton.getAttribute('data-comment-id')}`)?.remove();
|
2024-03-31 12:01:21 +00:00
|
|
|
|
|
|
|
if (conversationHolder && !conversationHolder.querySelector('.comment')) {
|
|
|
|
const path = conversationHolder.getAttribute('data-path');
|
|
|
|
const side = conversationHolder.getAttribute('data-side');
|
|
|
|
const idx = conversationHolder.getAttribute('data-idx');
|
2024-10-16 12:39:47 +00:00
|
|
|
const lineType = conversationHolder.closest('tr')?.getAttribute('data-line-type');
|
|
|
|
|
|
|
|
// the conversation holder could appear either on the "Conversation" page, or the "Files Changed" page
|
|
|
|
// on the Conversation page, there is no parent "tr", so no need to do anything for "add-code-comment"
|
|
|
|
if (lineType) {
|
|
|
|
if (lineType === 'same') {
|
|
|
|
document.querySelector(`[data-path="${path}"] .add-code-comment[data-idx="${idx}"]`).classList.remove('tw-invisible');
|
|
|
|
} else {
|
|
|
|
document.querySelector(`[data-path="${path}"] .add-code-comment[data-side="${side}"][data-idx="${idx}"]`).classList.remove('tw-invisible');
|
|
|
|
}
|
2021-10-16 17:28:04 +00:00
|
|
|
}
|
2024-03-31 12:01:21 +00:00
|
|
|
conversationHolder.remove();
|
2021-10-16 17:28:04 +00:00
|
|
|
}
|
2024-03-31 12:01:21 +00:00
|
|
|
|
2024-03-21 10:38:27 +00:00
|
|
|
// Check if there is no review content, move the time avatar upward to avoid overlapping the content below.
|
2024-03-31 12:01:21 +00:00
|
|
|
if (!parentTimelineGroup?.querySelector('.timeline-item.comment') && !parentTimelineItem?.querySelector('.conversation-holder')) {
|
|
|
|
const timelineAvatar = parentTimelineGroup?.querySelector('.timeline-avatar');
|
|
|
|
timelineAvatar?.classList.remove('timeline-avatar-offset');
|
2024-03-21 10:38:27 +00:00
|
|
|
}
|
2024-03-14 21:36:17 +00:00
|
|
|
} catch (error) {
|
|
|
|
console.error(error);
|
|
|
|
}
|
2021-10-16 17:28:04 +00:00
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
export function initRepoIssueDependencyDelete() {
|
|
|
|
// Delete Issue dependency
|
|
|
|
$(document).on('click', '.delete-dependency-button', (e) => {
|
2021-11-22 08:19:01 +00:00
|
|
|
const id = e.currentTarget.getAttribute('data-id');
|
|
|
|
const type = e.currentTarget.getAttribute('data-type');
|
2021-10-16 17:28:04 +00:00
|
|
|
|
|
|
|
$('.remove-dependency').modal({
|
|
|
|
closable: false,
|
|
|
|
duration: 200,
|
|
|
|
onApprove: () => {
|
|
|
|
$('#removeDependencyID').val(id);
|
|
|
|
$('#dependencyType').val(type);
|
|
|
|
$('#removeDependencyForm').trigger('submit');
|
|
|
|
},
|
|
|
|
}).modal('show');
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
export function initRepoIssueCodeCommentCancel() {
|
|
|
|
// Cancel inline code comment
|
2024-12-11 08:29:04 +00:00
|
|
|
document.addEventListener('click', (e: MouseEvent & {target: HTMLElement}) => {
|
2024-03-31 12:01:21 +00:00
|
|
|
if (!e.target.matches('.cancel-code-comment')) return;
|
|
|
|
|
|
|
|
const form = e.target.closest('form');
|
|
|
|
if (form?.classList.contains('comment-form')) {
|
|
|
|
hideElem(form);
|
|
|
|
showElem(form.closest('.comment-code-cloud')?.querySelectorAll('button.comment-form-reply'));
|
2021-10-16 17:28:04 +00:00
|
|
|
} else {
|
2024-03-31 12:01:21 +00:00
|
|
|
form.closest('.comment-code-cloud')?.remove();
|
2021-10-16 17:28:04 +00:00
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
export function initRepoPullRequestUpdate() {
|
|
|
|
// Pull Request update button
|
2024-03-31 12:01:21 +00:00
|
|
|
const pullUpdateButton = document.querySelector('.update-button > button');
|
|
|
|
if (!pullUpdateButton) return;
|
|
|
|
|
|
|
|
pullUpdateButton.addEventListener('click', async function (e) {
|
2021-10-16 17:28:04 +00:00
|
|
|
e.preventDefault();
|
2024-03-31 12:01:21 +00:00
|
|
|
const redirect = this.getAttribute('data-redirect');
|
|
|
|
this.classList.add('is-loading');
|
2024-03-14 21:36:17 +00:00
|
|
|
let response;
|
|
|
|
try {
|
2024-03-31 12:01:21 +00:00
|
|
|
response = await POST(this.getAttribute('data-do'));
|
2024-03-14 21:36:17 +00:00
|
|
|
} catch (error) {
|
|
|
|
console.error(error);
|
|
|
|
} finally {
|
2024-03-31 12:01:21 +00:00
|
|
|
this.classList.remove('is-loading');
|
2024-03-14 21:36:17 +00:00
|
|
|
}
|
|
|
|
let data;
|
|
|
|
try {
|
|
|
|
data = await response?.json(); // the response is probably not a JSON
|
|
|
|
} catch (error) {
|
|
|
|
console.error(error);
|
|
|
|
}
|
|
|
|
if (data?.redirect) {
|
|
|
|
window.location.href = data.redirect;
|
|
|
|
} else if (redirect) {
|
|
|
|
window.location.href = redirect;
|
|
|
|
} else {
|
|
|
|
window.location.reload();
|
|
|
|
}
|
2021-10-16 17:28:04 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
$('.update-button > .dropdown').dropdown({
|
|
|
|
onChange(_text, _value, $choice) {
|
2024-06-10 10:12:31 +00:00
|
|
|
const choiceEl = $choice[0];
|
|
|
|
const url = choiceEl.getAttribute('data-do');
|
2024-03-31 12:01:21 +00:00
|
|
|
if (url) {
|
|
|
|
const buttonText = pullUpdateButton.querySelector('.button-text');
|
|
|
|
if (buttonText) {
|
2024-06-10 10:12:31 +00:00
|
|
|
buttonText.textContent = choiceEl.textContent;
|
2024-03-31 12:01:21 +00:00
|
|
|
}
|
|
|
|
pullUpdateButton.setAttribute('data-do', url);
|
2021-10-16 17:28:04 +00:00
|
|
|
}
|
2024-03-22 14:06:53 +00:00
|
|
|
},
|
2021-10-16 17:28:04 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
export function initRepoPullRequestMergeInstruction() {
|
|
|
|
$('.show-instruction').on('click', () => {
|
2023-02-19 04:06:14 +00:00
|
|
|
toggleElem($('.instruct-content'));
|
2021-10-16 17:28:04 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2022-04-28 15:45:33 +00:00
|
|
|
export function initRepoPullRequestAllowMaintainerEdit() {
|
2024-06-10 20:49:33 +00:00
|
|
|
const wrapper = document.querySelector('#allow-edits-from-maintainers');
|
2024-03-29 04:56:01 +00:00
|
|
|
if (!wrapper) return;
|
2024-12-11 08:29:04 +00:00
|
|
|
const checkbox = wrapper.querySelector<HTMLInputElement>('input[type="checkbox"]');
|
2024-05-06 06:32:05 +00:00
|
|
|
checkbox.addEventListener('input', async () => {
|
2024-03-29 04:56:01 +00:00
|
|
|
const url = `${wrapper.getAttribute('data-url')}/set_allow_maintainer_edit`;
|
|
|
|
wrapper.classList.add('is-loading');
|
|
|
|
try {
|
2024-12-11 08:29:04 +00:00
|
|
|
const resp = await POST(url, {data: new URLSearchParams({
|
|
|
|
allow_maintainer_edit: String(checkbox.checked),
|
|
|
|
})});
|
2024-05-06 06:32:05 +00:00
|
|
|
if (!resp.ok) {
|
2024-03-29 04:56:01 +00:00
|
|
|
throw new Error('Failed to update maintainer edit permission');
|
2024-03-14 21:36:17 +00:00
|
|
|
}
|
2024-05-06 06:32:05 +00:00
|
|
|
const data = await resp.json();
|
|
|
|
checkbox.checked = data.allow_maintainer_edit;
|
2024-03-29 04:56:01 +00:00
|
|
|
} catch (error) {
|
2024-05-06 06:32:05 +00:00
|
|
|
checkbox.checked = !checkbox.checked;
|
2024-03-29 04:56:01 +00:00
|
|
|
console.error(error);
|
|
|
|
showTemporaryTooltip(wrapper, wrapper.getAttribute('data-prompt-error'));
|
|
|
|
} finally {
|
|
|
|
wrapper.classList.remove('is-loading');
|
|
|
|
}
|
2022-04-28 15:45:33 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2021-10-16 17:28:04 +00:00
|
|
|
export function initRepoIssueReferenceRepositorySearch() {
|
|
|
|
$('.issue_reference_repository_search')
|
|
|
|
.dropdown({
|
|
|
|
apiSettings: {
|
2022-04-07 18:59:56 +00:00
|
|
|
url: `${appSubUrl}/repo/search?q={query}&limit=20`,
|
2021-10-16 17:28:04 +00:00
|
|
|
onResponse(response) {
|
|
|
|
const filteredResponse = {success: true, results: []};
|
|
|
|
$.each(response.data, (_r, repo) => {
|
|
|
|
filteredResponse.results.push({
|
2023-05-13 21:59:01 +00:00
|
|
|
name: htmlEscape(repo.repository.full_name),
|
2024-03-22 14:06:53 +00:00
|
|
|
value: repo.repository.full_name,
|
2021-10-16 17:28:04 +00:00
|
|
|
});
|
|
|
|
});
|
|
|
|
return filteredResponse;
|
|
|
|
},
|
|
|
|
cache: false,
|
|
|
|
},
|
|
|
|
onChange(_value, _text, $choice) {
|
|
|
|
const $form = $choice.closest('form');
|
2024-03-26 19:33:32 +00:00
|
|
|
if (!$form.length) return;
|
|
|
|
|
|
|
|
$form[0].setAttribute('action', `${appSubUrl}/${_text}/issues/new`);
|
2021-10-16 17:28:04 +00:00
|
|
|
},
|
2024-03-22 14:06:53 +00:00
|
|
|
fullTextSearch: true,
|
2021-10-16 17:28:04 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
export function initRepoIssueWipTitle() {
|
|
|
|
$('.title_wip_desc > a').on('click', (e) => {
|
|
|
|
e.preventDefault();
|
|
|
|
|
|
|
|
const $issueTitle = $('#issue_title');
|
2023-04-20 09:28:27 +00:00
|
|
|
$issueTitle.trigger('focus');
|
2024-12-11 08:29:04 +00:00
|
|
|
const value = ($issueTitle.val() as string).trim().toUpperCase();
|
2021-10-16 17:28:04 +00:00
|
|
|
|
|
|
|
const wipPrefixes = $('.title_wip_desc').data('wip-prefixes');
|
|
|
|
for (const prefix of wipPrefixes) {
|
|
|
|
if (value.startsWith(prefix.toUpperCase())) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
$issueTitle.val(`${wipPrefixes[0]} ${$issueTitle.val()}`);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
export function initRepoIssueComments() {
|
2024-03-25 18:37:55 +00:00
|
|
|
if (!$('.repository.view.issue .timeline').length) return;
|
2021-10-16 17:28:04 +00:00
|
|
|
|
2024-12-11 08:29:04 +00:00
|
|
|
document.addEventListener('click', (e: MouseEvent & {target: HTMLElement}) => {
|
2024-03-26 19:33:32 +00:00
|
|
|
const urlTarget = document.querySelector(':target');
|
|
|
|
if (!urlTarget) return;
|
2021-10-16 17:28:04 +00:00
|
|
|
|
2024-03-26 19:33:32 +00:00
|
|
|
const urlTargetId = urlTarget.id;
|
2021-10-16 17:28:04 +00:00
|
|
|
if (!urlTargetId) return;
|
|
|
|
|
2024-03-26 19:33:32 +00:00
|
|
|
if (!/^(issue|pull)(comment)?-\d+$/.test(urlTargetId)) return;
|
2021-10-16 17:28:04 +00:00
|
|
|
|
2024-03-26 19:33:32 +00:00
|
|
|
if (!e.target.closest(`#${urlTargetId}`)) {
|
2021-10-16 17:28:04 +00:00
|
|
|
const scrollPosition = $(window).scrollTop();
|
|
|
|
window.location.hash = '';
|
|
|
|
$(window).scrollTop(scrollPosition);
|
|
|
|
window.history.pushState(null, null, ' ');
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2024-06-26 17:01:20 +00:00
|
|
|
export async function handleReply(el) {
|
|
|
|
const form = el.closest('.comment-code-cloud').querySelector('.comment-form');
|
|
|
|
const textarea = form.querySelector('textarea');
|
|
|
|
|
|
|
|
hideElem(el);
|
|
|
|
showElem(form);
|
|
|
|
const editor = getComboMarkdownEditor(textarea) ?? await initComboMarkdownEditor(form.querySelector('.combo-markdown-editor'));
|
2023-04-03 10:06:57 +00:00
|
|
|
editor.focus();
|
|
|
|
return editor;
|
2023-03-02 19:53:22 +00:00
|
|
|
}
|
|
|
|
|
2021-10-16 17:28:04 +00:00
|
|
|
export function initRepoPullRequestReview() {
|
|
|
|
if (window.location.hash && window.location.hash.startsWith('#issuecomment-')) {
|
Imrove scroll behavior to hash issuecomment(scroll position, auto expand if file is folded, and on refreshing) (#23513)
Close #23466
Right now on pull request "files Changed" tab, if a file is viewed, when
the comments' links are visited, the comment will not be shown as the
file is folded after viewed. This PR is to improve the behavior, to make
the comment seen even the related file is folded, like on github.
And right now scroll position will be remembered and hence it won’t
scroll to hashed comment after refreshing, this PR also adjust the
scroll position remembering behavior: When there is hash comment in url,
do not remember the scroll position.
Before:
https://user-images.githubusercontent.com/17645053/225512079-6cf79581-9346-44cf-95d6-06919642e6a8.mov
After:
https://user-images.githubusercontent.com/17645053/225523753-3f6728f2-977b-4ed0-a65c-63dcef2ace80.mov
Update - long comment's behavior after using `scrollTop ` (Comment div
scroll to the position which is 30px below the diff header, or 30px
below top on conversation tab):
https://user-images.githubusercontent.com/17645053/225614460-0602c1a6-229c-41f4-84d2-334e78251486.mov
2023-03-17 10:24:18 +00:00
|
|
|
// set scrollRestoration to 'manual' when there is a hash in url, so that the scroll position will not be remembered after refreshing
|
|
|
|
if (window.history.scrollRestoration !== 'manual') {
|
|
|
|
window.history.scrollRestoration = 'manual';
|
|
|
|
}
|
2024-03-26 19:33:32 +00:00
|
|
|
const commentDiv = document.querySelector(window.location.hash);
|
|
|
|
if (commentDiv) {
|
2021-10-16 17:28:04 +00:00
|
|
|
// get the name of the parent id
|
2024-03-26 19:33:32 +00:00
|
|
|
const groupID = commentDiv.closest('div[id^="code-comments-"]')?.getAttribute('id');
|
2021-10-16 17:28:04 +00:00
|
|
|
if (groupID && groupID.startsWith('code-comments-')) {
|
2022-02-18 06:50:36 +00:00
|
|
|
const id = groupID.slice(14);
|
2024-03-26 19:33:32 +00:00
|
|
|
const ancestorDiffBox = commentDiv.closest('.diff-file-box');
|
Imrove scroll behavior to hash issuecomment(scroll position, auto expand if file is folded, and on refreshing) (#23513)
Close #23466
Right now on pull request "files Changed" tab, if a file is viewed, when
the comments' links are visited, the comment will not be shown as the
file is folded after viewed. This PR is to improve the behavior, to make
the comment seen even the related file is folded, like on github.
And right now scroll position will be remembered and hence it won’t
scroll to hashed comment after refreshing, this PR also adjust the
scroll position remembering behavior: When there is hash comment in url,
do not remember the scroll position.
Before:
https://user-images.githubusercontent.com/17645053/225512079-6cf79581-9346-44cf-95d6-06919642e6a8.mov
After:
https://user-images.githubusercontent.com/17645053/225523753-3f6728f2-977b-4ed0-a65c-63dcef2ace80.mov
Update - long comment's behavior after using `scrollTop ` (Comment div
scroll to the position which is 30px below the diff header, or 30px
below top on conversation tab):
https://user-images.githubusercontent.com/17645053/225614460-0602c1a6-229c-41f4-84d2-334e78251486.mov
2023-03-17 10:24:18 +00:00
|
|
|
// on pages like conversation, there is no diff header
|
2024-03-26 19:33:32 +00:00
|
|
|
const diffHeader = ancestorDiffBox?.querySelector('.diff-file-header');
|
|
|
|
|
Imrove scroll behavior to hash issuecomment(scroll position, auto expand if file is folded, and on refreshing) (#23513)
Close #23466
Right now on pull request "files Changed" tab, if a file is viewed, when
the comments' links are visited, the comment will not be shown as the
file is folded after viewed. This PR is to improve the behavior, to make
the comment seen even the related file is folded, like on github.
And right now scroll position will be remembered and hence it won’t
scroll to hashed comment after refreshing, this PR also adjust the
scroll position remembering behavior: When there is hash comment in url,
do not remember the scroll position.
Before:
https://user-images.githubusercontent.com/17645053/225512079-6cf79581-9346-44cf-95d6-06919642e6a8.mov
After:
https://user-images.githubusercontent.com/17645053/225523753-3f6728f2-977b-4ed0-a65c-63dcef2ace80.mov
Update - long comment's behavior after using `scrollTop ` (Comment div
scroll to the position which is 30px below the diff header, or 30px
below top on conversation tab):
https://user-images.githubusercontent.com/17645053/225614460-0602c1a6-229c-41f4-84d2-334e78251486.mov
2023-03-17 10:24:18 +00:00
|
|
|
// offset is for scrolling
|
|
|
|
let offset = 30;
|
2024-03-26 19:33:32 +00:00
|
|
|
if (diffHeader) {
|
|
|
|
offset += $('.diff-detail-box').outerHeight() + $(diffHeader).outerHeight();
|
Imrove scroll behavior to hash issuecomment(scroll position, auto expand if file is folded, and on refreshing) (#23513)
Close #23466
Right now on pull request "files Changed" tab, if a file is viewed, when
the comments' links are visited, the comment will not be shown as the
file is folded after viewed. This PR is to improve the behavior, to make
the comment seen even the related file is folded, like on github.
And right now scroll position will be remembered and hence it won’t
scroll to hashed comment after refreshing, this PR also adjust the
scroll position remembering behavior: When there is hash comment in url,
do not remember the scroll position.
Before:
https://user-images.githubusercontent.com/17645053/225512079-6cf79581-9346-44cf-95d6-06919642e6a8.mov
After:
https://user-images.githubusercontent.com/17645053/225523753-3f6728f2-977b-4ed0-a65c-63dcef2ace80.mov
Update - long comment's behavior after using `scrollTop ` (Comment div
scroll to the position which is 30px below the diff header, or 30px
below top on conversation tab):
https://user-images.githubusercontent.com/17645053/225614460-0602c1a6-229c-41f4-84d2-334e78251486.mov
2023-03-17 10:24:18 +00:00
|
|
|
}
|
2024-03-26 19:33:32 +00:00
|
|
|
|
2024-04-14 07:16:03 +00:00
|
|
|
hideElem(`#show-outdated-${id}`);
|
|
|
|
showElem(`#code-comments-${id}, #code-preview-${id}, #hide-outdated-${id}`);
|
Imrove scroll behavior to hash issuecomment(scroll position, auto expand if file is folded, and on refreshing) (#23513)
Close #23466
Right now on pull request "files Changed" tab, if a file is viewed, when
the comments' links are visited, the comment will not be shown as the
file is folded after viewed. This PR is to improve the behavior, to make
the comment seen even the related file is folded, like on github.
And right now scroll position will be remembered and hence it won’t
scroll to hashed comment after refreshing, this PR also adjust the
scroll position remembering behavior: When there is hash comment in url,
do not remember the scroll position.
Before:
https://user-images.githubusercontent.com/17645053/225512079-6cf79581-9346-44cf-95d6-06919642e6a8.mov
After:
https://user-images.githubusercontent.com/17645053/225523753-3f6728f2-977b-4ed0-a65c-63dcef2ace80.mov
Update - long comment's behavior after using `scrollTop ` (Comment div
scroll to the position which is 30px below the diff header, or 30px
below top on conversation tab):
https://user-images.githubusercontent.com/17645053/225614460-0602c1a6-229c-41f4-84d2-334e78251486.mov
2023-03-17 10:24:18 +00:00
|
|
|
// if the comment box is folded, expand it
|
2024-04-14 07:16:03 +00:00
|
|
|
if (ancestorDiffBox?.getAttribute('data-folded') === 'true') {
|
2024-03-26 19:33:32 +00:00
|
|
|
setFileFolding(ancestorDiffBox, ancestorDiffBox.querySelector('.fold-file'), false);
|
Imrove scroll behavior to hash issuecomment(scroll position, auto expand if file is folded, and on refreshing) (#23513)
Close #23466
Right now on pull request "files Changed" tab, if a file is viewed, when
the comments' links are visited, the comment will not be shown as the
file is folded after viewed. This PR is to improve the behavior, to make
the comment seen even the related file is folded, like on github.
And right now scroll position will be remembered and hence it won’t
scroll to hashed comment after refreshing, this PR also adjust the
scroll position remembering behavior: When there is hash comment in url,
do not remember the scroll position.
Before:
https://user-images.githubusercontent.com/17645053/225512079-6cf79581-9346-44cf-95d6-06919642e6a8.mov
After:
https://user-images.githubusercontent.com/17645053/225523753-3f6728f2-977b-4ed0-a65c-63dcef2ace80.mov
Update - long comment's behavior after using `scrollTop ` (Comment div
scroll to the position which is 30px below the diff header, or 30px
below top on conversation tab):
https://user-images.githubusercontent.com/17645053/225614460-0602c1a6-229c-41f4-84d2-334e78251486.mov
2023-03-17 10:24:18 +00:00
|
|
|
}
|
2024-03-26 19:33:32 +00:00
|
|
|
|
Imrove scroll behavior to hash issuecomment(scroll position, auto expand if file is folded, and on refreshing) (#23513)
Close #23466
Right now on pull request "files Changed" tab, if a file is viewed, when
the comments' links are visited, the comment will not be shown as the
file is folded after viewed. This PR is to improve the behavior, to make
the comment seen even the related file is folded, like on github.
And right now scroll position will be remembered and hence it won’t
scroll to hashed comment after refreshing, this PR also adjust the
scroll position remembering behavior: When there is hash comment in url,
do not remember the scroll position.
Before:
https://user-images.githubusercontent.com/17645053/225512079-6cf79581-9346-44cf-95d6-06919642e6a8.mov
After:
https://user-images.githubusercontent.com/17645053/225523753-3f6728f2-977b-4ed0-a65c-63dcef2ace80.mov
Update - long comment's behavior after using `scrollTop ` (Comment div
scroll to the position which is 30px below the diff header, or 30px
below top on conversation tab):
https://user-images.githubusercontent.com/17645053/225614460-0602c1a6-229c-41f4-84d2-334e78251486.mov
2023-03-17 10:24:18 +00:00
|
|
|
window.scrollTo({
|
2024-03-26 19:33:32 +00:00
|
|
|
top: $(commentDiv).offset().top - offset,
|
2024-03-22 14:06:53 +00:00
|
|
|
behavior: 'instant',
|
Imrove scroll behavior to hash issuecomment(scroll position, auto expand if file is folded, and on refreshing) (#23513)
Close #23466
Right now on pull request "files Changed" tab, if a file is viewed, when
the comments' links are visited, the comment will not be shown as the
file is folded after viewed. This PR is to improve the behavior, to make
the comment seen even the related file is folded, like on github.
And right now scroll position will be remembered and hence it won’t
scroll to hashed comment after refreshing, this PR also adjust the
scroll position remembering behavior: When there is hash comment in url,
do not remember the scroll position.
Before:
https://user-images.githubusercontent.com/17645053/225512079-6cf79581-9346-44cf-95d6-06919642e6a8.mov
After:
https://user-images.githubusercontent.com/17645053/225523753-3f6728f2-977b-4ed0-a65c-63dcef2ace80.mov
Update - long comment's behavior after using `scrollTop ` (Comment div
scroll to the position which is 30px below the diff header, or 30px
below top on conversation tab):
https://user-images.githubusercontent.com/17645053/225614460-0602c1a6-229c-41f4-84d2-334e78251486.mov
2023-03-17 10:24:18 +00:00
|
|
|
});
|
2021-10-16 17:28:04 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
$(document).on('click', '.show-outdated', function (e) {
|
|
|
|
e.preventDefault();
|
2024-03-31 12:01:21 +00:00
|
|
|
const id = this.getAttribute('data-comment');
|
|
|
|
hideElem(this);
|
|
|
|
showElem(`#code-comments-${id}`);
|
|
|
|
showElem(`#code-preview-${id}`);
|
|
|
|
showElem(`#hide-outdated-${id}`);
|
2021-10-16 17:28:04 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
$(document).on('click', '.hide-outdated', function (e) {
|
|
|
|
e.preventDefault();
|
2024-03-31 12:01:21 +00:00
|
|
|
const id = this.getAttribute('data-comment');
|
|
|
|
hideElem(this);
|
|
|
|
hideElem(`#code-comments-${id}`);
|
|
|
|
hideElem(`#code-preview-${id}`);
|
|
|
|
showElem(`#show-outdated-${id}`);
|
2021-10-16 17:28:04 +00:00
|
|
|
});
|
|
|
|
|
2022-01-05 12:17:25 +00:00
|
|
|
$(document).on('click', 'button.comment-form-reply', async function (e) {
|
2021-10-16 17:28:04 +00:00
|
|
|
e.preventDefault();
|
2024-06-26 17:01:20 +00:00
|
|
|
await handleReply(this);
|
2021-10-16 17:28:04 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
// The following part is only for diff views
|
2024-03-25 18:37:55 +00:00
|
|
|
if (!$('.repository.pull.diff').length) return;
|
2021-10-16 17:28:04 +00:00
|
|
|
|
2023-03-17 17:24:00 +00:00
|
|
|
const $reviewBtn = $('.js-btn-review');
|
|
|
|
const $panel = $reviewBtn.parent().find('.review-box-panel');
|
|
|
|
const $closeBtn = $panel.find('.close');
|
|
|
|
|
2023-03-18 21:08:38 +00:00
|
|
|
if ($reviewBtn.length && $panel.length) {
|
|
|
|
const tippy = createTippy($reviewBtn[0], {
|
|
|
|
content: $panel[0],
|
2024-04-30 14:52:46 +00:00
|
|
|
theme: 'default',
|
2023-03-18 21:08:38 +00:00
|
|
|
placement: 'bottom',
|
|
|
|
trigger: 'click',
|
|
|
|
maxWidth: 'none',
|
|
|
|
interactive: true,
|
|
|
|
hideOnClick: true,
|
|
|
|
});
|
2023-03-17 17:24:00 +00:00
|
|
|
|
2023-03-18 21:08:38 +00:00
|
|
|
$closeBtn.on('click', (e) => {
|
|
|
|
e.preventDefault();
|
|
|
|
tippy.hide();
|
|
|
|
});
|
|
|
|
}
|
2021-10-16 17:28:04 +00:00
|
|
|
|
2024-11-21 14:09:16 +00:00
|
|
|
addDelegatedEventListener(document, 'click', '.add-code-comment', async (el, e) => {
|
2021-10-16 17:28:04 +00:00
|
|
|
e.preventDefault();
|
|
|
|
|
2024-11-21 14:09:16 +00:00
|
|
|
const isSplit = el.closest('.code-diff')?.classList.contains('code-diff-split');
|
|
|
|
const side = el.getAttribute('data-side');
|
|
|
|
const idx = el.getAttribute('data-idx');
|
|
|
|
const path = el.closest('[data-path]')?.getAttribute('data-path');
|
|
|
|
const tr = el.closest('tr');
|
2024-03-31 12:01:21 +00:00
|
|
|
const lineType = tr.getAttribute('data-line-type');
|
2021-10-16 17:28:04 +00:00
|
|
|
|
2024-11-21 14:09:16 +00:00
|
|
|
let ntr = tr.nextElementSibling;
|
2024-03-31 12:01:21 +00:00
|
|
|
if (!ntr?.classList.contains('add-comment')) {
|
2024-11-21 14:09:16 +00:00
|
|
|
ntr = createElementFromHTML(`
|
2021-10-16 17:28:04 +00:00
|
|
|
<tr class="add-comment" data-line-type="${lineType}">
|
|
|
|
${isSplit ? `
|
2022-10-25 11:11:49 +00:00
|
|
|
<td class="add-comment-left" colspan="4"></td>
|
|
|
|
<td class="add-comment-right" colspan="4"></td>
|
2021-10-16 17:28:04 +00:00
|
|
|
` : `
|
2022-10-25 11:11:49 +00:00
|
|
|
<td class="add-comment-left add-comment-right" colspan="5"></td>
|
2021-10-16 17:28:04 +00:00
|
|
|
`}
|
|
|
|
</tr>`);
|
2024-11-21 14:09:16 +00:00
|
|
|
tr.after(ntr);
|
2021-10-16 17:28:04 +00:00
|
|
|
}
|
2024-11-21 14:09:16 +00:00
|
|
|
const td = ntr.querySelector(`.add-comment-${side}`);
|
|
|
|
const commentCloud = td.querySelector('.comment-code-cloud');
|
|
|
|
if (!commentCloud && !ntr.querySelector('button[name="pending_review"]')) {
|
|
|
|
const response = await GET(el.closest('[data-new-comment-url]')?.getAttribute('data-new-comment-url'));
|
|
|
|
td.innerHTML = await response.text();
|
|
|
|
td.querySelector<HTMLInputElement>("input[name='line']").value = idx;
|
|
|
|
td.querySelector<HTMLInputElement>("input[name='side']").value = (side === 'left' ? 'previous' : 'proposed');
|
|
|
|
td.querySelector<HTMLInputElement>("input[name='path']").value = path;
|
|
|
|
const editor = await initComboMarkdownEditor(td.querySelector<HTMLElement>('.combo-markdown-editor'));
|
|
|
|
editor.focus();
|
2021-10-16 17:28:04 +00:00
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
export function initRepoIssueReferenceIssue() {
|
|
|
|
// Reference issue
|
2024-12-11 08:29:04 +00:00
|
|
|
$(document).on('click', '.reference-issue', function (e) {
|
2024-06-10 10:12:31 +00:00
|
|
|
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');
|
2024-12-11 08:29:04 +00:00
|
|
|
e.preventDefault();
|
2021-10-16 17:28:04 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
export function initRepoIssueWipToggle() {
|
|
|
|
// Toggle WIP
|
|
|
|
$('.toggle-wip a, .toggle-wip button').on('click', async (e) => {
|
|
|
|
e.preventDefault();
|
2021-11-22 08:19:01 +00:00
|
|
|
const toggleWip = e.currentTarget.closest('.toggle-wip');
|
|
|
|
const title = toggleWip.getAttribute('data-title');
|
|
|
|
const wipPrefix = toggleWip.getAttribute('data-wip-prefix');
|
|
|
|
const updateUrl = toggleWip.getAttribute('data-update-url');
|
2024-03-14 21:36:17 +00:00
|
|
|
|
|
|
|
try {
|
|
|
|
const params = new URLSearchParams();
|
|
|
|
params.append('title', title?.startsWith(wipPrefix) ? title.slice(wipPrefix.length).trim() : `${wipPrefix.trim()} ${title}`);
|
|
|
|
|
|
|
|
const response = await POST(updateUrl, {data: params});
|
|
|
|
if (!response.ok) {
|
|
|
|
throw new Error('Failed to toggle WIP status');
|
|
|
|
}
|
|
|
|
window.location.reload();
|
|
|
|
} catch (error) {
|
|
|
|
console.error(error);
|
|
|
|
}
|
2021-10-16 17:28:04 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
export function initRepoIssueTitleEdit() {
|
2024-05-05 13:09:41 +00:00
|
|
|
const issueTitleDisplay = document.querySelector('#issue-title-display');
|
|
|
|
const issueTitleEditor = document.querySelector('#issue-title-editor');
|
|
|
|
if (!issueTitleEditor) return;
|
|
|
|
|
|
|
|
const issueTitleInput = issueTitleEditor.querySelector('input');
|
|
|
|
const oldTitle = issueTitleInput.getAttribute('data-old-title');
|
|
|
|
issueTitleDisplay.querySelector('#issue-title-edit-show').addEventListener('click', () => {
|
|
|
|
hideElem(issueTitleDisplay);
|
|
|
|
hideElem('#pull-desc-display');
|
|
|
|
showElem(issueTitleEditor);
|
|
|
|
showElem('#pull-desc-editor');
|
|
|
|
if (!issueTitleInput.value.trim()) {
|
|
|
|
issueTitleInput.value = oldTitle;
|
|
|
|
}
|
|
|
|
issueTitleInput.focus();
|
|
|
|
});
|
|
|
|
issueTitleEditor.querySelector('.ui.cancel.button').addEventListener('click', () => {
|
|
|
|
hideElem(issueTitleEditor);
|
|
|
|
hideElem('#pull-desc-editor');
|
|
|
|
showElem(issueTitleDisplay);
|
|
|
|
showElem('#pull-desc-display');
|
|
|
|
});
|
2024-05-16 13:04:25 +00:00
|
|
|
|
|
|
|
const pullDescEditor = document.querySelector('#pull-desc-editor'); // it may not exist for a merged PR
|
|
|
|
const prTargetUpdateUrl = pullDescEditor?.getAttribute('data-target-update-url');
|
|
|
|
|
2024-05-05 13:09:41 +00:00
|
|
|
const editSaveButton = issueTitleEditor.querySelector('.ui.primary.button');
|
|
|
|
editSaveButton.addEventListener('click', async () => {
|
|
|
|
const newTitle = issueTitleInput.value.trim();
|
|
|
|
try {
|
|
|
|
if (newTitle && newTitle !== oldTitle) {
|
|
|
|
const resp = await POST(editSaveButton.getAttribute('data-update-url'), {data: new URLSearchParams({title: newTitle})});
|
|
|
|
if (!resp.ok) {
|
|
|
|
throw new Error(`Failed to update issue title: ${resp.statusText}`);
|
2023-02-09 17:11:16 +00:00
|
|
|
}
|
2024-03-14 21:36:17 +00:00
|
|
|
}
|
2024-05-05 13:09:41 +00:00
|
|
|
if (prTargetUpdateUrl) {
|
|
|
|
const newTargetBranch = document.querySelector('#pull-target-branch').getAttribute('data-branch');
|
|
|
|
const oldTargetBranch = document.querySelector('#branch_target').textContent;
|
|
|
|
if (newTargetBranch !== oldTargetBranch) {
|
|
|
|
const resp = await POST(prTargetUpdateUrl, {data: new URLSearchParams({target_branch: newTargetBranch})});
|
|
|
|
if (!resp.ok) {
|
|
|
|
throw new Error(`Failed to update PR target branch: ${resp.statusText}`);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
window.location.reload();
|
|
|
|
} catch (error) {
|
|
|
|
console.error(error);
|
|
|
|
showErrorToast(error.message);
|
2021-10-16 17:28:04 +00:00
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
export function initRepoIssueBranchSelect() {
|
2024-12-11 08:29:04 +00:00
|
|
|
document.querySelector('#branch-select')?.addEventListener('click', (e: MouseEvent & {target: HTMLElement}) => {
|
2024-05-05 13:09:41 +00:00
|
|
|
const el = e.target.closest('.item[data-branch]');
|
|
|
|
if (!el) return;
|
|
|
|
const pullTargetBranch = document.querySelector('#pull-target-branch');
|
|
|
|
const baseName = pullTargetBranch.getAttribute('data-basename');
|
|
|
|
const branchNameNew = el.getAttribute('data-branch');
|
|
|
|
const branchNameOld = pullTargetBranch.getAttribute('data-branch');
|
|
|
|
pullTargetBranch.textContent = pullTargetBranch.textContent.replace(`${baseName}:${branchNameOld}`, `${baseName}:${branchNameNew}`);
|
|
|
|
pullTargetBranch.setAttribute('data-branch', branchNameNew);
|
|
|
|
});
|
2021-10-16 17:28:04 +00:00
|
|
|
}
|
2023-05-07 15:44:16 +00:00
|
|
|
|
2024-11-03 11:00:12 +00:00
|
|
|
async function initSingleCommentEditor($commentForm) {
|
2023-05-08 22:22:52 +00:00
|
|
|
// pages:
|
2024-06-23 18:41:01 +00:00
|
|
|
// * normal new issue/pr page: no status-button, no comment-button (there is only a normal submit button which can submit empty content)
|
|
|
|
// * issue/pr view page: with comment form, has status-button and comment-button
|
2024-10-23 02:48:04 +00:00
|
|
|
const editor = await initComboMarkdownEditor($commentForm[0].querySelector('.combo-markdown-editor'));
|
|
|
|
const statusButton = document.querySelector<HTMLButtonElement>('#status-button');
|
|
|
|
const commentButton = document.querySelector<HTMLButtonElement>('#comment-button');
|
|
|
|
const syncUiState = () => {
|
|
|
|
const editorText = editor.value().trim(), isUploading = editor.isUploading();
|
2024-06-23 18:41:01 +00:00
|
|
|
if (statusButton) {
|
|
|
|
statusButton.textContent = statusButton.getAttribute(editorText ? 'data-status-and-comment' : 'data-status');
|
2024-10-23 02:48:04 +00:00
|
|
|
statusButton.disabled = isUploading;
|
2024-06-23 18:41:01 +00:00
|
|
|
}
|
|
|
|
if (commentButton) {
|
2024-10-23 02:48:04 +00:00
|
|
|
commentButton.disabled = !editorText || isUploading;
|
2024-06-23 18:41:01 +00:00
|
|
|
}
|
|
|
|
};
|
2024-10-23 02:48:04 +00:00
|
|
|
editor.container.addEventListener(ComboMarkdownEditor.EventUploadStateChanged, syncUiState);
|
|
|
|
editor.container.addEventListener(ComboMarkdownEditor.EventEditorContentChanged, syncUiState);
|
|
|
|
syncUiState();
|
2023-05-08 22:22:52 +00:00
|
|
|
}
|
|
|
|
|
2024-11-03 11:00:12 +00:00
|
|
|
function initIssueTemplateCommentEditors($commentForm) {
|
2023-05-08 22:22:52 +00:00
|
|
|
// pages:
|
|
|
|
// * new issue with issue template
|
|
|
|
const $comboFields = $commentForm.find('.combo-editor-dropzone');
|
|
|
|
|
2024-10-23 02:48:04 +00:00
|
|
|
const initCombo = async (elCombo) => {
|
|
|
|
const $formField = $(elCombo.querySelector('.form-field-real'));
|
|
|
|
const dropzoneContainer = elCombo.querySelector('.form-field-dropzone');
|
|
|
|
const markdownEditor = elCombo.querySelector('.combo-markdown-editor');
|
2023-05-08 22:22:52 +00:00
|
|
|
|
2024-10-23 02:48:04 +00:00
|
|
|
const editor = await initComboMarkdownEditor(markdownEditor);
|
|
|
|
editor.container.addEventListener(ComboMarkdownEditor.EventEditorContentChanged, () => $formField.val(editor.value()));
|
2023-05-08 22:22:52 +00:00
|
|
|
|
|
|
|
$formField.on('focus', async () => {
|
|
|
|
// deactivate all markdown editors
|
|
|
|
showElem($commentForm.find('.combo-editor-dropzone .form-field-real'));
|
|
|
|
hideElem($commentForm.find('.combo-editor-dropzone .combo-markdown-editor'));
|
|
|
|
hideElem($commentForm.find('.combo-editor-dropzone .form-field-dropzone'));
|
|
|
|
|
|
|
|
// activate this markdown editor
|
|
|
|
hideElem($formField);
|
2024-10-23 02:48:04 +00:00
|
|
|
showElem(markdownEditor);
|
|
|
|
showElem(dropzoneContainer);
|
2023-05-08 22:22:52 +00:00
|
|
|
|
|
|
|
await editor.switchToUserPreference();
|
|
|
|
editor.focus();
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
|
|
|
for (const el of $comboFields) {
|
2024-10-23 02:48:04 +00:00
|
|
|
initCombo(el);
|
2023-05-08 22:22:52 +00:00
|
|
|
}
|
|
|
|
}
|
2023-10-17 14:10:45 +00:00
|
|
|
|
2024-11-03 11:00:12 +00:00
|
|
|
export function initRepoCommentFormAndSidebar() {
|
|
|
|
const $commentForm = $('.comment.form');
|
|
|
|
if (!$commentForm.length) return;
|
|
|
|
|
|
|
|
if ($commentForm.find('.field.combo-editor-dropzone').length) {
|
|
|
|
// at the moment, if a form has multiple combo-markdown-editors, it must be an issue template form
|
|
|
|
initIssueTemplateCommentEditors($commentForm);
|
|
|
|
} else if ($commentForm.find('.combo-markdown-editor').length) {
|
|
|
|
// it's quite unclear about the "comment form" elements, sometimes it's for issue comment, sometimes it's for file editor/uploader message
|
|
|
|
initSingleCommentEditor($commentForm);
|
|
|
|
}
|
|
|
|
|
|
|
|
initRepoIssueSidebar();
|
|
|
|
}
|