diff options
author | Giteabot <teabot@gitea.io> | 2024-05-05 21:53:12 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-05-05 13:53:12 +0000 |
commit | 60fa2a5960e8c51c164f4083fac6707c54a747ba (patch) | |
tree | c699e05c1326306a8b1d42c6481e27e78fe04243 | |
parent | 054602977a8b142881460b46ddaafaf3c04c6a3d (diff) | |
download | gitea-60fa2a5960e8c51c164f4083fac6707c54a747ba.tar.gz gitea-60fa2a5960e8c51c164f4083fac6707c54a747ba.zip |
Fix issue/PR title edit (#30858) (#30865)
Backport #30858 by wxiaoguang
1. "enter" doesn't work (I think it is the last enter support for #14843)
2. if a branch name contains something like `&`, then the branch selector doesn't update
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
-rw-r--r-- | templates/repo/issue/view_title.tmpl | 43 | ||||
-rw-r--r-- | tests/integration/issue_test.go | 2 | ||||
-rw-r--r-- | tests/integration/pull_create_test.go | 2 | ||||
-rw-r--r-- | web_src/css/repo.css | 73 | ||||
-rw-r--r-- | web_src/js/features/common-global.js | 16 | ||||
-rw-r--r-- | web_src/js/features/comp/QuickSubmit.js | 13 | ||||
-rw-r--r-- | web_src/js/features/repo-issue.js | 127 |
7 files changed, 139 insertions, 137 deletions
diff --git a/templates/repo/issue/view_title.tmpl b/templates/repo/issue/view_title.tmpl index fccf8cca91..4415ad79f5 100644 --- a/templates/repo/issue/view_title.tmpl +++ b/templates/repo/issue/view_title.tmpl @@ -4,29 +4,36 @@ </div> {{end}} <div class="issue-title-header"> - <div class="issue-title" id="issue-title-wrapper"> + {{$canEditIssueTitle := and (or .HasIssuesOrPullsWritePermission .IsIssuePoster) (not .Repository.IsArchived)}} + <div class="issue-title" id="issue-title-display"> <h1 class="gt-word-break"> - <span id="issue-title">{{RenderIssueTitle $.Context .Issue.Title ($.Repository.ComposeMetas ctx) | RenderCodeBlock}} <span class="index">#{{.Issue.Index}}</span> -</span> - <div id="edit-title-input" class="ui input tw-flex-1 tw-hidden"> - <input value="{{.Issue.Title}}" maxlength="255" autocomplete="off"> - </div> + {{RenderIssueTitle $.Context .Issue.Title ($.Repository.ComposeMetas ctx) | RenderCodeBlock}} + <span class="index">#{{.Issue.Index}}</span> </h1> <div class="issue-title-buttons"> - {{if and (or .HasIssuesOrPullsWritePermission .IsIssuePoster) (not .Repository.IsArchived)}} - <button id="edit-title" class="ui small basic button edit-button not-in-edit{{if .Issue.IsPull}} tw-mr-0{{end}}">{{ctx.Locale.Tr "repo.issues.edit"}}</button> + {{if $canEditIssueTitle}} + <button id="issue-title-edit-show" class="ui small basic button">{{ctx.Locale.Tr "repo.issues.edit"}}</button> {{end}} {{if not .Issue.IsPull}} - <a role="button" class="ui small primary button new-issue-button tw-mr-0" href="{{.RepoLink}}/issues/new{{if .NewIssueChooseTemplate}}/choose{{end}}">{{ctx.Locale.Tr "repo.issues.new"}}</a> + <a role="button" class="ui small primary button" href="{{.RepoLink}}/issues/new{{if .NewIssueChooseTemplate}}/choose{{end}}">{{ctx.Locale.Tr "repo.issues.new"}}</a> {{end}} </div> - {{if and (or .HasIssuesOrPullsWritePermission .IsIssuePoster) (not .Repository.IsArchived)}} - <div class="edit-buttons"> - <button id="cancel-edit-title" class="ui small basic button in-edit tw-hidden">{{ctx.Locale.Tr "repo.issues.cancel"}}</button> - <button id="save-edit-title" class="ui small primary button in-edit tw-hidden tw-mr-0" data-update-url="{{$.RepoLink}}/issues/{{.Issue.Index}}/title" {{if .Issue.IsPull}}data-target-update-url="{{$.RepoLink}}/pull/{{.Issue.Index}}/target_branch"{{end}}>{{ctx.Locale.Tr "repo.issues.save"}}</button> - </div> - {{end}} </div> + {{if $canEditIssueTitle}} + <div class="ui form issue-title tw-hidden" id="issue-title-editor"> + <div class="ui input tw-flex-1"> + <input value="{{.Issue.Title}}" data-old-title="{{.Issue.Title}}" maxlength="255" autocomplete="off"> + </div> + <div class="issue-title-buttons"> + <button class="ui small basic cancel button">{{ctx.Locale.Tr "repo.issues.cancel"}}</button> + <button class="ui small primary button" + data-update-url="{{$.RepoLink}}/issues/{{.Issue.Index}}/title" + {{if .Issue.IsPull}}data-target-update-url="{{$.RepoLink}}/pull/{{.Issue.Index}}/target_branch"{{end}}> + {{ctx.Locale.Tr "repo.issues.save"}} + </button> + </div> + </div> + {{end}} <div class="issue-title-meta"> {{if .HasMerged}} <div class="ui purple label issue-state-label">{{svg "octicon-git-merge" 16 "tw-mr-1"}} {{if eq .Issue.PullRequest.Status 3}}{{ctx.Locale.Tr "repo.pulls.manually_merged"}}{{else}}{{ctx.Locale.Tr "repo.pulls.merged"}}{{end}}</div> @@ -63,14 +70,14 @@ {{end}} {{else}} {{if .Issue.OriginalAuthor}} - <span id="pull-desc" class="pull-desc">{{.Issue.OriginalAuthor}} {{ctx.Locale.Tr "repo.pulls.title_desc" .NumCommits $headHref $baseHref}}</span> + <span id="pull-desc-display" class="pull-desc">{{.Issue.OriginalAuthor}} {{ctx.Locale.Tr "repo.pulls.title_desc" .NumCommits $headHref $baseHref}}</span> {{else}} - <span id="pull-desc" class="pull-desc"> + <span id="pull-desc-display" class="pull-desc"> <a {{if gt .Issue.Poster.ID 0}}href="{{.Issue.Poster.HomeLink}}"{{end}}>{{.Issue.Poster.GetDisplayName}}</a> {{ctx.Locale.Tr "repo.pulls.title_desc" .NumCommits $headHref $baseHref}} </span> {{end}} - <span id="pull-desc-edit" class="tw-hidden flex-text-block"> + <span id="pull-desc-editor" class="tw-hidden flex-text-block"> <div class="ui floating filter dropdown"> <div class="ui basic small button tw-mr-0"> <span class="text">{{ctx.Locale.Tr "repo.pulls.compare_compare"}}: {{$.HeadTarget}}</span> diff --git a/tests/integration/issue_test.go b/tests/integration/issue_test.go index b7952b0879..d74516d110 100644 --- a/tests/integration/issue_test.go +++ b/tests/integration/issue_test.go @@ -144,7 +144,7 @@ func testNewIssue(t *testing.T, session *TestSession, user, repo, title, content resp = session.MakeRequest(t, req, http.StatusOK) htmlDoc = NewHTMLParser(t, resp.Body) - val := htmlDoc.doc.Find("#issue-title").Text() + val := htmlDoc.doc.Find("#issue-title-display").Text() assert.Contains(t, val, title) val = htmlDoc.doc.Find(".comment .render-content p").First().Text() assert.Equal(t, content, val) diff --git a/tests/integration/pull_create_test.go b/tests/integration/pull_create_test.go index 609bd73fd5..7add8e1db6 100644 --- a/tests/integration/pull_create_test.go +++ b/tests/integration/pull_create_test.go @@ -125,7 +125,7 @@ func TestPullCreate_TitleEscape(t *testing.T) { req := NewRequest(t, "GET", url) resp = session.MakeRequest(t, req, http.StatusOK) htmlDoc := NewHTMLParser(t, resp.Body) - editTestTitleURL, exists := htmlDoc.doc.Find("#save-edit-title").First().Attr("data-update-url") + editTestTitleURL, exists := htmlDoc.doc.Find(".issue-title-buttons button[data-update-url]").First().Attr("data-update-url") assert.True(t, exists, "The template has changed") req = NewRequestWithValues(t, "POST", editTestTitleURL, map[string]string{ diff --git a/web_src/css/repo.css b/web_src/css/repo.css index 408b62ad3c..7695b632b4 100644 --- a/web_src/css/repo.css +++ b/web_src/css/repo.css @@ -575,6 +575,22 @@ td .commit-summary { display: inline-block; } +@media (max-width: 767.98px) { + .comment.form .issue-content-left .avatar { + display: none; + } + .comment.form .issue-content-left .content { + margin-left: 0 !important; + } + .comment.form .issue-content-left .content::before, + .comment.form .issue-content-left .content::after, + .comment.form .content .form::before, + .comment.form .content .form::after { + display: none; + } +} + +/* issue title & meta & edit */ .issue-title-header { width: 100%; padding-bottom: 4px; @@ -586,46 +602,25 @@ td .commit-summary { align-items: center; } -.repository.view.issue .issue-title-buttons, -.repository.view.issue .edit-buttons { +.repository.view.issue .issue-title-buttons { display: flex; + gap: 0.5em; } -@media (max-width: 767.98px) { - .repository.view.issue .issue-title { - flex-direction: column; - } - .repository.view.issue .issue-title-buttons, - .repository.view.issue .edit-buttons { - width: 100%; - justify-content: space-between; - } - .repository.view.issue .edit-buttons { - margin-top: .5rem; - } - .comment.form .issue-content-left .avatar { - display: none; - } - .comment.form .issue-content-left .content { - margin-left: 0 !important; - } - .comment.form .issue-content-left .content::before, - .comment.form .issue-content-left .content::after, - .comment.form .content .form::before, - .comment.form .content .form::after { - display: none; - } +.repository.view.issue .issue-title-buttons > .ui.button { + margin: 0; + height: 35px; } .repository.view.issue .issue-title { display: flex; align-items: center; + gap: 0.5em; margin-bottom: 8px; + min-height: 40px; /* avoid layout shift on edit */ } .repository.view.issue .issue-title h1 { - display: flex; - align-items: center; flex: 1; width: 100%; font-weight: var(--font-weight-normal); @@ -633,14 +628,24 @@ td .commit-summary { line-height: 40px; margin: 0; padding-right: 0.25rem; - min-height: 41px; /* avoid layout shift on edit */ } -.repository.view.issue .issue-title h1 .ui.input { - font-size: 0.5em; +@media (max-width: 767.98px) { + .repository.view.issue .issue-title { + flex-direction: column; + } + .repository.view.issue .issue-title-buttons { + width: 100%; + justify-content: space-between; + } } -.repository.view.issue .issue-title h1 .ui.input input { +.repository.view.issue .issue-title .ui.input { + width: 100%; + height: 35px; +} + +.repository.view.issue .issue-title .ui.input input { font-size: 1.5em; padding: 2px .5rem; } @@ -653,10 +658,6 @@ td .commit-summary { margin-right: 10px; } -.issue-title .edit-zone { - margin-top: 10px; -} - .issue-state-label { display: flex !important; align-items: center !important; diff --git a/web_src/js/features/common-global.js b/web_src/js/features/common-global.js index a821e1b921..5b8673105d 100644 --- a/web_src/js/features/common-global.js +++ b/web_src/js/features/common-global.js @@ -47,10 +47,18 @@ export function initFootLanguageMenu() { export function initGlobalEnterQuickSubmit() { document.addEventListener('keydown', (e) => { - const isQuickSubmitEnter = ((e.ctrlKey && !e.altKey) || e.metaKey) && (e.key === 'Enter'); - if (isQuickSubmitEnter && e.target.matches('textarea')) { - e.preventDefault(); - handleGlobalEnterQuickSubmit(e.target); + if (e.key !== 'Enter') return; + const hasCtrlOrMeta = ((e.ctrlKey || e.metaKey) && !e.altKey); + if (hasCtrlOrMeta && e.target.matches('textarea')) { + if (handleGlobalEnterQuickSubmit(e.target)) { + e.preventDefault(); + } + } else if (e.target.matches('input') && !e.target.closest('form')) { + // input in a normal form could handle Enter key by default, so we only handle the input outside a form + // eslint-disable-next-line unicorn/no-lonely-if + if (handleGlobalEnterQuickSubmit(e.target)) { + e.preventDefault(); + } } }); } diff --git a/web_src/js/features/comp/QuickSubmit.js b/web_src/js/features/comp/QuickSubmit.js index 6bd5f6644d..3ff29f4fac 100644 --- a/web_src/js/features/comp/QuickSubmit.js +++ b/web_src/js/features/comp/QuickSubmit.js @@ -3,16 +3,17 @@ export function handleGlobalEnterQuickSubmit(target) { if (form) { if (!form.checkValidity()) { form.reportValidity(); - return; + } else { + // here use the event to trigger the submit event (instead of calling `submit()` method directly) + // otherwise the `areYouSure` handler won't be executed, then there will be an annoying "confirm to leave" dialog + form.dispatchEvent(new SubmitEvent('submit', {bubbles: true, cancelable: true})); } - - // here use the event to trigger the submit event (instead of calling `submit()` method directly) - // otherwise the `areYouSure` handler won't be executed, then there will be an annoying "confirm to leave" dialog - form.dispatchEvent(new SubmitEvent('submit', {bubbles: true, cancelable: true})); - return; + return true; } form = target.closest('.ui.form'); if (form) { form.querySelector('.ui.primary.button')?.click(); + return true; } + return false; } diff --git a/web_src/js/features/repo-issue.js b/web_src/js/features/repo-issue.js index c4e14c62c4..39c364ca50 100644 --- a/web_src/js/features/repo-issue.js +++ b/web_src/js/features/repo-issue.js @@ -7,6 +7,7 @@ import {getComboMarkdownEditor, initComboMarkdownEditor} from './comp/ComboMarkd import {toAbsoluteUrl} from '../utils.js'; import {initDropzone} from './common-global.js'; import {POST, GET} from '../modules/fetch.js'; +import {showErrorToast} from '../modules/toast.js'; const {appSubUrl} = window.config; @@ -602,85 +603,69 @@ export function initRepoIssueWipToggle() { }); } -async function pullrequest_targetbranch_change(update_url) { - const targetBranch = $('#pull-target-branch').data('branch'); - const $branchTarget = $('#branch_target'); - if (targetBranch === $branchTarget.text()) { - window.location.reload(); - return false; - } - try { - await POST(update_url, {data: new URLSearchParams({target_branch: targetBranch})}); - } catch (error) { - console.error(error); - } finally { - window.location.reload(); - } -} - export function initRepoIssueTitleEdit() { - // Edit issue title - const $issueTitle = $('#issue-title'); - const $editInput = $('#edit-title-input input'); - - const editTitleToggle = function () { - toggleElem($issueTitle); - toggleElem('.not-in-edit'); - toggleElem('#edit-title-input'); - toggleElem('#pull-desc'); - toggleElem('#pull-desc-edit'); - toggleElem('.in-edit'); - toggleElem('.new-issue-button'); - document.getElementById('issue-title-wrapper')?.classList.toggle('edit-active'); - $editInput[0].focus(); - $editInput[0].select(); - return false; - }; - - $('#edit-title').on('click', editTitleToggle); - $('#cancel-edit-title').on('click', editTitleToggle); - $('#save-edit-title').on('click', editTitleToggle).on('click', async function () { - const pullrequest_target_update_url = this.getAttribute('data-target-update-url'); - if (!$editInput.val().length || $editInput.val() === $issueTitle.text()) { - $editInput.val($issueTitle.text()); - await pullrequest_targetbranch_change(pullrequest_target_update_url); - } else { - try { - const params = new URLSearchParams(); - params.append('title', $editInput.val()); - const response = await POST(this.getAttribute('data-update-url'), {data: params}); - const data = await response.json(); - $editInput.val(data.title); - $issueTitle.text(data.title); - if (pullrequest_target_update_url) { - await pullrequest_targetbranch_change(pullrequest_target_update_url); // it will reload the window - } else { - window.location.reload(); + const issueTitleDisplay = document.querySelector('#issue-title-display'); + const issueTitleEditor = document.querySelector('#issue-title-editor'); + if (!issueTitleEditor) return; + + const issueTitleInput = issueTitleEditor.querySelector('input'); + const oldTitle = issueTitleInput.getAttribute('data-old-title'); + issueTitleDisplay.querySelector('#issue-title-edit-show').addEventListener('click', () => { + hideElem(issueTitleDisplay); + hideElem('#pull-desc-display'); + showElem(issueTitleEditor); + showElem('#pull-desc-editor'); + if (!issueTitleInput.value.trim()) { + issueTitleInput.value = oldTitle; + } + issueTitleInput.focus(); + }); + issueTitleEditor.querySelector('.ui.cancel.button').addEventListener('click', () => { + hideElem(issueTitleEditor); + hideElem('#pull-desc-editor'); + showElem(issueTitleDisplay); + showElem('#pull-desc-display'); + }); + const editSaveButton = issueTitleEditor.querySelector('.ui.primary.button'); + editSaveButton.addEventListener('click', async () => { + const prTargetUpdateUrl = editSaveButton.getAttribute('data-target-update-url'); + const newTitle = issueTitleInput.value.trim(); + try { + if (newTitle && newTitle !== oldTitle) { + const resp = await POST(editSaveButton.getAttribute('data-update-url'), {data: new URLSearchParams({title: newTitle})}); + if (!resp.ok) { + throw new Error(`Failed to update issue title: ${resp.statusText}`); } - } catch (error) { - console.error(error); } + if (prTargetUpdateUrl) { + const newTargetBranch = document.querySelector('#pull-target-branch').getAttribute('data-branch'); + const oldTargetBranch = document.querySelector('#branch_target').textContent; + if (newTargetBranch !== oldTargetBranch) { + const resp = await POST(prTargetUpdateUrl, {data: new URLSearchParams({target_branch: newTargetBranch})}); + if (!resp.ok) { + throw new Error(`Failed to update PR target branch: ${resp.statusText}`); + } + } + } + window.location.reload(); + } catch (error) { + console.error(error); + showErrorToast(error.message); } - return false; }); } export function initRepoIssueBranchSelect() { - const changeBranchSelect = function () { - const $selectionTextField = $('#pull-target-branch'); - - const baseName = $selectionTextField.data('basename'); - const branchNameNew = $(this).data('branch'); - const branchNameOld = $selectionTextField.data('branch'); - - // Replace branch name to keep translation from HTML template - $selectionTextField.html($selectionTextField.html().replace( - `${baseName}:${branchNameOld}`, - `${baseName}:${branchNameNew}`, - )); - $selectionTextField.data('branch', branchNameNew); // update branch name in setting - }; - $('#branch-select > .item').on('click', changeBranchSelect); + document.querySelector('#branch-select')?.addEventListener('click', (e) => { + const el = e.target.closest('.item[data-branch]'); + if (!el) return; + const pullTargetBranch = document.querySelector('#pull-target-branch'); + const baseName = pullTargetBranch.getAttribute('data-basename'); + const branchNameNew = el.getAttribute('data-branch'); + const branchNameOld = pullTargetBranch.getAttribute('data-branch'); + pullTargetBranch.textContent = pullTargetBranch.textContent.replace(`${baseName}:${branchNameOld}`, `${baseName}:${branchNameNew}`); + pullTargetBranch.setAttribute('data-branch', branchNameNew); + }); } export function initSingleCommentEditor($commentForm) { |