mirror of
https://github.com/go-gitea/gitea.git
synced 2025-10-03 00:02:14 -04:00
Compare commits
7 Commits
c1b90d4dee
...
8f263371a4
Author | SHA1 | Date | |
---|---|---|---|
|
8f263371a4 | ||
|
b907b9fb1a | ||
|
c5d74e5869 | ||
|
c5332fdc55 | ||
|
0f668145e9 | ||
|
fbe80e6df2 | ||
|
fc4b73eac9 |
12
go.mod
12
go.mod
@ -16,7 +16,7 @@ require (
|
||||
gitea.com/go-chi/binding v0.0.0-20240430071103-39a851e106ed
|
||||
gitea.com/go-chi/cache v0.2.1
|
||||
gitea.com/go-chi/captcha v0.0.0-20240315150714-fb487f629098
|
||||
gitea.com/go-chi/session v0.0.0-20240316035857-16768d98ec96
|
||||
gitea.com/go-chi/session v0.0.0-20250926004215-636cadd82e15
|
||||
gitea.com/lunny/dingtalk_webhook v0.0.0-20171025031554-e3534c89ef96
|
||||
gitea.com/lunny/levelqueue v0.4.2-0.20230414023320-3c0159fe0fe4
|
||||
github.com/42wim/httpsig v1.2.3
|
||||
@ -61,6 +61,7 @@ require (
|
||||
github.com/go-redsync/redsync/v4 v4.13.0
|
||||
github.com/go-sql-driver/mysql v1.9.3
|
||||
github.com/go-webauthn/webauthn v0.13.4
|
||||
github.com/goccy/go-json v0.10.5
|
||||
github.com/gogs/chardet v0.0.0-20211120154057-b7413eaefb8f
|
||||
github.com/gogs/go-gogs-client v0.0.0-20210131175652-1d7215cd8d85
|
||||
github.com/golang-jwt/jwt/v5 v5.3.0
|
||||
@ -75,7 +76,6 @@ require (
|
||||
github.com/huandu/xstrings v1.5.0
|
||||
github.com/jaytaylor/html2text v0.0.0-20230321000545-74c2419ad056
|
||||
github.com/jhillyerd/enmime v1.3.0
|
||||
github.com/json-iterator/go v1.1.12
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51
|
||||
github.com/klauspost/compress v1.18.0
|
||||
github.com/klauspost/cpuid/v2 v2.3.0
|
||||
@ -109,7 +109,7 @@ require (
|
||||
github.com/ulikunitz/xz v0.5.15
|
||||
github.com/urfave/cli-docs/v3 v3.0.0-alpha6
|
||||
github.com/urfave/cli/v3 v3.4.1
|
||||
github.com/wneessen/go-mail v0.6.2
|
||||
github.com/wneessen/go-mail v0.7.1
|
||||
github.com/xeipuuv/gojsonschema v1.2.0
|
||||
github.com/yohcop/openid-go v1.0.1
|
||||
github.com/yuin/goldmark v1.7.13
|
||||
@ -120,9 +120,9 @@ require (
|
||||
golang.org/x/image v0.30.0
|
||||
golang.org/x/net v0.43.0
|
||||
golang.org/x/oauth2 v0.30.0
|
||||
golang.org/x/sync v0.16.0
|
||||
golang.org/x/sync v0.17.0
|
||||
golang.org/x/sys v0.35.0
|
||||
golang.org/x/text v0.28.0
|
||||
golang.org/x/text v0.29.0
|
||||
google.golang.org/grpc v1.75.0
|
||||
google.golang.org/protobuf v1.36.8
|
||||
gopkg.in/ini.v1 v1.67.0
|
||||
@ -200,7 +200,6 @@ require (
|
||||
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
|
||||
github.com/go-ini/ini v1.67.0 // indirect
|
||||
github.com/go-webauthn/x v0.1.24 // indirect
|
||||
github.com/goccy/go-json v0.10.5 // indirect
|
||||
github.com/golang-jwt/jwt/v4 v4.5.2 // indirect
|
||||
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 // indirect
|
||||
github.com/golang-sql/sqlexp v0.1.0 // indirect
|
||||
@ -220,6 +219,7 @@ require (
|
||||
github.com/hashicorp/go-retryablehttp v0.7.8 // indirect
|
||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
|
||||
github.com/josharian/intern v1.0.0 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/kevinburke/ssh_config v1.4.0 // indirect
|
||||
github.com/klauspost/pgzip v1.2.6 // indirect
|
||||
github.com/libdns/libdns v1.1.1 // indirect
|
||||
|
21
go.sum
21
go.sum
@ -43,8 +43,8 @@ gitea.com/go-chi/cache v0.2.1 h1:bfAPkvXlbcZxPCpcmDVCWoHgiBSBmZN/QosnZvEC0+g=
|
||||
gitea.com/go-chi/cache v0.2.1/go.mod h1:Qic0HZ8hOHW62ETGbonpwz8WYypj9NieU9659wFUJ8Q=
|
||||
gitea.com/go-chi/captcha v0.0.0-20240315150714-fb487f629098 h1:p2ki+WK0cIeNQuqjR98IP2KZQKRzJJiV7aTeMAFwaWo=
|
||||
gitea.com/go-chi/captcha v0.0.0-20240315150714-fb487f629098/go.mod h1:LjzIOHlRemuUyO7WR12fmm18VZIlCAaOt9L3yKw40pk=
|
||||
gitea.com/go-chi/session v0.0.0-20240316035857-16768d98ec96 h1:IFDiMBObsP6CZIRaDLd54SR6zPYAffPXiXck5Xslu0Q=
|
||||
gitea.com/go-chi/session v0.0.0-20240316035857-16768d98ec96/go.mod h1:0iEpFKnwO5dG0aF98O4eq6FMsAiXkNBaDIlUOlq4BtM=
|
||||
gitea.com/go-chi/session v0.0.0-20250926004215-636cadd82e15 h1:qFYmz05u/s9664o7+XEgrlHXSPQ4uHO8/ccZGUb1uxA=
|
||||
gitea.com/go-chi/session v0.0.0-20250926004215-636cadd82e15/go.mod h1:0iEpFKnwO5dG0aF98O4eq6FMsAiXkNBaDIlUOlq4BtM=
|
||||
gitea.com/lunny/dingtalk_webhook v0.0.0-20171025031554-e3534c89ef96 h1:+wWBi6Qfruqu7xJgjOIrKVQGiLUZdpKYCZewJ4clqhw=
|
||||
gitea.com/lunny/dingtalk_webhook v0.0.0-20171025031554-e3534c89ef96/go.mod h1:VyMQP6ue6MKHM8UsOXfNfuMKD0oSAWZdXVcpHIN2yaY=
|
||||
gitea.com/lunny/levelqueue v0.4.2-0.20230414023320-3c0159fe0fe4 h1:IFT+hup2xejHqdhS7keYWioqfmxdnfblFDTGoOwcZ+o=
|
||||
@ -765,8 +765,8 @@ github.com/urfave/cli/v3 v3.4.1/go.mod h1:FJSKtM/9AiiTOJL4fJ6TbMUkxBXn7GO9guZqoZ
|
||||
github.com/valyala/fastjson v1.6.4 h1:uAUNq9Z6ymTgGhcm0UynUAB6tlbakBrz6CQFax3BXVQ=
|
||||
github.com/valyala/fastjson v1.6.4/go.mod h1:CLCAqky6SMuOcxStkYQvblddUtoRxhYMGLrsQns1aXY=
|
||||
github.com/willf/bitset v1.1.10/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4=
|
||||
github.com/wneessen/go-mail v0.6.2 h1:c6V7c8D2mz868z9WJ+8zDKtUyLfZ1++uAZmo2GRFji8=
|
||||
github.com/wneessen/go-mail v0.6.2/go.mod h1:L/PYjPK3/2ZlNb2/FjEBIn9n1rUWjW+Toy531oVmeb4=
|
||||
github.com/wneessen/go-mail v0.7.1 h1:rvy63sp14N06/kdGqCYwW8Na5gDCXjTQM1E7So4PuKk=
|
||||
github.com/wneessen/go-mail v0.7.1/go.mod h1:+TkW6QP3EVkgTEqHtVmnAE/1MRhmzb8Y9/W3pweuS+k=
|
||||
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
|
||||
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
|
||||
github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM=
|
||||
@ -837,7 +837,6 @@ golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDf
|
||||
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
|
||||
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
|
||||
golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc=
|
||||
golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M=
|
||||
golang.org/x/crypto v0.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4=
|
||||
golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
@ -930,9 +929,8 @@ golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
|
||||
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw=
|
||||
golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug=
|
||||
golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
@ -974,7 +972,6 @@ golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI=
|
||||
golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
|
||||
@ -987,7 +984,6 @@ golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
|
||||
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
|
||||
golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM=
|
||||
golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek=
|
||||
golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s=
|
||||
golang.org/x/term v0.34.0 h1:O/2T7POpk0ZZ7MAzMeWFSg6S5IpWd/RXDlM9hgM3DR4=
|
||||
golang.org/x/term v0.34.0/go.mod h1:5jC53AEywhIVebHgPVeg0mj8OD3VO9OzclacVrqpaAw=
|
||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
@ -1003,9 +999,8 @@ golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
|
||||
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
|
||||
golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng=
|
||||
golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU=
|
||||
golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk=
|
||||
golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE=
|
||||
|
@ -559,6 +559,74 @@ func (issues IssueList) LoadDiscussComments(ctx context.Context) error {
|
||||
return issues.loadComments(ctx, builder.Eq{"comment.type": CommentTypeComment})
|
||||
}
|
||||
|
||||
// GetBlockedByCounts returns a map of issue ID to number of open issues that are blocking it
|
||||
func (issues IssueList) GetBlockedByCount(ctx context.Context) (map[int64]int64, error) {
|
||||
type BlockedByCount struct {
|
||||
IssueID int64
|
||||
Count int64
|
||||
}
|
||||
|
||||
bCounts := make([]*BlockedByCount, len(issues))
|
||||
ids := make([]int64, len(issues))
|
||||
for i, issue := range issues {
|
||||
ids[i] = issue.ID
|
||||
}
|
||||
|
||||
sess := db.GetEngine(ctx).In("issue_id", ids)
|
||||
err := sess.Select("issue_id, count(issue_dependency.id) as `count`").
|
||||
Join("INNER", "issue", "issue.id = issue_dependency.dependency_id").
|
||||
Where("is_closed = ?", false).
|
||||
GroupBy("issue_id").
|
||||
OrderBy("issue_id").
|
||||
Table("issue_dependency").
|
||||
Find(&bCounts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
blockedByCountMap := make(map[int64]int64, len(issues))
|
||||
for _, c := range bCounts {
|
||||
if c != nil {
|
||||
blockedByCountMap[c.IssueID] = c.Count
|
||||
}
|
||||
}
|
||||
|
||||
return blockedByCountMap, nil
|
||||
}
|
||||
|
||||
// GetBlockingCounts returns a map of issue ID to number of issues that are blocked by it
|
||||
func (issues IssueList) GetBlockingCount(ctx context.Context) (map[int64]int64, error) {
|
||||
type BlockingCount struct {
|
||||
IssueID int64
|
||||
Count int64
|
||||
}
|
||||
|
||||
bCounts := make([]*BlockingCount, 0, len(issues))
|
||||
ids := make([]int64, len(issues))
|
||||
for i, issue := range issues {
|
||||
ids[i] = issue.ID
|
||||
}
|
||||
|
||||
sess := db.GetEngine(ctx).In("dependency_id", ids)
|
||||
err := sess.Select("dependency_id as `issue_id`, count(id) as `count`").
|
||||
GroupBy("dependency_id").
|
||||
OrderBy("dependency_id").
|
||||
Table("issue_dependency").
|
||||
Find(&bCounts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
blockingCountMap := make(map[int64]int64, len(issues))
|
||||
for _, c := range bCounts {
|
||||
if c != nil {
|
||||
blockingCountMap[c.IssueID] = c.Count
|
||||
}
|
||||
}
|
||||
|
||||
return blockingCountMap, nil
|
||||
}
|
||||
|
||||
// GetApprovalCounts returns a map of issue ID to slice of approval counts
|
||||
// FIXME: only returns official counts due to double counting of non-official approvals
|
||||
func (issues IssueList) GetApprovalCounts(ctx context.Context) (map[int64][]*ReviewCount, error) {
|
||||
|
@ -8,8 +8,6 @@ import (
|
||||
"encoding/binary"
|
||||
"encoding/json" //nolint:depguard // this package wraps it
|
||||
"io"
|
||||
|
||||
jsoniter "github.com/json-iterator/go"
|
||||
)
|
||||
|
||||
// Encoder represents an encoder for json
|
||||
@ -31,70 +29,7 @@ type Interface interface {
|
||||
Indent(dst *bytes.Buffer, src []byte, prefix, indent string) error
|
||||
}
|
||||
|
||||
var (
|
||||
DefaultJSONHandler = getDefaultJSONHandler()
|
||||
|
||||
_ Interface = StdJSON{}
|
||||
_ Interface = JSONiter{}
|
||||
)
|
||||
|
||||
// StdJSON implements Interface via encoding/json
|
||||
type StdJSON struct{}
|
||||
|
||||
// Marshal implements Interface
|
||||
func (StdJSON) Marshal(v any) ([]byte, error) {
|
||||
return json.Marshal(v)
|
||||
}
|
||||
|
||||
// Unmarshal implements Interface
|
||||
func (StdJSON) Unmarshal(data []byte, v any) error {
|
||||
return json.Unmarshal(data, v)
|
||||
}
|
||||
|
||||
// NewEncoder implements Interface
|
||||
func (StdJSON) NewEncoder(writer io.Writer) Encoder {
|
||||
return json.NewEncoder(writer)
|
||||
}
|
||||
|
||||
// NewDecoder implements Interface
|
||||
func (StdJSON) NewDecoder(reader io.Reader) Decoder {
|
||||
return json.NewDecoder(reader)
|
||||
}
|
||||
|
||||
// Indent implements Interface
|
||||
func (StdJSON) Indent(dst *bytes.Buffer, src []byte, prefix, indent string) error {
|
||||
return json.Indent(dst, src, prefix, indent)
|
||||
}
|
||||
|
||||
// JSONiter implements Interface via jsoniter
|
||||
type JSONiter struct {
|
||||
jsoniter.API
|
||||
}
|
||||
|
||||
// Marshal implements Interface
|
||||
func (j JSONiter) Marshal(v any) ([]byte, error) {
|
||||
return j.API.Marshal(v)
|
||||
}
|
||||
|
||||
// Unmarshal implements Interface
|
||||
func (j JSONiter) Unmarshal(data []byte, v any) error {
|
||||
return j.API.Unmarshal(data, v)
|
||||
}
|
||||
|
||||
// NewEncoder implements Interface
|
||||
func (j JSONiter) NewEncoder(writer io.Writer) Encoder {
|
||||
return j.API.NewEncoder(writer)
|
||||
}
|
||||
|
||||
// NewDecoder implements Interface
|
||||
func (j JSONiter) NewDecoder(reader io.Reader) Decoder {
|
||||
return j.API.NewDecoder(reader)
|
||||
}
|
||||
|
||||
// Indent implements Interface, since jsoniter don't support Indent, just use encoding/json's
|
||||
func (j JSONiter) Indent(dst *bytes.Buffer, src []byte, prefix, indent string) error {
|
||||
return json.Indent(dst, src, prefix, indent)
|
||||
}
|
||||
var DefaultJSONHandler = getDefaultJSONHandler()
|
||||
|
||||
// Marshal converts object as bytes
|
||||
func Marshal(v any) ([]byte, error) {
|
||||
|
35
modules/json/jsongoccy.go
Normal file
35
modules/json/jsongoccy.go
Normal file
@ -0,0 +1,35 @@
|
||||
// Copyright 2025 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package json
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
|
||||
"github.com/goccy/go-json"
|
||||
)
|
||||
|
||||
var _ Interface = jsonGoccy{}
|
||||
|
||||
type jsonGoccy struct{}
|
||||
|
||||
func (jsonGoccy) Marshal(v any) ([]byte, error) {
|
||||
return json.Marshal(v)
|
||||
}
|
||||
|
||||
func (jsonGoccy) Unmarshal(data []byte, v any) error {
|
||||
return json.Unmarshal(data, v)
|
||||
}
|
||||
|
||||
func (jsonGoccy) NewEncoder(writer io.Writer) Encoder {
|
||||
return json.NewEncoder(writer)
|
||||
}
|
||||
|
||||
func (jsonGoccy) NewDecoder(reader io.Reader) Decoder {
|
||||
return json.NewDecoder(reader)
|
||||
}
|
||||
|
||||
func (jsonGoccy) Indent(dst *bytes.Buffer, src []byte, prefix, indent string) error {
|
||||
return json.Indent(dst, src, prefix, indent)
|
||||
}
|
@ -7,12 +7,10 @@ package json
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
jsoniter "github.com/json-iterator/go"
|
||||
)
|
||||
|
||||
func getDefaultJSONHandler() Interface {
|
||||
return JSONiter{jsoniter.ConfigCompatibleWithStandardLibrary}
|
||||
return jsonGoccy{}
|
||||
}
|
||||
|
||||
func MarshalKeepOptionalEmpty(v any) ([]byte, error) {
|
||||
|
34
modules/json/jsonv1.go
Normal file
34
modules/json/jsonv1.go
Normal file
@ -0,0 +1,34 @@
|
||||
// Copyright 2025 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package json
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json" //nolint:depguard // this package wraps it
|
||||
"io"
|
||||
)
|
||||
|
||||
type jsonV1 struct{}
|
||||
|
||||
var _ Interface = jsonV1{}
|
||||
|
||||
func (jsonV1) Marshal(v any) ([]byte, error) {
|
||||
return json.Marshal(v)
|
||||
}
|
||||
|
||||
func (jsonV1) Unmarshal(data []byte, v any) error {
|
||||
return json.Unmarshal(data, v)
|
||||
}
|
||||
|
||||
func (jsonV1) NewEncoder(writer io.Writer) Encoder {
|
||||
return json.NewEncoder(writer)
|
||||
}
|
||||
|
||||
func (jsonV1) NewDecoder(reader io.Reader) Decoder {
|
||||
return json.NewDecoder(reader)
|
||||
}
|
||||
|
||||
func (jsonV1) Indent(dst *bytes.Buffer, src []byte, prefix, indent string) error {
|
||||
return json.Indent(dst, src, prefix, indent)
|
||||
}
|
@ -193,7 +193,7 @@ func TestHTTPClientDownload(t *testing.T) {
|
||||
},
|
||||
{
|
||||
endpoint: "https://invalid-json-response.io",
|
||||
expectedError: "/(invalid json|jsontext: invalid character)/",
|
||||
expectedError: "/(invalid json|invalid character)/",
|
||||
},
|
||||
{
|
||||
endpoint: "https://valid-batch-request-download.io",
|
||||
@ -301,7 +301,7 @@ func TestHTTPClientUpload(t *testing.T) {
|
||||
},
|
||||
{
|
||||
endpoint: "https://invalid-json-response.io",
|
||||
expectedError: "/(invalid json|jsontext: invalid character)/",
|
||||
expectedError: "/(invalid json|invalid character)/",
|
||||
},
|
||||
{
|
||||
endpoint: "https://valid-batch-request-upload.io",
|
||||
|
@ -5,6 +5,7 @@ package session
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"sync"
|
||||
|
||||
@ -121,12 +122,12 @@ func (p *DBProvider) Read(sid string) (session.RawStore, error) {
|
||||
}
|
||||
|
||||
// Exist returns true if session with given ID exists.
|
||||
func (p *DBProvider) Exist(sid string) bool {
|
||||
func (p *DBProvider) Exist(sid string) (bool, error) {
|
||||
has, err := auth.ExistSession(dbContext(), sid)
|
||||
if err != nil {
|
||||
panic("session/DB: error checking existence: " + err.Error())
|
||||
return false, fmt.Errorf("session/DB: error checking existence: %w", err)
|
||||
}
|
||||
return has
|
||||
return has, nil
|
||||
}
|
||||
|
||||
// Destroy deletes a session by session ID.
|
||||
@ -155,12 +156,12 @@ func (p *DBProvider) Regenerate(oldsid, sid string) (_ session.RawStore, err err
|
||||
}
|
||||
|
||||
// Count counts and returns number of sessions.
|
||||
func (p *DBProvider) Count() int {
|
||||
func (p *DBProvider) Count() (int, error) {
|
||||
total, err := auth.CountSessions(dbContext())
|
||||
if err != nil {
|
||||
panic("session/DB: error counting records: " + err.Error())
|
||||
return 0, fmt.Errorf("session/DB: error counting records: %w", err)
|
||||
}
|
||||
return int(total)
|
||||
return int(total), nil
|
||||
}
|
||||
|
||||
// GC calls GC to clean expired sessions.
|
||||
|
@ -135,10 +135,12 @@ func (p *RedisProvider) Init(maxlifetime int64, configs string) (err error) {
|
||||
// Read returns raw session store by session ID.
|
||||
func (p *RedisProvider) Read(sid string) (session.RawStore, error) {
|
||||
psid := p.prefix + sid
|
||||
if !p.Exist(sid) {
|
||||
if exist, err := p.Exist(sid); err == nil && !exist {
|
||||
if err := p.c.Set(graceful.GetManager().HammerContext(), psid, "", p.duration).Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var kv map[any]any
|
||||
@ -159,9 +161,9 @@ func (p *RedisProvider) Read(sid string) (session.RawStore, error) {
|
||||
}
|
||||
|
||||
// Exist returns true if session with given ID exists.
|
||||
func (p *RedisProvider) Exist(sid string) bool {
|
||||
func (p *RedisProvider) Exist(sid string) (bool, error) {
|
||||
v, err := p.c.Exists(graceful.GetManager().HammerContext(), p.prefix+sid).Result()
|
||||
return err == nil && v == 1
|
||||
return err == nil && v == 1, err
|
||||
}
|
||||
|
||||
// Destroy deletes a session by session ID.
|
||||
@ -174,13 +176,18 @@ func (p *RedisProvider) Regenerate(oldsid, sid string) (_ session.RawStore, err
|
||||
poldsid := p.prefix + oldsid
|
||||
psid := p.prefix + sid
|
||||
|
||||
if p.Exist(sid) {
|
||||
if exist, err := p.Exist(sid); err != nil {
|
||||
return nil, err
|
||||
} else if exist {
|
||||
return nil, fmt.Errorf("new sid '%s' already exists", sid)
|
||||
} else if !p.Exist(oldsid) {
|
||||
}
|
||||
if exist, err := p.Exist(oldsid); err == nil && !exist {
|
||||
// Make a fake old session.
|
||||
if err = p.c.Set(graceful.GetManager().HammerContext(), poldsid, "", p.duration).Err(); err != nil {
|
||||
if err := p.c.Set(graceful.GetManager().HammerContext(), poldsid, "", p.duration).Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// do not use Rename here, because the old sid and new sid may be in different redis cluster slot.
|
||||
@ -211,12 +218,9 @@ func (p *RedisProvider) Regenerate(oldsid, sid string) (_ session.RawStore, err
|
||||
}
|
||||
|
||||
// Count counts and returns number of sessions.
|
||||
func (p *RedisProvider) Count() int {
|
||||
func (p *RedisProvider) Count() (int, error) {
|
||||
size, err := p.c.DBSize(graceful.GetManager().HammerContext()).Result()
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
return int(size)
|
||||
return int(size), err
|
||||
}
|
||||
|
||||
// GC calls GC to clean expired sessions.
|
||||
|
@ -59,8 +59,10 @@ func (o *VirtualSessionProvider) Init(gcLifetime int64, config string) error {
|
||||
func (o *VirtualSessionProvider) Read(sid string) (session.RawStore, error) {
|
||||
o.lock.RLock()
|
||||
defer o.lock.RUnlock()
|
||||
if o.provider.Exist(sid) {
|
||||
if exist, err := o.provider.Exist(sid); err == nil && exist {
|
||||
return o.provider.Read(sid)
|
||||
} else if err != nil {
|
||||
return nil, fmt.Errorf("check if '%s' exist failed: %w", sid, err)
|
||||
}
|
||||
kv := make(map[any]any)
|
||||
kv["_old_uid"] = "0"
|
||||
@ -68,8 +70,8 @@ func (o *VirtualSessionProvider) Read(sid string) (session.RawStore, error) {
|
||||
}
|
||||
|
||||
// Exist returns true if session with given ID exists.
|
||||
func (o *VirtualSessionProvider) Exist(sid string) bool {
|
||||
return true
|
||||
func (o *VirtualSessionProvider) Exist(sid string) (bool, error) {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// Destroy deletes a session by session ID.
|
||||
@ -87,7 +89,7 @@ func (o *VirtualSessionProvider) Regenerate(oldsid, sid string) (session.RawStor
|
||||
}
|
||||
|
||||
// Count counts and returns number of sessions.
|
||||
func (o *VirtualSessionProvider) Count() int {
|
||||
func (o *VirtualSessionProvider) Count() (int, error) {
|
||||
o.lock.RLock()
|
||||
defer o.lock.RUnlock()
|
||||
return o.provider.Count()
|
||||
@ -162,9 +164,13 @@ func (s *VirtualStore) Release() error {
|
||||
// Now ensure that we don't exist!
|
||||
realProvider := s.p.provider
|
||||
|
||||
if !s.released && realProvider.Exist(s.sid) {
|
||||
// This is an error!
|
||||
return fmt.Errorf("new sid '%s' already exists", s.sid)
|
||||
if !s.released {
|
||||
if exist, err := realProvider.Exist(s.sid); err == nil && exist {
|
||||
// This is an error!
|
||||
return fmt.Errorf("new sid '%s' already exists", s.sid)
|
||||
} else if err != nil {
|
||||
return fmt.Errorf("check if '%s' exist failed: %w", s.sid, err)
|
||||
}
|
||||
}
|
||||
realStore, err := realProvider.Read(s.sid)
|
||||
if err != nil {
|
||||
|
@ -1805,6 +1805,10 @@ issues.dependency.add_error_dep_not_exist = Dependency does not exist.
|
||||
issues.dependency.add_error_dep_exists = Dependency already exists.
|
||||
issues.dependency.add_error_cannot_create_circular = You cannot create a dependency with two issues that block each other.
|
||||
issues.dependency.add_error_dep_not_same_repo = Both issues must be in the same repository.
|
||||
issues.dependency.blocking_count_1 = "This issue is blocking %d other issue."
|
||||
issues.dependency.blocking_count_n = "This issue is blocking %d other issues."
|
||||
issues.dependency.blocked_by_count_1 = "This issue is blocked by %d issue."
|
||||
issues.dependency.blocked_by_count_n = "This issue is blocked by %d issues."
|
||||
issues.review.self.approval = You cannot approve your own pull request.
|
||||
issues.review.self.rejection = You cannot request changes on your own pull request.
|
||||
issues.review.approve = "approved these changes %s"
|
||||
|
@ -44,9 +44,11 @@ func isRoutePathExpensive(routePattern string) bool {
|
||||
"/{username}/{reponame}/blame/",
|
||||
"/{username}/{reponame}/commit/",
|
||||
"/{username}/{reponame}/commits/",
|
||||
"/{username}/{reponame}/compare/",
|
||||
"/{username}/{reponame}/graph",
|
||||
"/{username}/{reponame}/media/",
|
||||
"/{username}/{reponame}/raw/",
|
||||
"/{username}/{reponame}/rss/branch/",
|
||||
"/{username}/{reponame}/src/",
|
||||
|
||||
// issue & PR related (no trailing slash)
|
||||
|
@ -107,8 +107,8 @@ func ForwardedHeadersHandler(limit int, trustedProxies []string) func(h http.Han
|
||||
return proxy.ForwardedHeaders(opt)
|
||||
}
|
||||
|
||||
func Sessioner() func(next http.Handler) http.Handler {
|
||||
return session.Sessioner(session.Options{
|
||||
func Sessioner() (func(next http.Handler) http.Handler, error) {
|
||||
middleware, err := session.Sessioner(session.Options{
|
||||
Provider: setting.SessionConfig.Provider,
|
||||
ProviderConfig: setting.SessionConfig.ProviderConfig,
|
||||
CookieName: setting.SessionConfig.CookieName,
|
||||
@ -119,4 +119,9 @@ func Sessioner() func(next http.Handler) http.Handler {
|
||||
SameSite: setting.SessionConfig.SameSite,
|
||||
Domain: setting.SessionConfig.Domain,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create session middleware: %w", err)
|
||||
}
|
||||
|
||||
return middleware, nil
|
||||
}
|
||||
|
@ -8,6 +8,7 @@ import (
|
||||
"html"
|
||||
"net/http"
|
||||
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/public"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/web"
|
||||
@ -23,7 +24,11 @@ func Routes() *web.Router {
|
||||
base.Methods("GET, HEAD", "/assets/*", public.FileHandlerFunc())
|
||||
|
||||
r := web.NewRouter()
|
||||
r.Use(common.Sessioner(), Contexter())
|
||||
if sessionMid, err := common.Sessioner(); err == nil && sessionMid != nil {
|
||||
r.Use(sessionMid, Contexter())
|
||||
} else {
|
||||
log.Fatal("common.Sessioner failed: %v", err)
|
||||
}
|
||||
r.Get("/", Install) // it must be on the root, because the "install.js" use the window.location to replace the "localhost" AppURL
|
||||
r.Post("/", web.Bind(forms.InstallForm{}), SubmitInstall)
|
||||
r.Get("/post-install", InstallDone)
|
||||
|
@ -654,6 +654,18 @@ func prepareIssueFilterAndList(ctx *context.Context, milestoneID, projectID int6
|
||||
return
|
||||
}
|
||||
|
||||
blockingCounts, err := issues.GetBlockingCount(ctx)
|
||||
if err != nil {
|
||||
ctx.ServerError("BlockingCounts", err)
|
||||
return
|
||||
}
|
||||
|
||||
blockedByCounts, err := issues.GetBlockedByCount(ctx)
|
||||
if err != nil {
|
||||
ctx.ServerError("BlockedByCounts", err)
|
||||
return
|
||||
}
|
||||
|
||||
if ctx.IsSigned {
|
||||
if err := issues.LoadIsRead(ctx, ctx.Doer.ID); err != nil {
|
||||
ctx.ServerError("LoadIsRead", err)
|
||||
@ -718,6 +730,21 @@ func prepareIssueFilterAndList(ctx *context.Context, milestoneID, projectID int6
|
||||
return 0
|
||||
}
|
||||
|
||||
ctx.Data["BlockingCounts"] = func(issueID int64) int64 {
|
||||
counts, ok := blockingCounts[issueID]
|
||||
if !ok {
|
||||
return 0
|
||||
}
|
||||
return counts
|
||||
}
|
||||
ctx.Data["BlockedByCounts"] = func(issueID int64) int64 {
|
||||
counts, ok := blockedByCounts[issueID]
|
||||
if !ok {
|
||||
return 0
|
||||
}
|
||||
return counts
|
||||
}
|
||||
|
||||
retrieveProjectsForIssueList(ctx, repo)
|
||||
if ctx.Written() {
|
||||
return
|
||||
|
@ -627,6 +627,33 @@ func buildIssueOverview(ctx *context.Context, unitType unit.Type) {
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
blockingCounts, err := issues.GetBlockingCount(ctx)
|
||||
if err != nil {
|
||||
ctx.ServerError("BlockingCounts", err)
|
||||
return
|
||||
}
|
||||
|
||||
blockedByCounts, err := issues.GetBlockedByCount(ctx)
|
||||
if err != nil {
|
||||
ctx.ServerError("BlockedByCounts", err)
|
||||
return
|
||||
}
|
||||
ctx.Data["BlockingCounts"] = func(issueID int64) int64 {
|
||||
counts, ok := blockingCounts[issueID]
|
||||
if !ok {
|
||||
return 0
|
||||
}
|
||||
return counts
|
||||
}
|
||||
ctx.Data["BlockedByCounts"] = func(issueID int64) int64 {
|
||||
counts, ok := blockedByCounts[issueID]
|
||||
if !ok {
|
||||
return 0
|
||||
}
|
||||
return counts
|
||||
}
|
||||
|
||||
ctx.Data["CommitLastStatus"] = lastStatus
|
||||
ctx.Data["CommitStatuses"] = commitStatuses
|
||||
ctx.Data["IssueStats"] = issueStats
|
||||
|
@ -279,6 +279,32 @@ func NotificationSubscriptions(ctx *context.Context) {
|
||||
return 0
|
||||
}
|
||||
|
||||
blockingCounts, err := issues.GetBlockingCount(ctx)
|
||||
if err != nil {
|
||||
ctx.ServerError("BlockingCounts", err)
|
||||
return
|
||||
}
|
||||
|
||||
blockedByCounts, err := issues.GetBlockedByCount(ctx)
|
||||
if err != nil {
|
||||
ctx.ServerError("BlockedByCounts", err)
|
||||
return
|
||||
}
|
||||
ctx.Data["BlockingCounts"] = func(issueID int64) int64 {
|
||||
counts, ok := blockingCounts[issueID]
|
||||
if !ok {
|
||||
return 0
|
||||
}
|
||||
return counts
|
||||
}
|
||||
ctx.Data["BlockedByCounts"] = func(issueID int64) int64 {
|
||||
counts, ok := blockedByCounts[issueID]
|
||||
if !ok {
|
||||
return 0
|
||||
}
|
||||
return counts
|
||||
}
|
||||
|
||||
ctx.Data["Status"] = 1
|
||||
ctx.Data["Title"] = ctx.Tr("notification.subscriptions")
|
||||
|
||||
|
@ -267,7 +267,11 @@ func Routes() *web.Router {
|
||||
routes.Get("/ssh_info", misc.SSHInfo)
|
||||
routes.Get("/api/healthz", healthcheck.Check)
|
||||
|
||||
mid = append(mid, common.Sessioner(), context.Contexter())
|
||||
if sessionMid, err := common.Sessioner(); err == nil && sessionMid != nil {
|
||||
mid = append(mid, sessionMid, context.Contexter())
|
||||
} else {
|
||||
log.Fatal("common.Sessioner failed: %v", err)
|
||||
}
|
||||
|
||||
// Get user from session if logged in.
|
||||
mid = append(mid, webAuth(buildAuthGroup()))
|
||||
|
@ -1,6 +1,10 @@
|
||||
<div id="issue-list" class="flex-list">
|
||||
{{$approvalCounts := .ApprovalCounts}}
|
||||
{{$blockedByCounts := .BlockedByCounts}}
|
||||
{{$blockingCounts := .BlockingCounts}}
|
||||
{{range .Issues}}
|
||||
{{$blockedByCount := call $blockedByCounts .ID}}
|
||||
{{$blockingCount := call $blockingCounts .ID}}
|
||||
<div class="flex-item">
|
||||
|
||||
<div class="flex-item-leading">
|
||||
@ -22,6 +26,22 @@
|
||||
{{template "repo/commit_statuses" dict "Status" (index $.CommitLastStatus .PullRequest.ID) "Statuses" (index $.CommitStatuses .PullRequest.ID)}}
|
||||
{{end}}
|
||||
{{end}}
|
||||
{{if gt $blockedByCount 0}}
|
||||
<div class="ui label label-blocking">
|
||||
<span data-tooltip-content="{{ctx.Locale.TrN $blockedByCount "repo.issues.dependency.blocked_by_count_1" "repo.issues.dependency.blocked_by_count_n" $blockedByCount}}" class="text red flex-text-block">
|
||||
{{svg "octicon-blocked" 16}}
|
||||
{{$blockedByCount}}
|
||||
</span>
|
||||
</div>
|
||||
{{end}}
|
||||
{{if and (gt $blockingCount 0) (not .IsClosed)}}
|
||||
<div class="ui label label-blocking">
|
||||
<span data-tooltip-content="{{ctx.Locale.TrN $blockingCount "repo.issues.dependency.blocking_count_1" "repo.issues.dependency.blocking_count_n" $blockingCount}}" class="text red flex-text-block">
|
||||
{{svg "octicon-report" 16}}
|
||||
{{$blockingCount}}
|
||||
</span>
|
||||
</div>
|
||||
{{end}}
|
||||
<span class="labels-list">
|
||||
{{range .Labels}}
|
||||
<a href="?q={{$.Keyword}}&type={{$.ViewType}}&state={{$.State}}&labels={{.ID}}{{if ne $.listType "milestone"}}&milestone={{$.MilestoneID}}{{end}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}{{if $.ShowArchivedLabels}}&archived=true{{end}}">{{ctx.RenderUtils.RenderLabel .}}</a>
|
||||
|
@ -1,7 +1,7 @@
|
||||
{
|
||||
"include": [
|
||||
"${configDir}/.*",
|
||||
"${configDir}/*",
|
||||
"${configDir}/.*",
|
||||
"${configDir}/tests/e2e/**/*",
|
||||
"${configDir}/tests/e2e/**/.*",
|
||||
"${configDir}/tools/**/*",
|
||||
@ -17,27 +17,31 @@
|
||||
"allowImportingTsExtensions": true,
|
||||
"allowJs": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"allowUnreachableCode": false,
|
||||
"allowUnusedLabels": false,
|
||||
"alwaysStrict": true,
|
||||
"erasableSyntaxOnly": true,
|
||||
"esModuleInterop": true,
|
||||
"exactOptionalPropertyTypes": false,
|
||||
"isolatedModules": true,
|
||||
"libReplacement": false,
|
||||
"noEmit": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"noImplicitAny": true,
|
||||
"noImplicitReturns": true,
|
||||
"noImplicitThis": true,
|
||||
"noPropertyAccessFromIndexSignature": false,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"resolveJsonModule": true,
|
||||
"skipLibCheck": true,
|
||||
"verbatimModuleSyntax": true,
|
||||
"stripInternal": true,
|
||||
"sourceMap": true,
|
||||
"strict": false,
|
||||
"strictBindCallApply": true,
|
||||
"strictBuiltinIteratorReturn": true,
|
||||
"strictFunctionTypes": true,
|
||||
"noImplicitAny": true,
|
||||
"noImplicitThis": true,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"noPropertyAccessFromIndexSignature": false,
|
||||
"exactOptionalPropertyTypes": false,
|
||||
"sourceMap": true,
|
||||
"stripInternal": true,
|
||||
"verbatimModuleSyntax": true,
|
||||
"types": [
|
||||
"vitest/globals",
|
||||
"./web_src/js/globals.d.ts",
|
||||
|
@ -56,3 +56,10 @@
|
||||
top: 10px;
|
||||
right: 5px;
|
||||
}
|
||||
|
||||
.label-blocking {
|
||||
border: 1px solid var(--color-secondary) !important;
|
||||
background: none transparent !important;
|
||||
margin-left: 1px;
|
||||
margin-right: 1px;
|
||||
}
|
||||
|
@ -10,7 +10,7 @@ export function initTableSort() {
|
||||
}
|
||||
|
||||
function tableSort(normSort: string, revSort: string, isDefault: string) {
|
||||
if (!normSort) return false;
|
||||
if (!normSort) return;
|
||||
if (!revSort) revSort = '';
|
||||
|
||||
const url = new URL(window.location.href);
|
||||
|
@ -43,7 +43,7 @@ type ToastOpts = {
|
||||
type ToastifyElement = HTMLElement & {_giteaToastifyInstance?: Toast};
|
||||
|
||||
/** See https://github.com/apvarun/toastify-js#api for options */
|
||||
function showToast(message: string, level: Intent, {gravity, position, duration, useHtmlBody, preventDuplicates = true, ...other}: ToastOpts = {}): Toast {
|
||||
function showToast(message: string, level: Intent, {gravity, position, duration, useHtmlBody, preventDuplicates = true, ...other}: ToastOpts = {}): Toast | null {
|
||||
const body = useHtmlBody ? message : htmlEscape(message);
|
||||
const parent = document.querySelector('.ui.dimmer.active') ?? document.body;
|
||||
const duplicateKey = preventDuplicates ? (preventDuplicates === true ? `${level}-${body}` : preventDuplicates) : '';
|
||||
@ -56,7 +56,7 @@ function showToast(message: string, level: Intent, {gravity, position, duration,
|
||||
showElem(toastDupNumEl);
|
||||
toastDupNumEl.textContent = String(Number(toastDupNumEl.textContent) + 1);
|
||||
animateOnce(toastDupNumEl, 'pulse-1p5-200');
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@ -83,15 +83,15 @@ function showToast(message: string, level: Intent, {gravity, position, duration,
|
||||
return toast;
|
||||
}
|
||||
|
||||
export function showInfoToast(message: string, opts?: ToastOpts): Toast {
|
||||
export function showInfoToast(message: string, opts?: ToastOpts): Toast | null {
|
||||
return showToast(message, 'info', opts);
|
||||
}
|
||||
|
||||
export function showWarningToast(message: string, opts?: ToastOpts): Toast {
|
||||
export function showWarningToast(message: string, opts?: ToastOpts): Toast | null {
|
||||
return showToast(message, 'warning', opts);
|
||||
}
|
||||
|
||||
export function showErrorToast(message: string, opts?: ToastOpts): Toast {
|
||||
export function showErrorToast(message: string, opts?: ToastOpts): Toast | null {
|
||||
return showToast(message, 'error', opts);
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user