aboutsummaryrefslogtreecommitdiffstats
path: root/web_src/js/features/repo-issue.js
diff options
context:
space:
mode:
Diffstat (limited to 'web_src/js/features/repo-issue.js')
-rw-r--r--web_src/js/features/repo-issue.js638
1 files changed, 638 insertions, 0 deletions
diff --git a/web_src/js/features/repo-issue.js b/web_src/js/features/repo-issue.js
new file mode 100644
index 0000000000..858398aac9
--- /dev/null
+++ b/web_src/js/features/repo-issue.js
@@ -0,0 +1,638 @@
+import {htmlEscape} from 'escape-goat';
+import attachTribute from './tribute.js';
+import {createCommentSimpleMDE} from './comp/CommentSimpleMDE.js';
+import {initCompImagePaste} from './comp/ImagePaste.js';
+import {initCompMarkupContentPreviewTab} from './comp/MarkupContentPreview.js';
+
+const {AppSubUrl, csrf} = window.config;
+
+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')}"]`;
+ $(sel).modal({
+ duration: 200,
+ onApprove() {
+ $(`${sel} form`).trigger('submit');
+ },
+ }).modal('show');
+ });
+}
+
+function updateDeadline(deadlineString) {
+ $('#deadline-err-invalid-date').hide();
+ $('#deadline-loader').addClass('loading');
+
+ let realDeadline = null;
+ if (deadlineString !== '') {
+ const newDate = Date.parse(deadlineString);
+
+ if (Number.isNaN(newDate)) {
+ $('#deadline-loader').removeClass('loading');
+ $('#deadline-err-invalid-date').show();
+ return false;
+ }
+ realDeadline = new Date(newDate);
+ }
+
+ $.ajax(`${$('#update-issue-deadline-form').attr('action')}/deadline`, {
+ data: JSON.stringify({
+ due_date: realDeadline,
+ }),
+ headers: {
+ 'X-Csrf-Token': csrf,
+ 'X-Remote': true,
+ },
+ contentType: 'application/json',
+ type: 'POST',
+ success() {
+ window.location.reload();
+ },
+ error() {
+ $('#deadline-loader').removeClass('loading');
+ $('#deadline-err-invalid-date').show();
+ },
+ });
+}
+
+export function initRepoIssueDue() {
+ $(document).on('click', '.issue-due-edit', () => {
+ $('#deadlineForm').fadeToggle(150);
+ });
+ $(document).on('click', '.issue-due-remove', () => {
+ updateDeadline('');
+ });
+ $(document).on('submit', '.issue-due-form', () => {
+ updateDeadline($('#deadlineDate').val());
+ return false;
+ });
+}
+
+export function initRepoIssueList() {
+ const repolink = $('#repolink').val();
+ const repoId = $('#repoId').val();
+ const crossRepoSearch = $('#crossRepoSearch').val();
+ const tp = $('#type').val();
+ let issueSearchUrl = `${AppSubUrl}/api/v1/repos/${repolink}/issues?q={query}&type=${tp}`;
+ if (crossRepoSearch === 'true') {
+ issueSearchUrl = `${AppSubUrl}/api/v1/repos/issues/search?q={query}&priority_repo_id=${repoId}&type=${tp}`;
+ }
+ $('#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({
+ name: `#${issue.number} ${htmlEscape(issue.title)
+ }<div class="text small dont-break-out">${htmlEscape(issue.repository.full_name)}</div>`,
+ value: issue.id,
+ });
+ });
+ return filteredResponse;
+ },
+ cache: false,
+ },
+
+ fullTextSearch: true,
+ });
+
+ function excludeLabel(item) {
+ const href = $(item).attr('href');
+ const id = $(item).data('label-id');
+
+ const regStr = `labels=((?:-?[0-9]+%2c)*)(${id})((?:%2c-?[0-9]+)*)&`;
+ const newStr = 'labels=$1-$2$3&';
+
+ window.location = href.replace(new RegExp(regStr), newStr);
+ }
+
+ $('.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) {
+ const selectedItems = $('.menu .ui.dropdown.label-filter .menu .item.selected');
+ if (selectedItems.length > 0) {
+ excludeLabel($(selectedItems[0]));
+ }
+ }
+ });
+}
+
+export function initRepoIssueCommentDelete() {
+ // Delete comment
+ $(document).on('click', '.delete-comment', function () {
+ const $this = $(this);
+ if (window.confirm($this.data('locale'))) {
+ $.post($this.data('url'), {
+ _csrf: csrf,
+ }).done(() => {
+ const $conversationHolder = $this.closest('.conversation-holder');
+ $(`#${$this.data('comment-id')}`).remove();
+ if ($conversationHolder.length && !$conversationHolder.find('.comment').length) {
+ const path = $conversationHolder.data('path');
+ const side = $conversationHolder.data('side');
+ const idx = $conversationHolder.data('idx');
+ const lineType = $conversationHolder.closest('tr').data('line-type');
+ if (lineType === 'same') {
+ $(`a.add-code-comment[data-path="${path}"][data-idx="${idx}"]`).removeClass('invisible');
+ } else {
+ $(`a.add-code-comment[data-path="${path}"][data-side="${side}"][data-idx="${idx}"]`).removeClass('invisible');
+ }
+ $conversationHolder.remove();
+ }
+ });
+ }
+ return false;
+ });
+}
+
+export function initRepoIssueDependencyDelete() {
+ // Delete Issue dependency
+ $(document).on('click', '.delete-dependency-button', (e) => {
+ const {id, type} = e.currentTarget.dataset;
+
+ $('.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
+ $(document).on('click', '.cancel-code-comment', (e) => {
+ const form = $(e.currentTarget).closest('form');
+ if (form.length > 0 && form.hasClass('comment-form')) {
+ form.addClass('hide');
+ form.closest('.comment-code-cloud').find('button.comment-form-reply').show();
+ } else {
+ form.closest('.comment-code-cloud').remove();
+ }
+ });
+}
+
+export function initRepoIssueStatusButton() {
+ // Change status
+ const $statusButton = $('#status-button');
+ $('#comment-form textarea').on('keyup', function () {
+ const $simplemde = $(this).data('simplemde');
+ const value = ($simplemde && $simplemde.value()) ? $simplemde.value() : $(this).val();
+ $statusButton.text($statusButton.data(value.length === 0 ? 'status' : 'status-and-comment'));
+ });
+ $statusButton.on('click', () => {
+ $('#status').val($statusButton.data('status-val'));
+ $('#comment-form').trigger('submit');
+ });
+}
+
+export function initRepoPullRequestMerge() {
+ // Pull Request merge button
+ const $mergeButton = $('.merge-button > button');
+ $mergeButton.on('click', function (e) {
+ e.preventDefault();
+ $(`.${$(this).data('do')}-fields`).show();
+ $(this).parent().hide();
+ $('.instruct-toggle').hide();
+ $('.instruct-content').hide();
+ });
+ $('.merge-button > .dropdown').dropdown({
+ onChange(_text, _value, $choice) {
+ if ($choice.data('do')) {
+ $mergeButton.find('.button-text').text($choice.text());
+ $mergeButton.data('do', $choice.data('do'));
+ }
+ }
+ });
+ $('.merge-cancel').on('click', function (e) {
+ e.preventDefault();
+ $(this).closest('.form').hide();
+ $mergeButton.parent().show();
+ $('.instruct-toggle').show();
+ });
+}
+
+export function initRepoPullRequestUpdate() {
+ // Pull Request update button
+ const $pullUpdateButton = $('.update-button > button');
+ $pullUpdateButton.on('click', function (e) {
+ e.preventDefault();
+ const $this = $(this);
+ const redirect = $this.data('redirect');
+ $this.addClass('loading');
+ $.post($this.data('do'), {
+ _csrf: csrf
+ }).done((data) => {
+ if (data.redirect) {
+ window.location.href = data.redirect;
+ } else if (redirect) {
+ window.location.href = redirect;
+ } else {
+ window.location.reload();
+ }
+ });
+ });
+
+ $('.update-button > .dropdown').dropdown({
+ onChange(_text, _value, $choice) {
+ const $url = $choice.data('do');
+ if ($url) {
+ $pullUpdateButton.find('.button-text').text($choice.text());
+ $pullUpdateButton.data('do', $url);
+ }
+ }
+ });
+}
+
+export function initRepoPullRequestMergeInstruction() {
+ $('.show-instruction').on('click', () => {
+ $('.instruct-content').toggle();
+ });
+}
+
+export function initRepoIssueReferenceRepositorySearch() {
+ $('.issue_reference_repository_search')
+ .dropdown({
+ apiSettings: {
+ url: `${AppSubUrl}/api/v1/repos/search?q={query}&limit=20`,
+ onResponse(response) {
+ const filteredResponse = {success: true, results: []};
+ $.each(response.data, (_r, repo) => {
+ filteredResponse.results.push({
+ name: htmlEscape(repo.full_name),
+ value: repo.full_name
+ });
+ });
+ return filteredResponse;
+ },
+ cache: false,
+ },
+ onChange(_value, _text, $choice) {
+ const $form = $choice.closest('form');
+ $form.attr('action', `${AppSubUrl}/${_text}/issues/new`);
+ },
+ fullTextSearch: true
+ });
+}
+
+
+export function initRepoIssueWipTitle() {
+ $('.title_wip_desc > a').on('click', (e) => {
+ e.preventDefault();
+
+ const $issueTitle = $('#issue_title');
+ $issueTitle.focus();
+ 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()}`);
+ });
+}
+
+export function updateIssuesMeta(url, action, issueIds, elementId) {
+ return new Promise((resolve, reject) => {
+ $.ajax({
+ type: 'POST',
+ url,
+ data: {
+ _csrf: csrf,
+ action,
+ issue_ids: issueIds,
+ id: elementId,
+ },
+ success: resolve,
+ error: reject,
+ });
+ });
+}
+
+export function initRepoIssueComments() {
+ if ($('.repository.view.issue .timeline').length === 0) return;
+
+ $('.re-request-review').on('click', function (event) {
+ const url = $(this).data('update-url');
+ const issueId = $(this).data('issue-id');
+ const id = $(this).data('id');
+ const isChecked = $(this).hasClass('checked');
+
+ event.preventDefault();
+ updateIssuesMeta(
+ url,
+ isChecked ? 'detach' : 'attach',
+ issueId,
+ id,
+ ).then(() => {
+ window.location.reload();
+ });
+ return false;
+ });
+
+ $('.dismiss-review-btn').on('click', function (e) {
+ e.preventDefault();
+ const $this = $(this);
+ const $dismissReviewModal = $this.next();
+ $dismissReviewModal.modal('show');
+ });
+
+ $(document).on('click', (event) => {
+ const urlTarget = $(':target');
+ if (urlTarget.length === 0) return;
+
+ const urlTargetId = urlTarget.attr('id');
+ if (!urlTargetId) return;
+ if (!/^(issue|pull)(comment)?-\d+$/.test(urlTargetId)) return;
+
+ const $target = $(event.target);
+
+ if ($target.closest(`#${urlTargetId}`).length === 0) {
+ const scrollPosition = $(window).scrollTop();
+ window.location.hash = '';
+ $(window).scrollTop(scrollPosition);
+ window.history.pushState(null, null, ' ');
+ }
+ });
+}
+
+
+function assignMenuAttributes(menu) {
+ const id = Math.floor(Math.random() * Math.floor(1000000));
+ menu.attr('data-write', menu.attr('data-write') + id);
+ menu.attr('data-preview', menu.attr('data-preview') + id);
+ menu.find('.item').each(function () {
+ const tab = $(this).attr('data-tab') + id;
+ $(this).attr('data-tab', tab);
+ });
+ menu.parent().find("*[data-tab='write']").attr('data-tab', `write${id}`);
+ menu.parent().find("*[data-tab='preview']").attr('data-tab', `preview${id}`);
+ initCompMarkupContentPreviewTab(menu.parent('.form'));
+ return id;
+}
+
+export function initRepoPullRequestReview() {
+ if (window.location.hash && window.location.hash.startsWith('#issuecomment-')) {
+ const commentDiv = $(window.location.hash);
+ if (commentDiv) {
+ // get the name of the parent id
+ const groupID = commentDiv.closest('div[id^="code-comments-"]').attr('id');
+ if (groupID && groupID.startsWith('code-comments-')) {
+ const id = groupID.substr(14);
+ $(`#show-outdated-${id}`).addClass('hide');
+ $(`#code-comments-${id}`).removeClass('hide');
+ $(`#code-preview-${id}`).removeClass('hide');
+ $(`#hide-outdated-${id}`).removeClass('hide');
+ commentDiv[0].scrollIntoView();
+ }
+ }
+ }
+
+ $(document).on('click', '.show-outdated', function (e) {
+ e.preventDefault();
+ const id = $(this).data('comment');
+ $(this).addClass('hide');
+ $(`#code-comments-${id}`).removeClass('hide');
+ $(`#code-preview-${id}`).removeClass('hide');
+ $(`#hide-outdated-${id}`).removeClass('hide');
+ });
+
+ $(document).on('click', '.hide-outdated', function (e) {
+ e.preventDefault();
+ const id = $(this).data('comment');
+ $(this).addClass('hide');
+ $(`#code-comments-${id}`).addClass('hide');
+ $(`#code-preview-${id}`).addClass('hide');
+ $(`#show-outdated-${id}`).removeClass('hide');
+ });
+
+ $(document).on('click', 'button.comment-form-reply', function (e) {
+ e.preventDefault();
+ $(this).hide();
+ const form = $(this).closest('.comment-code-cloud').find('.comment-form');
+ form.removeClass('hide');
+ const $textarea = form.find('textarea');
+ let $simplemde;
+ if ($textarea.data('simplemde')) {
+ $simplemde = $textarea.data('simplemde');
+ } else {
+ attachTribute($textarea.get(), {mentions: true, emoji: true});
+ $simplemde = createCommentSimpleMDE($textarea);
+ $textarea.data('simplemde', $simplemde);
+ }
+ $textarea.focus();
+ $simplemde.codemirror.focus();
+ assignMenuAttributes(form.find('.menu'));
+ });
+
+ const $reviewBox = $('.review-box');
+ if ($reviewBox.length === 1) {
+ createCommentSimpleMDE($reviewBox.find('textarea'));
+ initCompImagePaste($reviewBox);
+ }
+
+ // The following part is only for diff views
+ if ($('.repository.pull.diff').length === 0) {
+ return;
+ }
+
+ $('.btn-review').on('click', function (e) {
+ e.preventDefault();
+ $(this).closest('.dropdown').find('.menu').toggle('visible');
+ }).closest('.dropdown').find('.close').on('click', function (e) {
+ e.preventDefault();
+ $(this).closest('.menu').toggle('visible');
+ });
+
+ $('a.add-code-comment').on('click', async function (e) {
+ if ($(e.target).hasClass('btn-add-single')) return; // https://github.com/go-gitea/gitea/issues/4745
+ e.preventDefault();
+
+ const isSplit = $(this).closest('.code-diff').hasClass('code-diff-split');
+ const side = $(this).data('side');
+ const idx = $(this).data('idx');
+ const path = $(this).data('path');
+ const tr = $(this).closest('tr');
+ const lineType = tr.data('line-type');
+
+ let ntr = tr.next();
+ if (!ntr.hasClass('add-comment')) {
+ ntr = $(`
+ <tr class="add-comment" data-line-type="${lineType}">
+ ${isSplit ? `
+ <td class="lines-num"></td>
+ <td class="lines-type-marker"></td>
+ <td class="add-comment-left"></td>
+ <td class="lines-num"></td>
+ <td class="lines-type-marker"></td>
+ <td class="add-comment-right"></td>
+ ` : `
+ <td colspan="2" class="lines-num"></td>
+ <td class="add-comment-left add-comment-right" colspan="2"></td>
+ `}
+ </tr>`);
+ tr.after(ntr);
+ }
+
+ const td = ntr.find(`.add-comment-${side}`);
+ let commentCloud = td.find('.comment-code-cloud');
+ if (commentCloud.length === 0 && !ntr.find('button[name="is_review"]').length) {
+ const data = await $.get($(this).data('new-comment-url'));
+ td.html(data);
+ commentCloud = td.find('.comment-code-cloud');
+ assignMenuAttributes(commentCloud.find('.menu'));
+ td.find("input[name='line']").val(idx);
+ td.find("input[name='side']").val(side === 'left' ? 'previous' : 'proposed');
+ td.find("input[name='path']").val(path);
+ const $textarea = commentCloud.find('textarea');
+ attachTribute($textarea.get(), {mentions: true, emoji: true});
+ const $simplemde = createCommentSimpleMDE($textarea);
+ $textarea.focus();
+ $simplemde.codemirror.focus();
+ }
+ });
+}
+
+export function initRepoIssueReferenceIssue() {
+ // Reference issue
+ $(document).on('click', '.reference-issue', function (event) {
+ const $this = $(this);
+ $this.closest('.dropdown').find('.menu').toggle('visible');
+
+ const content = $(`#comment-${$this.data('target')}`).text();
+ const poster = $this.data('poster-username');
+ const reference = $this.data('reference');
+ const $modal = $($this.data('modal'));
+ $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();
+ const {title, wipPrefix, updateUrl} = e.currentTarget.closest('.toggle-wip').dataset;
+ await $.post(updateUrl, {
+ _csrf: csrf,
+ title: title?.startsWith(wipPrefix) ? title.substr(wipPrefix.length).trim() : `${wipPrefix.trim()} ${title}`,
+ });
+ window.location.reload();
+ });
+}
+
+
+export function initRepoIssueTitleEdit() {
+ // Edit issue title
+ const $issueTitle = $('#issue-title');
+ const $editInput = $('#edit-title-input input');
+
+ const editTitleToggle = function () {
+ $issueTitle.toggle();
+ $('.not-in-edit').toggle();
+ $('#edit-title-input').toggle();
+ $('#pull-desc').toggle();
+ $('#pull-desc-edit').toggle();
+ $('.in-edit').toggle();
+ $('#issue-title-wrapper').toggleClass('edit-active');
+ $editInput.focus();
+ return false;
+ };
+
+ $('#edit-title').on('click', editTitleToggle);
+ $('#cancel-edit-title').on('click', editTitleToggle);
+ $('#save-edit-title').on('click', editTitleToggle).on('click', function () {
+ const pullrequest_targetbranch_change = function (update_url) {
+ const targetBranch = $('#pull-target-branch').data('branch');
+ const $branchTarget = $('#branch_target');
+ if (targetBranch === $branchTarget.text()) {
+ return false;
+ }
+ $.post(update_url, {
+ _csrf: csrf,
+ target_branch: targetBranch
+ }).done((data) => {
+ $branchTarget.text(data.base_branch);
+ }).always(() => {
+ window.location.reload();
+ });
+ };
+
+ const pullrequest_target_update_url = $(this).data('target-update-url');
+ if ($editInput.val().length === 0 || $editInput.val() === $issueTitle.text()) {
+ $editInput.val($issueTitle.text());
+ pullrequest_targetbranch_change(pullrequest_target_update_url);
+ } else {
+ $.post($(this).data('update-url'), {
+ _csrf: csrf,
+ title: $editInput.val()
+ }, (data) => {
+ $editInput.val(data.title);
+ $issueTitle.text(data.title);
+ pullrequest_targetbranch_change(pullrequest_target_update_url);
+ window.location.reload();
+ });
+ }
+ return false;
+ });
+}
+
+export function initRepoIssueBranchSelect() {
+ const changeBranchSelect = function () {
+ const selectionTextField = $('#pull-target-branch');
+
+ const baseName = selectionTextField.data('basename');
+ const branchNameNew = $(this).data('branch');
+ const branchNameOld = selectionTextField.data('branch');
+
+ // Replace branch name to keep translation from HTML template
+ selectionTextField.html(selectionTextField.html().replace(
+ `${baseName}:${branchNameOld}`,
+ `${baseName}:${branchNameNew}`
+ ));
+ selectionTextField.data('branch', branchNameNew); // update branch name in setting
+ };
+ $('#branch-select > .item').on('click', changeBranchSelect);
+}