gitea源码

repo-settings.ts 6.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156
  1. import {createMonaco} from './codeeditor.ts';
  2. import {onInputDebounce, queryElems, toggleElem} from '../utils/dom.ts';
  3. import {POST} from '../modules/fetch.ts';
  4. import {initRepoSettingsBranchesDrag} from './repo-settings-branches.ts';
  5. import {fomanticQuery} from '../modules/fomantic/base.ts';
  6. import {globMatch} from '../utils/glob.ts';
  7. const {appSubUrl, csrfToken} = window.config;
  8. function initRepoSettingsCollaboration() {
  9. // Change collaborator access mode
  10. for (const dropdownEl of queryElems(document, '.page-content.repository .ui.dropdown.access-mode')) {
  11. const textEl = dropdownEl.querySelector(':scope > .text');
  12. const $dropdown = fomanticQuery(dropdownEl);
  13. $dropdown.dropdown({
  14. async action(text: string, value: string) {
  15. dropdownEl.classList.add('is-loading', 'loading-icon-2px');
  16. const lastValue = dropdownEl.getAttribute('data-last-value');
  17. $dropdown.dropdown('hide');
  18. try {
  19. const uid = dropdownEl.getAttribute('data-uid');
  20. await POST(dropdownEl.getAttribute('data-url'), {data: new URLSearchParams({uid, 'mode': value})});
  21. textEl.textContent = text;
  22. dropdownEl.setAttribute('data-last-value', value);
  23. } catch {
  24. textEl.textContent = '(error)'; // prevent from misleading users when error occurs
  25. dropdownEl.setAttribute('data-last-value', lastValue);
  26. } finally {
  27. dropdownEl.classList.remove('is-loading');
  28. }
  29. },
  30. onHide() {
  31. // set to the really selected value, defer to next tick to make sure `action` has finished
  32. // its work because the calling order might be onHide -> action
  33. setTimeout(() => {
  34. const $item = $dropdown.dropdown('get item', dropdownEl.getAttribute('data-last-value'));
  35. if ($item) {
  36. $dropdown.dropdown('set selected', dropdownEl.getAttribute('data-last-value'));
  37. } else {
  38. textEl.textContent = '(none)'; // prevent from misleading users when the access mode is undefined
  39. }
  40. }, 0);
  41. },
  42. });
  43. }
  44. }
  45. function initRepoSettingsSearchTeamBox() {
  46. const searchTeamBox = document.querySelector('#search-team-box');
  47. if (!searchTeamBox) return;
  48. fomanticQuery(searchTeamBox).search({
  49. minCharacters: 2,
  50. searchFields: ['name', 'description'],
  51. showNoResults: false,
  52. rawResponse: true,
  53. apiSettings: {
  54. url: `${appSubUrl}/org/${searchTeamBox.getAttribute('data-org-name')}/teams/-/search?q={query}`,
  55. headers: {'X-Csrf-Token': csrfToken},
  56. onResponse(response: any) {
  57. const items: Array<Record<string, any>> = [];
  58. for (const item of response.data) {
  59. items.push({
  60. title: item.name,
  61. description: `${item.permission} access`, // TODO: translate this string
  62. });
  63. }
  64. return {results: items};
  65. },
  66. },
  67. });
  68. }
  69. function initRepoSettingsGitHook() {
  70. if (!document.querySelector('.page-content.repository.settings.edit.githook')) return;
  71. const filename = document.querySelector('.hook-filename').textContent;
  72. createMonaco(document.querySelector<HTMLTextAreaElement>('#content'), filename, {language: 'shell'});
  73. }
  74. function initRepoSettingsBranches() {
  75. if (!document.querySelector('.repository.settings.branches')) return;
  76. for (const el of document.querySelectorAll<HTMLInputElement>('.toggle-target-enabled')) {
  77. el.addEventListener('change', function () {
  78. const target = document.querySelector(this.getAttribute('data-target'));
  79. target?.classList.toggle('disabled', !this.checked);
  80. });
  81. }
  82. for (const el of document.querySelectorAll<HTMLInputElement>('.toggle-target-disabled')) {
  83. el.addEventListener('change', function () {
  84. const target = document.querySelector(this.getAttribute('data-target'));
  85. if (this.checked) target?.classList.add('disabled'); // only disable, do not auto enable
  86. });
  87. }
  88. document.querySelector<HTMLInputElement>('#dismiss_stale_approvals')?.addEventListener('change', function () {
  89. document.querySelector('#ignore_stale_approvals_box')?.classList.toggle('disabled', this.checked);
  90. });
  91. // show the `Matched` mark for the status checks that match the pattern
  92. const markMatchedStatusChecks = () => {
  93. const patterns = (document.querySelector<HTMLTextAreaElement>('#status_check_contexts').value || '').split(/[\r\n]+/);
  94. const validPatterns = patterns.map((item) => item.trim()).filter(Boolean);
  95. const marks = document.querySelectorAll('.status-check-matched-mark');
  96. for (const el of marks) {
  97. let matched = false;
  98. const statusCheck = el.getAttribute('data-status-check');
  99. for (const pattern of validPatterns) {
  100. if (globMatch(statusCheck, pattern, '/')) {
  101. matched = true;
  102. break;
  103. }
  104. }
  105. toggleElem(el, matched);
  106. }
  107. };
  108. markMatchedStatusChecks();
  109. document.querySelector('#status_check_contexts').addEventListener('input', onInputDebounce(markMatchedStatusChecks));
  110. }
  111. function initRepoSettingsOptions() {
  112. const pageContent = document.querySelector('.page-content.repository.settings.options');
  113. if (!pageContent) return;
  114. // toggle related panels for the checkbox/radio inputs, the "selector" may not exist
  115. const toggleTargetContextPanel = (selector: string, enabled: boolean) => {
  116. if (!selector) return;
  117. queryElems(document, selector, (el) => el.classList.toggle('disabled', !enabled));
  118. };
  119. queryElems<HTMLInputElement>(pageContent, '.enable-system', (el) => el.addEventListener('change', () => {
  120. toggleTargetContextPanel(el.getAttribute('data-target'), el.checked);
  121. toggleTargetContextPanel(el.getAttribute('data-context'), !el.checked);
  122. }));
  123. queryElems<HTMLInputElement>(pageContent, '.enable-system-radio', (el) => el.addEventListener('change', () => {
  124. toggleTargetContextPanel(el.getAttribute('data-target'), el.value === 'true');
  125. toggleTargetContextPanel(el.getAttribute('data-context'), el.value === 'false');
  126. }));
  127. queryElems<HTMLInputElement>(pageContent, '.js-tracker-issue-style', (el) => el.addEventListener('change', () => {
  128. const checkedVal = el.value;
  129. pageContent.querySelector('#tracker-issue-style-regex-box').classList.toggle('disabled', checkedVal !== 'regexp');
  130. }));
  131. }
  132. export function initRepoSettings() {
  133. if (!document.querySelector('.page-content.repository.settings')) return;
  134. initRepoSettingsOptions();
  135. initRepoSettingsBranches();
  136. initRepoSettingsCollaboration();
  137. initRepoSettingsSearchTeamBox();
  138. initRepoSettingsGitHook();
  139. initRepoSettingsBranchesDrag();
  140. }