Compare commits

..

7 Commits

Author SHA1 Message Date
6543
77af0a23c4 Changelog v1.12.3 (#12356)
* Changelog v1.12.3

* better description for 12351

* @techknowlogick suggestions
2020-07-28 16:41:36 -04:00
Richard Mahn
87bfe02b5b Backport to v1.12 for #12341 - Release date fix (#12351)
* Backport for Issue #12341 PR #12343 - Release date fix

* Adds sleep for comparing times

* Fixes imports

* Fixes tests
2020-07-28 14:10:50 -04:00
techknowlogick
9bac656b7d Show 404 page when release not found (#12328) (#12332)
Signed-off-by: a1012112796 <1012112796@qq.com>

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>

Co-authored-by: 赵智超 <1012112796@qq.com>
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2020-07-27 00:39:44 +03:00
silverwind
ad68c9ccb2 Backport emoji fixes to 1.12 (#12327)
* Fix emoji detection in certain cases (#12320)

* Fix emoji detection certain cases

Previous tests weren't complicated enough so there were some situations where emojis were't detected properly. Find the earliest occurance in addition to checking for the longest combination.

Fixes #12312

* ok spell bot

Co-authored-by: Lauris BH <lauris@nix.lv>

* Reduce emoji size (#12317)

* Reduce emoji size

Rendering should now pretty much match GitHub with 1.25em. I verified
that emojis don't increase the line height and removed unecessary size
overrides because now all emojis should appear similar in relation to
the font size.

* fix reaction hover

Co-authored-by: mrsdizzie <info@mrsdizzie.com>
Co-authored-by: Lauris BH <lauris@nix.lv>
2020-07-25 12:50:57 -04:00
techknowlogick
8d1cd4d252 Fix double-indirection bug in logging IDs (#12294) (#12308)
This PR fixes a bug in log.NewColoredIDValue() which led to a double
indirection and incorrect IDs being printed out.

Signed-off-by: Andrew Thornton <art27@cantab.net>

Co-authored-by: zeripath <art27@cantab.net>
2020-07-24 02:24:22 +03:00
techknowlogick
64eaa2a942 [ui] Link to pr list page on sidebar when view pr (#12256) (#12263)
Fix #12254

Signed-off-by: a1012112796 <1012112796@qq.com>

Co-authored-by: 赵智超 <1012112796@qq.com>
2020-07-16 11:56:09 -04:00
zeripath
489e9162fc Extend Notifications API and return pinned notifications by default (#12164) (#12232)
Backport #12164

This PR extends the notifications API to allow specific notification statuses to be searched for and to allow setting of notifications to statuses other than read.

By default unread and pinned statuses will be returned when querying for notifications - however pinned statuses will not be marked as read.

Close #12152

Signed-off-by: Andrew Thornton art27@cantab.net
2020-07-13 21:52:05 +01:00
17 changed files with 404 additions and 50 deletions

View File

@@ -4,6 +4,17 @@ This changelog goes through all the changes that have been made in each release
without substantial changes to our git log; to see the highlights of what has
been added to each release, please refer to the [blog](https://blog.gitea.io).
## [1.12.3](https://github.com/go-gitea/gitea/releases/tag/v1.12.3) - 2020-07-28
* BUGFIXES
* Don't change creation date when updating Release (#12343) (#12351)
* Show 404 page when release not found (#12328) (#12332)
* Fix emoji detection in certain cases (#12320) (#12327)
* Reduce emoji size (#12317) (#12327)
* Fix double-indirection bug in logging IDs (#12294) (#12308)
* Link to pull list page on sidebar when view pr (#12256) (#12263)
* Extend Notifications API and return pinned notifications by default (#12164) (#12232)
## [1.12.2](https://github.com/go-gitea/gitea/releases/tag/v1.12.2) - 2020-07-11
* BUGFIXES

View File

@@ -55,7 +55,7 @@ func TestAPINotification(t *testing.T) {
assert.EqualValues(t, false, apiNL[2].Pinned)
// -- GET /repos/{owner}/{repo}/notifications --
req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/repos/%s/%s/notifications?token=%s", user2.Name, repo1.Name, token))
req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/repos/%s/%s/notifications?status-types=unread&token=%s", user2.Name, repo1.Name, token))
resp = session.MakeRequest(t, req, http.StatusOK)
DecodeJSON(t, resp, &apiNL)
@@ -92,7 +92,7 @@ func TestAPINotification(t *testing.T) {
assert.True(t, new.New > 0)
// -- mark notifications as read --
req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/notifications?token=%s", token))
req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/notifications?status-types=unread&token=%s", token))
resp = session.MakeRequest(t, req, http.StatusOK)
DecodeJSON(t, resp, &apiNL)
assert.Len(t, apiNL, 2)
@@ -101,7 +101,7 @@ func TestAPINotification(t *testing.T) {
req = NewRequest(t, "PUT", fmt.Sprintf("/api/v1/repos/%s/%s/notifications?last_read_at=%s&token=%s", user2.Name, repo1.Name, lastReadAt, token))
resp = session.MakeRequest(t, req, http.StatusResetContent)
req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/notifications?token=%s", token))
req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/notifications?status-types=unread&token=%s", token))
resp = session.MakeRequest(t, req, http.StatusOK)
DecodeJSON(t, resp, &apiNL)
assert.Len(t, apiNL, 1)

View File

@@ -59,7 +59,7 @@ func TestEventSourceManagerRun(t *testing.T) {
var apiNL []api.NotificationThread
// -- mark notifications as read --
req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/notifications?token=%s", token))
req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/notifications?status-types=unread&token=%s", token))
resp := session.MakeRequest(t, req, http.StatusOK)
DecodeJSON(t, resp, &apiNL)
@@ -69,7 +69,7 @@ func TestEventSourceManagerRun(t *testing.T) {
req = NewRequest(t, "PUT", fmt.Sprintf("/api/v1/repos/%s/%s/notifications?last_read_at=%s&token=%s", user2.Name, repo1.Name, lastReadAt, token))
resp = session.MakeRequest(t, req, http.StatusResetContent)
req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/notifications?token=%s", token))
req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/notifications?token=%s&status-types=unread", token))
resp = session.MakeRequest(t, req, http.StatusOK)
DecodeJSON(t, resp, &apiNL)
assert.Len(t, apiNL, 1)

View File

@@ -72,7 +72,7 @@ type FindNotificationOptions struct {
UserID int64
RepoID int64
IssueID int64
Status NotificationStatus
Status []NotificationStatus
UpdatedAfterUnix int64
UpdatedBeforeUnix int64
}
@@ -89,8 +89,8 @@ func (opts *FindNotificationOptions) ToCond() builder.Cond {
if opts.IssueID != 0 {
cond = cond.And(builder.Eq{"notification.issue_id": opts.IssueID})
}
if opts.Status != 0 {
cond = cond.And(builder.Eq{"notification.status": opts.Status})
if len(opts.Status) > 0 {
cond = cond.And(builder.In("notification.status", opts.Status))
}
if opts.UpdatedAfterUnix != 0 {
cond = cond.And(builder.Gte{"notification.updated_unix": opts.UpdatedAfterUnix})

View File

@@ -130,6 +130,8 @@ func ReplaceAliases(s string) string {
// FindEmojiSubmatchIndex returns index pair of longest emoji in a string
func FindEmojiSubmatchIndex(s string) []int {
loadMap()
found := make(map[int]int)
keys := make([]int, 0)
//see if there are any emoji in string before looking for position of specific ones
//no performance difference when there is a match but 10x faster when there are not
@@ -137,11 +139,26 @@ func FindEmojiSubmatchIndex(s string) []int {
return nil
}
// get index of first emoji occurrence while also checking for longest combination
for j := range GemojiData {
i := strings.Index(s, GemojiData[j].Emoji)
if i != -1 {
return []int{i, i + len(GemojiData[j].Emoji)}
if _, ok := found[i]; !ok {
if len(keys) == 0 || i < keys[0] {
found[i] = j
keys = []int{i}
}
if i == 0 {
break
}
}
}
}
if len(keys) > 0 {
index := keys[0]
return []int{index, index + len(GemojiData[found[index]].Emoji)}
}
return nil
}

View File

@@ -355,7 +355,7 @@ func NewColoredValueBytes(value interface{}, colorBytes *[]byte) *ColoredValue {
// The Value will be colored with FgCyan
// If a ColoredValue is provided it is not changed
func NewColoredIDValue(value interface{}) *ColoredValue {
return NewColoredValueBytes(&value, &fgCyanBytes)
return NewColoredValueBytes(value, &fgCyanBytes)
}
// Format will format the provided value and protect against ANSI color spoofing within the value

View File

@@ -266,6 +266,10 @@ func TestRender_emoji(t *testing.T) {
test(
"Some text with 😄😄 2 emoji next to each other",
`<p>Some text with <span class="emoji" aria-label="grinning face with smiling eyes">😄</span><span class="emoji" aria-label="grinning face with smiling eyes">😄</span> 2 emoji next to each other</p>`)
test(
"😎🤪🔐🤑❓",
`<p><span class="emoji" aria-label="smiling face with sunglasses">😎</span><span class="emoji" aria-label="zany face">🤪</span><span class="emoji" aria-label="locked with key">🔐</span><span class="emoji" aria-label="money-mouth face">🤑</span><span class="emoji" aria-label="question mark">❓</span></p>`)
// should match nothing
test(
"2001:0db8:85a3:0000:0000:8a2e:0370:7334",

View File

@@ -11,9 +11,37 @@ import (
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/routers/api/v1/utils"
)
func statusStringToNotificationStatus(status string) models.NotificationStatus {
switch strings.ToLower(strings.TrimSpace(status)) {
case "unread":
return models.NotificationStatusUnread
case "read":
return models.NotificationStatusRead
case "pinned":
return models.NotificationStatusPinned
default:
return 0
}
}
func statusStringsToNotificationStatuses(statuses []string, defaultStatuses []string) []models.NotificationStatus {
if len(statuses) == 0 {
statuses = defaultStatuses
}
results := make([]models.NotificationStatus, 0, len(statuses))
for _, status := range statuses {
notificationStatus := statusStringToNotificationStatus(status)
if notificationStatus > 0 {
results = append(results, notificationStatus)
}
}
return results
}
// ListRepoNotifications list users's notification threads on a specific repo
func ListRepoNotifications(ctx *context.APIContext) {
// swagger:operation GET /repos/{owner}/{repo}/notifications notification notifyGetRepoList
@@ -39,6 +67,14 @@ func ListRepoNotifications(ctx *context.APIContext) {
// description: If true, show notifications marked as read. Default value is false
// type: string
// required: false
// - name: status-types
// in: query
// description: "Show notifications with the provided status types. Options are: unread, read and/or pinned. Defaults to unread & pinned"
// type: array
// collectionFormat: multi
// items:
// type: string
// required: false
// - name: since
// in: query
// description: Only show notifications updated after the given time. This is a timestamp in RFC 3339 format
@@ -75,9 +111,10 @@ func ListRepoNotifications(ctx *context.APIContext) {
UpdatedBeforeUnix: before,
UpdatedAfterUnix: since,
}
qAll := strings.Trim(ctx.Query("all"), " ")
if qAll != "true" {
opts.Status = models.NotificationStatusUnread
if !ctx.QueryBool("all") {
statuses := ctx.QueryStrings("status-types")
opts.Status = statusStringsToNotificationStatuses(statuses, []string{"unread", "pinned"})
}
nl, err := models.GetNotifications(opts)
if err != nil {
@@ -97,7 +134,7 @@ func ListRepoNotifications(ctx *context.APIContext) {
func ReadRepoNotifications(ctx *context.APIContext) {
// swagger:operation PUT /repos/{owner}/{repo}/notifications notification notifyReadRepoList
// ---
// summary: Mark notification threads as read on a specific repo
// summary: Mark notification threads as read, pinned or unread on a specific repo
// consumes:
// - application/json
// produces:
@@ -113,6 +150,24 @@ func ReadRepoNotifications(ctx *context.APIContext) {
// description: name of the repo
// type: string
// required: true
// - name: all
// in: query
// description: If true, mark all notifications on this repo. Default value is false
// type: string
// required: false
// - name: status-types
// in: query
// description: "Mark notifications with the provided status types. Options are: unread, read and/or pinned. Defaults to unread."
// type: array
// collectionFormat: multi
// items:
// type: string
// required: false
// - name: to-status
// in: query
// description: Status to mark notifications as. Defaults to read.
// type: string
// required: false
// - name: last_read_at
// in: query
// description: Describes the last point that notifications were checked. Anything updated since this time will not be updated.
@@ -135,11 +190,17 @@ func ReadRepoNotifications(ctx *context.APIContext) {
lastRead = tmpLastRead.Unix()
}
}
opts := models.FindNotificationOptions{
UserID: ctx.User.ID,
RepoID: ctx.Repo.Repository.ID,
UpdatedBeforeUnix: lastRead,
Status: models.NotificationStatusUnread,
}
if !ctx.QueryBool("all") {
statuses := ctx.QueryStrings("status-types")
opts.Status = statusStringsToNotificationStatuses(statuses, []string{"unread"})
log.Error("%v", opts.Status)
}
nl, err := models.GetNotifications(opts)
if err != nil {
@@ -147,8 +208,13 @@ func ReadRepoNotifications(ctx *context.APIContext) {
return
}
targetStatus := statusStringToNotificationStatus(ctx.Query("to-status"))
if targetStatus == 0 {
targetStatus = models.NotificationStatusRead
}
for _, n := range nl {
err := models.SetNotificationStatus(n.ID, ctx.User, models.NotificationStatusRead)
err := models.SetNotificationStatus(n.ID, ctx.User, targetStatus)
if err != nil {
ctx.InternalServerError(err)
return

View File

@@ -62,6 +62,12 @@ func ReadThread(ctx *context.APIContext) {
// description: id of notification thread
// type: string
// required: true
// - name: to-status
// in: query
// description: Status to mark notifications as
// type: string
// default: read
// required: false
// responses:
// "205":
// "$ref": "#/responses/empty"
@@ -75,7 +81,12 @@ func ReadThread(ctx *context.APIContext) {
return
}
err := models.SetNotificationStatus(n.ID, ctx.User, models.NotificationStatusRead)
targetStatus := statusStringToNotificationStatus(ctx.Query("to-status"))
if targetStatus == 0 {
targetStatus = models.NotificationStatusRead
}
err := models.SetNotificationStatus(n.ID, ctx.User, targetStatus)
if err != nil {
ctx.InternalServerError(err)
return

View File

@@ -29,6 +29,14 @@ func ListNotifications(ctx *context.APIContext) {
// description: If true, show notifications marked as read. Default value is false
// type: string
// required: false
// - name: status-types
// in: query
// description: "Show notifications with the provided status types. Options are: unread, read and/or pinned. Defaults to unread & pinned."
// type: array
// collectionFormat: multi
// items:
// type: string
// required: false
// - name: since
// in: query
// description: Only show notifications updated after the given time. This is a timestamp in RFC 3339 format
@@ -64,9 +72,9 @@ func ListNotifications(ctx *context.APIContext) {
UpdatedBeforeUnix: before,
UpdatedAfterUnix: since,
}
qAll := strings.Trim(ctx.Query("all"), " ")
if qAll != "true" {
opts.Status = models.NotificationStatusUnread
if !ctx.QueryBool("all") {
statuses := ctx.QueryStrings("status-types")
opts.Status = statusStringsToNotificationStatuses(statuses, []string{"unread", "pinned"})
}
nl, err := models.GetNotifications(opts)
if err != nil {
@@ -82,11 +90,11 @@ func ListNotifications(ctx *context.APIContext) {
ctx.JSON(http.StatusOK, nl.APIFormat())
}
// ReadNotifications mark notification threads as read
// ReadNotifications mark notification threads as read, unread, or pinned
func ReadNotifications(ctx *context.APIContext) {
// swagger:operation PUT /notifications notification notifyReadList
// ---
// summary: Mark notification threads as read
// summary: Mark notification threads as read, pinned or unread
// consumes:
// - application/json
// produces:
@@ -98,6 +106,24 @@ func ReadNotifications(ctx *context.APIContext) {
// type: string
// format: date-time
// required: false
// - name: all
// in: query
// description: If true, mark all notifications on this repo. Default value is false
// type: string
// required: false
// - name: status-types
// in: query
// description: "Mark notifications with the provided status types. Options are: unread, read and/or pinned. Defaults to unread."
// type: array
// collectionFormat: multi
// items:
// type: string
// required: false
// - name: to-status
// in: query
// description: Status to mark notifications as, Defaults to read.
// type: string
// required: false
// responses:
// "205":
// "$ref": "#/responses/empty"
@@ -117,7 +143,10 @@ func ReadNotifications(ctx *context.APIContext) {
opts := models.FindNotificationOptions{
UserID: ctx.User.ID,
UpdatedBeforeUnix: lastRead,
Status: models.NotificationStatusUnread,
}
if !ctx.QueryBool("all") {
statuses := ctx.QueryStrings("status-types")
opts.Status = statusStringsToNotificationStatuses(statuses, []string{"unread"})
}
nl, err := models.GetNotifications(opts)
if err != nil {
@@ -125,8 +154,13 @@ func ReadNotifications(ctx *context.APIContext) {
return
}
targetStatus := statusStringToNotificationStatus(ctx.Query("to-status"))
if targetStatus == 0 {
targetStatus = models.NotificationStatusRead
}
for _, n := range nl {
err := models.SetNotificationStatus(n.ID, ctx.User, models.NotificationStatusRead)
err := models.SetNotificationStatus(n.ID, ctx.User, targetStatus)
if err != nil {
ctx.InternalServerError(err)
return

View File

@@ -134,6 +134,10 @@ func SingleRelease(ctx *context.Context) {
release, err := models.GetRelease(ctx.Repo.Repository.ID, ctx.Params("tag"))
if err != nil {
if models.IsErrReleaseNotExist(err) {
ctx.NotFound("GetRelease", err)
return
}
ctx.ServerError("GetReleasesByRepoID", err)
return
}

View File

@@ -46,6 +46,7 @@ func createTag(gitRepo *git.Repository, rel *models.Release) error {
rel.Publisher, rel.Repo, git.TagPrefix+rel.TagName,
git.EmptySHA, commit.ID.String(), repository.NewPushCommits())
notification.NotifyCreateRef(rel.Publisher, rel.Repo, "tag", git.TagPrefix+rel.TagName)
rel.CreatedUnix = timeutil.TimeStampNow()
}
commit, err := gitRepo.GetTagCommit(rel.TagName)
if err != nil {
@@ -53,7 +54,6 @@ func createTag(gitRepo *git.Repository, rel *models.Release) error {
}
rel.Sha1 = commit.ID.String()
rel.CreatedUnix = timeutil.TimeStampNow()
rel.NumCommits, err = commit.CommitsCount()
if err != nil {
return fmt.Errorf("CommitsCount: %v", err)

View File

@@ -7,6 +7,7 @@ package release
import (
"path/filepath"
"testing"
"time"
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/git"
@@ -101,3 +102,153 @@ func TestRelease_Create(t *testing.T) {
IsTag: true,
}, nil))
}
func TestRelease_Update(t *testing.T) {
assert.NoError(t, models.PrepareTestDatabase())
user := models.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User)
repo := models.AssertExistsAndLoadBean(t, &models.Repository{ID: 1}).(*models.Repository)
repoPath := models.RepoPath(user.Name, repo.Name)
gitRepo, err := git.OpenRepository(repoPath)
assert.NoError(t, err)
defer gitRepo.Close()
// Test a changed release
assert.NoError(t, CreateRelease(gitRepo, &models.Release{
RepoID: repo.ID,
PublisherID: user.ID,
TagName: "v1.1.1",
Target: "master",
Title: "v1.1.1 is released",
Note: "v1.1.1 is released",
IsDraft: false,
IsPrerelease: false,
IsTag: false,
}, nil))
release, err := models.GetRelease(repo.ID, "v1.1.1")
assert.NoError(t, err)
releaseCreatedUnix := release.CreatedUnix
time.Sleep(2 * time.Second) // sleep 2 seconds to ensure a different timestamp
release.Note = "Changed note"
assert.NoError(t, UpdateRelease(user, gitRepo, release, nil))
release, err = models.GetReleaseByID(release.ID)
assert.NoError(t, err)
assert.Equal(t, int64(releaseCreatedUnix), int64(release.CreatedUnix))
// Test a changed draft
assert.NoError(t, CreateRelease(gitRepo, &models.Release{
RepoID: repo.ID,
PublisherID: user.ID,
TagName: "v1.2.1",
Target: "65f1bf2",
Title: "v1.2.1 is draft",
Note: "v1.2.1 is draft",
IsDraft: true,
IsPrerelease: false,
IsTag: false,
}, nil))
release, err = models.GetRelease(repo.ID, "v1.2.1")
assert.NoError(t, err)
releaseCreatedUnix = release.CreatedUnix
time.Sleep(2 * time.Second) // sleep 2 seconds to ensure a different timestamp
release.Title = "Changed title"
assert.NoError(t, UpdateRelease(user, gitRepo, release, nil))
release, err = models.GetReleaseByID(release.ID)
assert.NoError(t, err)
assert.Less(t, int64(releaseCreatedUnix), int64(release.CreatedUnix))
// Test a changed pre-release
assert.NoError(t, CreateRelease(gitRepo, &models.Release{
RepoID: repo.ID,
PublisherID: user.ID,
TagName: "v1.3.1",
Target: "65f1bf2",
Title: "v1.3.1 is pre-released",
Note: "v1.3.1 is pre-released",
IsDraft: false,
IsPrerelease: true,
IsTag: false,
}, nil))
release, err = models.GetRelease(repo.ID, "v1.3.1")
assert.NoError(t, err)
releaseCreatedUnix = release.CreatedUnix
time.Sleep(2 * time.Second) // sleep 2 seconds to ensure a different timestamp
release.Title = "Changed title"
release.Note = "Changed note"
assert.NoError(t, UpdateRelease(user, gitRepo, release, nil))
release, err = models.GetReleaseByID(release.ID)
assert.NoError(t, err)
assert.Equal(t, int64(releaseCreatedUnix), int64(release.CreatedUnix))
}
func TestRelease_createTag(t *testing.T) {
assert.NoError(t, models.PrepareTestDatabase())
user := models.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User)
repo := models.AssertExistsAndLoadBean(t, &models.Repository{ID: 1}).(*models.Repository)
repoPath := models.RepoPath(user.Name, repo.Name)
gitRepo, err := git.OpenRepository(repoPath)
assert.NoError(t, err)
defer gitRepo.Close()
// Test a changed release
release := &models.Release{
RepoID: repo.ID,
PublisherID: user.ID,
TagName: "v2.1.1",
Target: "master",
Title: "v2.1.1 is released",
Note: "v2.1.1 is released",
IsDraft: false,
IsPrerelease: false,
IsTag: false,
}
assert.NoError(t, createTag(gitRepo, release))
assert.NotEmpty(t, release.CreatedUnix)
releaseCreatedUnix := release.CreatedUnix
time.Sleep(2 * time.Second) // sleep 2 seconds to ensure a different timestamp
release.Note = "Changed note"
assert.NoError(t, createTag(gitRepo, release))
assert.Equal(t, int64(releaseCreatedUnix), int64(release.CreatedUnix))
// Test a changed draft
release = &models.Release{
RepoID: repo.ID,
PublisherID: user.ID,
TagName: "v2.2.1",
Target: "65f1bf2",
Title: "v2.2.1 is draft",
Note: "v2.2.1 is draft",
IsDraft: true,
IsPrerelease: false,
IsTag: false,
}
assert.NoError(t, createTag(gitRepo, release))
releaseCreatedUnix = release.CreatedUnix
time.Sleep(2 * time.Second) // sleep 2 seconds to ensure a different timestamp
release.Title = "Changed title"
assert.NoError(t, createTag(gitRepo, release))
assert.Less(t, int64(releaseCreatedUnix), int64(release.CreatedUnix))
// Test a changed pre-release
release = &models.Release{
RepoID: repo.ID,
PublisherID: user.ID,
TagName: "v2.3.1",
Target: "65f1bf2",
Title: "v2.3.1 is pre-released",
Note: "v2.3.1 is pre-released",
IsDraft: false,
IsPrerelease: true,
IsTag: false,
}
assert.NoError(t, createTag(gitRepo, release))
releaseCreatedUnix = release.CreatedUnix
time.Sleep(2 * time.Second) // sleep 2 seconds to ensure a different timestamp
release.Title = "Changed title"
release.Note = "Changed note"
assert.NoError(t, createTag(gitRepo, release))
assert.Equal(t, int64(releaseCreatedUnix), int64(release.CreatedUnix))
}

View File

@@ -128,12 +128,12 @@
<span class="no-select item {{if .HasSelectedLabel}}hide{{end}}">{{.i18n.Tr "repo.issues.new.no_label"}}</span>
{{range .Labels}}
<div class="item">
<a class="ui label {{if not .IsChecked}}hide{{end}}" id="label_{{.ID}}" href="{{$.RepoLink}}/issues?labels={{.ID}}" style="color: {{.ForegroundColor}}; background-color: {{.Color}}" title="{{.Description | RenderEmojiPlain}}">{{.Name | RenderEmoji}}</a>
<a class="ui label {{if not .IsChecked}}hide{{end}}" id="label_{{.ID}}" href="{{$.RepoLink}}/{{if $.Issue.IsPull}}pulls{{else}}issues{{end}}?labels={{.ID}}" style="color: {{.ForegroundColor}}; background-color: {{.Color}}" title="{{.Description | RenderEmojiPlain}}">{{.Name | RenderEmoji}}</a>
</div>
{{end}}
{{range .OrgLabels}}
<div class="item">
<a class="ui label {{if not .IsChecked}}hide{{end}}" id="label_{{.ID}}" href="{{$.RepoLink}}/issues?labels={{.ID}}" style="color: {{.ForegroundColor}}; background-color: {{.Color}}" title="{{.Description | RenderEmojiPlain}}">{{.Name | RenderEmoji}}</a>
<a class="ui label {{if not .IsChecked}}hide{{end}}" id="label_{{.ID}}" href="{{$.RepoLink}}/{{if $.Issue.IsPull}}pulls{{else}}issues{{end}}?labels={{.ID}}" style="color: {{.ForegroundColor}}; background-color: {{.Color}}" title="{{.Description | RenderEmojiPlain}}">{{.Name | RenderEmoji}}</a>
</div>
{{end}}
@@ -238,7 +238,7 @@
<div class="selected">
{{range .Issue.Assignees}}
<div class="item" style="margin-bottom: 10px;">
<a href="{{$.RepoLink}}/issues?assignee={{.ID}}"><img class="ui avatar image" src="{{.RelAvatarLink}}">&nbsp;{{.GetDisplayName}}</a>
<a href="{{$.RepoLink}}/{{if $.Issue.IsPull}}pulls{{else}}issues{{end}}?assignee={{.ID}}"><img class="ui avatar image" src="{{.RelAvatarLink}}">&nbsp;{{.GetDisplayName}}</a>
</div>
{{end}}
</div>

View File

@@ -459,6 +459,16 @@
"name": "all",
"in": "query"
},
{
"type": "array",
"items": {
"type": "string"
},
"collectionFormat": "multi",
"description": "Show notifications with the provided status types. Options are: unread, read and/or pinned. Defaults to unread \u0026 pinned.",
"name": "status-types",
"in": "query"
},
{
"type": "string",
"format": "date-time",
@@ -502,7 +512,7 @@
"tags": [
"notification"
],
"summary": "Mark notification threads as read",
"summary": "Mark notification threads as read, pinned or unread",
"operationId": "notifyReadList",
"parameters": [
{
@@ -511,6 +521,28 @@
"description": "Describes the last point that notifications were checked. Anything updated since this time will not be updated.",
"name": "last_read_at",
"in": "query"
},
{
"type": "string",
"description": "If true, mark all notifications on this repo. Default value is false",
"name": "all",
"in": "query"
},
{
"type": "array",
"items": {
"type": "string"
},
"collectionFormat": "multi",
"description": "Mark notifications with the provided status types. Options are: unread, read and/or pinned. Defaults to unread.",
"name": "status-types",
"in": "query"
},
{
"type": "string",
"description": "Status to mark notifications as, Defaults to read.",
"name": "to-status",
"in": "query"
}
],
"responses": {
@@ -587,6 +619,13 @@
"name": "id",
"in": "path",
"required": true
},
{
"type": "string",
"default": "read",
"description": "Status to mark notifications as",
"name": "to-status",
"in": "query"
}
],
"responses": {
@@ -6290,6 +6329,16 @@
"name": "all",
"in": "query"
},
{
"type": "array",
"items": {
"type": "string"
},
"collectionFormat": "multi",
"description": "Show notifications with the provided status types. Options are: unread, read and/or pinned. Defaults to unread \u0026 pinned",
"name": "status-types",
"in": "query"
},
{
"type": "string",
"format": "date-time",
@@ -6333,7 +6382,7 @@
"tags": [
"notification"
],
"summary": "Mark notification threads as read on a specific repo",
"summary": "Mark notification threads as read, pinned or unread on a specific repo",
"operationId": "notifyReadRepoList",
"parameters": [
{
@@ -6350,6 +6399,28 @@
"in": "path",
"required": true
},
{
"type": "string",
"description": "If true, mark all notifications on this repo. Default value is false",
"name": "all",
"in": "query"
},
{
"type": "array",
"items": {
"type": "string"
},
"collectionFormat": "multi",
"description": "Mark notifications with the provided status types. Options are: unread, read and/or pinned. Defaults to unread.",
"name": "status-types",
"in": "query"
},
{
"type": "string",
"description": "Status to mark notifications as. Defaults to read.",
"name": "to-status",
"in": "query"
},
{
"type": "string",
"format": "date-time",

View File

@@ -1271,33 +1271,18 @@ i.icon.centerlock {
.emoji,
.reaction {
font-size: 1.5em;
line-height: 1.2;
font-size: 1.25em;
line-height: 1;
font-style: normal !important;
font-weight: normal !important;
vertical-align: middle;
vertical-align: -.075em;
}
#issue-title > .emoji {
font-size: 1em;
}
.commit-summary > .emoji {
font-size: 1em;
}
.label > .emoji {
font-size: 1em;
}
.dropdown .emoji {
font-size: 1em;
}
.emoji img,
.reaction img {
border-width: 0 !important;
margin: 0 !important;
width: 1em !important;
height: 1em !important;
vertical-align: middle !important;
vertical-align: -.15em;
}

View File

@@ -2246,7 +2246,7 @@
.select-reaction {
display: flex;
align-items: center;
padding: .5rem;
padding: 0 .5rem;
&:not(.active) a {
display: none;