2022-01-28 16:00:11 -05:00
|
|
|
import $ from 'jquery';
|
2021-10-16 13:28:04 -04:00
|
|
|
import {htmlEscape} from 'escape-goat';
|
Use a general approach to show tooltip, fix temporary tooltip bug (#23574)
## TLDR
* Improve performance: lazy creating the tippy instances.
* Transparently support all "tooltip" elements, no need to call
`initTooltip` again and again.
* Fix a temporary tooltip re-entrance bug, which causes showing temp
content forever.
* Upgrade vue3-calendar-heatmap to 2.0.2 with lazy tippy init
(initHeatmap time decreases from 100ms to 50ms)
## Details
### The performance
Creating a lot of tippy tooltip instances is expensive. This PR doesn't
create all tippy tooltip instances, instead, it only adds "mouseover"
event listener to necessary elements, and then switches to the tippy
tooltip
### The general approach for all tooltips
Before, dynamically generated tooltips need to be called with
`initTooltip`.
After, use MutationObserver to:
* Attach the event listeners to newly created tooltip elements, work for
Vue (easier than before)
* Catch changed attributes and update the tooltip content (better than
before)
It does help a lot, eg:
https://github.com/go-gitea/gitea/blob/1a4efa0ee9a49d48549be7479a46be133b9bc260/web_src/js/components/PullRequestMergeForm.vue#L33-L36
### Temporary tooltip re-entrance bug
To reproduce, on try.gitea.io, click the "copy clone url" quickly, then
the tooltip will be "Copied!" forever.
After this PR, with the help of `attachTippyTooltip`, the tooltip
content could be reset to the default correctly.
### Other changes
* `data-tooltip-content` is preferred from now on, the old
`data-content` may cause conflicts with other modules.
* `data-placement` was only used for tooltip, so it's renamed to
`data-tooltip-placement`, and removed from `createTippy`.
2023-03-23 05:56:15 -04:00
|
|
|
import {showTemporaryTooltip, createTippy} from '../modules/tippy.js';
|
2023-02-18 23:06:14 -05:00
|
|
|
import {hideElem, showElem, toggleElem} from '../utils/dom.js';
|
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 06:24:18 -04:00
|
|
|
import {setFileFolding} from './file-fold.js';
|
2023-04-03 06:06:57 -04:00
|
|
|
import {getComboMarkdownEditor, initComboMarkdownEditor} from './comp/ComboMarkdownEditor.js';
|
2024-01-07 05:50:03 -05:00
|
|
|
import {toAbsoluteUrl} from '../utils.js';
|
2024-02-25 01:00:55 -05:00
|
|
|
import {initDropzone} from './common-global.js';
|
2024-03-14 17:36:17 -04:00
|
|
|
import {POST, GET} from '../modules/fetch.js';
|
2024-05-20 19:32:48 -04:00
|
|
|
import {showErrorToast} from '../modules/toast.js';
|
2024-07-16 19:37:20 -04:00
|
|
|
import {emojiHTML} from './emoji.js';
|
2021-10-16 13:28:04 -04:00
|
|
|
|
2024-03-14 17:36:17 -04:00
|
|
|
const {appSubUrl} = window.config;
|
2021-10-16 13:28:04 -04:00
|
|
|
|
2024-10-08 04:39:52 -04:00
|
|
|
// if there are draft comments, confirm before reloading, to avoid losing comments
|
|
|
|
export 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();
|
|
|
|
}
|
|
|
|
|
2021-10-16 13:28:04 -04:00
|
|
|
export function initRepoIssueTimeTracking() {
|
|
|
|
$(document).on('click', '.issue-add-time', () => {
|
|
|
|
$('.issue-start-time-modal').modal({
|
|
|
|
duration: 200,
|
|
|
|
onApprove() {
|
|
|
|
$('#add_time_manual_form').trigger('submit');
|
|
|
|
},
|
|
|
|
}).modal('show');
|
|
|
|
$('.issue-start-time-modal input').on('keydown', (e) => {
|
|
|
|
if ((e.keyCode || e.key) === 13) {
|
|
|
|
$('#add_time_manual_form').trigger('submit');
|
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
|
|
|
$(document).on('click', '.issue-start-time, .issue-stop-time', () => {
|
|
|
|
$('#toggle_stopwatch_form').trigger('submit');
|
|
|
|
});
|
|
|
|
$(document).on('click', '.issue-cancel-time', () => {
|
|
|
|
$('#cancel_stopwatch_form').trigger('submit');
|
|
|
|
});
|
|
|
|
$(document).on('click', 'button.issue-delete-time', function () {
|
|
|
|
const sel = `.issue-delete-time-modal[data-id="${$(this).data('id')}"]`;
|
2022-01-16 06:19:26 -05:00
|
|
|
$(sel).modal({
|
2021-10-16 13:28:04 -04:00
|
|
|
duration: 200,
|
|
|
|
onApprove() {
|
|
|
|
$(`${sel} form`).trigger('submit');
|
|
|
|
},
|
|
|
|
}).modal('show');
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2024-03-14 17:36:17 -04:00
|
|
|
async function updateDeadline(deadlineString) {
|
2024-03-26 15:33:32 -04:00
|
|
|
hideElem('#deadline-err-invalid-date');
|
|
|
|
document.getElementById('deadline-loader')?.classList.add('is-loading');
|
2021-10-16 13:28:04 -04:00
|
|
|
|
|
|
|
let realDeadline = null;
|
|
|
|
if (deadlineString !== '') {
|
|
|
|
const newDate = Date.parse(deadlineString);
|
|
|
|
|
|
|
|
if (Number.isNaN(newDate)) {
|
2024-03-26 15:33:32 -04:00
|
|
|
document.getElementById('deadline-loader')?.classList.remove('is-loading');
|
|
|
|
showElem('#deadline-err-invalid-date');
|
2021-10-16 13:28:04 -04:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
realDeadline = new Date(newDate);
|
|
|
|
}
|
|
|
|
|
2024-03-14 17:36:17 -04:00
|
|
|
try {
|
2024-03-26 15:33:32 -04:00
|
|
|
const response = await POST(document.getElementById('update-issue-deadline-form').getAttribute('action'), {
|
2024-03-22 10:06:53 -04:00
|
|
|
data: {due_date: realDeadline},
|
2024-03-14 17:36:17 -04:00
|
|
|
});
|
|
|
|
|
|
|
|
if (response.ok) {
|
2021-10-16 13:28:04 -04:00
|
|
|
window.location.reload();
|
2024-03-14 17:36:17 -04:00
|
|
|
} else {
|
|
|
|
throw new Error('Invalid response');
|
|
|
|
}
|
|
|
|
} catch (error) {
|
|
|
|
console.error(error);
|
2024-03-26 15:33:32 -04:00
|
|
|
document.getElementById('deadline-loader').classList.remove('is-loading');
|
|
|
|
showElem('#deadline-err-invalid-date');
|
2024-03-14 17:36:17 -04:00
|
|
|
}
|
2021-10-16 13:28:04 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
export function initRepoIssueDue() {
|
|
|
|
$(document).on('click', '.issue-due-edit', () => {
|
2023-03-27 21:07:21 -04:00
|
|
|
toggleElem('#deadlineForm');
|
2021-10-16 13:28:04 -04:00
|
|
|
});
|
|
|
|
$(document).on('click', '.issue-due-remove', () => {
|
|
|
|
updateDeadline('');
|
|
|
|
});
|
|
|
|
$(document).on('submit', '.issue-due-form', () => {
|
|
|
|
updateDeadline($('#deadlineDate').val());
|
|
|
|
return false;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2024-03-26 15:33:32 -04:00
|
|
|
/**
|
|
|
|
* @param {HTMLElement} item
|
|
|
|
*/
|
|
|
|
function excludeLabel(item) {
|
|
|
|
const href = item.getAttribute('href');
|
|
|
|
const id = item.getAttribute('data-label-id');
|
|
|
|
|
|
|
|
const regStr = `labels=((?:-?[0-9]+%2c)*)(${id})((?:%2c-?[0-9]+)*)&`;
|
|
|
|
const newStr = 'labels=$1-$2$3&';
|
|
|
|
|
2024-10-31 10:57:40 -04:00
|
|
|
window.location.assign(href.replace(new RegExp(regStr), newStr));
|
2024-03-26 15:33:32 -04:00
|
|
|
}
|
|
|
|
|
2023-04-06 20:11:02 -04:00
|
|
|
export function initRepoIssueSidebarList() {
|
2021-10-16 13:28:04 -04:00
|
|
|
const repolink = $('#repolink').val();
|
|
|
|
const repoId = $('#repoId').val();
|
|
|
|
const crossRepoSearch = $('#crossRepoSearch').val();
|
|
|
|
const tp = $('#type').val();
|
2022-04-07 14:59:56 -04:00
|
|
|
let issueSearchUrl = `${appSubUrl}/${repolink}/issues/search?q={query}&type=${tp}`;
|
2021-10-16 13:28:04 -04:00
|
|
|
if (crossRepoSearch === 'true') {
|
2022-04-07 14:59:56 -04:00
|
|
|
issueSearchUrl = `${appSubUrl}/issues/search?q={query}&priority_repo_id=${repoId}&type=${tp}`;
|
2021-10-16 13:28:04 -04:00
|
|
|
}
|
|
|
|
$('#new-dependency-drop-list')
|
|
|
|
.dropdown({
|
|
|
|
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({
|
2024-07-16 19:37:20 -04:00
|
|
|
name: `#${issue.number} ${issueTitleHTML(htmlEscape(issue.title))
|
2024-06-10 23:57:27 -04:00
|
|
|
}<div class="text small tw-break-anywhere">${htmlEscape(issue.repository.full_name)}</div>`,
|
2021-10-16 13:28:04 -04:00
|
|
|
value: issue.id,
|
|
|
|
});
|
|
|
|
});
|
|
|
|
return filteredResponse;
|
|
|
|
},
|
|
|
|
cache: false,
|
|
|
|
},
|
|
|
|
|
|
|
|
fullTextSearch: true,
|
|
|
|
});
|
|
|
|
|
|
|
|
$('.menu a.label-filter-item').each(function () {
|
|
|
|
$(this).on('click', function (e) {
|
|
|
|
if (e.altKey) {
|
|
|
|
e.preventDefault();
|
|
|
|
excludeLabel(this);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
$('.menu .ui.dropdown.label-filter').on('keydown', (e) => {
|
|
|
|
if (e.altKey && e.keyCode === 13) {
|
2024-03-26 15:33:32 -04:00
|
|
|
const selectedItem = document.querySelector('.menu .ui.dropdown.label-filter .menu .item.selected');
|
|
|
|
if (selectedItem) {
|
|
|
|
excludeLabel(selectedItem);
|
2021-10-16 13:28:04 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
2023-06-19 13:49:50 -04:00
|
|
|
$('.ui.dropdown.label-filter, .ui.dropdown.select-label').dropdown('setting', {'hideDividers': 'empty'}).dropdown('refreshItems');
|
2021-10-16 13:28:04 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
export function initRepoIssueCommentDelete() {
|
|
|
|
// Delete comment
|
2024-03-31 08:01:21 -04:00
|
|
|
document.addEventListener('click', async (e) => {
|
|
|
|
if (!e.target.matches('.delete-comment')) return;
|
|
|
|
e.preventDefault();
|
|
|
|
|
|
|
|
const deleteButton = e.target;
|
|
|
|
if (window.confirm(deleteButton.getAttribute('data-locale'))) {
|
2024-03-14 17:36:17 -04:00
|
|
|
try {
|
2024-03-31 08:01:21 -04:00
|
|
|
const response = await POST(deleteButton.getAttribute('data-url'));
|
2024-03-14 17:36:17 -04:00
|
|
|
if (!response.ok) throw new Error('Failed to delete comment');
|
2024-03-31 08:01:21 -04:00
|
|
|
|
|
|
|
const conversationHolder = deleteButton.closest('.conversation-holder');
|
|
|
|
const parentTimelineItem = deleteButton.closest('.timeline-item');
|
|
|
|
const parentTimelineGroup = deleteButton.closest('.timeline-item-group');
|
|
|
|
|
2022-05-07 01:35:12 -04:00
|
|
|
// Check if this was a pending comment.
|
2024-03-31 08:01:21 -04:00
|
|
|
if (conversationHolder?.querySelector('.pending-label')) {
|
2024-03-26 15:33:32 -04:00
|
|
|
const counter = document.querySelector('#review-box .review-comments-counter');
|
|
|
|
let num = parseInt(counter?.getAttribute('data-pending-comment-number')) - 1 || 0;
|
2022-05-07 01:35:12 -04:00
|
|
|
num = Math.max(num, 0);
|
2024-03-26 15:33:32 -04:00
|
|
|
counter.setAttribute('data-pending-comment-number', num);
|
|
|
|
counter.textContent = String(num);
|
2022-05-07 01:35:12 -04:00
|
|
|
}
|
|
|
|
|
2024-03-31 08:01:21 -04:00
|
|
|
document.getElementById(deleteButton.getAttribute('data-comment-id'))?.remove();
|
|
|
|
|
|
|
|
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 08:39:47 -04: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 13:28:04 -04:00
|
|
|
}
|
2024-03-31 08:01:21 -04:00
|
|
|
conversationHolder.remove();
|
2021-10-16 13:28:04 -04:00
|
|
|
}
|
2024-03-31 08:01:21 -04:00
|
|
|
|
2024-03-21 06:38:27 -04:00
|
|
|
// Check if there is no review content, move the time avatar upward to avoid overlapping the content below.
|
2024-03-31 08:01:21 -04: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 06:38:27 -04:00
|
|
|
}
|
2024-03-14 17:36:17 -04:00
|
|
|
} catch (error) {
|
|
|
|
console.error(error);
|
|
|
|
}
|
2021-10-16 13:28:04 -04:00
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
export function initRepoIssueDependencyDelete() {
|
|
|
|
// Delete Issue dependency
|
|
|
|
$(document).on('click', '.delete-dependency-button', (e) => {
|
2021-11-22 03:19:01 -05:00
|
|
|
const id = e.currentTarget.getAttribute('data-id');
|
|
|
|
const type = e.currentTarget.getAttribute('data-type');
|
2021-10-16 13:28:04 -04: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-03-31 08:01:21 -04:00
|
|
|
document.addEventListener('click', (e) => {
|
|
|
|
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 13:28:04 -04:00
|
|
|
} else {
|
2024-03-31 08:01:21 -04:00
|
|
|
form.closest('.comment-code-cloud')?.remove();
|
2021-10-16 13:28:04 -04:00
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
export function initRepoPullRequestUpdate() {
|
|
|
|
// Pull Request update button
|
2024-03-31 08:01:21 -04:00
|
|
|
const pullUpdateButton = document.querySelector('.update-button > button');
|
|
|
|
if (!pullUpdateButton) return;
|
|
|
|
|
|
|
|
pullUpdateButton.addEventListener('click', async function (e) {
|
2021-10-16 13:28:04 -04:00
|
|
|
e.preventDefault();
|
2024-03-31 08:01:21 -04:00
|
|
|
const redirect = this.getAttribute('data-redirect');
|
|
|
|
this.classList.add('is-loading');
|
2024-03-14 17:36:17 -04:00
|
|
|
let response;
|
|
|
|
try {
|
2024-03-31 08:01:21 -04:00
|
|
|
response = await POST(this.getAttribute('data-do'));
|
2024-03-14 17:36:17 -04:00
|
|
|
} catch (error) {
|
|
|
|
console.error(error);
|
|
|
|
} finally {
|
2024-03-31 08:01:21 -04:00
|
|
|
this.classList.remove('is-loading');
|
2024-03-14 17:36:17 -04: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 13:28:04 -04:00
|
|
|
});
|
|
|
|
|
|
|
|
$('.update-button > .dropdown').dropdown({
|
|
|
|
onChange(_text, _value, $choice) {
|
2024-03-31 08:01:21 -04:00
|
|
|
const url = $choice[0].getAttribute('data-do');
|
|
|
|
if (url) {
|
|
|
|
const buttonText = pullUpdateButton.querySelector('.button-text');
|
|
|
|
if (buttonText) {
|
|
|
|
buttonText.textContent = $choice.text();
|
|
|
|
}
|
|
|
|
pullUpdateButton.setAttribute('data-do', url);
|
2021-10-16 13:28:04 -04:00
|
|
|
}
|
2024-03-22 10:06:53 -04:00
|
|
|
},
|
2021-10-16 13:28:04 -04:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2022-04-28 11:45:33 -04:00
|
|
|
export function initRepoPullRequestAllowMaintainerEdit() {
|
2024-03-29 00:56:01 -04:00
|
|
|
const wrapper = document.getElementById('allow-edits-from-maintainers');
|
|
|
|
if (!wrapper) return;
|
2024-05-08 17:14:46 -04:00
|
|
|
const checkbox = wrapper.querySelector('input[type="checkbox"]');
|
|
|
|
checkbox.addEventListener('input', async () => {
|
2024-03-29 00:56:01 -04:00
|
|
|
const url = `${wrapper.getAttribute('data-url')}/set_allow_maintainer_edit`;
|
|
|
|
wrapper.classList.add('is-loading');
|
|
|
|
try {
|
2024-05-08 17:14:46 -04:00
|
|
|
const resp = await POST(url, {data: new URLSearchParams({allow_maintainer_edit: checkbox.checked})});
|
|
|
|
if (!resp.ok) {
|
2024-03-29 00:56:01 -04:00
|
|
|
throw new Error('Failed to update maintainer edit permission');
|
2024-03-14 17:36:17 -04:00
|
|
|
}
|
2024-05-08 17:14:46 -04:00
|
|
|
const data = await resp.json();
|
|
|
|
checkbox.checked = data.allow_maintainer_edit;
|
2024-03-29 00:56:01 -04:00
|
|
|
} catch (error) {
|
2024-05-08 17:14:46 -04:00
|
|
|
checkbox.checked = !checkbox.checked;
|
2024-03-29 00:56:01 -04:00
|
|
|
console.error(error);
|
|
|
|
showTemporaryTooltip(wrapper, wrapper.getAttribute('data-prompt-error'));
|
|
|
|
} finally {
|
|
|
|
wrapper.classList.remove('is-loading');
|
|
|
|
}
|
2022-04-28 11:45:33 -04:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2021-10-16 13:28:04 -04:00
|
|
|
export function initRepoIssueReferenceRepositorySearch() {
|
|
|
|
$('.issue_reference_repository_search')
|
|
|
|
.dropdown({
|
|
|
|
apiSettings: {
|
2022-04-07 14:59:56 -04:00
|
|
|
url: `${appSubUrl}/repo/search?q={query}&limit=20`,
|
2021-10-16 13:28:04 -04:00
|
|
|
onResponse(response) {
|
|
|
|
const filteredResponse = {success: true, results: []};
|
|
|
|
$.each(response.data, (_r, repo) => {
|
|
|
|
filteredResponse.results.push({
|
2023-05-13 17:59:01 -04:00
|
|
|
name: htmlEscape(repo.repository.full_name),
|
2024-03-22 10:06:53 -04:00
|
|
|
value: repo.repository.full_name,
|
2021-10-16 13:28:04 -04:00
|
|
|
});
|
|
|
|
});
|
|
|
|
return filteredResponse;
|
|
|
|
},
|
|
|
|
cache: false,
|
|
|
|
},
|
|
|
|
onChange(_value, _text, $choice) {
|
|
|
|
const $form = $choice.closest('form');
|
2024-03-26 15:33:32 -04:00
|
|
|
if (!$form.length) return;
|
|
|
|
|
|
|
|
$form[0].setAttribute('action', `${appSubUrl}/${_text}/issues/new`);
|
2021-10-16 13:28:04 -04:00
|
|
|
},
|
2024-03-22 10:06:53 -04:00
|
|
|
fullTextSearch: true,
|
2021-10-16 13:28:04 -04:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
export function initRepoIssueWipTitle() {
|
|
|
|
$('.title_wip_desc > a').on('click', (e) => {
|
|
|
|
e.preventDefault();
|
|
|
|
|
|
|
|
const $issueTitle = $('#issue_title');
|
2023-04-20 05:28:27 -04:00
|
|
|
$issueTitle.trigger('focus');
|
2021-10-16 13:28:04 -04:00
|
|
|
const value = $issueTitle.val().trim().toUpperCase();
|
|
|
|
|
|
|
|
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()}`);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2024-03-14 17:36:17 -04:00
|
|
|
export async function updateIssuesMeta(url, action, issue_ids, id) {
|
|
|
|
try {
|
|
|
|
const response = await POST(url, {data: new URLSearchParams({action, issue_ids, id})});
|
|
|
|
if (!response.ok) {
|
|
|
|
throw new Error('Failed to update issues meta');
|
|
|
|
}
|
|
|
|
} catch (error) {
|
|
|
|
console.error(error);
|
|
|
|
}
|
2021-10-16 13:28:04 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
export function initRepoIssueComments() {
|
2024-03-25 14:37:55 -04:00
|
|
|
if (!$('.repository.view.issue .timeline').length) return;
|
2021-10-16 13:28:04 -04:00
|
|
|
|
2024-02-16 16:41:23 -05:00
|
|
|
$('.re-request-review').on('click', async function (e) {
|
2021-11-22 03:19:01 -05:00
|
|
|
e.preventDefault();
|
2024-03-31 08:01:21 -04:00
|
|
|
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');
|
2021-10-16 13:28:04 -04:00
|
|
|
|
2024-02-16 16:41:23 -05:00
|
|
|
await updateIssuesMeta(url, isChecked ? 'detach' : 'attach', issueId, id);
|
|
|
|
window.location.reload();
|
2021-10-16 13:28:04 -04:00
|
|
|
});
|
|
|
|
|
2024-03-26 15:33:32 -04:00
|
|
|
document.addEventListener('click', (e) => {
|
|
|
|
const urlTarget = document.querySelector(':target');
|
|
|
|
if (!urlTarget) return;
|
2021-10-16 13:28:04 -04:00
|
|
|
|
2024-03-26 15:33:32 -04:00
|
|
|
const urlTargetId = urlTarget.id;
|
2021-10-16 13:28:04 -04:00
|
|
|
if (!urlTargetId) return;
|
|
|
|
|
2024-03-26 15:33:32 -04:00
|
|
|
if (!/^(issue|pull)(comment)?-\d+$/.test(urlTargetId)) return;
|
2021-10-16 13:28:04 -04:00
|
|
|
|
2024-03-26 15:33:32 -04:00
|
|
|
if (!e.target.closest(`#${urlTargetId}`)) {
|
2021-10-16 13:28:04 -04:00
|
|
|
const scrollPosition = $(window).scrollTop();
|
|
|
|
window.location.hash = '';
|
|
|
|
$(window).scrollTop(scrollPosition);
|
|
|
|
window.history.pushState(null, null, ' ');
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2023-03-02 14:53:22 -05:00
|
|
|
export async function handleReply($el) {
|
|
|
|
hideElem($el);
|
2024-03-16 08:22:16 -04:00
|
|
|
const $form = $el.closest('.comment-code-cloud').find('.comment-form');
|
2024-03-31 08:01:21 -04:00
|
|
|
showElem($form);
|
2023-04-03 06:06:57 -04:00
|
|
|
|
2024-03-16 08:22:16 -04:00
|
|
|
const $textarea = $form.find('textarea');
|
2023-04-03 06:06:57 -04:00
|
|
|
let editor = getComboMarkdownEditor($textarea);
|
|
|
|
if (!editor) {
|
2024-02-25 01:00:55 -05:00
|
|
|
// FIXME: the initialization of the dropzone is not consistent.
|
|
|
|
// When the page is loaded, the dropzone is initialized by initGlobalDropzone, but the editor is not initialized.
|
|
|
|
// When the form is submitted and partially reload, none of them is initialized.
|
2024-03-16 08:22:16 -04:00
|
|
|
const dropzone = $form.find('.dropzone')[0];
|
2024-02-25 01:00:55 -05:00
|
|
|
if (!dropzone.dropzone) initDropzone(dropzone);
|
2024-03-16 08:22:16 -04:00
|
|
|
editor = await initComboMarkdownEditor($form.find('.combo-markdown-editor'));
|
2023-03-02 14:53:22 -05:00
|
|
|
}
|
2023-04-03 06:06:57 -04:00
|
|
|
editor.focus();
|
|
|
|
return editor;
|
2023-03-02 14:53:22 -05:00
|
|
|
}
|
|
|
|
|
2021-10-16 13:28:04 -04: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 06:24:18 -04: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 15:33:32 -04:00
|
|
|
const commentDiv = document.querySelector(window.location.hash);
|
|
|
|
if (commentDiv) {
|
2021-10-16 13:28:04 -04:00
|
|
|
// get the name of the parent id
|
2024-03-26 15:33:32 -04:00
|
|
|
const groupID = commentDiv.closest('div[id^="code-comments-"]')?.getAttribute('id');
|
2021-10-16 13:28:04 -04:00
|
|
|
if (groupID && groupID.startsWith('code-comments-')) {
|
2022-02-18 01:50:36 -05:00
|
|
|
const id = groupID.slice(14);
|
2024-03-26 15:33:32 -04: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 06:24:18 -04:00
|
|
|
// on pages like conversation, there is no diff header
|
2024-03-26 15:33:32 -04: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 06:24:18 -04:00
|
|
|
// offset is for scrolling
|
|
|
|
let offset = 30;
|
2024-03-26 15:33:32 -04: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 06:24:18 -04:00
|
|
|
}
|
2024-03-26 15:33:32 -04:00
|
|
|
|
2024-04-14 03:16:03 -04: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 06:24:18 -04:00
|
|
|
// if the comment box is folded, expand it
|
2024-04-14 03:16:03 -04:00
|
|
|
if (ancestorDiffBox?.getAttribute('data-folded') === 'true') {
|
2024-03-26 15:33:32 -04: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 06:24:18 -04:00
|
|
|
}
|
2024-03-26 15:33:32 -04: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 06:24:18 -04:00
|
|
|
window.scrollTo({
|
2024-03-26 15:33:32 -04:00
|
|
|
top: $(commentDiv).offset().top - offset,
|
2024-03-22 10:06:53 -04: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 06:24:18 -04:00
|
|
|
});
|
2021-10-16 13:28:04 -04:00
|
|
|
}
|
|
|
|
}
|
2024-10-24 09:13:58 -04:00
|
|
|
} else if (window.history.scrollRestoration === 'manual') {
|
|
|
|
// reset scrollRestoration to 'auto' if there is no hash in url and we set it to 'manual' before
|
|
|
|
window.history.scrollRestoration = 'auto';
|
2021-10-16 13:28:04 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
$(document).on('click', '.show-outdated', function (e) {
|
|
|
|
e.preventDefault();
|
2024-03-31 08:01:21 -04:00
|
|
|
const id = this.getAttribute('data-comment');
|
|
|
|
hideElem(this);
|
|
|
|
showElem(`#code-comments-${id}`);
|
|
|
|
showElem(`#code-preview-${id}`);
|
|
|
|
showElem(`#hide-outdated-${id}`);
|
2021-10-16 13:28:04 -04:00
|
|
|
});
|
|
|
|
|
|
|
|
$(document).on('click', '.hide-outdated', function (e) {
|
|
|
|
e.preventDefault();
|
2024-03-31 08:01:21 -04:00
|
|
|
const id = this.getAttribute('data-comment');
|
|
|
|
hideElem(this);
|
|
|
|
hideElem(`#code-comments-${id}`);
|
|
|
|
hideElem(`#code-preview-${id}`);
|
|
|
|
showElem(`#show-outdated-${id}`);
|
2021-10-16 13:28:04 -04:00
|
|
|
});
|
|
|
|
|
2022-01-05 07:17:25 -05:00
|
|
|
$(document).on('click', 'button.comment-form-reply', async function (e) {
|
2021-10-16 13:28:04 -04:00
|
|
|
e.preventDefault();
|
2023-03-02 14:53:22 -05:00
|
|
|
await handleReply($(this));
|
2021-10-16 13:28:04 -04:00
|
|
|
});
|
|
|
|
|
2023-02-21 08:36:53 -05:00
|
|
|
const $reviewBox = $('.review-box-panel');
|
2021-10-16 13:28:04 -04:00
|
|
|
if ($reviewBox.length === 1) {
|
2023-04-03 06:06:57 -04:00
|
|
|
const _promise = initComboMarkdownEditor($reviewBox.find('.combo-markdown-editor'));
|
2021-10-16 13:28:04 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// The following part is only for diff views
|
2024-03-25 14:37:55 -04:00
|
|
|
if (!$('.repository.pull.diff').length) return;
|
2021-10-16 13:28:04 -04:00
|
|
|
|
2023-03-17 13:24:00 -04:00
|
|
|
const $reviewBtn = $('.js-btn-review');
|
|
|
|
const $panel = $reviewBtn.parent().find('.review-box-panel');
|
|
|
|
const $closeBtn = $panel.find('.close');
|
|
|
|
|
2023-03-18 17:08:38 -04:00
|
|
|
if ($reviewBtn.length && $panel.length) {
|
|
|
|
const tippy = createTippy($reviewBtn[0], {
|
|
|
|
content: $panel[0],
|
|
|
|
placement: 'bottom',
|
|
|
|
trigger: 'click',
|
|
|
|
maxWidth: 'none',
|
|
|
|
interactive: true,
|
|
|
|
hideOnClick: true,
|
|
|
|
});
|
2023-03-17 13:24:00 -04:00
|
|
|
|
2023-03-18 17:08:38 -04:00
|
|
|
$closeBtn.on('click', (e) => {
|
|
|
|
e.preventDefault();
|
|
|
|
tippy.hide();
|
|
|
|
});
|
|
|
|
}
|
2021-10-16 13:28:04 -04:00
|
|
|
|
2023-05-21 16:47:41 -04:00
|
|
|
$(document).on('click', '.add-code-comment', async function (e) {
|
2024-03-31 08:01:21 -04:00
|
|
|
if (e.target.classList.contains('btn-add-single')) return; // https://github.com/go-gitea/gitea/issues/4745
|
2021-10-16 13:28:04 -04:00
|
|
|
e.preventDefault();
|
|
|
|
|
2024-03-31 08:01:21 -04:00
|
|
|
const isSplit = this.closest('.code-diff')?.classList.contains('code-diff-split');
|
|
|
|
const side = this.getAttribute('data-side');
|
|
|
|
const idx = this.getAttribute('data-idx');
|
|
|
|
const path = this.closest('[data-path]')?.getAttribute('data-path');
|
|
|
|
const tr = this.closest('tr');
|
|
|
|
const lineType = tr.getAttribute('data-line-type');
|
2021-10-16 13:28:04 -04:00
|
|
|
|
2024-03-31 08:01:21 -04:00
|
|
|
const ntr = tr.nextElementSibling;
|
|
|
|
let $ntr = $(ntr);
|
|
|
|
if (!ntr?.classList.contains('add-comment')) {
|
2024-03-16 08:22:16 -04:00
|
|
|
$ntr = $(`
|
2021-10-16 13:28:04 -04:00
|
|
|
<tr class="add-comment" data-line-type="${lineType}">
|
|
|
|
${isSplit ? `
|
2022-10-25 07:11:49 -04:00
|
|
|
<td class="add-comment-left" colspan="4"></td>
|
|
|
|
<td class="add-comment-right" colspan="4"></td>
|
2021-10-16 13:28:04 -04:00
|
|
|
` : `
|
2022-10-25 07:11:49 -04:00
|
|
|
<td class="add-comment-left add-comment-right" colspan="5"></td>
|
2021-10-16 13:28:04 -04:00
|
|
|
`}
|
|
|
|
</tr>`);
|
2024-03-31 08:01:21 -04:00
|
|
|
$(tr).after($ntr);
|
2021-10-16 13:28:04 -04:00
|
|
|
}
|
|
|
|
|
2024-03-16 08:22:16 -04:00
|
|
|
const $td = $ntr.find(`.add-comment-${side}`);
|
|
|
|
const $commentCloud = $td.find('.comment-code-cloud');
|
2024-03-25 14:37:55 -04:00
|
|
|
if (!$commentCloud.length && !$ntr.find('button[name="pending_review"]').length) {
|
2024-03-14 17:36:17 -04:00
|
|
|
try {
|
2024-03-26 15:33:32 -04:00
|
|
|
const response = await GET(this.closest('[data-new-comment-url]')?.getAttribute('data-new-comment-url'));
|
2024-03-14 17:36:17 -04:00
|
|
|
const html = await response.text();
|
2024-03-16 08:22:16 -04:00
|
|
|
$td.html(html);
|
|
|
|
$td.find("input[name='line']").val(idx);
|
|
|
|
$td.find("input[name='side']").val(side === 'left' ? 'previous' : 'proposed');
|
|
|
|
$td.find("input[name='path']").val(path);
|
2024-03-14 17:36:17 -04:00
|
|
|
|
2024-03-16 08:22:16 -04:00
|
|
|
initDropzone($td.find('.dropzone')[0]);
|
|
|
|
const editor = await initComboMarkdownEditor($td.find('.combo-markdown-editor'));
|
2024-03-14 17:36:17 -04:00
|
|
|
editor.focus();
|
|
|
|
} catch (error) {
|
|
|
|
console.error(error);
|
|
|
|
}
|
2021-10-16 13:28:04 -04:00
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
export function initRepoIssueReferenceIssue() {
|
|
|
|
// Reference issue
|
|
|
|
$(document).on('click', '.reference-issue', function (event) {
|
|
|
|
const $this = $(this);
|
2022-12-09 11:25:32 -05:00
|
|
|
const content = $(`#${$this.data('target')}`).text();
|
2021-10-16 13:28:04 -04:00
|
|
|
const poster = $this.data('poster-username');
|
2024-01-07 05:50:03 -05:00
|
|
|
const reference = toAbsoluteUrl($this.data('reference'));
|
2022-01-16 06:19:26 -05:00
|
|
|
const $modal = $($this.data('modal'));
|
2021-10-16 13:28:04 -04:00
|
|
|
$modal.find('textarea[name="content"]').val(`${content}\n\n_Originally posted by @${poster} in ${reference}_`);
|
|
|
|
$modal.modal('show');
|
|
|
|
|
|
|
|
event.preventDefault();
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
export function initRepoIssueWipToggle() {
|
|
|
|
// Toggle WIP
|
|
|
|
$('.toggle-wip a, .toggle-wip button').on('click', async (e) => {
|
|
|
|
e.preventDefault();
|
2021-11-22 03:19:01 -05:00
|
|
|
const toggleWip = e.currentTarget.closest('.toggle-wip');
|
|
|
|
const title = toggleWip.getAttribute('data-title');
|
2024-04-22 19:21:28 -04:00
|
|
|
const wipPrefixes = JSON.parse(toggleWip.getAttribute('data-wip-prefixes'));
|
2021-11-22 03:19:01 -05:00
|
|
|
const updateUrl = toggleWip.getAttribute('data-update-url');
|
2024-04-22 19:21:28 -04:00
|
|
|
const prefix = wipPrefixes.find((prefix) => title.startsWith(prefix));
|
2024-03-14 17:36:17 -04:00
|
|
|
|
|
|
|
try {
|
|
|
|
const params = new URLSearchParams();
|
2024-04-22 19:21:28 -04:00
|
|
|
params.append('title', prefix !== undefined ? title.slice(prefix.length).trim() : `${wipPrefixes[0].trim()} ${title}`);
|
2024-03-14 17:36:17 -04:00
|
|
|
|
|
|
|
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 13:28:04 -04:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
export function initRepoIssueTitleEdit() {
|
2024-05-20 19:32:48 -04: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');
|
|
|
|
});
|
2021-10-16 13:28:04 -04:00
|
|
|
|
2024-05-20 19:32:48 -04:00
|
|
|
const pullDescEditor = document.querySelector('#pull-desc-editor'); // it may not exist for a merged PR
|
|
|
|
const prTargetUpdateUrl = pullDescEditor?.getAttribute('data-target-update-url');
|
|
|
|
|
|
|
|
const editSaveButton = issueTitleEditor.querySelector('.ui.primary.button');
|
|
|
|
const saveAndRefresh = 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}`);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
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}`);
|
|
|
|
}
|
2023-02-09 12:11:16 -05:00
|
|
|
}
|
2024-03-14 17:36:17 -04:00
|
|
|
}
|
2024-05-20 19:32:48 -04:00
|
|
|
window.location.reload();
|
|
|
|
} catch (error) {
|
|
|
|
console.error(error);
|
|
|
|
showErrorToast(error.message);
|
2021-10-16 13:28:04 -04:00
|
|
|
}
|
2024-05-20 19:32:48 -04:00
|
|
|
};
|
|
|
|
editSaveButton.addEventListener('click', saveAndRefresh);
|
|
|
|
issueTitleEditor.querySelector('input').addEventListener('ce-quick-submit', saveAndRefresh);
|
2021-10-16 13:28:04 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
export function initRepoIssueBranchSelect() {
|
2024-05-20 19:32:48 -04:00
|
|
|
document.querySelector('#branch-select')?.addEventListener('click', (e) => {
|
|
|
|
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 13:28:04 -04:00
|
|
|
}
|
2023-05-07 11:44:16 -04:00
|
|
|
|
2024-10-08 04:39:52 -04:00
|
|
|
export function initRepoIssueAssignMe() {
|
|
|
|
// Assign to me button
|
|
|
|
document.querySelector('.ui.assignees.list .item.no-select .select-assign-me')
|
|
|
|
?.addEventListener('click', (e) => {
|
|
|
|
e.preventDefault();
|
|
|
|
const selectMe = e.target;
|
|
|
|
const noSelect = selectMe.parentElement;
|
|
|
|
const selectorList = document.querySelector('.ui.select-assignees .menu');
|
|
|
|
|
|
|
|
if (selectMe.getAttribute('data-action') === 'update') {
|
|
|
|
(async () => {
|
|
|
|
await updateIssuesMeta(
|
|
|
|
selectMe.getAttribute('data-update-url'),
|
|
|
|
selectMe.getAttribute('data-action'),
|
|
|
|
selectMe.getAttribute('data-issue-id'),
|
|
|
|
selectMe.getAttribute('data-id'),
|
|
|
|
);
|
|
|
|
reloadConfirmDraftComment();
|
|
|
|
})();
|
|
|
|
} else {
|
|
|
|
for (const item of selectorList.querySelectorAll('.item')) {
|
|
|
|
if (item.getAttribute('data-id') === selectMe.getAttribute('data-id')) {
|
|
|
|
item.classList.add('checked');
|
|
|
|
item.querySelector('.octicon-check').classList.remove('tw-invisible');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
document.querySelector(selectMe.getAttribute('data-id-selector')).classList.remove('tw-hidden');
|
|
|
|
noSelect.classList.add('tw-hidden');
|
|
|
|
document.querySelector(selectorList.getAttribute('data-id')).value = selectMe.getAttribute('data-id');
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2023-05-08 18:22:52 -04:00
|
|
|
export function initSingleCommentEditor($commentForm) {
|
|
|
|
// pages:
|
|
|
|
// * normal new issue/pr page, no status-button
|
|
|
|
// * issue/pr view page, with comment form, has status-button
|
|
|
|
const opts = {};
|
2024-03-26 15:33:32 -04:00
|
|
|
const statusButton = document.getElementById('status-button');
|
|
|
|
if (statusButton) {
|
2023-05-08 18:22:52 -04:00
|
|
|
opts.onContentChanged = (editor) => {
|
2024-03-26 15:33:32 -04:00
|
|
|
const statusText = statusButton.getAttribute(editor.value().trim() ? 'data-status-and-comment' : 'data-status');
|
|
|
|
statusButton.textContent = statusText;
|
2023-05-08 18:22:52 -04:00
|
|
|
};
|
|
|
|
}
|
|
|
|
initComboMarkdownEditor($commentForm.find('.combo-markdown-editor'), opts);
|
|
|
|
}
|
|
|
|
|
|
|
|
export function initIssueTemplateCommentEditors($commentForm) {
|
|
|
|
// pages:
|
|
|
|
// * new issue with issue template
|
|
|
|
const $comboFields = $commentForm.find('.combo-editor-dropzone');
|
|
|
|
|
|
|
|
const initCombo = async ($combo) => {
|
|
|
|
const $dropzoneContainer = $combo.find('.form-field-dropzone');
|
|
|
|
const $formField = $combo.find('.form-field-real');
|
|
|
|
const $markdownEditor = $combo.find('.combo-markdown-editor');
|
|
|
|
|
|
|
|
const editor = await initComboMarkdownEditor($markdownEditor, {
|
|
|
|
onContentChanged: (editor) => {
|
|
|
|
$formField.val(editor.value());
|
2024-03-22 10:06:53 -04:00
|
|
|
},
|
2023-05-08 18:22:52 -04: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);
|
|
|
|
showElem($markdownEditor);
|
|
|
|
showElem($dropzoneContainer);
|
|
|
|
|
|
|
|
await editor.switchToUserPreference();
|
|
|
|
editor.focus();
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
|
|
|
for (const el of $comboFields) {
|
|
|
|
initCombo($(el));
|
|
|
|
}
|
|
|
|
}
|
2023-10-17 10:10:45 -04:00
|
|
|
|
|
|
|
// This function used to show and hide archived label on issue/pr
|
|
|
|
// page in the sidebar where we select the labels
|
|
|
|
// If we have any archived label tagged to issue and pr. We will show that
|
|
|
|
// archived label with checked classed otherwise we will hide it
|
|
|
|
// with the help of this function.
|
|
|
|
// This function runs globally.
|
|
|
|
export function initArchivedLabelHandler() {
|
|
|
|
if (!document.querySelector('.archived-label-hint')) return;
|
|
|
|
for (const label of document.querySelectorAll('[data-is-archived]')) {
|
|
|
|
toggleElem(label, label.classList.contains('checked'));
|
|
|
|
}
|
|
|
|
}
|
2024-07-16 19:37:20 -04:00
|
|
|
|
|
|
|
// Render the issue's title. It converts emojis and code blocks syntax into their respective HTML equivalent.
|
|
|
|
export function issueTitleHTML(title) {
|
|
|
|
return title.replaceAll(/:[-+\w]+:/g, (emoji) => emojiHTML(emoji.substring(1, emoji.length - 1)))
|
|
|
|
.replaceAll(/`[^`]+`/g, (code) => `<code class="inline-code-block">${code.substring(1, code.length - 1)}</code>`);
|
|
|
|
}
|