Skip to content

Commit 89e37f9

Browse files
committed
fix
1 parent bb0e4ce commit 89e37f9

File tree

6 files changed

+115
-118
lines changed

6 files changed

+115
-118
lines changed

templates/repo/issue/view_title.tmpl

Lines changed: 25 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -4,29 +4,36 @@
44
</div>
55
{{end}}
66
<div class="issue-title-header">
7-
<div class="issue-title" id="issue-title-wrapper">
7+
{{$canEditIssueTitle := and (or .HasIssuesOrPullsWritePermission .IsIssuePoster) (not .Repository.IsArchived)}}
8+
<div class="issue-title" id="issue-title-display">
89
<h1 class="gt-word-break">
9-
<span id="issue-title">{{RenderIssueTitle $.Context .Issue.Title ($.Repository.ComposeMetas ctx) | RenderCodeBlock}} <span class="index">#{{.Issue.Index}}</span>
10-
</span>
11-
<div id="edit-title-input" class="ui input tw-flex-1 tw-hidden">
12-
<input value="{{.Issue.Title}}" maxlength="255" autocomplete="off">
13-
</div>
10+
{{RenderIssueTitle $.Context .Issue.Title ($.Repository.ComposeMetas ctx) | RenderCodeBlock}}
11+
<span class="index">#{{.Issue.Index}}</span>
1412
</h1>
1513
<div class="issue-title-buttons">
16-
{{if and (or .HasIssuesOrPullsWritePermission .IsIssuePoster) (not .Repository.IsArchived)}}
17-
<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>
14+
{{if $canEditIssueTitle}}
15+
<button id="issue-title-edit-show" class="ui small basic button">{{ctx.Locale.Tr "repo.issues.edit"}}</button>
1816
{{end}}
1917
{{if not .Issue.IsPull}}
20-
<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>
18+
<a role="button" class="ui small primary button" href="{{.RepoLink}}/issues/new{{if .NewIssueChooseTemplate}}/choose{{end}}">{{ctx.Locale.Tr "repo.issues.new"}}</a>
2119
{{end}}
2220
</div>
23-
{{if and (or .HasIssuesOrPullsWritePermission .IsIssuePoster) (not .Repository.IsArchived)}}
24-
<div class="edit-buttons">
25-
<button id="cancel-edit-title" class="ui small basic button in-edit tw-hidden">{{ctx.Locale.Tr "repo.issues.cancel"}}</button>
26-
<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>
27-
</div>
28-
{{end}}
2921
</div>
22+
{{if $canEditIssueTitle}}
23+
<div class="ui form issue-title tw-hidden" id="issue-title-editor">
24+
<div class="ui input tw-flex-1">
25+
<input value="{{.Issue.Title}}" data-old-title="{{.Issue.Title}}" maxlength="255" autocomplete="off">
26+
</div>
27+
<div class="issue-title-buttons">
28+
<button class="ui small basic cancel button">{{ctx.Locale.Tr "repo.issues.cancel"}}</button>
29+
<button class="ui small primary button"
30+
data-update-url="{{$.RepoLink}}/issues/{{.Issue.Index}}/title"
31+
{{if .Issue.IsPull}}data-target-update-url="{{$.RepoLink}}/pull/{{.Issue.Index}}/target_branch"{{end}}>
32+
{{ctx.Locale.Tr "repo.issues.save"}}
33+
</button>
34+
</div>
35+
</div>
36+
{{end}}
3037
<div class="issue-title-meta">
3138
{{if .HasMerged}}
3239
<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 @@
6370
{{end}}
6471
{{else}}
6572
{{if .Issue.OriginalAuthor}}
66-
<span id="pull-desc" class="pull-desc">{{.Issue.OriginalAuthor}} {{ctx.Locale.Tr "repo.pulls.title_desc" .NumCommits $headHref $baseHref}}</span>
73+
<span id="pull-desc-display" class="pull-desc">{{.Issue.OriginalAuthor}} {{ctx.Locale.Tr "repo.pulls.title_desc" .NumCommits $headHref $baseHref}}</span>
6774
{{else}}
68-
<span id="pull-desc" class="pull-desc">
75+
<span id="pull-desc-display" class="pull-desc">
6976
<a {{if gt .Issue.Poster.ID 0}}href="{{.Issue.Poster.HomeLink}}"{{end}}>{{.Issue.Poster.GetDisplayName}}</a>
7077
{{ctx.Locale.Tr "repo.pulls.title_desc" .NumCommits $headHref $baseHref}}
7178
</span>
7279
{{end}}
73-
<span id="pull-desc-edit" class="tw-hidden flex-text-block">
80+
<span id="pull-desc-editor" class="tw-hidden flex-text-block">
7481
<div class="ui floating filter dropdown">
7582
<div class="ui basic small button tw-mr-0">
7683
<span class="text">{{ctx.Locale.Tr "repo.pulls.compare_compare"}}: {{$.HeadTarget}}</span>

