Port: Fix issue/PR title edit (gitea#30858) (#3797)
This PR ports [gitea#30858](https://github.com/go-gitea/gitea/pull/30858) / [this commit](5c236bd4c0
) to forgejo.
[week 2024-20 cherry pick](https://codeberg.org/forgejo/forgejo/pulls/3729)
## Tests
- [ ] Click "edit" to get into edit mode, change the title and then use Alt+Enter to save the title
## Screenshots
Before:
![grafik](/attachments/bb0b2562-7da0-4205-a647-3270d66f2ad7)
![grafik](/attachments/c3d05a21-659d-4616-b357-87de57232182)
After:
![grafik](/attachments/d9af6966-3282-439b-a845-76618a24b9a6)
![grafik](/attachments/5acd6684-69c4-41a4-8e27-7cb75fe3c7e4)
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/3797
Reviewed-by: 0ko <0ko@noreply.codeberg.org>
Co-authored-by: Mai-Lapyst <mai-lapyst@noreply.codeberg.org>
Co-committed-by: Mai-Lapyst <mai-lapyst@noreply.codeberg.org>
This commit is contained in:
parent
3561cc710b
commit
d4b1ed859c
|
@ -4,29 +4,35 @@
|
|||
</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="top-right-buttons">
|
||||
{{if and (or .HasIssuesOrPullsWritePermission .IsIssuePoster) (not .Repository.IsArchived)}}
|
||||
<button id="edit-title" class="ui small basic button edit-button not-in-edit">{{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" 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="top-right-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">{{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" class="js-quick-submit">
|
||||
</div>
|
||||
<div class="top-right-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">
|
||||
{{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>
|
||||
|
@ -65,9 +71,9 @@
|
|||
{{end}}
|
||||
{{else}}
|
||||
{{if .Issue.OriginalAuthor}}
|
||||
<span id="pull-desc" class="pull-desc">{{.Issue.OriginalAuthor}} {{ctx.Locale.TrN .NumCommits "repo.pulls.title_desc_one" "repo.pulls.title_desc_few" .NumCommits $headHref $baseHref}}</span>
|
||||
<span id="pull-desc-display" class="pull-desc">{{.Issue.OriginalAuthor}} {{ctx.Locale.TrN .NumCommits "repo.pulls.title_desc_one" "repo.pulls.title_desc_few" .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.TrN .NumCommits "repo.pulls.title_desc_one" "repo.pulls.title_desc_few" .NumCommits $headHref $baseHref}}
|
||||
</span>
|
||||
|
@ -81,7 +87,7 @@
|
|||
</span>
|
||||
</a>
|
||||
{{end}}
|
||||
<span id="pull-desc-edit" class="tw-hidden flex-text-block" data-target-update-url="{{$.RepoLink}}/pull/{{.Issue.Index}}/target_branch">
|
||||
<span id="pull-desc-editor" class="tw-hidden flex-text-block" data-target-update-url="{{$.RepoLink}}/pull/{{.Issue.Index}}/target_branch">
|
||||
<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>
|
||||
|
|
|
@ -148,7 +148,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)
|
||||
|
|
|
@ -213,7 +213,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(".top-right-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{
|
||||
|
|
|
@ -628,12 +628,12 @@ td .commit-summary {
|
|||
.repository.view.issue .issue-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5em;
|
||||
margin-bottom: 8px;
|
||||
min-height: var(--repo-header-issue-min-height);
|
||||
}
|
||||
|
||||
.repository.view.issue .issue-title h1 {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex: 1;
|
||||
width: 100%;
|
||||
font-weight: var(--font-weight-normal);
|
||||
|
@ -641,14 +641,13 @@ td .commit-summary {
|
|||
line-height: 40px;
|
||||
margin: 0;
|
||||
padding-right: 0.5rem;
|
||||
min-height: var(--repo-header-issue-min-height);
|
||||
}
|
||||
|
||||
.repository.view.issue .issue-title h1 .ui.input {
|
||||
font-size: 0.5em;
|
||||
.repository.view.issue .issue-title .ui.input {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.repository.view.issue .issue-title h1 .ui.input input {
|
||||
.repository.view.issue .issue-title .ui.input input {
|
||||
font-size: 1.5em;
|
||||
padding: 2px .5rem;
|
||||
}
|
||||
|
@ -661,10 +660,6 @@ td .commit-summary {
|
|||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.repository.view.issue .issue-title .edit-zone {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.issue-state-label {
|
||||
display: flex !important;
|
||||
align-items: center !important;
|
||||
|
|
|
@ -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,74 @@ 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 issueTitleDisplay = document.querySelector('#issue-title-display');
|
||||
const issueTitleEditor = document.querySelector('#issue-title-editor');
|
||||
if (!issueTitleEditor) return;
|
||||
|
||||
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();
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
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;
|
||||
}
|
||||
return false;
|
||||
issueTitleInput.focus();
|
||||
});
|
||||
issueTitleEditor.querySelector('.ui.cancel.button').addEventListener('click', () => {
|
||||
hideElem(issueTitleEditor);
|
||||
hideElem('#pull-desc-editor');
|
||||
showElem(issueTitleDisplay);
|
||||
showElem('#pull-desc-display');
|
||||
});
|
||||
|
||||
const pullDescEditor = document.querySelector('#pull-desc-editor'); // it may not exist for a merged PR
|
||||
const prTargetUpdateUrl = pullDescEditor?.getAttribute('data-target-update-url');
|
||||
|
||||
const editSaveButton = issueTitleEditor.querySelector('.ui.primary.button');
|
||||
const saveAndRefresh = async () => {
|
||||
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}`);
|
||||
}
|
||||
}
|
||||
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);
|
||||
}
|
||||
};
|
||||
editSaveButton.addEventListener('click', saveAndRefresh);
|
||||
issueTitleEditor.querySelector('input').addEventListener('ce-quick-submit', saveAndRefresh);
|
||||
}
|
||||
|
||||
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) {
|
||||
|
|
Loading…
Reference in New Issue