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