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.js 22KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659
  1. import $ from 'jquery';
  2. import {htmlEscape} from 'escape-goat';
  3. import attachTribute from './tribute.js';
  4. import {createCommentEasyMDE, getAttachedEasyMDE} from './comp/EasyMDE.js';
  5. import {initCompImagePaste} from './comp/ImagePaste.js';
  6. import {initCompMarkupContentPreviewTab} from './comp/MarkupContentPreview.js';
  7. const {appSubUrl, csrfToken} = window.config;
  8. export function initRepoIssueTimeTracking() {
  9. $(document).on('click', '.issue-add-time', () => {
  10. $('.issue-start-time-modal').modal({
  11. duration: 200,
  12. onApprove() {
  13. $('#add_time_manual_form').trigger('submit');
  14. },
  15. }).modal('show');
  16. $('.issue-start-time-modal input').on('keydown', (e) => {
  17. if ((e.keyCode || e.key) === 13) {
  18. $('#add_time_manual_form').trigger('submit');
  19. }
  20. });
  21. });
  22. $(document).on('click', '.issue-start-time, .issue-stop-time', () => {
  23. $('#toggle_stopwatch_form').trigger('submit');
  24. });
  25. $(document).on('click', '.issue-cancel-time', () => {
  26. $('#cancel_stopwatch_form').trigger('submit');
  27. });
  28. $(document).on('click', 'button.issue-delete-time', function () {
  29. const sel = `.issue-delete-time-modal[data-id="${$(this).data('id')}"]`;
  30. $(sel).modal({
  31. duration: 200,
  32. onApprove() {
  33. $(`${sel} form`).trigger('submit');
  34. },
  35. }).modal('show');
  36. });
  37. }
  38. function updateDeadline(deadlineString) {
  39. $('#deadline-err-invalid-date').hide();
  40. $('#deadline-loader').addClass('loading');
  41. let realDeadline = null;
  42. if (deadlineString !== '') {
  43. const newDate = Date.parse(deadlineString);
  44. if (Number.isNaN(newDate)) {
  45. $('#deadline-loader').removeClass('loading');
  46. $('#deadline-err-invalid-date').show();
  47. return false;
  48. }
  49. realDeadline = new Date(newDate);
  50. }
  51. $.ajax(`${$('#update-issue-deadline-form').attr('action')}`, {
  52. data: JSON.stringify({
  53. due_date: realDeadline,
  54. }),
  55. headers: {
  56. 'X-Csrf-Token': csrfToken,
  57. },
  58. contentType: 'application/json',
  59. type: 'POST',
  60. success() {
  61. window.location.reload();
  62. },
  63. error() {
  64. $('#deadline-loader').removeClass('loading');
  65. $('#deadline-err-invalid-date').show();
  66. },
  67. });
  68. }
  69. export function initRepoIssueDue() {
  70. $(document).on('click', '.issue-due-edit', () => {
  71. $('#deadlineForm').fadeToggle(150);
  72. });
  73. $(document).on('click', '.issue-due-remove', () => {
  74. updateDeadline('');
  75. });
  76. $(document).on('submit', '.issue-due-form', () => {
  77. updateDeadline($('#deadlineDate').val());
  78. return false;
  79. });
  80. }
  81. export function initRepoIssueList() {
  82. const repolink = $('#repolink').val();
  83. const repoId = $('#repoId').val();
  84. const crossRepoSearch = $('#crossRepoSearch').val();
  85. const tp = $('#type').val();
  86. let issueSearchUrl = `${appSubUrl}/${repolink}/issues/search?q={query}&type=${tp}`;
  87. if (crossRepoSearch === 'true') {
  88. issueSearchUrl = `${appSubUrl}/issues/search?q={query}&priority_repo_id=${repoId}&type=${tp}`;
  89. }
  90. $('#new-dependency-drop-list')
  91. .dropdown({
  92. apiSettings: {
  93. url: issueSearchUrl,
  94. onResponse(response) {
  95. const filteredResponse = {success: true, results: []};
  96. const currIssueId = $('#new-dependency-drop-list').data('issue-id');
  97. // Parse the response from the api to work with our dropdown
  98. $.each(response, (_i, issue) => {
  99. // Don't list current issue in the dependency list.
  100. if (issue.id === currIssueId) {
  101. return;
  102. }
  103. filteredResponse.results.push({
  104. name: `#${issue.number} ${htmlEscape(issue.title)
  105. }<div class="text small dont-break-out">${htmlEscape(issue.repository.full_name)}</div>`,
  106. value: issue.id,
  107. });
  108. });
  109. return filteredResponse;
  110. },
  111. cache: false,
  112. },
  113. fullTextSearch: true,
  114. });
  115. function excludeLabel(item) {
  116. const href = $(item).attr('href');
  117. const id = $(item).data('label-id');
  118. const regStr = `labels=((?:-?[0-9]+%2c)*)(${id})((?:%2c-?[0-9]+)*)&`;
  119. const newStr = 'labels=$1-$2$3&';
  120. window.location = href.replace(new RegExp(regStr), newStr);
  121. }
  122. $('.menu a.label-filter-item').each(function () {
  123. $(this).on('click', function (e) {
  124. if (e.altKey) {
  125. e.preventDefault();
  126. excludeLabel(this);
  127. }
  128. });
  129. });
  130. $('.menu .ui.dropdown.label-filter').on('keydown', (e) => {
  131. if (e.altKey && e.keyCode === 13) {
  132. const selectedItems = $('.menu .ui.dropdown.label-filter .menu .item.selected');
  133. if (selectedItems.length > 0) {
  134. excludeLabel($(selectedItems[0]));
  135. }
  136. }
  137. });
  138. }
  139. export function initRepoIssueCommentDelete() {
  140. // Delete comment
  141. $(document).on('click', '.delete-comment', function () {
  142. const $this = $(this);
  143. if (window.confirm($this.data('locale'))) {
  144. $.post($this.data('url'), {
  145. _csrf: csrfToken,
  146. }).done(() => {
  147. const $conversationHolder = $this.closest('.conversation-holder');
  148. // Check if this was a pending comment.
  149. if ($conversationHolder.find('.pending-label').length) {
  150. const $counter = $('#review-box .review-comments-counter');
  151. let num = parseInt($counter.attr('data-pending-comment-number')) - 1 || 0;
  152. num = Math.max(num, 0);
  153. $counter.attr('data-pending-comment-number', num);
  154. $counter.text(num);
  155. }
  156. $(`#${$this.data('comment-id')}`).remove();
  157. if ($conversationHolder.length && !$conversationHolder.find('.comment').length) {
  158. const path = $conversationHolder.data('path');
  159. const side = $conversationHolder.data('side');
  160. const idx = $conversationHolder.data('idx');
  161. const lineType = $conversationHolder.closest('tr').data('line-type');
  162. if (lineType === 'same') {
  163. $(`[data-path="${path}"] a.add-code-comment[data-idx="${idx}"]`).removeClass('invisible');
  164. } else {
  165. $(`[data-path="${path}"] a.add-code-comment[data-side="${side}"][data-idx="${idx}"]`).removeClass('invisible');
  166. }
  167. $conversationHolder.remove();
  168. }
  169. });
  170. }
  171. return false;
  172. });
  173. }
  174. export function initRepoIssueDependencyDelete() {
  175. // Delete Issue dependency
  176. $(document).on('click', '.delete-dependency-button', (e) => {
  177. const id = e.currentTarget.getAttribute('data-id');
  178. const type = e.currentTarget.getAttribute('data-type');
  179. $('.remove-dependency').modal({
  180. closable: false,
  181. duration: 200,
  182. onApprove: () => {
  183. $('#removeDependencyID').val(id);
  184. $('#dependencyType').val(type);
  185. $('#removeDependencyForm').trigger('submit');
  186. },
  187. }).modal('show');
  188. });
  189. }
  190. export function initRepoIssueCodeCommentCancel() {
  191. // Cancel inline code comment
  192. $(document).on('click', '.cancel-code-comment', (e) => {
  193. const form = $(e.currentTarget).closest('form');
  194. if (form.length > 0 && form.hasClass('comment-form')) {
  195. form.addClass('hide');
  196. form.closest('.comment-code-cloud').find('button.comment-form-reply').show();
  197. } else {
  198. form.closest('.comment-code-cloud').remove();
  199. }
  200. });
  201. }
  202. export function initRepoIssueStatusButton() {
  203. // Change status
  204. const $statusButton = $('#status-button');
  205. $('#comment-form textarea').on('keyup', function () {
  206. const easyMDE = getAttachedEasyMDE(this);
  207. const value = easyMDE?.value() || $(this).val();
  208. $statusButton.text($statusButton.data(value.length === 0 ? 'status' : 'status-and-comment'));
  209. });
  210. $statusButton.on('click', () => {
  211. $('#status').val($statusButton.data('status-val'));
  212. $('#comment-form').trigger('submit');
  213. });
  214. }
  215. export function initRepoPullRequestUpdate() {
  216. // Pull Request update button
  217. const $pullUpdateButton = $('.update-button > button');
  218. $pullUpdateButton.on('click', function (e) {
  219. e.preventDefault();
  220. const $this = $(this);
  221. const redirect = $this.data('redirect');
  222. $this.addClass('loading');
  223. $.post($this.data('do'), {
  224. _csrf: csrfToken
  225. }).done((data) => {
  226. if (data.redirect) {
  227. window.location.href = data.redirect;
  228. } else if (redirect) {
  229. window.location.href = redirect;
  230. } else {
  231. window.location.reload();
  232. }
  233. });
  234. });
  235. $('.update-button > .dropdown').dropdown({
  236. onChange(_text, _value, $choice) {
  237. const $url = $choice.data('do');
  238. if ($url) {
  239. $pullUpdateButton.find('.button-text').text($choice.text());
  240. $pullUpdateButton.data('do', $url);
  241. }
  242. }
  243. });
  244. }
  245. export function initRepoPullRequestMergeInstruction() {
  246. $('.show-instruction').on('click', () => {
  247. $('.instruct-content').toggle();
  248. });
  249. }
  250. export function initRepoPullRequestAllowMaintainerEdit() {
  251. const $checkbox = $('#allow-edits-from-maintainers');
  252. if (!$checkbox.length) return;
  253. const promptTip = $checkbox.attr('data-prompt-tip');
  254. const promptError = $checkbox.attr('data-prompt-error');
  255. $checkbox.popup({content: promptTip});
  256. $checkbox.checkbox({
  257. 'onChange': () => {
  258. const checked = $checkbox.checkbox('is checked');
  259. let url = $checkbox.attr('data-url');
  260. url += '/set_allow_maintainer_edit';
  261. $checkbox.checkbox('set disabled');
  262. $.ajax({url, type: 'POST',
  263. data: {_csrf: csrfToken, allow_maintainer_edit: checked},
  264. error: () => {
  265. $checkbox.popup({
  266. content: promptError,
  267. onHidden: () => {
  268. // the error popup should be shown only once, then we restore the popup to the default message
  269. $checkbox.popup({content: promptTip});
  270. },
  271. });
  272. $checkbox.popup('show');
  273. },
  274. complete: () => {
  275. $checkbox.checkbox('set enabled');
  276. },
  277. });
  278. },
  279. });
  280. }
  281. export function initRepoIssueReferenceRepositorySearch() {
  282. $('.issue_reference_repository_search')
  283. .dropdown({
  284. apiSettings: {
  285. url: `${appSubUrl}/repo/search?q={query}&limit=20`,
  286. onResponse(response) {
  287. const filteredResponse = {success: true, results: []};
  288. $.each(response.data, (_r, repo) => {
  289. filteredResponse.results.push({
  290. name: htmlEscape(repo.full_name),
  291. value: repo.full_name
  292. });
  293. });
  294. return filteredResponse;
  295. },
  296. cache: false,
  297. },
  298. onChange(_value, _text, $choice) {
  299. const $form = $choice.closest('form');
  300. $form.attr('action', `${appSubUrl}/${_text}/issues/new`);
  301. },
  302. fullTextSearch: true
  303. });
  304. }
  305. export function initRepoIssueWipTitle() {
  306. $('.title_wip_desc > a').on('click', (e) => {
  307. e.preventDefault();
  308. const $issueTitle = $('#issue_title');
  309. $issueTitle.focus();
  310. const value = $issueTitle.val().trim().toUpperCase();
  311. const wipPrefixes = $('.title_wip_desc').data('wip-prefixes');
  312. for (const prefix of wipPrefixes) {
  313. if (value.startsWith(prefix.toUpperCase())) {
  314. return;
  315. }
  316. }
  317. $issueTitle.val(`${wipPrefixes[0]} ${$issueTitle.val()}`);
  318. });
  319. }
  320. export async function updateIssuesMeta(url, action, issueIds, elementId) {
  321. return $.ajax({
  322. type: 'POST',
  323. url,
  324. data: {
  325. _csrf: csrfToken,
  326. action,
  327. issue_ids: issueIds,
  328. id: elementId,
  329. },
  330. });
  331. }
  332. export function initRepoIssueComments() {
  333. if ($('.repository.view.issue .timeline').length === 0) return;
  334. $('.re-request-review').on('click', function (e) {
  335. e.preventDefault();
  336. const url = $(this).data('update-url');
  337. const issueId = $(this).data('issue-id');
  338. const id = $(this).data('id');
  339. const isChecked = $(this).hasClass('checked');
  340. updateIssuesMeta(
  341. url,
  342. isChecked ? 'detach' : 'attach',
  343. issueId,
  344. id,
  345. ).then(() => window.location.reload());
  346. });
  347. $('.dismiss-review-btn').on('click', function (e) {
  348. e.preventDefault();
  349. const $this = $(this);
  350. const $dismissReviewModal = $this.next();
  351. $dismissReviewModal.modal('show');
  352. });
  353. $(document).on('click', (event) => {
  354. const urlTarget = $(':target');
  355. if (urlTarget.length === 0) return;
  356. const urlTargetId = urlTarget.attr('id');
  357. if (!urlTargetId) return;
  358. if (!/^(issue|pull)(comment)?-\d+$/.test(urlTargetId)) return;
  359. const $target = $(event.target);
  360. if ($target.closest(`#${urlTargetId}`).length === 0) {
  361. const scrollPosition = $(window).scrollTop();
  362. window.location.hash = '';
  363. $(window).scrollTop(scrollPosition);
  364. window.history.pushState(null, null, ' ');
  365. }
  366. });
  367. }
  368. function assignMenuAttributes(menu) {
  369. const id = Math.floor(Math.random() * Math.floor(1000000));
  370. menu.attr('data-write', menu.attr('data-write') + id);
  371. menu.attr('data-preview', menu.attr('data-preview') + id);
  372. menu.find('.item').each(function () {
  373. const tab = $(this).attr('data-tab') + id;
  374. $(this).attr('data-tab', tab);
  375. });
  376. menu.parent().find("*[data-tab='write']").attr('data-tab', `write${id}`);
  377. menu.parent().find("*[data-tab='preview']").attr('data-tab', `preview${id}`);
  378. initCompMarkupContentPreviewTab(menu.parent('.form'));
  379. return id;
  380. }
  381. export function initRepoPullRequestReview() {
  382. if (window.location.hash && window.location.hash.startsWith('#issuecomment-')) {
  383. const commentDiv = $(window.location.hash);
  384. if (commentDiv) {
  385. // get the name of the parent id
  386. const groupID = commentDiv.closest('div[id^="code-comments-"]').attr('id');
  387. if (groupID && groupID.startsWith('code-comments-')) {
  388. const id = groupID.slice(14);
  389. $(`#show-outdated-${id}`).addClass('hide');
  390. $(`#code-comments-${id}`).removeClass('hide');
  391. $(`#code-preview-${id}`).removeClass('hide');
  392. $(`#hide-outdated-${id}`).removeClass('hide');
  393. commentDiv[0].scrollIntoView();
  394. }
  395. }
  396. }
  397. $(document).on('click', '.show-outdated', function (e) {
  398. e.preventDefault();
  399. const id = $(this).data('comment');
  400. $(this).addClass('hide');
  401. $(`#code-comments-${id}`).removeClass('hide');
  402. $(`#code-preview-${id}`).removeClass('hide');
  403. $(`#hide-outdated-${id}`).removeClass('hide');
  404. });
  405. $(document).on('click', '.hide-outdated', function (e) {
  406. e.preventDefault();
  407. const id = $(this).data('comment');
  408. $(this).addClass('hide');
  409. $(`#code-comments-${id}`).addClass('hide');
  410. $(`#code-preview-${id}`).addClass('hide');
  411. $(`#show-outdated-${id}`).removeClass('hide');
  412. });
  413. $(document).on('click', 'button.comment-form-reply', async function (e) {
  414. e.preventDefault();
  415. $(this).hide();
  416. const form = $(this).closest('.comment-code-cloud').find('.comment-form');
  417. form.removeClass('hide');
  418. const $textarea = form.find('textarea');
  419. let easyMDE = getAttachedEasyMDE($textarea);
  420. if (!easyMDE) {
  421. await attachTribute($textarea.get(), {mentions: true, emoji: true});
  422. easyMDE = await createCommentEasyMDE($textarea);
  423. }
  424. $textarea.focus();
  425. easyMDE.codemirror.focus();
  426. assignMenuAttributes(form.find('.menu'));
  427. });
  428. const $reviewBox = $('.review-box');
  429. if ($reviewBox.length === 1) {
  430. (async () => {
  431. // the editor's height is too large in some cases, and the panel cannot be scrolled with page now because there is `.repository .diff-detail-box.sticky { position: sticky; }`
  432. // the temporary solution is to make the editor's height smaller (about 4 lines). GitHub also only show 4 lines for default. We can improve the UI (including Dropzone area) in future
  433. // EasyMDE's options can not handle minHeight & maxHeight together correctly, we have to set max-height for .CodeMirror-scroll in CSS.
  434. await createCommentEasyMDE($reviewBox.find('textarea'), {minHeight: '80px'});
  435. initCompImagePaste($reviewBox);
  436. })();
  437. }
  438. // The following part is only for diff views
  439. if ($('.repository.pull.diff').length === 0) {
  440. return;
  441. }
  442. $('.btn-review').on('click', function (e) {
  443. e.preventDefault();
  444. $(this).closest('.dropdown').find('.menu').toggle('visible');
  445. }).closest('.dropdown').find('.close').on('click', function (e) {
  446. e.preventDefault();
  447. $(this).closest('.menu').toggle('visible');
  448. });
  449. $(document).on('click', 'a.add-code-comment', async function (e) {
  450. if ($(e.target).hasClass('btn-add-single')) return; // https://github.com/go-gitea/gitea/issues/4745
  451. e.preventDefault();
  452. const isSplit = $(this).closest('.code-diff').hasClass('code-diff-split');
  453. const side = $(this).data('side');
  454. const idx = $(this).data('idx');
  455. const path = $(this).closest('[data-path]').data('path');
  456. const tr = $(this).closest('tr');
  457. const lineType = tr.data('line-type');
  458. let ntr = tr.next();
  459. if (!ntr.hasClass('add-comment')) {
  460. ntr = $(`
  461. <tr class="add-comment" data-line-type="${lineType}">
  462. ${isSplit ? `
  463. <td class="lines-num"></td>
  464. <td class="lines-escape"></td>
  465. <td class="lines-type-marker"></td>
  466. <td class="add-comment-left"></td>
  467. <td class="lines-num"></td>
  468. <td class="lines-escape"></td>
  469. <td class="lines-type-marker"></td>
  470. <td class="add-comment-right"></td>
  471. ` : `
  472. <td class="lines-num"></td>
  473. <td class="lines-num"></td>
  474. <td class="lines-escape"></td>
  475. <td class="add-comment-left add-comment-right" colspan="2"></td>
  476. `}
  477. </tr>`);
  478. tr.after(ntr);
  479. }
  480. const td = ntr.find(`.add-comment-${side}`);
  481. let commentCloud = td.find('.comment-code-cloud');
  482. if (commentCloud.length === 0 && !ntr.find('button[name="is_review"]').length) {
  483. const data = await $.get($(this).closest('[data-new-comment-url]').data('new-comment-url'));
  484. td.html(data);
  485. commentCloud = td.find('.comment-code-cloud');
  486. assignMenuAttributes(commentCloud.find('.menu'));
  487. td.find("input[name='line']").val(idx);
  488. td.find("input[name='side']").val(side === 'left' ? 'previous' : 'proposed');
  489. td.find("input[name='path']").val(path);
  490. const $textarea = commentCloud.find('textarea');
  491. await attachTribute($textarea.get(), {mentions: true, emoji: true});
  492. const easyMDE = await createCommentEasyMDE($textarea);
  493. $textarea.focus();
  494. easyMDE.codemirror.focus();
  495. }
  496. });
  497. }
  498. export function initRepoIssueReferenceIssue() {
  499. // Reference issue
  500. $(document).on('click', '.reference-issue', function (event) {
  501. const $this = $(this);
  502. $this.closest('.dropdown').find('.menu').toggle('visible');
  503. const content = $(`#comment-${$this.data('target')}`).text();
  504. const poster = $this.data('poster-username');
  505. const reference = $this.data('reference');
  506. const $modal = $($this.data('modal'));
  507. $modal.find('textarea[name="content"]').val(`${content}\n\n_Originally posted by @${poster} in ${reference}_`);
  508. $modal.modal('show');
  509. event.preventDefault();
  510. });
  511. }
  512. export function initRepoIssueWipToggle() {
  513. // Toggle WIP
  514. $('.toggle-wip a, .toggle-wip button').on('click', async (e) => {
  515. e.preventDefault();
  516. const toggleWip = e.currentTarget.closest('.toggle-wip');
  517. const title = toggleWip.getAttribute('data-title');
  518. const wipPrefix = toggleWip.getAttribute('data-wip-prefix');
  519. const updateUrl = toggleWip.getAttribute('data-update-url');
  520. await $.post(updateUrl, {
  521. _csrf: csrfToken,
  522. title: title?.startsWith(wipPrefix) ? title.slice(wipPrefix.length).trim() : `${wipPrefix.trim()} ${title}`,
  523. });
  524. window.location.reload();
  525. });
  526. }
  527. export function initRepoIssueTitleEdit() {
  528. // Edit issue title
  529. const $issueTitle = $('#issue-title');
  530. const $editInput = $('#edit-title-input input');
  531. const editTitleToggle = function () {
  532. $issueTitle.toggle();
  533. $('.not-in-edit').toggle();
  534. $('#edit-title-input').toggle();
  535. $('#pull-desc').toggle();
  536. $('#pull-desc-edit').toggle();
  537. $('.in-edit').toggle();
  538. $('#issue-title-wrapper').toggleClass('edit-active');
  539. $editInput.focus();
  540. return false;
  541. };
  542. $('#edit-title').on('click', editTitleToggle);
  543. $('#cancel-edit-title').on('click', editTitleToggle);
  544. $('#save-edit-title').on('click', editTitleToggle).on('click', function () {
  545. const pullrequest_targetbranch_change = function (update_url) {
  546. const targetBranch = $('#pull-target-branch').data('branch');
  547. const $branchTarget = $('#branch_target');
  548. if (targetBranch === $branchTarget.text()) {
  549. return false;
  550. }
  551. $.post(update_url, {
  552. _csrf: csrfToken,
  553. target_branch: targetBranch
  554. }).done((data) => {
  555. $branchTarget.text(data.base_branch);
  556. }).always(() => {
  557. window.location.reload();
  558. });
  559. };
  560. const pullrequest_target_update_url = $(this).data('target-update-url');
  561. if ($editInput.val().length === 0 || $editInput.val() === $issueTitle.text()) {
  562. $editInput.val($issueTitle.text());
  563. pullrequest_targetbranch_change(pullrequest_target_update_url);
  564. } else {
  565. $.post($(this).data('update-url'), {
  566. _csrf: csrfToken,
  567. title: $editInput.val()
  568. }, (data) => {
  569. $editInput.val(data.title);
  570. $issueTitle.text(data.title);
  571. pullrequest_targetbranch_change(pullrequest_target_update_url);
  572. window.location.reload();
  573. });
  574. }
  575. return false;
  576. });
  577. }
  578. export function initRepoIssueBranchSelect() {
  579. const changeBranchSelect = function () {
  580. const selectionTextField = $('#pull-target-branch');
  581. const baseName = selectionTextField.data('basename');
  582. const branchNameNew = $(this).data('branch');
  583. const branchNameOld = selectionTextField.data('branch');
  584. // Replace branch name to keep translation from HTML template
  585. selectionTextField.html(selectionTextField.html().replace(
  586. `${baseName}:${branchNameOld}`,
  587. `${baseName}:${branchNameNew}`
  588. ));
  589. selectionTextField.data('branch', branchNameNew); // update branch name in setting
  590. };
  591. $('#branch-select > .item').on('click', changeBranchSelect);
  592. }