tests/integration/issue_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ func testNewIssue(t *testing.T, session *TestSession, user, repo, title, content
144144
resp = session.MakeRequest(t, req, http.StatusOK)
145145

146146
htmlDoc = NewHTMLParser(t, resp.Body)
147-
val := htmlDoc.doc.Find("#issue-title").Text()
147+
val := htmlDoc.doc.Find("#issue-title-display").Text()
148148
assert.Contains(t, val, title)
149149
val = htmlDoc.doc.Find(".comment .render-content p").First().Text()
150150
assert.Equal(t, content, val)

web_src/css/repo.css

Lines changed: 14 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -586,22 +586,23 @@ td .commit-summary {
586586
align-items: center;
587587
}
588588

589-
.repository.view.issue .issue-title-buttons,
590-
.repository.view.issue .edit-buttons {
589+
.repository.view.issue .issue-title-buttons {
591590
display: flex;
591+
gap: 0.5em;
592+
}
593+
594+
.repository.view.issue .issue-title-buttons > .ui.button {
595+
margin: 0;
592596
}
593597

594598
@media (max-width: 767.98px) {
595599
.repository.view.issue .issue-title {
596600
flex-direction: column;
601+
gap: 0.25em;
597602
}
598-
.repository.view.issue .issue-title-buttons,
599-
.repository.view.issue .edit-buttons {
603+
.repository.view.issue .issue-title-buttons {
600604
width: 100%;
601-
justify-content: space-between;
602-
}
603-
.repository.view.issue .edit-buttons {
604-
margin-top: .5rem;
605+
justify-content: right;
605606
}
606607
.comment.form .issue-content-left .avatar {
607608
display: none;
@@ -620,27 +621,26 @@ td .commit-summary {
620621
.repository.view.issue .issue-title {
621622
display: flex;
622623
align-items: center;
624+
gap: 0.5em;
623625
margin-bottom: 8px;
626+
min-height: 40px; /* avoid layout shift on edit */
624627
}
625628

626629
.repository.view.issue .issue-title h1 {
627-
display: flex;
628-
align-items: center;
629630
flex: 1;
630631
width: 100%;
631632
font-weight: var(--font-weight-normal);
632633
font-size: 32px;
633634
line-height: 40px;
634635
margin: 0;
635636
padding-right: 0.25rem;
636-
min-height: 41px; /* avoid layout shift on edit */
637637
}
638638

639-
.repository.view.issue .issue-title h1 .ui.input {
640-
font-size: 0.5em;
639+
.repository.view.issue .issue-title .ui.input {
640+
width: 100%;
641641
}
642642

643-
.repository.view.issue .issue-title h1 .ui.input input {
643+
.repository.view.issue .issue-title .ui.input input {
644644
font-size: 1.5em;
645645
padding: 2px .5rem;
646646
}
@@ -653,10 +653,6 @@ td .commit-summary {
653653
margin-right: 10px;
654654
}
655655

656-
.issue-title .edit-zone {
657-
margin-top: 10px;
658-
}
659-
660656
.issue-state-label {
661657
display: flex !important;
662658
align-items: center !important;

web_src/js/features/common-global.js

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -47,10 +47,18 @@ export function initFootLanguageMenu() {
4747

4848
export function initGlobalEnterQuickSubmit() {
4949
document.addEventListener('keydown', (e) => {
50-
const isQuickSubmitEnter = ((e.ctrlKey && !e.altKey) || e.metaKey) && (e.key === 'Enter');
51-
if (isQuickSubmitEnter && e.target.matches('textarea')) {
52-
e.preventDefault();
53-
handleGlobalEnterQuickSubmit(e.target);
50+
if (e.key !== 'Enter') return;
51+
const hasCtrlOrMeta = ((e.ctrlKey || e.metaKey) && !e.altKey);
52+
if (hasCtrlOrMeta && e.target.matches('textarea')) {
53+
if (handleGlobalEnterQuickSubmit(e.target)) {
54+
e.preventDefault();
55+
}
56+
} else if (e.target.matches('input') && !e.target.closest('form')) {
57+
// input in a normal form could handle Enter key by default, so we only handle the input outside a form
58+
// eslint-disable-next-line unicorn/no-lonely-if
59+
if (handleGlobalEnterQuickSubmit(e.target)) {
60+
e.preventDefault();
61+
}
5462
}
5563
});
5664
}

web_src/js/features/comp/QuickSubmit.js

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,17 @@ export function handleGlobalEnterQuickSubmit(target) {
33
if (form) {
44
if (!form.checkValidity()) {
55
form.reportValidity();
6-
return;
6+
} else {
7+
// here use the event to trigger the submit event (instead of calling `submit()` method directly)
8+
// otherwise the `areYouSure` handler won't be executed, then there will be an annoying "confirm to leave" dialog
9+
form.dispatchEvent(new SubmitEvent('submit', {bubbles: true, cancelable: true}));
710
}
8-
9-
// here use the event to trigger the submit event (instead of calling `submit()` method directly)
10-
// otherwise the `areYouSure` handler won't be executed, then there will be an annoying "confirm to leave" dialog
11-
form.dispatchEvent(new SubmitEvent('submit', {bubbles: true, cancelable: true}));
12-
return;
11+
return true;
1312
}
1413
form = target.closest('.ui.form');
1514
if (form) {
1615
form.querySelector('.ui.primary.button')?.click();
16+
return true;
1717
}
18+
return false;
1819
}

web_src/js/features/repo-issue.js

Lines changed: 56 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {getComboMarkdownEditor, initComboMarkdownEditor} from './comp/ComboMarkd
77
import {toAbsoluteUrl} from '../utils.js';
88
import {initDropzone} from './common-global.js';
99
import {POST, GET} from '../modules/fetch.js';
10+
import {showErrorToast} from '../modules/toast.js';
1011

1112
const {appSubUrl} = window.config;
1213

@@ -602,85 +603,69 @@ export function initRepoIssueWipToggle() {
602603
});
603604
}
604605

605-
async function pullrequest_targetbranch_change(update_url) {
606-
const targetBranch = $('#pull-target-branch').data('branch');
607-
const $branchTarget = $('#branch_target');
608-
if (targetBranch === $branchTarget.text()) {
609-
window.location.reload();
610-
return false;
611-
}
612-
try {
613-
await POST(update_url, {data: new URLSearchParams({target_branch: targetBranch})});
614-
} catch (error) {
615-
console.error(error);
616-
} finally {
617-
window.location.reload();
618-
}
619-
}
620-
621606
export function initRepoIssueTitleEdit() {
622-
// Edit issue title
623-
const $issueTitle = $('#issue-title');
624-
const $editInput = $('#edit-title-input input');
625-
626-
const editTitleToggle = function () {
627-
toggleElem($issueTitle);
628-
toggleElem('.not-in-edit');
629-
toggleElem('#edit-title-input');
630-
toggleElem('#pull-desc');
631-
toggleElem('#pull-desc-edit');
632-
toggleElem('.in-edit');
633-
toggleElem('.new-issue-button');
634-
document.getElementById('issue-title-wrapper')?.classList.toggle('edit-active');
635-
$editInput[0].focus();
636-
$editInput[0].select();
637-
return false;
638-
};
639-
640-
$('#edit-title').on('click', editTitleToggle);
641-
$('#cancel-edit-title').on('click', editTitleToggle);
642-
$('#save-edit-title').on('click', editTitleToggle).on('click', async function () {
643-
const pullrequest_target_update_url = this.getAttribute('data-target-update-url');
644-
if (!$editInput.val().length || $editInput.val() === $issueTitle.text()) {
645-
$editInput.val($issueTitle.text());
646-
await pullrequest_targetbranch_change(pullrequest_target_update_url);
647-
} else {
648-
try {
649-
const params = new URLSearchParams();
650-
params.append('title', $editInput.val());
651-
const response = await POST(this.getAttribute('data-update-url'), {data: params});
652-
const data = await response.json();
653-
$editInput.val(data.title);
654-
$issueTitle.text(data.title);
655-
if (pullrequest_target_update_url) {
656-
await pullrequest_targetbranch_change(pullrequest_target_update_url); // it will reload the window
657-
} else {
658-
window.location.reload();
607+
const issueTitleDisplay = document.querySelector('#issue-title-display');
608+
const issueTitleEditor = document.querySelector('#issue-title-editor');
609+
if (!issueTitleEditor) return;
610+
611+
const issueTitleInput = issueTitleEditor.querySelector('input');
612+
const oldTitle = issueTitleInput.getAttribute('data-old-title');
613+
issueTitleDisplay.querySelector('#issue-title-edit-show').addEventListener('click', () => {
614+
hideElem(issueTitleDisplay);
615+
hideElem('#pull-desc-display');
616+
showElem(issueTitleEditor);
617+
showElem('#pull-desc-editor');
618+
if (!issueTitleInput.value.trim()) {
619+
issueTitleInput.value = oldTitle;
620+
}
621+
issueTitleInput.focus();
622+
});
623+
issueTitleEditor.querySelector('.ui.cancel.button').addEventListener('click', () => {
624+
hideElem(issueTitleEditor);
625+
hideElem('#pull-desc-editor');
626+
showElem(issueTitleDisplay);
627+
showElem('#pull-desc-display');
628+
});
629+
const editSaveButton = issueTitleEditor.querySelector('.ui.primary.button');
630+
editSaveButton.addEventListener('click', async () => {
631+
const prTargetUpdateUrl = editSaveButton.getAttribute('data-target-update-url');
632+
const newTitle = issueTitleInput.value.trim();
633+
try {
634+
if (newTitle && newTitle !== oldTitle) {
635+
const resp = await POST(editSaveButton.getAttribute('data-update-url'), {data: new URLSearchParams({title: newTitle})});
636+
if (!resp.ok) {
637+
throw new Error(`Failed to update issue title: ${resp.statusText}`);
659638
}
660-
} catch (error) {
661-
console.error(error);
662639
}
640+
if (prTargetUpdateUrl) {
641+
const newTargetBranch = document.querySelector('#pull-target-branch').getAttribute('data-branch');
642+
const oldTargetBranch = document.querySelector('#branch_target').textContent;
643+
if (newTargetBranch !== oldTargetBranch) {
644+
const resp = await POST(prTargetUpdateUrl, {data: new URLSearchParams({target_branch: newTargetBranch})});
645+
if (!resp.ok) {
646+
throw new Error(`Failed to update PR target branch: ${resp.statusText}`);
647+
}
648+
}
649+
}
650+
window.location.reload();
651+
} catch (error) {
652+
console.error(error);
653+
showErrorToast(error.message);
663654
}
664-
return false;
665655
});
666656
}
667657

668658
export function initRepoIssueBranchSelect() {
669-
const changeBranchSelect = function () {
670-
const $selectionTextField = $('#pull-target-branch');
671-
672-
const baseName = $selectionTextField.data('basename');
673-
const branchNameNew = $(this).data('branch');
674-
const branchNameOld = $selectionTextField.data('branch');
675-
676-
// Replace branch name to keep translation from HTML template
677-
$selectionTextField.html($selectionTextField.html().replace(
678-
`${baseName}:${branchNameOld}`,
679-
`${baseName}:${branchNameNew}`,
680-
));
681-
$selectionTextField.data('branch', branchNameNew); // update branch name in setting
682-
};
683-
$('#branch-select > .item').on('click', changeBranchSelect);
659+
document.querySelector('#branch-select')?.addEventListener('click', (e) => {
660+
const el = e.target.closest('.item[data-branch]');
661+
if (!el) return;
662+
const pullTargetBranch = document.querySelector('#pull-target-branch');
663+
const baseName = pullTargetBranch.getAttribute('data-basename');
664+
const branchNameNew = el.getAttribute('data-branch');
665+
const branchNameOld = pullTargetBranch.getAttribute('data-branch');
666+
pullTargetBranch.textContent = pullTargetBranch.textContent.replace(`${baseName}:${branchNameOld}`, `${baseName}:${branchNameNew}`);
667+
pullTargetBranch.setAttribute('data-branch', branchNameNew);
668+
});
684669
}
685670

686671
export function initSingleCommentEditor($commentForm) {

0 commit comments

Comments
 (0)