[Notifications Step 6] Per issue/PR watch/unwatchfor-closed-social
@ -0,0 +1,15 @@ | |||
- | |||
id: 1 | |||
user_id: 1 | |||
issue_id: 1 | |||
is_watching: true | |||
created_unix: 946684800 | |||
updated_unix: 946684800 | |||
- | |||
id: 2 | |||
user_id: 2 | |||
issue_id: 2 | |||
is_watching: false | |||
created_unix: 946684800 | |||
updated_unix: 946684800 |
@ -0,0 +1,96 @@ | |||
// Copyright 2017 The Gitea Authors. All rights reserved. | |||
// Use of this source code is governed by a MIT-style | |||
// license that can be found in the LICENSE file. | |||
package models | |||
import ( | |||
"time" | |||
) | |||
// IssueWatch is connection request for receiving issue notification. | |||
type IssueWatch struct { | |||
ID int64 `xorm:"pk autoincr"` | |||
UserID int64 `xorm:"UNIQUE(watch) NOT NULL"` | |||
IssueID int64 `xorm:"UNIQUE(watch) NOT NULL"` | |||
IsWatching bool `xorm:"NOT NULL"` | |||
Created time.Time `xorm:"-"` | |||
CreatedUnix int64 `xorm:"NOT NULL"` | |||
Updated time.Time `xorm:"-"` | |||
UpdatedUnix int64 `xorm:"NOT NULL"` | |||
} | |||
// BeforeInsert is invoked from XORM before inserting an object of this type. | |||
func (iw *IssueWatch) BeforeInsert() { | |||
var ( | |||
t = time.Now() | |||
u = t.Unix() | |||
) | |||
iw.Created = t | |||
iw.CreatedUnix = u | |||
iw.Updated = t | |||
iw.UpdatedUnix = u | |||
} | |||
// BeforeUpdate is invoked from XORM before updating an object of this type. | |||
func (iw *IssueWatch) BeforeUpdate() { | |||
var ( | |||
t = time.Now() | |||
u = t.Unix() | |||
) | |||
iw.Updated = t | |||
iw.UpdatedUnix = u | |||
} | |||
// CreateOrUpdateIssueWatch set watching for a user and issue | |||
func CreateOrUpdateIssueWatch(userID, issueID int64, isWatching bool) error { | |||
iw, exists, err := getIssueWatch(x, userID, issueID) | |||
if err != nil { | |||
return err | |||
} | |||
if !exists { | |||
iw = &IssueWatch{ | |||
UserID: userID, | |||
IssueID: issueID, | |||
IsWatching: isWatching, | |||
} | |||
if _, err := x.Insert(iw); err != nil { | |||
return err | |||
} | |||
} else { | |||
iw.IsWatching = isWatching | |||
if _, err := x.Id(iw.ID).Cols("is_watching", "updated_unix").Update(iw); err != nil { | |||
return err | |||
} | |||
} | |||
return nil | |||
} | |||
// GetIssueWatch returns an issue watch by user and issue | |||
func GetIssueWatch(userID, issueID int64) (iw *IssueWatch, exists bool, err error) { | |||
return getIssueWatch(x, userID, issueID) | |||
} | |||
func getIssueWatch(e Engine, userID, issueID int64) (iw *IssueWatch, exists bool, err error) { | |||
iw = new(IssueWatch) | |||
exists, err = e. | |||
Where("user_id = ?", userID). | |||
And("issue_id = ?", issueID). | |||
Get(iw) | |||
return | |||
} | |||
// GetIssueWatchers returns watchers/unwatchers of a given issue | |||
func GetIssueWatchers(issueID int64) ([]*IssueWatch, error) { | |||
return getIssueWatchers(x, issueID) | |||
} | |||
func getIssueWatchers(e Engine, issueID int64) (watches []*IssueWatch, err error) { | |||
err = e. | |||
Where("issue_id = ?", issueID). | |||
Find(&watches) | |||
return | |||
} |
@ -0,0 +1,51 @@ | |||
// Copyright 2017 The Gitea Authors. All rights reserved. | |||
// Use of this source code is governed by a MIT-style | |||
// license that can be found in the LICENSE file. | |||
package models | |||
import ( | |||
"testing" | |||
"github.com/stretchr/testify/assert" | |||
) | |||
func TestCreateOrUpdateIssueWatch(t *testing.T) { | |||
assert.NoError(t, PrepareTestDatabase()) | |||
assert.NoError(t, CreateOrUpdateIssueWatch(3, 1, true)) | |||
iw := AssertExistsAndLoadBean(t, &IssueWatch{UserID: 3, IssueID: 1}).(*IssueWatch) | |||
assert.Equal(t, true, iw.IsWatching) | |||
assert.NoError(t, CreateOrUpdateIssueWatch(1, 1, false)) | |||
iw = AssertExistsAndLoadBean(t, &IssueWatch{UserID: 1, IssueID: 1}).(*IssueWatch) | |||
assert.Equal(t, false, iw.IsWatching) | |||
} | |||
func TestGetIssueWatch(t *testing.T) { | |||
assert.NoError(t, PrepareTestDatabase()) | |||
_, exists, err := GetIssueWatch(1, 1) | |||
assert.Equal(t, true, exists) | |||
assert.NoError(t, err) | |||
_, exists, err = GetIssueWatch(2, 2) | |||
assert.Equal(t, true, exists) | |||
assert.NoError(t, err) | |||
_, exists, err = GetIssueWatch(3, 1) | |||
assert.Equal(t, false, exists) | |||
assert.NoError(t, err) | |||
} | |||
func TestGetIssueWatchers(t *testing.T) { | |||
assert.NoError(t, PrepareTestDatabase()) | |||
iws, err := GetIssueWatchers(1) | |||
assert.NoError(t, err) | |||
assert.Equal(t, 1, len(iws)) | |||
iws, err = GetIssueWatchers(5) | |||
assert.NoError(t, err) | |||
assert.Equal(t, 0, len(iws)) | |||
} |
@ -0,0 +1,38 @@ | |||
// Copyright 2017 The Gitea Authors. All rights reserved. | |||
// Use of this source code is governed by a MIT-style | |||
// license that can be found in the LICENSE file. | |||
package repo | |||
import ( | |||
"fmt" | |||
"net/http" | |||
"strconv" | |||
"code.gitea.io/gitea/models" | |||
"code.gitea.io/gitea/modules/context" | |||
) | |||
// IssueWatch sets issue watching | |||
func IssueWatch(c *context.Context) { | |||
watch, err := strconv.ParseBool(c.Req.PostForm.Get("watch")) | |||
if err != nil { | |||
c.Handle(http.StatusInternalServerError, "watch is not bool", err) | |||
return | |||
} | |||
issueIndex := c.ParamsInt64("index") | |||
issue, err := models.GetIssueByIndex(c.Repo.Repository.ID, issueIndex) | |||
if err != nil { | |||
c.Handle(http.StatusInternalServerError, "GetIssueByIndex", err) | |||
return | |||
} | |||
if err := models.CreateOrUpdateIssueWatch(c.User.ID, issue.ID, watch); err != nil { | |||
c.Handle(http.StatusInternalServerError, "CreateOrUpdateIssueWatch", err) | |||
return | |||
} | |||
url := fmt.Sprintf("%s/issues/%d", c.Repo.RepoLink, issueIndex) | |||
c.Redirect(url, http.StatusSeeOther) | |||
} |