diff --git a/models/issues/comment.go b/models/issues/comment.go index a47dc7c151340..2348eff3bd1d0 100644 --- a/models/issues/comment.go +++ b/models/issues/comment.go @@ -130,6 +130,12 @@ const ( CommentTypePRScheduledToAutoMerge // 35 pr was un scheduled to auto merge when checks succeed CommentTypePRUnScheduledToAutoMerge + // 36 + CommentTypeCloseAsArchived + // 37 + CommentTypeCloseAsResolved + // 38 + CommentTypeCloseAsMerged ) var commentStrings = []string{ @@ -169,6 +175,9 @@ var commentStrings = []string{ "change_issue_ref", "pull_scheduled_merge", "pull_cancel_scheduled_merge", + "close_as_archived", + "close_as_resolved", + "close_as_merged", } func (t CommentType) String() string { diff --git a/models/issues/dependency_test.go b/models/issues/dependency_test.go index cdc8e3182d361..0a92002260267 100644 --- a/models/issues/dependency_test.go +++ b/models/issues/dependency_test.go @@ -49,7 +49,7 @@ func TestCreateIssueDependency(t *testing.T) { assert.False(t, left) // Close #2 and check again - _, err = issues_model.ChangeIssueStatus(db.DefaultContext, issue2, user1, true) + _, err = issues_model.ChangeIssueStatus(db.DefaultContext, issue2, user1, true, issues_model.IssueClosedStatusCommonClosed) assert.NoError(t, err) left, err = issues_model.IssueNoDependenciesLeft(db.DefaultContext, issue1) diff --git a/models/issues/issue.go b/models/issues/issue.go index edd74261ecfa5..ea76936d295bd 100644 --- a/models/issues/issue.go +++ b/models/issues/issue.go @@ -125,6 +125,7 @@ type Issue struct { IsRead bool `xorm:"-"` IsPull bool `xorm:"INDEX"` // Indicates whether is a pull request or not. PullRequest *PullRequest `xorm:"-"` + ClosedStatus IssueClosedStatus NumComments int Ref string @@ -643,7 +644,7 @@ func UpdateIssueCols(ctx context.Context, issue *Issue, cols ...string) error { return nil } -func changeIssueStatus(ctx context.Context, issue *Issue, doer *user_model.User, isClosed, isMergePull bool) (*Comment, error) { +func changeIssueStatus(ctx context.Context, issue *Issue, doer *user_model.User, isClosed, isMergePull bool, status IssueClosedStatus) (*Comment, error) { // Reload the issue currentIssue, err := GetIssueByID(ctx, issue.ID) if err != nil { @@ -651,7 +652,7 @@ func changeIssueStatus(ctx context.Context, issue *Issue, doer *user_model.User, } // Nothing should be performed if current status is same as target status - if currentIssue.IsClosed == isClosed { + if currentIssue.IsClosed == isClosed && (!issue.IsPull && issue.ClosedStatus == status) { if !issue.IsPull { return nil, ErrIssueWasClosed{ ID: issue.ID, @@ -663,6 +664,9 @@ func changeIssueStatus(ctx context.Context, issue *Issue, doer *user_model.User, } issue.IsClosed = isClosed + if !issue.IsPull { + issue.ClosedStatus = status + } return doChangeIssueStatus(ctx, issue, doer, isMergePull) } @@ -686,7 +690,7 @@ func doChangeIssueStatus(ctx context.Context, issue *Issue, doer *user_model.Use issue.ClosedUnix = 0 } - if err := UpdateIssueCols(ctx, issue, "is_closed", "closed_unix"); err != nil { + if err := UpdateIssueCols(ctx, issue, "is_closed", "closed_status", "closed_unix"); err != nil { return nil, err } @@ -713,11 +717,24 @@ func doChangeIssueStatus(ctx context.Context, issue *Issue, doer *user_model.Use } // New action comment - cmtType := CommentTypeClose - if !issue.IsClosed { - cmtType = CommentTypeReopen - } else if isMergePull { + cmtType := CommentTypeUnknown + if isMergePull { cmtType = CommentTypeMergePull + } else if !issue.IsClosed { + cmtType = CommentTypeReopen + } else if issue.IsPull { + cmtType = CommentTypeClose + } else { + switch issue.ClosedStatus { + case IssueClosedStatusCommonClosed: + cmtType = CommentTypeClose + case IssueClosedStatusArchived: + cmtType = CommentTypeCloseAsArchived + case IssueClosedStatusResolved: + cmtType = CommentTypeCloseAsResolved + case IssueClosedStatusMerged: + cmtType = CommentTypeCloseAsMerged + } } return CreateComment(ctx, &CreateCommentOptions{ @@ -729,7 +746,7 @@ func doChangeIssueStatus(ctx context.Context, issue *Issue, doer *user_model.Use } // ChangeIssueStatus changes issue status to open or closed. -func ChangeIssueStatus(ctx context.Context, issue *Issue, doer *user_model.User, isClosed bool) (*Comment, error) { +func ChangeIssueStatus(ctx context.Context, issue *Issue, doer *user_model.User, isClosed bool, status IssueClosedStatus) (*Comment, error) { if err := issue.LoadRepo(ctx); err != nil { return nil, err } @@ -737,7 +754,7 @@ func ChangeIssueStatus(ctx context.Context, issue *Issue, doer *user_model.User, return nil, err } - return changeIssueStatus(ctx, issue, doer, isClosed, false) + return changeIssueStatus(ctx, issue, doer, isClosed, false, status) } // ChangeIssueTitle changes the title of this issue, as the given user. @@ -2051,6 +2068,10 @@ func UpdateIssueByAPI(issue *Issue, doer *user_model.User) (statusChangeComment } if currentIssue.IsClosed != issue.IsClosed { + issue.ClosedStatus = IssueClosedStatusOpen + if issue.IsClosed { + issue.ClosedStatus = IssueClosedStatusCommonClosed + } statusChangeComment, err = doChangeIssueStatus(ctx, issue, doer, false) if err != nil { return nil, false, err @@ -2498,3 +2519,20 @@ func DeleteOrphanedIssues(ctx context.Context) error { func (issue *Issue) HasOriginalAuthor() bool { return issue.OriginalAuthor != "" && issue.OriginalAuthorID != 0 } + +type IssueClosedStatus int64 + +const IssueClosedStatusUndefined = -1 + +const ( + // IssueClosedStatusOpen + IssueClosedStatusOpen IssueClosedStatus = iota + // IssueClosedStatusCommonClosed + IssueClosedStatusCommonClosed + // IssueClosedStatusArchived + IssueClosedStatusArchived + // IssueClosedStatusResolved + IssueClosedStatusResolved + // IssueClosedStatusMerged + IssueClosedStatusMerged +) diff --git a/models/issues/issue_xref_test.go b/models/issues/issue_xref_test.go index 6d96c398d0524..ed42d23f85bbe 100644 --- a/models/issues/issue_xref_test.go +++ b/models/issues/issue_xref_test.go @@ -98,7 +98,7 @@ func TestXRef_ResolveCrossReferences(t *testing.T) { i1 := testCreateIssue(t, 1, 2, "title1", "content1", false) i2 := testCreateIssue(t, 1, 2, "title2", "content2", false) i3 := testCreateIssue(t, 1, 2, "title3", "content3", false) - _, err := issues_model.ChangeIssueStatus(db.DefaultContext, i3, d, true) + _, err := issues_model.ChangeIssueStatus(db.DefaultContext, i3, d, true, issues_model.IssueClosedStatusCommonClosed) assert.NoError(t, err) pr := testCreatePR(t, 1, 2, "titlepr", fmt.Sprintf("closes #%d", i1.Index)) diff --git a/models/issues/pull.go b/models/issues/pull.go index a15ebec0b52a3..e39806e9c2fa9 100644 --- a/models/issues/pull.go +++ b/models/issues/pull.go @@ -502,7 +502,7 @@ func (pr *PullRequest) SetMerged(ctx context.Context) (bool, error) { return false, err } - if _, err := changeIssueStatus(ctx, pr.Issue, pr.Merger, true, true); err != nil { + if _, err := changeIssueStatus(ctx, pr.Issue, pr.Merger, true, true, IssueClosedStatusCommonClosed); err != nil { return false, fmt.Errorf("Issue.changeStatus: %w", err) } diff --git a/models/migrations/migrations.go b/models/migrations/migrations.go index 6224e1c8d7c0f..8cf9080b7a867 100644 --- a/models/migrations/migrations.go +++ b/models/migrations/migrations.go @@ -475,6 +475,8 @@ var migrations = []Migration{ NewMigration("Fix incorrect project type", v1_20.FixIncorrectProjectType), // v248 -> v249 NewMigration("Add version column to action_runner table", v1_20.AddVersionToActionRunner), + // v249 -> v250 + NewMigration("Add closed_status column to issue table", v1_20.AddClosedStatusToIssue), } // GetCurrentDBVersion returns the current db version diff --git a/models/migrations/v1_20/v249.go b/models/migrations/v1_20/v249.go new file mode 100644 index 0000000000000..5b44ef4959b0c --- /dev/null +++ b/models/migrations/v1_20/v249.go @@ -0,0 +1,26 @@ +// Copyright 2023 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package v1_20 //nolint + +import ( + "xorm.io/xorm" +) + +func AddClosedStatusToIssue(x *xorm.Engine) error { + type Issue struct { + ClosedStatus int8 + } + + if err := x.Sync(new(Issue)); err != nil { + return err + } + + // TODO: TBD Whether to use issues_model.IssueClosedStatusUndefined (-1) + if _, err := x.Exec("UPDATE issue SET closed_status = ? WHERE closed_status IS NULL and is_pull = false AND is_closed = true", 1); err != nil { + return err + } + + _, err := x.Exec("UPDATE issue SET closed_status = ? WHERE closed_status IS NULL and is_pull = false AND is_closed = false", 0) + return err +} diff --git a/modules/templates/helper.go b/modules/templates/helper.go index a8343428dc19b..fc4e2fb0da71b 100644 --- a/modules/templates/helper.go +++ b/modules/templates/helper.go @@ -487,6 +487,22 @@ func NewFuncMap() []template.FuncMap { "RefShortName": func(ref string) string { return git.RefName(ref).ShortName() }, + "ParseIssueClosedStatus2TranslateStr": func(cs issues_model.IssueClosedStatus) string { + switch cs { + case issues_model.IssueClosedStatusOpen: + return "" + case issues_model.IssueClosedStatusCommonClosed: + return "repo.issues.close_as.common" + case issues_model.IssueClosedStatusArchived: + return "repo.issues.close_as.archived" + case issues_model.IssueClosedStatusResolved: + return "repo.issues.close_as.resolved" + case issues_model.IssueClosedStatusMerged: + return "repo.issues.close_as.merged" + default: + return "" + } + }, }} } diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index da4ad47620209..cb3c70f4fa0f2 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -1378,6 +1378,9 @@ issues.reopen_issue = Reopen issues.reopen_comment_issue = Comment and Reopen issues.create_comment = Comment issues.closed_at = `closed this issue %[2]s` +issues.closed_as_archived_at = `closed this issue as archived %[2]s` +issues.closed_as_resolved_at = `closed this issue as resolved %[2]s` +issues.closed_as_merged_at = `closed this issue as merged %[2]s` issues.reopened_at = `reopened this issue %[2]s` issues.commit_ref_at = `referenced this issue from a commit %[2]s` issues.ref_issue_from = `referenced this issue %[4]s %[2]s` @@ -1387,6 +1390,16 @@ issues.ref_reopening_from = `referenced a pull request %[4]s tha issues.ref_closed_from = `closed this issue %[4]s %[2]s` issues.ref_reopened_from = `reopened this issue %[4]s %[2]s` issues.ref_from = `from %[1]s` +issues.close_as.common = Close Issue +issues.close_as.archived = Close as archived +issues.close_as.resolved = Close as resolved +issues.close_as.merged = Close as merged +issues.close_as.reopen = Reopen +issues.comment_and_close_as.common = Comment and Close Issue +issues.comment_and_close_as.archived = Comment and Close as archived +issues.comment_and_close_as.resolved = Comment and Close as resolved +issues.comment_and_close_as.merged = Comment and Close as merged +issues.comment_and_close_as.reopen = Comment and Reopen issues.poster = Poster issues.collaborator = Collaborator issues.owner = Owner diff --git a/routers/api/v1/repo/issue.go b/routers/api/v1/repo/issue.go index 06bf06b4e8472..08a177bba8181 100644 --- a/routers/api/v1/repo/issue.go +++ b/routers/api/v1/repo/issue.go @@ -661,7 +661,7 @@ func CreateIssue(ctx *context.APIContext) { } if form.Closed { - if err := issue_service.ChangeStatus(issue, ctx.Doer, "", true); err != nil { + if err := issue_service.ChangeStatus(issue, ctx.Doer, "", true, issues_model.IssueClosedStatusCommonClosed); err != nil { if issues_model.IsErrDependenciesLeft(err) { ctx.Error(http.StatusPreconditionFailed, "DependenciesLeft", "cannot close this issue because it still has open dependencies") return diff --git a/routers/web/repo/issue.go b/routers/web/repo/issue.go index 3715320f10c84..0b810d9c02930 100644 --- a/routers/web/repo/issue.go +++ b/routers/web/repo/issue.go @@ -2619,11 +2619,14 @@ func UpdateIssueStatus(ctx *context.Context) { } var isClosed bool + var status issues_model.IssueClosedStatus switch action := ctx.FormString("action"); action { case "open": isClosed = false + status = issues_model.IssueClosedStatusOpen case "close": isClosed = true + status = issues_model.IssueClosedStatusCommonClosed default: log.Warn("Unrecognized action: %s", action) } @@ -2634,7 +2637,7 @@ func UpdateIssueStatus(ctx *context.Context) { } for _, issue := range issues { if issue.IsClosed != isClosed { - if err := issue_service.ChangeStatus(issue, ctx.Doer, "", isClosed); err != nil { + if err := issue_service.ChangeStatus(issue, ctx.Doer, "", isClosed, status); err != nil { if issues_model.IsErrDependenciesLeft(err) { ctx.JSON(http.StatusPreconditionFailed, map[string]interface{}{ "error": "cannot close this issue because it still has open dependencies", @@ -2731,7 +2734,7 @@ func NewComment(ctx *context.Context) { ctx.Flash.Info(ctx.Tr("repo.pulls.open_unmerged_pull_exists", pr.Index)) } else { isClosed := form.Status == "close" - if err := issue_service.ChangeStatus(issue, ctx.Doer, "", isClosed); err != nil { + if err := issue_service.ChangeStatus(issue, ctx.Doer, "", isClosed, form.ClosedStatus); err != nil { log.Error("ChangeStatus: %v", err) if issues_model.IsErrDependenciesLeft(err) { diff --git a/services/forms/repo_form.go b/services/forms/repo_form.go index 3bd073c070d3f..fb5eef8b7c912 100644 --- a/services/forms/repo_form.go +++ b/services/forms/repo_form.go @@ -453,9 +453,10 @@ func (f *CreateIssueForm) Validate(req *http.Request, errs binding.Errors) bindi // CreateCommentForm form for creating comment type CreateCommentForm struct { - Content string - Status string `binding:"OmitEmpty;In(reopen,close)"` - Files []string + Content string + Status string `binding:"OmitEmpty;In(reopen,close)"` + ClosedStatus issues_model.IssueClosedStatus + Files []string } // Validate validates the fields diff --git a/services/issue/commit.go b/services/issue/commit.go index 7a8c71e609c4b..1f9031d1086bc 100644 --- a/services/issue/commit.go +++ b/services/issue/commit.go @@ -193,7 +193,11 @@ func UpdateIssuesCommit(doer *user_model.User, repo *repo_model.Repository, comm } if close != refIssue.IsClosed { refIssue.Repo = refRepo - if err := ChangeStatus(refIssue, doer, c.Sha1, close); err != nil { + status := issues_model.IssueClosedStatusOpen + if close { + status = issues_model.IssueClosedStatusCommonClosed + } + if err := ChangeStatus(refIssue, doer, c.Sha1, close, status); err != nil { return err } } diff --git a/services/issue/status.go b/services/issue/status.go index d4a0fce3e586a..4b052e6feaf9f 100644 --- a/services/issue/status.go +++ b/services/issue/status.go @@ -14,14 +14,14 @@ import ( ) // ChangeStatus changes issue status to open or closed. -func ChangeStatus(issue *issues_model.Issue, doer *user_model.User, commitID string, closed bool) error { - return changeStatusCtx(db.DefaultContext, issue, doer, commitID, closed) +func ChangeStatus(issue *issues_model.Issue, doer *user_model.User, commitID string, closed bool, status issues_model.IssueClosedStatus) error { + return changeStatusCtx(db.DefaultContext, issue, doer, commitID, closed, status) } // changeStatusCtx changes issue status to open or closed. // TODO: if context is not db.DefaultContext we get a deadlock!!! -func changeStatusCtx(ctx context.Context, issue *issues_model.Issue, doer *user_model.User, commitID string, closed bool) error { - comment, err := issues_model.ChangeIssueStatus(ctx, issue, doer, closed) +func changeStatusCtx(ctx context.Context, issue *issues_model.Issue, doer *user_model.User, commitID string, closed bool, status issues_model.IssueClosedStatus) error { + comment, err := issues_model.ChangeIssueStatus(ctx, issue, doer, closed, status) if err != nil { if issues_model.IsErrDependenciesLeft(err) && closed { if err := issues_model.FinishIssueStopwatchIfPossible(ctx, doer, issue); err != nil { diff --git a/services/pull/merge.go b/services/pull/merge.go index 12e01e8ce74a5..ce2e19d5a9942 100644 --- a/services/pull/merge.go +++ b/services/pull/merge.go @@ -221,7 +221,11 @@ func Merge(ctx context.Context, pr *issues_model.PullRequest, doer *user_model.U } close := ref.RefAction == references.XRefActionCloses if close != ref.Issue.IsClosed { - if err = issue_service.ChangeStatus(ref.Issue, doer, pr.MergedCommitID, close); err != nil { + status := issues_model.IssueClosedStatusOpen + if close { + status = issues_model.IssueClosedStatusCommonClosed + } + if err = issue_service.ChangeStatus(ref.Issue, doer, pr.MergedCommitID, close, status); err != nil { // Allow ErrDependenciesLeft if !issues_model.IsErrDependenciesLeft(err) { return err diff --git a/services/pull/pull.go b/services/pull/pull.go index e1d5a6f86dd42..5e339c12f345c 100644 --- a/services/pull/pull.go +++ b/services/pull/pull.go @@ -516,7 +516,7 @@ func CloseBranchPulls(doer *user_model.User, repoID int64, branch string) error var errs errlist for _, pr := range prs { - if err = issue_service.ChangeStatus(pr.Issue, doer, "", true); err != nil && !issues_model.IsErrPullWasClosed(err) && !issues_model.IsErrDependenciesLeft(err) { + if err = issue_service.ChangeStatus(pr.Issue, doer, "", true, issues_model.IssueClosedStatusCommonClosed); err != nil && !issues_model.IsErrPullWasClosed(err) && !issues_model.IsErrDependenciesLeft(err) { errs = append(errs, err) } } @@ -550,7 +550,7 @@ func CloseRepoBranchesPulls(ctx context.Context, doer *user_model.User, repo *re if pr.BaseRepoID == repo.ID { continue } - if err = issue_service.ChangeStatus(pr.Issue, doer, "", true); err != nil && !issues_model.IsErrPullWasClosed(err) { + if err = issue_service.ChangeStatus(pr.Issue, doer, "", true, issues_model.IssueClosedStatusCommonClosed); err != nil && !issues_model.IsErrPullWasClosed(err) { errs = append(errs, err) } } diff --git a/templates/repo/issue/view_content.tmpl b/templates/repo/issue/view_content.tmpl index 3e43701b63517..5aef4aa9817fe 100644 --- a/templates/repo/issue/view_content.tmpl +++ b/templates/repo/issue/view_content.tmpl @@ -112,21 +112,47 @@ {{end}} - diff --git a/templates/repo/issue/view_content/comments.tmpl b/templates/repo/issue/view_content/comments.tmpl index 9f2b7ec2d0cd1..d7f6288b22d50 100644 --- a/templates/repo/issue/view_content/comments.tmpl +++ b/templates/repo/issue/view_content/comments.tmpl @@ -11,7 +11,8 @@ 26 = DELETE_TIME_MANUAL, 27 = REVIEW_REQUEST, 28 = MERGE_PULL_REQUEST, 29 = PULL_PUSH_EVENT, 30 = PROJECT_CHANGED, 31 = PROJECT_BOARD_CHANGED 32 = DISMISSED_REVIEW, 33 = COMMENT_TYPE_CHANGE_ISSUE_REF, 34 = PR_SCHEDULE_TO_AUTO_MERGE, - 35 = CANCEL_SCHEDULED_AUTO_MERGE_PR --> + 35 = CANCEL_SCHEDULED_AUTO_MERGE_PR, + 36 = CLOSE_AS_ARCHIVED, 37 = CLOSE_AS_RESOLVED, 38 = CLOSE_AS_MERGED --> {{if eq .Type 0}}
{{if .OriginalAuthor}} @@ -117,6 +118,33 @@ {{end}}
+ {{else if eq .Type 36}} +
+ {{svg "octicon-circle-slash"}} + {{template "shared/user/avatarlink" Dict "Context" $.Context "user" .Poster}} + + {{template "shared/user/authorlink" .Poster}} + {{$.locale.Tr "repo.issues.closed_as_archived_at" .EventTag $createdStr | Safe}} + +
+ {{else if eq .Type 37}} +
+ {{svg "octicon-circle-slash"}} + {{template "shared/user/avatarlink" Dict "Context" $.Context "user" .Poster}} + + {{template "shared/user/authorlink" .Poster}} + {{$.locale.Tr "repo.issues.closed_as_resolved_at" .EventTag $createdStr | Safe}} + +
+ {{else if eq .Type 38}} +
+ {{svg "octicon-circle-slash"}} + {{template "shared/user/avatarlink" Dict "Context" $.Context "user" .Poster}} + + {{template "shared/user/authorlink" .Poster}} + {{$.locale.Tr "repo.issues.closed_as_merged_at" .EventTag $createdStr | Safe}} + +
{{else if eq .Type 28}}
{{svg "octicon-git-merge"}} diff --git a/templates/shared/issuelist.tmpl b/templates/shared/issuelist.tmpl index 40ddb4dab03a5..07ecba9be3bc3 100644 --- a/templates/shared/issuelist.tmpl +++ b/templates/shared/issuelist.tmpl @@ -25,7 +25,9 @@ {{end}} {{else}} {{if .IsClosed}} - {{svg "octicon-issue-closed" 16 "text red"}} +
+ {{svg "octicon-issue-closed" 16 "text red"}} +
{{else}} {{svg "octicon-issue-opened" 16 "text green"}} {{end}} diff --git a/web_src/css/repository.css b/web_src/css/repository.css index eb555abec8443..64752ad28191b 100644 --- a/web_src/css/repository.css +++ b/web_src/css/repository.css @@ -1060,7 +1060,24 @@ } .repository.view.issue .comment-list .comment .ui.form .field.footer { - overflow: hidden; + overflow: visible; +} + +.repository.view.issue .comment-list .comment .ui.form .field.footer .ui.button.status-button { + color: var(--color-text-light); + background-color: var(--color-light); + border: 1px solid var(--color-light-border); +} + +.repository.view.issue .comment-list .comment .ui.form .field.footer .ui.dropdown { + color: var(--color-text-light); + background-color: var(--color-light); + border-left: none; + margin-left: 0; +} + +.repository.view.issue .comment-list .comment .ui.form .field.footer .ui.dropdown .menu .item:not(.selected) svg { + visibility: hidden; } .repository.view.issue .comment-list .comment .ui.form .field .tab.markup { @@ -3644,4 +3661,4 @@ td.blob-excerpt { .search-fullname { color: var(--color-text-light-2); -} +} \ No newline at end of file diff --git a/web_src/js/features/repo-issue.js b/web_src/js/features/repo-issue.js index e49b1b2726f2d..39b678ec50ea0 100644 --- a/web_src/js/features/repo-issue.js +++ b/web_src/js/features/repo-issue.js @@ -226,16 +226,39 @@ export function initRepoIssueCodeCommentCancel() { export function initRepoIssueStatusButton() { // Change status const $statusButton = $('#status-button'); - $('#comment-form textarea').on('keyup', function () { - const easyMDE = getAttachedEasyMDE(this); - const value = easyMDE?.value() || $(this).val(); - $statusButton.text($statusButton.data(value.length === 0 ? 'status' : 'status-and-comment')); - }); + if (!$statusButton.length) return; + $statusButton.on('click', (e) => { e.preventDefault(); - $('#status').val($statusButton.data('status-val')); + $('#status').val(parseInt($statusButton.data('action')) === 0 ? 'reopen' : 'close'); + $('#comment-form').trigger('submit'); + }); + $('#comment-button').on('click', (e) => { + e.preventDefault(); + $('#status').val(''); $('#comment-form').trigger('submit'); }); + + const $statusDropdown = $('#status-dropdown'); + const selectedVal = $statusDropdown.find('input[type=hidden]').val(); + const onCloseStatusChange = (val) => { + $statusButton.attr('data-action', val); + const $textarea = $('#comment-form textarea'); + const easyMDE = getAttachedEasyMDE($('#comment-form textarea')); + const value = easyMDE?.value() || $textarea.val(); + $statusButton.text($statusDropdown.dropdown('get item').attr(value.length === 0 ? 'data-status' : 'data-status-and-comment')); + }; + $statusDropdown.dropdown('setting', {selectOnKeydown: false, onChange: onCloseStatusChange}); + $statusDropdown.dropdown('set selected', selectedVal); + onCloseStatusChange(selectedVal); + // hide unrelated item + $statusDropdown.find('> .menu').find('> .item').each((_, item) => { + if ($(item).data('value') === parseInt(selectedVal)) { + $(item).addClass('gt-hidden'); + } + }); + if (parseInt(selectedVal) === 0) $statusDropdown.dropdown('set selected', 1); // issue initial status is open. + else $statusDropdown.dropdown('set selected', 0); } export function initRepoPullRequestUpdate() { diff --git a/web_src/js/features/repo-legacy.js b/web_src/js/features/repo-legacy.js index f7fc1aa4eb92d..df4be5230ea02 100644 --- a/web_src/js/features/repo-legacy.js +++ b/web_src/js/features/repo-legacy.js @@ -86,6 +86,7 @@ export function initRepoCommentForm() { (async () => { const $statusButton = $('#status-button'); + const $statusDropdown = $('#status-dropdown'); for (const textarea of $commentForm.find('textarea:not(.review-textarea, .no-easymde)')) { // Don't initialize EasyMDE for the dormant #edit-content-form if (textarea.closest('#edit-content-form')) { @@ -94,7 +95,8 @@ export function initRepoCommentForm() { const easyMDE = await createCommentEasyMDE(textarea, { 'onChange': () => { const value = easyMDE?.value().trim(); - $statusButton.text($statusButton.attr(value.length === 0 ? 'data-status' : 'data-status-and-comment')); + const $source = $statusDropdown.length > 0 ? $statusDropdown.dropdown('get item') : $statusButton; + $statusButton.text($source.attr(value.length === 0 ? 'data-status' : 'data-status-and-comment')); }, }); initEasyMDEImagePaste(easyMDE, $commentForm.find('.dropzone'));