You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

repo-issue-content.js 4.6KB

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