2017-12-04 02:14:26 +03:00
// 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 (
"bytes"
"fmt"
2017-12-05 23:57:01 +03:00
"code.gitea.io/gitea/modules/setting"
2017-12-11 07:37:04 +03:00
"code.gitea.io/gitea/modules/util"
2017-12-05 23:57:01 +03:00
2017-12-04 02:14:26 +03:00
"github.com/go-xorm/builder"
"github.com/go-xorm/xorm"
)
// Reaction represents a reactions on issues and comments.
type Reaction struct {
2017-12-11 07:37:04 +03:00
ID int64 ` xorm:"pk autoincr" `
Type string ` xorm:"INDEX UNIQUE(s) NOT NULL" `
IssueID int64 ` xorm:"INDEX UNIQUE(s) NOT NULL" `
CommentID int64 ` xorm:"INDEX UNIQUE(s)" `
UserID int64 ` xorm:"INDEX UNIQUE(s) NOT NULL" `
User * User ` xorm:"-" `
CreatedUnix util . TimeStamp ` xorm:"INDEX created" `
2017-12-04 02:14:26 +03:00
}
// FindReactionsOptions describes the conditions to Find reactions
type FindReactionsOptions struct {
IssueID int64
CommentID int64
}
func ( opts * FindReactionsOptions ) toConds ( ) builder . Cond {
var cond = builder . NewCond ( )
if opts . IssueID > 0 {
cond = cond . And ( builder . Eq { "reaction.issue_id" : opts . IssueID } )
}
if opts . CommentID > 0 {
cond = cond . And ( builder . Eq { "reaction.comment_id" : opts . CommentID } )
}
return cond
}
func findReactions ( e Engine , opts FindReactionsOptions ) ( [ ] * Reaction , error ) {
reactions := make ( [ ] * Reaction , 0 , 10 )
sess := e . Where ( opts . toConds ( ) )
return reactions , sess .
Asc ( "reaction.issue_id" , "reaction.comment_id" , "reaction.created_unix" , "reaction.id" ) .
Find ( & reactions )
}
func createReaction ( e * xorm . Session , opts * ReactionOptions ) ( * Reaction , error ) {
reaction := & Reaction {
Type : opts . Type ,
UserID : opts . Doer . ID ,
IssueID : opts . Issue . ID ,
}
if opts . Comment != nil {
reaction . CommentID = opts . Comment . ID
}
if _ , err := e . Insert ( reaction ) ; err != nil {
return nil , err
}
return reaction , nil
}
// ReactionOptions defines options for creating or deleting reactions
type ReactionOptions struct {
Type string
Doer * User
Issue * Issue
Comment * Comment
}
// CreateReaction creates reaction for issue or comment.
func CreateReaction ( opts * ReactionOptions ) ( reaction * Reaction , err error ) {
sess := x . NewSession ( )
defer sess . Close ( )
if err = sess . Begin ( ) ; err != nil {
return nil , err
}
reaction , err = createReaction ( sess , opts )
if err != nil {
return nil , err
}
if err = sess . Commit ( ) ; err != nil {
return nil , err
}
return reaction , nil
}
// CreateIssueReaction creates a reaction on issue.
func CreateIssueReaction ( doer * User , issue * Issue , content string ) ( * Reaction , error ) {
return CreateReaction ( & ReactionOptions {
Type : content ,
Doer : doer ,
Issue : issue ,
} )
}
// CreateCommentReaction creates a reaction on comment.
func CreateCommentReaction ( doer * User , issue * Issue , comment * Comment , content string ) ( * Reaction , error ) {
return CreateReaction ( & ReactionOptions {
Type : content ,
Doer : doer ,
Issue : issue ,
Comment : comment ,
} )
}
func deleteReaction ( e * xorm . Session , opts * ReactionOptions ) error {
reaction := & Reaction {
Type : opts . Type ,
UserID : opts . Doer . ID ,
IssueID : opts . Issue . ID ,
}
if opts . Comment != nil {
reaction . CommentID = opts . Comment . ID
}
_ , err := e . Delete ( reaction )
return err
}
// DeleteReaction deletes reaction for issue or comment.
func DeleteReaction ( opts * ReactionOptions ) error {
sess := x . NewSession ( )
defer sess . Close ( )
if err := sess . Begin ( ) ; err != nil {
return err
}
if err := deleteReaction ( sess , opts ) ; err != nil {
return err
}
return sess . Commit ( )
}
// DeleteIssueReaction deletes a reaction on issue.
func DeleteIssueReaction ( doer * User , issue * Issue , content string ) error {
return DeleteReaction ( & ReactionOptions {
Type : content ,
Doer : doer ,
Issue : issue ,
} )
}
// DeleteCommentReaction deletes a reaction on comment.
func DeleteCommentReaction ( doer * User , issue * Issue , comment * Comment , content string ) error {
return DeleteReaction ( & ReactionOptions {
Type : content ,
Doer : doer ,
Issue : issue ,
Comment : comment ,
} )
}
// ReactionList represents list of reactions
type ReactionList [ ] * Reaction
// HasUser check if user has reacted
func ( list ReactionList ) HasUser ( userID int64 ) bool {
if userID == 0 {
return false
}
for _ , reaction := range list {
if reaction . UserID == userID {
return true
}
}
return false
}
// GroupByType returns reactions grouped by type
func ( list ReactionList ) GroupByType ( ) map [ string ] ReactionList {
var reactions = make ( map [ string ] ReactionList )
for _ , reaction := range list {
reactions [ reaction . Type ] = append ( reactions [ reaction . Type ] , reaction )
}
return reactions
}
func ( list ReactionList ) getUserIDs ( ) [ ] int64 {
userIDs := make ( map [ int64 ] struct { } , len ( list ) )
for _ , reaction := range list {
if _ , ok := userIDs [ reaction . UserID ] ; ! ok {
userIDs [ reaction . UserID ] = struct { } { }
}
}
return keysInt64 ( userIDs )
}
func ( list ReactionList ) loadUsers ( e Engine ) ( [ ] * User , error ) {
if len ( list ) == 0 {
return nil , nil
}
userIDs := list . getUserIDs ( )
userMaps := make ( map [ int64 ] * User , len ( userIDs ) )
err := e .
In ( "id" , userIDs ) .
Find ( & userMaps )
if err != nil {
return nil , fmt . Errorf ( "find user: %v" , err )
}
for _ , reaction := range list {
if user , ok := userMaps [ reaction . UserID ] ; ok {
reaction . User = user
} else {
reaction . User = NewGhostUser ( )
}
}
return valuesUser ( userMaps ) , nil
}
// LoadUsers loads reactions' all users
func ( list ReactionList ) LoadUsers ( ) ( [ ] * User , error ) {
return list . loadUsers ( x )
}
// GetFirstUsers returns first reacted user display names separated by comma
func ( list ReactionList ) GetFirstUsers ( ) string {
var buffer bytes . Buffer
var rem = setting . UI . ReactionMaxUserNum
for _ , reaction := range list {
if buffer . Len ( ) > 0 {
buffer . WriteString ( ", " )
}
buffer . WriteString ( reaction . User . DisplayName ( ) )
if rem -- ; rem == 0 {
break
}
}
return buffer . String ( )
}
// GetMoreUserCount returns count of not shown users in reaction tooltip
func ( list ReactionList ) GetMoreUserCount ( ) int {
if len ( list ) <= setting . UI . ReactionMaxUserNum {
return 0
}
return len ( list ) - setting . UI . ReactionMaxUserNum
}