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 5.2KB

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