code.gitea.io/gitea@v1.21.7/web_src/js/features/repo-issue-content.js (about)

     1  import $ from 'jquery';
     2  import {svg} from '../svg.js';
     3  import {showErrorToast} from '../modules/toast.js';
     4  
     5  const {appSubUrl, csrfToken} = window.config;
     6  let i18nTextEdited;
     7  let i18nTextOptions;
     8  let i18nTextDeleteFromHistory;
     9  let i18nTextDeleteFromHistoryConfirm;
    10  
    11  function showContentHistoryDetail(issueBaseUrl, commentId, historyId, itemTitleHtml) {
    12    let $dialog = $('.content-history-detail-dialog');
    13    if ($dialog.length) return;
    14  
    15    $dialog = $(`
    16  <div class="ui modal content-history-detail-dialog">
    17    ${svg('octicon-x', 16, 'close icon inside')}
    18    <div class="header gt-df gt-ac gt-sb">
    19      <div>${itemTitleHtml}</div>
    20      <div class="ui dropdown dialog-header-options gt-mr-5 gt-hidden">
    21        ${i18nTextOptions}
    22        ${svg('octicon-triangle-down', 14, 'dropdown icon')}
    23        <div class="menu">
    24          <div class="item red text" data-option-item="delete">${i18nTextDeleteFromHistory}</div>
    25        </div>
    26      </div>
    27    </div>
    28    <div class="comment-diff-data is-loading"></div>
    29  </div>`);
    30    $dialog.appendTo($('body'));
    31    $dialog.find('.dialog-header-options').dropdown({
    32      showOnFocus: false,
    33      allowReselection: true,
    34      onChange(_value, _text, $item) {
    35        const optionItem = $item.data('option-item');
    36        if (optionItem === 'delete') {
    37          if (window.confirm(i18nTextDeleteFromHistoryConfirm)) {
    38            $.post(`${issueBaseUrl}/content-history/soft-delete?comment_id=${commentId}&history_id=${historyId}`, {
    39              _csrf: csrfToken,
    40            }).done((resp) => {
    41              if (resp.ok) {
    42                $dialog.modal('hide');
    43              } else {
    44                showErrorToast(resp.message);
    45              }
    46            });
    47          }
    48        } else { // required by eslint
    49          showErrorToast(`unknown option item: ${optionItem}`);
    50        }
    51      },
    52      onHide() {
    53        $(this).dropdown('clear', true);
    54      }
    55    });
    56    $dialog.modal({
    57      onShow() {
    58        $.ajax({
    59          url: `${issueBaseUrl}/content-history/detail?comment_id=${commentId}&history_id=${historyId}`,
    60          data: {
    61            _csrf: csrfToken,
    62          },
    63        }).done((resp) => {
    64          $dialog.find('.comment-diff-data').removeClass('is-loading').html(resp.diffHtml);
    65          // there is only one option "item[data-option-item=delete]", so the dropdown can be entirely shown/hidden.
    66          if (resp.canSoftDelete) {
    67            $dialog.find('.dialog-header-options').removeClass('gt-hidden');
    68          }
    69        });
    70      },
    71      onHidden() {
    72        $dialog.remove();
    73      },
    74    }).modal('show');
    75  }
    76  
    77  function showContentHistoryMenu(issueBaseUrl, $item, commentId) {
    78    const $headerLeft = $item.find('.comment-header-left');
    79    const menuHtml = `
    80    <div class="ui dropdown interact-fg content-history-menu" data-comment-id="${commentId}">
    81      &bull; ${i18nTextEdited}${svg('octicon-triangle-down', 14, 'dropdown icon')}
    82      <div class="menu">
    83      </div>
    84    </div>`;
    85  
    86    $headerLeft.find(`.content-history-menu`).remove();
    87    $headerLeft.append($(menuHtml));
    88    $headerLeft.find('.dropdown').dropdown({
    89      action: 'hide',
    90      apiSettings: {
    91        cache: false,
    92        url: `${issueBaseUrl}/content-history/list?comment_id=${commentId}`,
    93      },
    94      saveRemoteData: false,
    95      onHide() {
    96        $(this).dropdown('change values', null);
    97      },
    98      onChange(value, itemHtml, $item) {
    99        if (value && !$item.find('[data-history-is-deleted=1]').length) {
   100          showContentHistoryDetail(issueBaseUrl, commentId, value, itemHtml);
   101        }
   102      },
   103    });
   104  }
   105  
   106  export function initRepoIssueContentHistory() {
   107    const issueIndex = $('#issueIndex').val();
   108    if (!issueIndex) return;
   109  
   110    const $itemIssue = $('.repository.issue .timeline-item.comment.first'); // issue(PR) main content
   111    const $comments = $('.repository.issue .comment-list .comment'); // includes: issue(PR) comments, review comments, code comments
   112    if (!$itemIssue.length && !$comments.length) return;
   113  
   114    const repoLink = $('#repolink').val();
   115    const issueBaseUrl = `${appSubUrl}/${repoLink}/issues/${issueIndex}`;
   116  
   117    $.ajax({
   118      url: `${issueBaseUrl}/content-history/overview`,
   119      data: {
   120        _csrf: csrfToken,
   121      },
   122    }).done((resp) => {
   123      i18nTextEdited = resp.i18n.textEdited;
   124      i18nTextDeleteFromHistory = resp.i18n.textDeleteFromHistory;
   125      i18nTextDeleteFromHistoryConfirm = resp.i18n.textDeleteFromHistoryConfirm;
   126      i18nTextOptions = resp.i18n.textOptions;
   127  
   128      if (resp.editedHistoryCountMap[0] && $itemIssue.length) {
   129        showContentHistoryMenu(issueBaseUrl, $itemIssue, '0');
   130      }
   131      for (const [commentId, _editedCount] of Object.entries(resp.editedHistoryCountMap)) {
   132        if (commentId === '0') continue;
   133        const $itemComment = $(`#issuecomment-${commentId}`);
   134        showContentHistoryMenu(issueBaseUrl, $itemComment, commentId);
   135      }
   136    });
   137  }