diff --git a/modules/git/error.go b/modules/git/error.go
index 91d25eca69..10fb37be07 100644
--- a/modules/git/error.go
+++ b/modules/git/error.go
@@ -4,28 +4,14 @@
package git
import (
+ "context"
+ "errors"
"fmt"
"strings"
- "time"
"code.gitea.io/gitea/modules/util"
)
-// ErrExecTimeout error when exec timed out
-type ErrExecTimeout struct {
- Duration time.Duration
-}
-
-// IsErrExecTimeout if some error is ErrExecTimeout
-func IsErrExecTimeout(err error) bool {
- _, ok := err.(ErrExecTimeout)
- return ok
-}
-
-func (err ErrExecTimeout) Error() string {
- return fmt.Sprintf("execution is timeout [duration: %v]", err.Duration)
-}
-
// ErrNotExist commit not exist error
type ErrNotExist struct {
ID string
@@ -62,21 +48,6 @@ func IsErrBadLink(err error) bool {
return ok
}
-// ErrUnsupportedVersion error when required git version not matched
-type ErrUnsupportedVersion struct {
- Required string
-}
-
-// IsErrUnsupportedVersion if some error is ErrUnsupportedVersion
-func IsErrUnsupportedVersion(err error) bool {
- _, ok := err.(ErrUnsupportedVersion)
- return ok
-}
-
-func (err ErrUnsupportedVersion) Error() string {
- return fmt.Sprintf("Operation requires higher version [required: %s]", err.Required)
-}
-
// ErrBranchNotExist represents a "BranchNotExist" kind of error.
type ErrBranchNotExist struct {
Name string
@@ -185,3 +156,10 @@ func IsErrMoreThanOne(err error) bool {
func (err *ErrMoreThanOne) Error() string {
return fmt.Sprintf("ErrMoreThanOne Error: %v: %s\n%s", err.Err, err.StdErr, err.StdOut)
}
+
+func IsErrCanceledOrKilled(err error) bool {
+ // When "cancel()" a git command's context, the returned error of "Run()" could be one of them:
+ // - context.Canceled
+ // - *exec.ExitError: "signal: killed"
+ return err != nil && (errors.Is(err, context.Canceled) || err.Error() == "signal: killed")
+}
diff --git a/modules/git/repo_attribute.go b/modules/git/repo_attribute.go
index 84f85d1b1a..90eb783fe8 100644
--- a/modules/git/repo_attribute.go
+++ b/modules/git/repo_attribute.go
@@ -166,9 +166,7 @@ func (c *CheckAttributeReader) Run() error {
Stdout: c.stdOut,
Stderr: stdErr,
})
- if err != nil && // If there is an error we need to return but:
- c.ctx.Err() != err && // 1. Ignore the context error if the context is cancelled or exceeds the deadline (RunWithContext could return c.ctx.Err() which is Canceled or DeadlineExceeded)
- err.Error() != "signal: killed" { // 2. We should not pass up errors due to the program being killed
+ if err != nil && !IsErrCanceledOrKilled(err) {
return fmt.Errorf("failed to run attr-check. Error: %w\nStderr: %s", err, stdErr.String())
}
return nil
diff --git a/modules/templates/helper.go b/modules/templates/helper.go
index 5038e8a132..a5168541d8 100644
--- a/modules/templates/helper.go
+++ b/modules/templates/helper.go
@@ -54,7 +54,7 @@ func NewFuncMap() template.FuncMap {
"StringUtils": NewStringUtils,
"SliceUtils": NewSliceUtils,
"JsonUtils": NewJsonUtils,
- "DateUtils": NewDateUtils, // TODO: to be replaced by DateUtils
+ "DateUtils": NewDateUtils,
// -----------------------------------------------------------------
// svg / avatar / icon / color
@@ -71,7 +71,7 @@ func NewFuncMap() template.FuncMap {
"CountFmt": base.FormatNumberSI,
"TimeSince": timeutil.TimeSince,
"TimeSinceUnix": timeutil.TimeSinceUnix,
- "DateTime": timeutil.DateTime,
+ "DateTime": dateTimeLegacy, // for backward compatibility only, do not use it anymore
"Sec2Time": util.SecToTime,
"LoadTimes": func(startTime time.Time) string {
return fmt.Sprint(time.Since(startTime).Nanoseconds()/1e6) + "ms"
diff --git a/modules/templates/util_date.go b/modules/templates/util_date.go
index ec48a7e4be..45dd8da02f 100644
--- a/modules/templates/util_date.go
+++ b/modules/templates/util_date.go
@@ -6,7 +6,9 @@ package templates
import (
"context"
"html/template"
+ "time"
+ "code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/timeutil"
)
@@ -32,3 +34,27 @@ func (du *DateUtils) AbsoluteLong(time any) template.HTML {
func (du *DateUtils) FullTime(time any) template.HTML {
return timeutil.DateTime("full", time)
}
+
+// ParseLegacy parses the datetime in legacy format, eg: "2016-01-02" in server's timezone.
+// It shouldn't be used in new code. New code should use Time or TimeStamp as much as possible.
+func (du *DateUtils) ParseLegacy(datetime string) time.Time {
+ return parseLegacy(datetime)
+}
+
+func parseLegacy(datetime string) time.Time {
+ t, err := time.Parse(time.RFC3339, datetime)
+ if err != nil {
+ t, _ = time.ParseInLocation(time.DateOnly, datetime, setting.DefaultUILocation)
+ }
+ return t
+}
+
+func dateTimeLegacy(format string, datetime any, _ ...string) template.HTML {
+ if !setting.IsProd || setting.IsInTesting {
+ panic("dateTimeLegacy is for backward compatibility only, do not use it in new code")
+ }
+ if s, ok := datetime.(string); ok {
+ datetime = parseLegacy(s)
+ }
+ return timeutil.DateTime(format, datetime)
+}
diff --git a/modules/timeutil/datetime_test.go b/modules/templates/util_date_test.go
similarity index 61%
rename from modules/timeutil/datetime_test.go
rename to modules/templates/util_date_test.go
index ac2ce35ba2..96c3776d39 100644
--- a/modules/timeutil/datetime_test.go
+++ b/modules/templates/util_date_test.go
@@ -1,7 +1,7 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package timeutil
+package templates
import (
"testing"
@@ -9,6 +9,7 @@ import (
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/test"
+ "code.gitea.io/gitea/modules/timeutil"
"github.com/stretchr/testify/assert"
)
@@ -16,32 +17,35 @@ import (
func TestDateTime(t *testing.T) {
testTz, _ := time.LoadLocation("America/New_York")
defer test.MockVariableValue(&setting.DefaultUILocation, testTz)()
+ defer test.MockVariableValue(&setting.IsInTesting, false)()
+
+ du := NewDateUtils(nil)
refTimeStr := "2018-01-01T00:00:00Z"
refDateStr := "2018-01-01"
refTime, _ := time.Parse(time.RFC3339, refTimeStr)
- refTimeStamp := TimeStamp(refTime.Unix())
+ refTimeStamp := timeutil.TimeStamp(refTime.Unix())
- assert.EqualValues(t, "-", DateTime("short", nil))
- assert.EqualValues(t, "-", DateTime("short", 0))
- assert.EqualValues(t, "-", DateTime("short", time.Time{}))
- assert.EqualValues(t, "-", DateTime("short", TimeStamp(0)))
+ assert.EqualValues(t, "-", du.AbsoluteShort(nil))
+ assert.EqualValues(t, "-", du.AbsoluteShort(0))
+ assert.EqualValues(t, "-", du.AbsoluteShort(time.Time{}))
+ assert.EqualValues(t, "-", du.AbsoluteShort(timeutil.TimeStamp(0)))
- actual := DateTime("short", "invalid")
- assert.EqualValues(t, `invalid`, actual)
+ actual := dateTimeLegacy("short", "invalid")
+ assert.EqualValues(t, `-`, actual)
- actual = DateTime("short", refTimeStr)
- assert.EqualValues(t, `2018-01-01T00:00:00Z`, actual)
-
- actual = DateTime("short", refTime)
+ actual = dateTimeLegacy("short", refTimeStr)
assert.EqualValues(t, `2018-01-01`, actual)
- actual = DateTime("short", refDateStr)
- assert.EqualValues(t, `2018-01-01`, actual)
+ actual = du.AbsoluteShort(refTime)
+ assert.EqualValues(t, `2018-01-01`, actual)
- actual = DateTime("short", refTimeStamp)
+ actual = dateTimeLegacy("short", refDateStr)
+ assert.EqualValues(t, `2018-01-01`, actual)
+
+ actual = du.AbsoluteShort(refTimeStamp)
assert.EqualValues(t, `2017-12-31`, actual)
- actual = DateTime("full", refTimeStamp)
+ actual = du.FullTime(refTimeStamp)
assert.EqualValues(t, `2017-12-31 19:00:00 -05:00`, actual)
}
diff --git a/modules/timeutil/datetime.go b/modules/timeutil/datetime.go
index c089173560..664e0320b0 100644
--- a/modules/timeutil/datetime.go
+++ b/modules/timeutil/datetime.go
@@ -12,9 +12,7 @@ import (
)
// DateTime renders an absolute time HTML element by datetime.
-func DateTime(format string, datetime any, extraAttrs ...string) template.HTML {
- // TODO: remove the extraAttrs argument, it's not used in any call to DateTime
-
+func DateTime(format string, datetime any) template.HTML {
if p, ok := datetime.(*time.Time); ok {
datetime = *p
}
@@ -34,9 +32,6 @@ func DateTime(format string, datetime any, extraAttrs ...string) template.HTML {
switch v := datetime.(type) {
case nil:
return "-"
- case string:
- datetimeEscaped = html.EscapeString(v)
- textEscaped = datetimeEscaped
case time.Time:
if v.IsZero() || v.Unix() == 0 {
return "-"
@@ -51,10 +46,7 @@ func DateTime(format string, datetime any, extraAttrs ...string) template.HTML {
panic(fmt.Sprintf("Unsupported time type %T", datetime))
}
- attrs := make([]string, 0, 10+len(extraAttrs))
- attrs = append(attrs, extraAttrs...)
- attrs = append(attrs, `weekday=""`, `year="numeric"`)
-
+ attrs := []string{`weekday=""`, `year="numeric"`}
switch format {
case "short", "long": // date only
attrs = append(attrs, `month="`+format+`"`, `day="numeric"`)
diff --git a/routers/api/v1/repo/repo.go b/routers/api/v1/repo/repo.go
index 698ba3cc94..69a95dd5a5 100644
--- a/routers/api/v1/repo/repo.go
+++ b/routers/api/v1/repo/repo.go
@@ -21,7 +21,6 @@ import (
repo_model "code.gitea.io/gitea/models/repo"
unit_model "code.gitea.io/gitea/models/unit"
user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/gitrepo"
"code.gitea.io/gitea/modules/label"
"code.gitea.io/gitea/modules/log"
@@ -739,10 +738,8 @@ func updateBasicProperties(ctx *context.APIContext, opts api.EditRepoOption) err
if opts.DefaultBranch != nil && repo.DefaultBranch != *opts.DefaultBranch && (repo.IsEmpty || ctx.Repo.GitRepo.IsBranchExist(*opts.DefaultBranch)) {
if !repo.IsEmpty {
if err := gitrepo.SetDefaultBranch(ctx, ctx.Repo.Repository, *opts.DefaultBranch); err != nil {
- if !git.IsErrUnsupportedVersion(err) {
- ctx.Error(http.StatusInternalServerError, "SetDefaultBranch", err)
- return err
- }
+ ctx.Error(http.StatusInternalServerError, "SetDefaultBranch", err)
+ return err
}
updateRepoLicense = true
}
diff --git a/routers/private/default_branch.go b/routers/private/default_branch.go
index 03c19c8ff4..8f6e9084df 100644
--- a/routers/private/default_branch.go
+++ b/routers/private/default_branch.go
@@ -8,7 +8,6 @@ import (
"net/http"
repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/gitrepo"
"code.gitea.io/gitea/modules/private"
gitea_context "code.gitea.io/gitea/services/context"
@@ -23,12 +22,10 @@ func SetDefaultBranch(ctx *gitea_context.PrivateContext) {
ctx.Repo.Repository.DefaultBranch = branch
if err := gitrepo.SetDefaultBranch(ctx, ctx.Repo.Repository, ctx.Repo.Repository.DefaultBranch); err != nil {
- if !git.IsErrUnsupportedVersion(err) {
- ctx.JSON(http.StatusInternalServerError, private.Response{
- Err: fmt.Sprintf("Unable to set default branch on repository: %s/%s Error: %v", ownerName, repoName, err),
- })
- return
- }
+ ctx.JSON(http.StatusInternalServerError, private.Response{
+ Err: fmt.Sprintf("Unable to set default branch on repository: %s/%s Error: %v", ownerName, repoName, err),
+ })
+ return
}
if err := repo_model.UpdateDefaultBranch(ctx, ctx.Repo.Repository); err != nil {
diff --git a/routers/web/repo/githttp.go b/routers/web/repo/githttp.go
index ee1ec1fd0c..58a2bdbab1 100644
--- a/routers/web/repo/githttp.go
+++ b/routers/web/repo/githttp.go
@@ -467,7 +467,7 @@ func serviceRPC(ctx *context.Context, h *serviceHandler, service string) {
Stderr: &stderr,
UseContextTimeout: true,
}); err != nil {
- if err.Error() != "signal: killed" {
+ if !git.IsErrCanceledOrKilled(err) {
log.Error("Fail to serve RPC(%s) in %s: %v - %s", service, h.getRepoDir(), err, stderr.String())
}
return
diff --git a/services/gitdiff/gitdiff.go b/services/gitdiff/gitdiff.go
index cf7da0707b..bb1722039e 100644
--- a/services/gitdiff/gitdiff.go
+++ b/services/gitdiff/gitdiff.go
@@ -1162,7 +1162,7 @@ func GetDiff(ctx context.Context, gitRepo *git.Repository, opts *DiffOptions, fi
Dir: repoPath,
Stdout: writer,
Stderr: stderr,
- }); err != nil && err.Error() != "signal: killed" {
+ }); err != nil && !git.IsErrCanceledOrKilled(err) {
log.Error("error during GetDiff(git diff dir: %s): %v, stderr: %s", repoPath, err, stderr.String())
}
diff --git a/services/mirror/mirror_pull.go b/services/mirror/mirror_pull.go
index 654a50d11e..a81fe6e9bd 100644
--- a/services/mirror/mirror_pull.go
+++ b/services/mirror/mirror_pull.go
@@ -613,14 +613,8 @@ func checkAndUpdateEmptyRepository(ctx context.Context, m *repo_model.Mirror, re
}
// Update the git repository default branch
if err := gitrepo.SetDefaultBranch(ctx, m.Repo, m.Repo.DefaultBranch); err != nil {
- if !git.IsErrUnsupportedVersion(err) {
- log.Error("Failed to update default branch of underlying git repository %-v. Error: %v", m.Repo, err)
- desc := fmt.Sprintf("Failed to update default branch of underlying git repository '%s': %v", m.Repo.RepoPath(), err)
- if err = system_model.CreateRepositoryNotice(desc); err != nil {
- log.Error("CreateRepositoryNotice: %v", err)
- }
- return false
- }
+ log.Error("Failed to update default branch of underlying git repository %-v. Error: %v", m.Repo, err)
+ return false
}
m.Repo.IsEmpty = false
// Update the is empty and default_branch columns
diff --git a/services/repository/branch.go b/services/repository/branch.go
index 67df4363e4..600ba96e92 100644
--- a/services/repository/branch.go
+++ b/services/repository/branch.go
@@ -602,12 +602,7 @@ func SetRepoDefaultBranch(ctx context.Context, repo *repo_model.Repository, gitR
log.Error("CancelPreviousJobs: %v", err)
}
- if err := gitrepo.SetDefaultBranch(ctx, repo, newBranchName); err != nil {
- if !git.IsErrUnsupportedVersion(err) {
- return err
- }
- }
- return nil
+ return gitrepo.SetDefaultBranch(ctx, repo, newBranchName)
}); err != nil {
return err
}
diff --git a/services/repository/files/temp_repo.go b/services/repository/files/temp_repo.go
index ce98967e79..30ab22db1e 100644
--- a/services/repository/files/temp_repo.go
+++ b/services/repository/files/temp_repo.go
@@ -339,8 +339,7 @@ func (t *TemporaryUploadRepository) Push(doer *user_model.User, commitHash, bran
func (t *TemporaryUploadRepository) DiffIndex() (*gitdiff.Diff, error) {
stdoutReader, stdoutWriter, err := os.Pipe()
if err != nil {
- log.Error("Unable to open stdout pipe: %v", err)
- return nil, fmt.Errorf("Unable to open stdout pipe: %w", err)
+ return nil, fmt.Errorf("unable to open stdout pipe: %w", err)
}
defer func() {
_ = stdoutReader.Close()
@@ -348,9 +347,7 @@ func (t *TemporaryUploadRepository) DiffIndex() (*gitdiff.Diff, error) {
}()
stderr := new(bytes.Buffer)
var diff *gitdiff.Diff
- var finalErr error
-
- if err := git.NewCommand(t.ctx, "diff-index", "--src-prefix=\\a/", "--dst-prefix=\\b/", "--cached", "-p", "HEAD").
+ err = git.NewCommand(t.ctx, "diff-index", "--src-prefix=\\a/", "--dst-prefix=\\b/", "--cached", "-p", "HEAD").
Run(&git.RunOpts{
Timeout: 30 * time.Second,
Dir: t.basePath,
@@ -359,27 +356,19 @@ func (t *TemporaryUploadRepository) DiffIndex() (*gitdiff.Diff, error) {
PipelineFunc: func(ctx context.Context, cancel context.CancelFunc) error {
_ = stdoutWriter.Close()
defer cancel()
- diff, finalErr = gitdiff.ParsePatch(t.ctx, setting.Git.MaxGitDiffLines, setting.Git.MaxGitDiffLineCharacters, setting.Git.MaxGitDiffFiles, stdoutReader, "")
- if finalErr != nil {
- log.Error("ParsePatch: %v", finalErr)
- cancel()
- }
+ var diffErr error
+ diff, diffErr = gitdiff.ParsePatch(t.ctx, setting.Git.MaxGitDiffLines, setting.Git.MaxGitDiffLineCharacters, setting.Git.MaxGitDiffFiles, stdoutReader, "")
_ = stdoutReader.Close()
- return finalErr
+ if diffErr != nil {
+ // if the diffErr is not nil, it will be returned as the error of "Run()"
+ return fmt.Errorf("ParsePatch: %w", diffErr)
+ }
+ return nil
},
- }); err != nil {
- if finalErr != nil {
- log.Error("Unable to ParsePatch in temporary repo %s (%s). Error: %v", t.repo.FullName(), t.basePath, finalErr)
- return nil, finalErr
- }
-
- // If the process exited early, don't error
- if err != context.Canceled {
- log.Error("Unable to run diff-index pipeline in temporary repo %s (%s). Error: %v\nStderr: %s",
- t.repo.FullName(), t.basePath, err, stderr)
- return nil, fmt.Errorf("Unable to run diff-index pipeline in temporary repo %s. Error: %w\nStderr: %s",
- t.repo.FullName(), err, stderr)
- }
+ })
+ if err != nil && !git.IsErrCanceledOrKilled(err) {
+ log.Error("Unable to diff-index in temporary repo %s (%s). Error: %v\nStderr: %s", t.repo.FullName(), t.basePath, err, stderr)
+ return nil, fmt.Errorf("unable to run diff-index pipeline in temporary repo: %w", err)
}
diff.NumFiles, diff.TotalAddition, diff.TotalDeletion, err = git.GetDiffShortStat(t.ctx, t.basePath, git.TrustedCmdArgs{"--cached"}, "HEAD")
diff --git a/services/repository/push.go b/services/repository/push.go
index 8b81588c07..36c7927ab6 100644
--- a/services/repository/push.go
+++ b/services/repository/push.go
@@ -183,9 +183,7 @@ func pushUpdates(optsList []*repo_module.PushUpdateOptions) error {
repo.IsEmpty = false
if repo.DefaultBranch != setting.Repository.DefaultBranch {
if err := gitrepo.SetDefaultBranch(ctx, repo, repo.DefaultBranch); err != nil {
- if !git.IsErrUnsupportedVersion(err) {
- return err
- }
+ return err
}
}
// Update the is empty and default_branch columns
diff --git a/templates/repo/issue/milestone_issues.tmpl b/templates/repo/issue/milestone_issues.tmpl
index 5bae6fc6d5..01bd944f46 100644
--- a/templates/repo/issue/milestone_issues.tmpl
+++ b/templates/repo/issue/milestone_issues.tmpl
@@ -38,7 +38,7 @@
{{if .Milestone.DeadlineString}}
{{svg "octicon-calendar"}}
- {{DateTime "short" .Milestone.DeadlineString}}
+ {{ctx.DateUtils.AbsoluteShort (.Milestone.DeadlineString|ctx.DateUtils.ParseLegacy)}}
{{else}}
{{svg "octicon-calendar"}}
diff --git a/templates/repo/issue/milestones.tmpl b/templates/repo/issue/milestones.tmpl
index bce7ad8717..8b84659d10 100644
--- a/templates/repo/issue/milestones.tmpl
+++ b/templates/repo/issue/milestones.tmpl
@@ -59,7 +59,7 @@
{{if .DeadlineString}}
{{svg "octicon-calendar" 14}}
- {{DateTime "short" .DeadlineString}}
+ {{ctx.DateUtils.AbsoluteShort (.DeadlineString|ctx.DateUtils.ParseLegacy)}}
{{else}}
{{svg "octicon-calendar" 14}}
diff --git a/templates/repo/issue/view_content/comments.tmpl b/templates/repo/issue/view_content/comments.tmpl
index 57abbeb8f7..9324959bed 100644
--- a/templates/repo/issue/view_content/comments.tmpl
+++ b/templates/repo/issue/view_content/comments.tmpl
@@ -296,7 +296,8 @@
{{template "shared/user/avatarlink" dict "user" .Poster}}
{{template "shared/user/authorlink" .Poster}}
- {{ctx.Locale.Tr "repo.issues.due_date_added" (DateTime "long" .Content) $createdStr}}
+ {{$dueDate := ctx.DateUtils.AbsoluteLong (.Content|ctx.DateUtils.ParseLegacy)}}
+ {{ctx.Locale.Tr "repo.issues.due_date_added" $dueDate $createdStr}}
{{else if eq .Type 17}}
@@ -307,8 +308,8 @@
{{template "shared/user/authorlink" .Poster}}
{{$parsedDeadline := StringUtils.Split .Content "|"}}
{{if eq (len $parsedDeadline) 2}}
- {{$from := DateTime "long" (index $parsedDeadline 1)}}
- {{$to := DateTime "long" (index $parsedDeadline 0)}}
+ {{$to := ctx.DateUtils.AbsoluteLong ((index $parsedDeadline 0)|ctx.DateUtils.ParseLegacy)}}
+ {{$from := ctx.DateUtils.AbsoluteLong ((index $parsedDeadline 1)|ctx.DateUtils.ParseLegacy)}}
{{ctx.Locale.Tr "repo.issues.due_date_modified" $to $from $createdStr}}
{{end}}
@@ -319,7 +320,8 @@
{{template "shared/user/avatarlink" dict "user" .Poster}}
{{template "shared/user/authorlink" .Poster}}
- {{ctx.Locale.Tr "repo.issues.due_date_remove" (DateTime "long" .Content) $createdStr}}
+ {{$dueDate := ctx.DateUtils.AbsoluteLong (.Content|ctx.DateUtils.ParseLegacy)}}
+ {{ctx.Locale.Tr "repo.issues.due_date_remove" $dueDate $createdStr}}
{{else if eq .Type 19}}
diff --git a/templates/user/dashboard/milestones.tmpl b/templates/user/dashboard/milestones.tmpl
index 71ff8dba3f..e01eca7c74 100644
--- a/templates/user/dashboard/milestones.tmpl
+++ b/templates/user/dashboard/milestones.tmpl
@@ -116,7 +116,7 @@
{{if .DeadlineString}}
{{svg "octicon-calendar" 14}}
- {{DateTime "short" .DeadlineString}}
+ {{ctx.DateUtils.AbsoluteShort (.DeadlineString|ctx.DateUtils.ParseLegacy)}}
{{else}}
{{svg "octicon-calendar" 14}}
diff --git a/web_src/js/features/repo-common.ts b/web_src/js/features/repo-common.ts
index de967ffba0..c7d84de9f0 100644
--- a/web_src/js/features/repo-common.ts
+++ b/web_src/js/features/repo-common.ts
@@ -90,3 +90,14 @@ export function initRepoCommonFilterSearchDropdown(selector) {
message: {noResults: $dropdown[0].getAttribute('data-no-results')},
});
}
+
+export async function updateIssuesMeta(url, action, issue_ids, id) {
+ try {
+ const response = await POST(url, {data: new URLSearchParams({action, issue_ids, id})});
+ if (!response.ok) {
+ throw new Error('Failed to update issues meta');
+ }
+ } catch (error) {
+ console.error(error);
+ }
+}
diff --git a/web_src/js/features/repo-issue-list.ts b/web_src/js/features/repo-issue-list.ts
index 134304617b..caf517c5e0 100644
--- a/web_src/js/features/repo-issue-list.ts
+++ b/web_src/js/features/repo-issue-list.ts
@@ -1,5 +1,5 @@
import $ from 'jquery';
-import {updateIssuesMeta} from './repo-issue.ts';
+import {updateIssuesMeta} from './repo-common.ts';
import {toggleElem, hideElem, isElemHidden} from '../utils/dom.ts';
import {htmlEscape} from 'escape-goat';
import {confirmModal} from './comp/ConfirmModal.ts';
diff --git a/web_src/js/features/repo-issue-sidebar.ts b/web_src/js/features/repo-issue-sidebar.ts
new file mode 100644
index 0000000000..f33e192f29
--- /dev/null
+++ b/web_src/js/features/repo-issue-sidebar.ts
@@ -0,0 +1,274 @@
+import $ from 'jquery';
+import {POST} from '../modules/fetch.ts';
+import {updateIssuesMeta} from './repo-common.ts';
+import {svg} from '../svg.ts';
+import {htmlEscape} from 'escape-goat';
+
+// if there are draft comments, confirm before reloading, to avoid losing comments
+function reloadConfirmDraftComment() {
+ const commentTextareas = [
+ document.querySelector('.edit-content-zone:not(.tw-hidden) textarea'),
+ document.querySelector('#comment-form textarea'),
+ ];
+ for (const textarea of commentTextareas) {
+ // Most users won't feel too sad if they lose a comment with 10 chars, they can re-type these in seconds.
+ // But if they have typed more (like 50) chars and the comment is lost, they will be very unhappy.
+ if (textarea && textarea.value.trim().length > 10) {
+ textarea.parentElement.scrollIntoView();
+ if (!window.confirm('Page will be reloaded, but there are draft comments. Continuing to reload will discard the comments. Continue?')) {
+ return;
+ }
+ break;
+ }
+ }
+ window.location.reload();
+}
+
+function initBranchSelector() {
+ const elSelectBranch = document.querySelector('.ui.dropdown.select-branch');
+ if (!elSelectBranch) return;
+
+ const urlUpdateIssueRef = elSelectBranch.getAttribute('data-url-update-issueref');
+ const $selectBranch = $(elSelectBranch);
+ const $branchMenu = $selectBranch.find('.reference-list-menu');
+ $branchMenu.find('.item:not(.no-select)').on('click', async function (e) {
+ e.preventDefault();
+ const selectedValue = this.getAttribute('data-id'); // eg: "refs/heads/my-branch"
+ const selectedText = this.getAttribute('data-name'); // eg: "my-branch"
+ if (urlUpdateIssueRef) {
+ // for existing issue, send request to update issue ref, and reload page
+ try {
+ await POST(urlUpdateIssueRef, {data: new URLSearchParams({ref: selectedValue})});
+ window.location.reload();
+ } catch (error) {
+ console.error(error);
+ }
+ } else {
+ // for new issue, only update UI&form, do not send request/reload
+ const selectedHiddenSelector = this.getAttribute('data-id-selector');
+ document.querySelector(selectedHiddenSelector).value = selectedValue;
+ elSelectBranch.querySelector('.text-branch-name').textContent = selectedText;
+ }
+ });
+}
+
+// List submits
+function initListSubmits(selector, outerSelector) {
+ const $list = $(`.ui.${outerSelector}.list`);
+ const $noSelect = $list.find('.no-select');
+ const $listMenu = $(`.${selector} .menu`);
+ let hasUpdateAction = $listMenu.data('action') === 'update';
+ const items = {};
+
+ $(`.${selector}`).dropdown({
+ 'action': 'nothing', // do not hide the menu if user presses Enter
+ fullTextSearch: 'exact',
+ async onHide() {
+ hasUpdateAction = $listMenu.data('action') === 'update'; // Update the var
+ if (hasUpdateAction) {
+ // TODO: Add batch functionality and make this 1 network request.
+ const itemEntries = Object.entries(items);
+ for (const [elementId, item] of itemEntries) {
+ await updateIssuesMeta(
+ item['update-url'],
+ item.action,
+ item['issue-id'],
+ elementId,
+ );
+ }
+ if (itemEntries.length) {
+ reloadConfirmDraftComment();
+ }
+ }
+ },
+ });
+
+ $listMenu.find('.item:not(.no-select)').on('click', function (e) {
+ e.preventDefault();
+ if (this.classList.contains('ban-change')) {
+ return false;
+ }
+
+ hasUpdateAction = $listMenu.data('action') === 'update'; // Update the var
+
+ const clickedItem = this; // eslint-disable-line unicorn/no-this-assignment
+ const scope = this.getAttribute('data-scope');
+
+ $(this).parent().find('.item').each(function () {
+ if (scope) {
+ // Enable only clicked item for scoped labels
+ if (this.getAttribute('data-scope') !== scope) {
+ return true;
+ }
+ if (this !== clickedItem && !this.classList.contains('checked')) {
+ return true;
+ }
+ } else if (this !== clickedItem) {
+ // Toggle for other labels
+ return true;
+ }
+
+ if (this.classList.contains('checked')) {
+ $(this).removeClass('checked');
+ $(this).find('.octicon-check').addClass('tw-invisible');
+ if (hasUpdateAction) {
+ if (!($(this).data('id') in items)) {
+ items[$(this).data('id')] = {
+ 'update-url': $listMenu.data('update-url'),
+ action: 'detach',
+ 'issue-id': $listMenu.data('issue-id'),
+ };
+ } else {
+ delete items[$(this).data('id')];
+ }
+ }
+ } else {
+ $(this).addClass('checked');
+ $(this).find('.octicon-check').removeClass('tw-invisible');
+ if (hasUpdateAction) {
+ if (!($(this).data('id') in items)) {
+ items[$(this).data('id')] = {
+ 'update-url': $listMenu.data('update-url'),
+ action: 'attach',
+ 'issue-id': $listMenu.data('issue-id'),
+ };
+ } else {
+ delete items[$(this).data('id')];
+ }
+ }
+ }
+ });
+
+ // TODO: Which thing should be done for choosing review requests
+ // to make chosen items be shown on time here?
+ if (selector === 'select-reviewers-modify' || selector === 'select-assignees-modify') {
+ return false;
+ }
+
+ const listIds = [];
+ $(this).parent().find('.item').each(function () {
+ if (this.classList.contains('checked')) {
+ listIds.push($(this).data('id'));
+ $($(this).data('id-selector')).removeClass('tw-hidden');
+ } else {
+ $($(this).data('id-selector')).addClass('tw-hidden');
+ }
+ });
+ if (!listIds.length) {
+ $noSelect.removeClass('tw-hidden');
+ } else {
+ $noSelect.addClass('tw-hidden');
+ }
+ $($(this).parent().data('id')).val(listIds.join(','));
+ return false;
+ });
+ $listMenu.find('.no-select.item').on('click', function (e) {
+ e.preventDefault();
+ if (hasUpdateAction) {
+ (async () => {
+ await updateIssuesMeta(
+ $listMenu.data('update-url'),
+ 'clear',
+ $listMenu.data('issue-id'),
+ '',
+ );
+ reloadConfirmDraftComment();
+ })();
+ }
+
+ $(this).parent().find('.item').each(function () {
+ $(this).removeClass('checked');
+ $(this).find('.octicon-check').addClass('tw-invisible');
+ });
+
+ if (selector === 'select-reviewers-modify' || selector === 'select-assignees-modify') {
+ return false;
+ }
+
+ $list.find('.item').each(function () {
+ $(this).addClass('tw-hidden');
+ });
+ $noSelect.removeClass('tw-hidden');
+ $($(this).parent().data('id')).val('');
+ });
+}
+
+function selectItem(select_id, input_id) {
+ const $menu = $(`${select_id} .menu`);
+ const $list = $(`.ui${select_id}.list`);
+ const hasUpdateAction = $menu.data('action') === 'update';
+
+ $menu.find('.item:not(.no-select)').on('click', function () {
+ $(this).parent().find('.item').each(function () {
+ $(this).removeClass('selected active');
+ });
+
+ $(this).addClass('selected active');
+ if (hasUpdateAction) {
+ (async () => {
+ await updateIssuesMeta(
+ $menu.data('update-url'),
+ '',
+ $menu.data('issue-id'),
+ $(this).data('id'),
+ );
+ reloadConfirmDraftComment();
+ })();
+ }
+
+ let icon = '';
+ if (input_id === '#milestone_id') {
+ icon = svg('octicon-milestone', 18, 'tw-mr-2');
+ } else if (input_id === '#project_id') {
+ icon = svg('octicon-project', 18, 'tw-mr-2');
+ } else if (input_id === '#assignee_id') {
+ icon = ``;
+ }
+
+ $list.find('.selected').html(`
+
+ `);
+
+ $(`.ui${select_id}.list .no-select`).addClass('tw-hidden');
+ $(input_id).val($(this).data('id'));
+ });
+ $menu.find('.no-select.item').on('click', function () {
+ $(this).parent().find('.item:not(.no-select)').each(function () {
+ $(this).removeClass('selected active');
+ });
+
+ if (hasUpdateAction) {
+ (async () => {
+ await updateIssuesMeta(
+ $menu.data('update-url'),
+ '',
+ $menu.data('issue-id'),
+ $(this).data('id'),
+ );
+ reloadConfirmDraftComment();
+ })();
+ }
+
+ $list.find('.selected').html('');
+ $list.find('.no-select').removeClass('tw-hidden');
+ $(input_id).val('');
+ });
+}
+
+export function initRepoIssueSidebar() {
+ initBranchSelector();
+
+ // Init labels and assignees
+ initListSubmits('select-label', 'labels');
+ initListSubmits('select-assignees', 'assignees');
+ initListSubmits('select-assignees-modify', 'assignees');
+ initListSubmits('select-reviewers-modify', 'assignees');
+
+ // Milestone, Assignee, Project
+ selectItem('.select-project', '#project_id');
+ selectItem('.select-milestone', '#milestone_id');
+ selectItem('.select-assignee', '#assignee_id');
+}
diff --git a/web_src/js/features/repo-issue.ts b/web_src/js/features/repo-issue.ts
index 520f2081b0..a9a65cdc81 100644
--- a/web_src/js/features/repo-issue.ts
+++ b/web_src/js/features/repo-issue.ts
@@ -7,6 +7,8 @@ import {ComboMarkdownEditor, getComboMarkdownEditor, initComboMarkdownEditor} fr
import {toAbsoluteUrl} from '../utils.ts';
import {GET, POST} from '../modules/fetch.ts';
import {showErrorToast} from '../modules/toast.ts';
+import {initRepoIssueSidebar} from './repo-issue-sidebar.ts';
+import {updateIssuesMeta} from './repo-common.ts';
const {appSubUrl} = window.config;
@@ -369,17 +371,6 @@ export function initRepoIssueWipTitle() {
});
}
-export async function updateIssuesMeta(url, action, issue_ids, id) {
- try {
- const response = await POST(url, {data: new URLSearchParams({action, issue_ids, id})});
- if (!response.ok) {
- throw new Error('Failed to update issues meta');
- }
- } catch (error) {
- console.error(error);
- }
-}
-
export function initRepoIssueComments() {
if (!$('.repository.view.issue .timeline').length) return;
@@ -665,7 +656,7 @@ export function initRepoIssueBranchSelect() {
});
}
-export async function initSingleCommentEditor($commentForm) {
+async function initSingleCommentEditor($commentForm) {
// pages:
// * normal new issue/pr page: no status-button, no comment-button (there is only a normal submit button which can submit empty content)
// * issue/pr view page: with comment form, has status-button and comment-button
@@ -687,7 +678,7 @@ export async function initSingleCommentEditor($commentForm) {
syncUiState();
}
-export function initIssueTemplateCommentEditors($commentForm) {
+function initIssueTemplateCommentEditors($commentForm) {
// pages:
// * new issue with issue template
const $comboFields = $commentForm.find('.combo-editor-dropzone');
@@ -733,3 +724,18 @@ export function initArchivedLabelHandler() {
toggleElem(label, label.classList.contains('checked'));
}
}
+
+export function initRepoCommentFormAndSidebar() {
+ const $commentForm = $('.comment.form');
+ if (!$commentForm.length) return;
+
+ if ($commentForm.find('.field.combo-editor-dropzone').length) {
+ // at the moment, if a form has multiple combo-markdown-editors, it must be an issue template form
+ initIssueTemplateCommentEditors($commentForm);
+ } else if ($commentForm.find('.combo-markdown-editor').length) {
+ // it's quite unclear about the "comment form" elements, sometimes it's for issue comment, sometimes it's for file editor/uploader message
+ initSingleCommentEditor($commentForm);
+ }
+
+ initRepoIssueSidebar();
+}
diff --git a/web_src/js/features/repo-legacy.ts b/web_src/js/features/repo-legacy.ts
index e3fa547395..6be3252a2b 100644
--- a/web_src/js/features/repo-legacy.ts
+++ b/web_src/js/features/repo-legacy.ts
@@ -1,13 +1,12 @@
import $ from 'jquery';
import {
+ initRepoCommentFormAndSidebar,
initRepoIssueBranchSelect, initRepoIssueCodeCommentCancel, initRepoIssueCommentDelete,
initRepoIssueComments, initRepoIssueDependencyDelete, initRepoIssueReferenceIssue,
initRepoIssueTitleEdit, initRepoIssueWipToggle,
- initRepoPullRequestUpdate, updateIssuesMeta, initIssueTemplateCommentEditors, initSingleCommentEditor,
+ initRepoPullRequestUpdate,
} from './repo-issue.ts';
import {initUnicodeEscapeButton} from './repo-unicode-escape.ts';
-import {svg} from '../svg.ts';
-import {htmlEscape} from 'escape-goat';
import {initRepoBranchTagSelector} from '../components/RepoBranchTagSelector.vue';
import {
initRepoCloneLink, initRepoCommonBranchOrTagDropdown, initRepoCommonFilterSearchDropdown,
@@ -16,32 +15,13 @@ import {initCitationFileCopyContent} from './citation.ts';
import {initCompLabelEdit} from './comp/LabelEdit.ts';
import {initRepoDiffConversationNav} from './repo-diff.ts';
import {initCompReactionSelector} from './comp/ReactionSelector.ts';
-import {initRepoSettingBranches} from './repo-settings.ts';
+import {initRepoSettings} from './repo-settings.ts';
import {initRepoPullRequestMergeForm} from './repo-issue-pr-form.ts';
import {initRepoPullRequestCommitStatus} from './repo-issue-pr-status.ts';
import {hideElem, queryElemChildren, showElem} from '../utils/dom.ts';
-import {POST} from '../modules/fetch.ts';
import {initRepoIssueCommentEdit} from './repo-issue-edit.ts';
-
-// if there are draft comments, confirm before reloading, to avoid losing comments
-function reloadConfirmDraftComment() {
- const commentTextareas = [
- document.querySelector('.edit-content-zone:not(.tw-hidden) textarea'),
- document.querySelector('#comment-form textarea'),
- ];
- for (const textarea of commentTextareas) {
- // Most users won't feel too sad if they lose a comment with 10 chars, they can re-type these in seconds.
- // But if they have typed more (like 50) chars and the comment is lost, they will be very unhappy.
- if (textarea && textarea.value.trim().length > 10) {
- textarea.parentElement.scrollIntoView();
- if (!window.confirm('Page will be reloaded, but there are draft comments. Continuing to reload will discard the comments. Continue?')) {
- return;
- }
- break;
- }
- }
- window.location.reload();
-}
+import {initRepoMilestone} from './repo-milestone.ts';
+import {initRepoNew} from './repo-new.ts';
export function initBranchSelectorTabs() {
const elSelectBranch = document.querySelector('.ui.dropdown.select-branch');
@@ -56,324 +36,16 @@ export function initBranchSelectorTabs() {
});
}
-export function initRepoCommentForm() {
- const $commentForm = $('.comment.form');
- if (!$commentForm.length) return;
-
- if ($commentForm.find('.field.combo-editor-dropzone').length) {
- // at the moment, if a form has multiple combo-markdown-editors, it must be an issue template form
- initIssueTemplateCommentEditors($commentForm);
- } else if ($commentForm.find('.combo-markdown-editor').length) {
- // it's quite unclear about the "comment form" elements, sometimes it's for issue comment, sometimes it's for file editor/uploader message
- initSingleCommentEditor($commentForm);
- }
-
- function initBranchSelector() {
- const elSelectBranch = document.querySelector('.ui.dropdown.select-branch');
- if (!elSelectBranch) return;
-
- const urlUpdateIssueRef = elSelectBranch.getAttribute('data-url-update-issueref');
- const $selectBranch = $(elSelectBranch);
- const $branchMenu = $selectBranch.find('.reference-list-menu');
- $branchMenu.find('.item:not(.no-select)').on('click', async function (e) {
- e.preventDefault();
- const selectedValue = this.getAttribute('data-id'); // eg: "refs/heads/my-branch"
- const selectedText = this.getAttribute('data-name'); // eg: "my-branch"
- if (urlUpdateIssueRef) {
- // for existing issue, send request to update issue ref, and reload page
- try {
- await POST(urlUpdateIssueRef, {data: new URLSearchParams({ref: selectedValue})});
- window.location.reload();
- } catch (error) {
- console.error(error);
- }
- } else {
- // for new issue, only update UI&form, do not send request/reload
- const selectedHiddenSelector = this.getAttribute('data-id-selector');
- document.querySelector(selectedHiddenSelector).value = selectedValue;
- elSelectBranch.querySelector('.text-branch-name').textContent = selectedText;
- }
- });
- }
-
- initBranchSelector();
-
- // List submits
- function initListSubmits(selector, outerSelector) {
- const $list = $(`.ui.${outerSelector}.list`);
- const $noSelect = $list.find('.no-select');
- const $listMenu = $(`.${selector} .menu`);
- let hasUpdateAction = $listMenu.data('action') === 'update';
- const items = {};
-
- $(`.${selector}`).dropdown({
- 'action': 'nothing', // do not hide the menu if user presses Enter
- fullTextSearch: 'exact',
- async onHide() {
- hasUpdateAction = $listMenu.data('action') === 'update'; // Update the var
- if (hasUpdateAction) {
- // TODO: Add batch functionality and make this 1 network request.
- const itemEntries = Object.entries(items);
- for (const [elementId, item] of itemEntries) {
- await updateIssuesMeta(
- item['update-url'],
- item.action,
- item['issue-id'],
- elementId,
- );
- }
- if (itemEntries.length) {
- reloadConfirmDraftComment();
- }
- }
- },
- });
-
- $listMenu.find('.item:not(.no-select)').on('click', function (e) {
- e.preventDefault();
- if (this.classList.contains('ban-change')) {
- return false;
- }
-
- hasUpdateAction = $listMenu.data('action') === 'update'; // Update the var
-
- const clickedItem = this; // eslint-disable-line unicorn/no-this-assignment
- const scope = this.getAttribute('data-scope');
-
- $(this).parent().find('.item').each(function () {
- if (scope) {
- // Enable only clicked item for scoped labels
- if (this.getAttribute('data-scope') !== scope) {
- return true;
- }
- if (this !== clickedItem && !this.classList.contains('checked')) {
- return true;
- }
- } else if (this !== clickedItem) {
- // Toggle for other labels
- return true;
- }
-
- if (this.classList.contains('checked')) {
- $(this).removeClass('checked');
- $(this).find('.octicon-check').addClass('tw-invisible');
- if (hasUpdateAction) {
- if (!($(this).data('id') in items)) {
- items[$(this).data('id')] = {
- 'update-url': $listMenu.data('update-url'),
- action: 'detach',
- 'issue-id': $listMenu.data('issue-id'),
- };
- } else {
- delete items[$(this).data('id')];
- }
- }
- } else {
- $(this).addClass('checked');
- $(this).find('.octicon-check').removeClass('tw-invisible');
- if (hasUpdateAction) {
- if (!($(this).data('id') in items)) {
- items[$(this).data('id')] = {
- 'update-url': $listMenu.data('update-url'),
- action: 'attach',
- 'issue-id': $listMenu.data('issue-id'),
- };
- } else {
- delete items[$(this).data('id')];
- }
- }
- }
- });
-
- // TODO: Which thing should be done for choosing review requests
- // to make chosen items be shown on time here?
- if (selector === 'select-reviewers-modify' || selector === 'select-assignees-modify') {
- return false;
- }
-
- const listIds = [];
- $(this).parent().find('.item').each(function () {
- if (this.classList.contains('checked')) {
- listIds.push($(this).data('id'));
- $($(this).data('id-selector')).removeClass('tw-hidden');
- } else {
- $($(this).data('id-selector')).addClass('tw-hidden');
- }
- });
- if (!listIds.length) {
- $noSelect.removeClass('tw-hidden');
- } else {
- $noSelect.addClass('tw-hidden');
- }
- $($(this).parent().data('id')).val(listIds.join(','));
- return false;
- });
- $listMenu.find('.no-select.item').on('click', function (e) {
- e.preventDefault();
- if (hasUpdateAction) {
- (async () => {
- await updateIssuesMeta(
- $listMenu.data('update-url'),
- 'clear',
- $listMenu.data('issue-id'),
- '',
- );
- reloadConfirmDraftComment();
- })();
- }
-
- $(this).parent().find('.item').each(function () {
- $(this).removeClass('checked');
- $(this).find('.octicon-check').addClass('tw-invisible');
- });
-
- if (selector === 'select-reviewers-modify' || selector === 'select-assignees-modify') {
- return false;
- }
-
- $list.find('.item').each(function () {
- $(this).addClass('tw-hidden');
- });
- $noSelect.removeClass('tw-hidden');
- $($(this).parent().data('id')).val('');
- });
- }
-
- // Init labels and assignees
- initListSubmits('select-label', 'labels');
- initListSubmits('select-assignees', 'assignees');
- initListSubmits('select-assignees-modify', 'assignees');
- initListSubmits('select-reviewers', 'reviewers');
- initListSubmits('select-reviewers-modify', 'reviewers');
-
- function selectItem(select_id, input_id) {
- const $menu = $(`${select_id} .menu`);
- const $list = $(`.ui${select_id}.list`);
- const hasUpdateAction = $menu.data('action') === 'update';
-
- $menu.find('.item:not(.no-select)').on('click', function () {
- $(this).parent().find('.item').each(function () {
- $(this).removeClass('selected active');
- });
-
- $(this).addClass('selected active');
- if (hasUpdateAction) {
- (async () => {
- await updateIssuesMeta(
- $menu.data('update-url'),
- '',
- $menu.data('issue-id'),
- $(this).data('id'),
- );
- reloadConfirmDraftComment();
- })();
- }
-
- let icon = '';
- if (input_id === '#milestone_id') {
- icon = svg('octicon-milestone', 18, 'tw-mr-2');
- } else if (input_id === '#project_id') {
- icon = svg('octicon-project', 18, 'tw-mr-2');
- } else if (input_id === '#assignee_id') {
- icon = ``;
- } else if (input_id === '#reviewer_id') {
- icon = ``;
- }
-
- $list.find('.selected').html(`
-
- `);
-
- $(`.ui${select_id}.list .no-select`).addClass('tw-hidden');
- $(input_id).val($(this).data('id'));
- });
- $menu.find('.no-select.item').on('click', function () {
- $(this).parent().find('.item:not(.no-select)').each(function () {
- $(this).removeClass('selected active');
- });
-
- if (hasUpdateAction) {
- (async () => {
- await updateIssuesMeta(
- $menu.data('update-url'),
- '',
- $menu.data('issue-id'),
- $(this).data('id'),
- );
- reloadConfirmDraftComment();
- })();
- }
-
- $list.find('.selected').html('');
- $list.find('.no-select').removeClass('tw-hidden');
- $(input_id).val('');
- });
- }
-
- // Milestone, Assignee, Project
- selectItem('.select-project', '#project_id');
- selectItem('.select-milestone', '#milestone_id');
- selectItem('.select-assignee', '#assignee_id');
- selectItem('.select-reviewer', '#reviewer_id');
-}
-
export function initRepository() {
if (!$('.page-content.repository').length) return;
initRepoBranchTagSelector('.js-branch-tag-selector');
-
- // Options
- if ($('.repository.settings.options').length > 0) {
- // Enable or select internal/external wiki system and issue tracker.
- $('.enable-system').on('change', function () {
- if (this.checked) {
- $($(this).data('target')).removeClass('disabled');
- if (!$(this).data('context')) $($(this).data('context')).addClass('disabled');
- } else {
- $($(this).data('target')).addClass('disabled');
- if (!$(this).data('context')) $($(this).data('context')).removeClass('disabled');
- }
- });
- $('.enable-system-radio').on('change', function () {
- if (this.value === 'false') {
- $($(this).data('target')).addClass('disabled');
- if ($(this).data('context') !== undefined) $($(this).data('context')).removeClass('disabled');
- } else if (this.value === 'true') {
- $($(this).data('target')).removeClass('disabled');
- if ($(this).data('context') !== undefined) $($(this).data('context')).addClass('disabled');
- }
- });
- const $trackerIssueStyleRadios = $('.js-tracker-issue-style');
- $trackerIssueStyleRadios.on('change input', () => {
- const checkedVal = $trackerIssueStyleRadios.filter(':checked').val();
- $('#tracker-issue-style-regex-box').toggleClass('disabled', checkedVal !== 'regexp');
- });
- }
+ initRepoCommentFormAndSidebar();
// Labels
initCompLabelEdit('.repository.labels');
-
- // Milestones
- if ($('.repository.new.milestone').length > 0) {
- $('#clear-date').on('click', () => {
- $('#deadline').val('');
- return false;
- });
- }
-
- // Repo Creation
- if ($('.repository.new.repo').length > 0) {
- $('input[name="gitignores"], input[name="license"]').on('change', () => {
- const gitignores = $('input[name="gitignores"]').val();
- const license = $('input[name="license"]').val();
- if (gitignores || license) {
- document.querySelector('input[name="auto_init"]').checked = true;
- }
- });
- }
+ initRepoMilestone();
+ initRepoNew();
// Compare or pull request
const $repoDiff = $('.repository.diff');
@@ -384,7 +56,7 @@ export function initRepository() {
initRepoCloneLink();
initCitationFileCopyContent();
- initRepoSettingBranches();
+ initRepoSettings();
// Issues
if ($('.repository.view.issue').length > 0) {
diff --git a/web_src/js/features/repo-milestone.ts b/web_src/js/features/repo-milestone.ts
new file mode 100644
index 0000000000..ddef723b48
--- /dev/null
+++ b/web_src/js/features/repo-milestone.ts
@@ -0,0 +1,11 @@
+import $ from 'jquery';
+
+export function initRepoMilestone() {
+ // Milestones
+ if ($('.repository.new.milestone').length > 0) {
+ $('#clear-date').on('click', () => {
+ $('#deadline').val('');
+ return false;
+ });
+ }
+}
diff --git a/web_src/js/features/repo-new.ts b/web_src/js/features/repo-new.ts
new file mode 100644
index 0000000000..22d8c8a47b
--- /dev/null
+++ b/web_src/js/features/repo-new.ts
@@ -0,0 +1,14 @@
+import $ from 'jquery';
+
+export function initRepoNew() {
+ // Repo Creation
+ if ($('.repository.new.repo').length > 0) {
+ $('input[name="gitignores"], input[name="license"]').on('change', () => {
+ const gitignores = $('input[name="gitignores"]').val();
+ const license = $('input[name="license"]').val();
+ if (gitignores || license) {
+ document.querySelector('input[name="auto_init"]').checked = true;
+ }
+ });
+ }
+}
diff --git a/web_src/js/features/repo-settings.ts b/web_src/js/features/repo-settings.ts
index 97211d035e..34a3b635b2 100644
--- a/web_src/js/features/repo-settings.ts
+++ b/web_src/js/features/repo-settings.ts
@@ -6,7 +6,7 @@ import {POST} from '../modules/fetch.ts';
const {appSubUrl, csrfToken} = window.config;
-export function initRepoSettingsCollaboration() {
+function initRepoSettingsCollaboration() {
// Change collaborator access mode
for (const dropdownEl of queryElems('.page-content.repository .ui.dropdown.access-mode')) {
const textEl = dropdownEl.querySelector(':scope > .text');
@@ -43,7 +43,7 @@ export function initRepoSettingsCollaboration() {
}
}
-export function initRepoSettingSearchTeamBox() {
+function initRepoSettingsSearchTeamBox() {
const searchTeamBox = document.querySelector('#search-team-box');
if (!searchTeamBox) return;
@@ -69,13 +69,13 @@ export function initRepoSettingSearchTeamBox() {
});
}
-export function initRepoSettingGitHook() {
+function initRepoSettingsGitHook() {
if (!$('.edit.githook').length) return;
const filename = document.querySelector('.hook-filename').textContent;
- const _promise = createMonaco($('#content')[0], filename, {language: 'shell'});
+ createMonaco($('#content')[0], filename, {language: 'shell'});
}
-export function initRepoSettingBranches() {
+function initRepoSettingsBranches() {
if (!document.querySelector('.repository.settings.branches')) return;
for (const el of document.querySelectorAll('.toggle-target-enabled')) {
@@ -117,3 +117,41 @@ export function initRepoSettingBranches() {
markMatchedStatusChecks();
document.querySelector('#status_check_contexts').addEventListener('input', onInputDebounce(markMatchedStatusChecks));
}
+
+function initRepoSettingsOptions() {
+ if ($('.repository.settings.options').length > 0) {
+ // Enable or select internal/external wiki system and issue tracker.
+ $('.enable-system').on('change', function () {
+ if (this.checked) {
+ $($(this).data('target')).removeClass('disabled');
+ if (!$(this).data('context')) $($(this).data('context')).addClass('disabled');
+ } else {
+ $($(this).data('target')).addClass('disabled');
+ if (!$(this).data('context')) $($(this).data('context')).removeClass('disabled');
+ }
+ });
+ $('.enable-system-radio').on('change', function () {
+ if (this.value === 'false') {
+ $($(this).data('target')).addClass('disabled');
+ if ($(this).data('context') !== undefined) $($(this).data('context')).removeClass('disabled');
+ } else if (this.value === 'true') {
+ $($(this).data('target')).removeClass('disabled');
+ if ($(this).data('context') !== undefined) $($(this).data('context')).addClass('disabled');
+ }
+ });
+ const $trackerIssueStyleRadios = $('.js-tracker-issue-style');
+ $trackerIssueStyleRadios.on('change input', () => {
+ const checkedVal = $trackerIssueStyleRadios.filter(':checked').val();
+ $('#tracker-issue-style-regex-box').toggleClass('disabled', checkedVal !== 'regexp');
+ });
+ }
+}
+
+export function initRepoSettings() {
+ if (!document.querySelector('.page-content.repository.settings')) return;
+ initRepoSettingsOptions();
+ initRepoSettingsBranches();
+ initRepoSettingsCollaboration();
+ initRepoSettingsSearchTeamBox();
+ initRepoSettingsGitHook();
+}
diff --git a/web_src/js/index.ts b/web_src/js/index.ts
index f63d199488..08d8997fd1 100644
--- a/web_src/js/index.ts
+++ b/web_src/js/index.ts
@@ -43,11 +43,6 @@ import {initSshKeyFormParser} from './features/sshkey-helper.ts';
import {initUserSettings} from './features/user-settings.ts';
import {initRepoActivityTopAuthorsChart, initRepoArchiveLinks} from './features/repo-common.ts';
import {initRepoMigrationStatusChecker} from './features/repo-migrate.ts';
-import {
- initRepoSettingGitHook,
- initRepoSettingsCollaboration,
- initRepoSettingSearchTeamBox,
-} from './features/repo-settings.ts';
import {initRepoDiffView} from './features/repo-diff.ts';
import {initOrgTeamSearchRepoBox, initOrgTeamSettings} from './features/org-team.ts';
import {initUserAuthWebAuthn, initUserAuthWebAuthnRegister} from './features/user-auth-webauthn.ts';
@@ -59,7 +54,7 @@ import {initCompWebHookEditor} from './features/comp/WebHookEditor.ts';
import {initRepoBranchButton} from './features/repo-branch.ts';
import {initCommonOrganization} from './features/common-organization.ts';
import {initRepoWikiForm} from './features/repo-wiki.ts';
-import {initRepoCommentForm, initRepository, initBranchSelectorTabs} from './features/repo-legacy.ts';
+import {initRepository, initBranchSelectorTabs} from './features/repo-legacy.ts';
import {initCopyContent} from './features/copycontent.ts';
import {initCaptcha} from './features/captcha.ts';
import {initRepositoryActionView} from './components/RepoActionView.vue';
@@ -180,7 +175,6 @@ onDomReady(() => {
initRepoArchiveLinks,
initRepoBranchButton,
initRepoCodeView,
- initRepoCommentForm,
initBranchSelectorTabs,
initRepoEllipsisButton,
initRepoDiffCommitBranchesAndTags,
@@ -202,9 +196,6 @@ onDomReady(() => {
initRepoPullRequestReview,
initRepoRelease,
initRepoReleaseNew,
- initRepoSettingGitHook,
- initRepoSettingSearchTeamBox,
- initRepoSettingsCollaboration,
initRepoTemplateSearch,
initRepoTopicBar,
initRepoWikiForm,