PLT-1378 Initial version of emoji reactions (#4520)
* Refactored emoji.json to support multiple aliases and emoji categories * Added custom category to emoji.jsx and stabilized all fields * Removed conflicting aliases for :mattermost: and :ca: * fixup after store changes * Added emoji reactions * Removed reactions for an emoji when that emoji is deleted * Fixed incorrect test case * Renamed ReactionList to ReactionListView * Fixed 👍 and 👎 not showing up as possible reactions * Removed text emoticons from emoji reaction autocomplete * Changed emoji reactions to be sorted by the order that they were first created * Set a maximum number of listeners for the ReactionStore * Removed unused code from Textbox component * Fixed reaction permissions * Changed error code when trying to modify reactions for another user * Fixed merge conflicts * Properly applied theme colours to reactions * Fixed ESLint and gofmt errors * Fixed ReactionListContainer to properly update when its post prop changes * Removed unnecessary escape characters from reaction regexes * Shared reaction message pattern between CreatePost and CreateComment * Removed an unnecessary select query when saving a reaction * Changed reactions route to be under /reactions * Fixed copyright dates on newly added files * Removed debug code that prevented all unit tests from being ran * Cleaned up unnecessary code for reactions * Renamed ReactionStore.List to ReactionStore.GetForPost
Этот коммит содержится в:
коммит произвёл
GitHub
родитель
2bf0342d13
Коммит
165ad0d4f7
@@ -103,6 +103,7 @@ func InitApi() {
|
||||
InitEmoji()
|
||||
InitStatus()
|
||||
InitWebrtc()
|
||||
InitReaction()
|
||||
InitDeprecated()
|
||||
|
||||
// 404 on any api route before web.go has a chance to serve it
|
||||
|
||||
13
api/emoji.go
13
api/emoji.go
@@ -209,11 +209,14 @@ func deleteEmoji(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
var emoji *model.Emoji
|
||||
if result := <-Srv.Store.Emoji().Get(id); result.Err != nil {
|
||||
c.Err = result.Err
|
||||
return
|
||||
} else {
|
||||
if c.Session.UserId != result.Data.(*model.Emoji).CreatorId && !HasPermissionToContext(c, model.PERMISSION_MANAGE_SYSTEM) {
|
||||
emoji = result.Data.(*model.Emoji)
|
||||
|
||||
if c.Session.UserId != emoji.CreatorId && !HasPermissionToContext(c, model.PERMISSION_MANAGE_SYSTEM) {
|
||||
c.Err = model.NewLocAppError("deleteEmoji", "api.emoji.delete.permissions.app_error", nil, "user_id="+c.Session.UserId)
|
||||
c.Err.StatusCode = http.StatusUnauthorized
|
||||
return
|
||||
@@ -226,6 +229,7 @@ func deleteEmoji(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
go deleteEmojiImage(id)
|
||||
go deleteReactionsForEmoji(emoji.Name)
|
||||
|
||||
ReturnStatusOK(w)
|
||||
}
|
||||
@@ -236,6 +240,13 @@ func deleteEmojiImage(id string) {
|
||||
}
|
||||
}
|
||||
|
||||
func deleteReactionsForEmoji(emojiName string) {
|
||||
if result := <-Srv.Store.Reaction().DeleteAllWithEmojiName(emojiName); result.Err != nil {
|
||||
l4g.Warn(utils.T("api.emoji.delete.delete_reactions.app_error"), emojiName)
|
||||
l4g.Warn(result.Err)
|
||||
}
|
||||
}
|
||||
|
||||
func getEmojiImage(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
if !*utils.Cfg.ServiceSettings.EnableCustomEmoji {
|
||||
c.Err = model.NewLocAppError("getEmojiImage", "api.emoji.disabled.app_error", nil, "")
|
||||
|
||||
203
api/reaction.go
Обычный файл
203
api/reaction.go
Обычный файл
@@ -0,0 +1,203 @@
|
||||
// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
l4g "github.com/alecthomas/log4go"
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/mattermost/platform/model"
|
||||
"github.com/mattermost/platform/utils"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func InitReaction() {
|
||||
l4g.Debug(utils.T("api.reaction.init.debug"))
|
||||
|
||||
BaseRoutes.NeedPost.Handle("/reactions/save", ApiUserRequired(saveReaction)).Methods("POST")
|
||||
BaseRoutes.NeedPost.Handle("/reactions/delete", ApiUserRequired(deleteReaction)).Methods("POST")
|
||||
BaseRoutes.NeedPost.Handle("/reactions", ApiUserRequired(listReactions)).Methods("GET")
|
||||
}
|
||||
|
||||
func saveReaction(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
reaction := model.ReactionFromJson(r.Body)
|
||||
if reaction == nil {
|
||||
c.SetInvalidParam("saveReaction", "reaction")
|
||||
return
|
||||
}
|
||||
|
||||
if reaction.UserId != c.Session.UserId {
|
||||
c.Err = model.NewLocAppError("saveReaction", "api.reaction.save_reaction.user_id.app_error", nil, "")
|
||||
c.Err.StatusCode = http.StatusForbidden
|
||||
return
|
||||
}
|
||||
|
||||
params := mux.Vars(r)
|
||||
|
||||
channelId := params["channel_id"]
|
||||
if len(channelId) != 26 {
|
||||
c.SetInvalidParam("saveReaction", "channelId")
|
||||
return
|
||||
}
|
||||
|
||||
if !HasPermissionToChannelContext(c, channelId, model.PERMISSION_READ_CHANNEL) {
|
||||
return
|
||||
}
|
||||
|
||||
postId := params["post_id"]
|
||||
if len(postId) != 26 || postId != reaction.PostId {
|
||||
c.SetInvalidParam("saveReaction", "postId")
|
||||
return
|
||||
}
|
||||
|
||||
pchan := Srv.Store.Post().Get(reaction.PostId)
|
||||
|
||||
var postHadReactions bool
|
||||
if result := <-pchan; result.Err != nil {
|
||||
c.Err = result.Err
|
||||
return
|
||||
} else if post := result.Data.(*model.PostList).Posts[postId]; post.ChannelId != channelId {
|
||||
c.Err = model.NewLocAppError("saveReaction", "api.reaction.save_reaction.mismatched_channel_id.app_error",
|
||||
nil, "channelId="+channelId+", post.ChannelId="+post.ChannelId+", postId="+postId)
|
||||
c.Err.StatusCode = http.StatusBadRequest
|
||||
return
|
||||
} else {
|
||||
postHadReactions = post.HasReactions
|
||||
}
|
||||
|
||||
if result := <-Srv.Store.Reaction().Save(reaction); result.Err != nil {
|
||||
c.Err = result.Err
|
||||
return
|
||||
} else {
|
||||
go sendReactionEvent(model.WEBSOCKET_EVENT_REACTION_ADDED, channelId, reaction, postHadReactions)
|
||||
|
||||
reaction := result.Data.(*model.Reaction)
|
||||
|
||||
w.Write([]byte(reaction.ToJson()))
|
||||
}
|
||||
}
|
||||
|
||||
func deleteReaction(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
reaction := model.ReactionFromJson(r.Body)
|
||||
if reaction == nil {
|
||||
c.SetInvalidParam("deleteReaction", "reaction")
|
||||
return
|
||||
}
|
||||
|
||||
if reaction.UserId != c.Session.UserId {
|
||||
c.Err = model.NewLocAppError("deleteReaction", "api.reaction.delete_reaction.user_id.app_error", nil, "")
|
||||
c.Err.StatusCode = http.StatusForbidden
|
||||
return
|
||||
}
|
||||
|
||||
params := mux.Vars(r)
|
||||
|
||||
channelId := params["channel_id"]
|
||||
if len(channelId) != 26 {
|
||||
c.SetInvalidParam("deleteReaction", "channelId")
|
||||
return
|
||||
}
|
||||
|
||||
if !HasPermissionToChannelContext(c, channelId, model.PERMISSION_READ_CHANNEL) {
|
||||
return
|
||||
}
|
||||
|
||||
postId := params["post_id"]
|
||||
if len(postId) != 26 || postId != reaction.PostId {
|
||||
c.SetInvalidParam("deleteReaction", "postId")
|
||||
return
|
||||
}
|
||||
|
||||
pchan := Srv.Store.Post().Get(reaction.PostId)
|
||||
|
||||
var postHadReactions bool
|
||||
if result := <-pchan; result.Err != nil {
|
||||
c.Err = result.Err
|
||||
return
|
||||
} else if post := result.Data.(*model.PostList).Posts[postId]; post.ChannelId != channelId {
|
||||
c.Err = model.NewLocAppError("deleteReaction", "api.reaction.delete_reaction.mismatched_channel_id.app_error",
|
||||
nil, "channelId="+channelId+", post.ChannelId="+post.ChannelId+", postId="+postId)
|
||||
c.Err.StatusCode = http.StatusBadRequest
|
||||
return
|
||||
} else {
|
||||
postHadReactions = post.HasReactions
|
||||
}
|
||||
|
||||
if result := <-Srv.Store.Reaction().Delete(reaction); result.Err != nil {
|
||||
c.Err = result.Err
|
||||
return
|
||||
} else {
|
||||
go sendReactionEvent(model.WEBSOCKET_EVENT_REACTION_REMOVED, channelId, reaction, postHadReactions)
|
||||
|
||||
ReturnStatusOK(w)
|
||||
}
|
||||
}
|
||||
|
||||
func sendReactionEvent(event string, channelId string, reaction *model.Reaction, postHadReactions bool) {
|
||||
// send out that a reaction has been added/removed
|
||||
go func() {
|
||||
message := model.NewWebSocketEvent(event, "", channelId, "", nil)
|
||||
message.Add("reaction", reaction.ToJson())
|
||||
|
||||
Publish(message)
|
||||
}()
|
||||
|
||||
// send out that a post was updated if post.HasReactions has changed
|
||||
go func() {
|
||||
var post *model.Post
|
||||
if result := <-Srv.Store.Post().Get(reaction.PostId); result.Err != nil {
|
||||
l4g.Warn(utils.T("api.reaction.send_reaction_event.post.app_error"))
|
||||
return
|
||||
} else {
|
||||
post = result.Data.(*model.PostList).Posts[reaction.PostId]
|
||||
}
|
||||
|
||||
if post.HasReactions != postHadReactions {
|
||||
message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_POST_EDITED, "", channelId, "", nil)
|
||||
message.Add("post", post.ToJson())
|
||||
|
||||
Publish(message)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func listReactions(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
params := mux.Vars(r)
|
||||
|
||||
channelId := params["channel_id"]
|
||||
if len(channelId) != 26 {
|
||||
c.SetInvalidParam("deletePost", "channelId")
|
||||
return
|
||||
}
|
||||
|
||||
postId := params["post_id"]
|
||||
if len(postId) != 26 {
|
||||
c.SetInvalidParam("listReactions", "postId")
|
||||
return
|
||||
}
|
||||
|
||||
pchan := Srv.Store.Post().Get(postId)
|
||||
|
||||
if !HasPermissionToChannelContext(c, channelId, model.PERMISSION_READ_CHANNEL) {
|
||||
return
|
||||
}
|
||||
|
||||
if result := <-pchan; result.Err != nil {
|
||||
c.Err = result.Err
|
||||
return
|
||||
} else if post := result.Data.(*model.PostList).Posts[postId]; post.ChannelId != channelId {
|
||||
c.Err = model.NewLocAppError("listReactions", "api.reaction.list_reactions.mismatched_channel_id.app_error",
|
||||
nil, "channelId="+channelId+", post.ChannelId="+post.ChannelId+", postId="+postId)
|
||||
c.Err.StatusCode = http.StatusBadRequest
|
||||
return
|
||||
}
|
||||
|
||||
if result := <-Srv.Store.Reaction().GetForPost(postId); result.Err != nil {
|
||||
c.Err = result.Err
|
||||
return
|
||||
} else {
|
||||
reactions := result.Data.([]*model.Reaction)
|
||||
|
||||
w.Write([]byte(model.ReactionsToJson(reactions)))
|
||||
}
|
||||
}
|
||||
314
api/reaction_test.go
Обычный файл
314
api/reaction_test.go
Обычный файл
@@ -0,0 +1,314 @@
|
||||
// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/mattermost/platform/model"
|
||||
)
|
||||
|
||||
func TestSaveReaction(t *testing.T) {
|
||||
th := Setup().InitBasic()
|
||||
|
||||
Client := th.BasicClient
|
||||
|
||||
user := th.BasicUser
|
||||
user2 := th.BasicUser2
|
||||
|
||||
channel := th.BasicChannel
|
||||
post := th.BasicPost
|
||||
|
||||
// saving a reaction
|
||||
reaction := &model.Reaction{
|
||||
UserId: user.Id,
|
||||
PostId: post.Id,
|
||||
EmojiName: "smile",
|
||||
}
|
||||
if returned, err := Client.SaveReaction(channel.Id, reaction); err != nil {
|
||||
t.Fatal(err)
|
||||
} else {
|
||||
reaction = returned
|
||||
}
|
||||
|
||||
if reactions := Client.MustGeneric(Client.ListReactions(channel.Id, post.Id)).([]*model.Reaction); len(reactions) != 1 || *reactions[0] != *reaction {
|
||||
t.Fatal("didn't save reaction correctly")
|
||||
}
|
||||
|
||||
// saving a duplicate reaction
|
||||
if _, err := Client.SaveReaction(channel.Id, reaction); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// saving a second reaction on a post
|
||||
reaction2 := &model.Reaction{
|
||||
UserId: user.Id,
|
||||
PostId: post.Id,
|
||||
EmojiName: "sad",
|
||||
}
|
||||
if returned, err := Client.SaveReaction(channel.Id, reaction2); err != nil {
|
||||
t.Fatal(err)
|
||||
} else {
|
||||
reaction2 = returned
|
||||
}
|
||||
|
||||
if reactions := Client.MustGeneric(Client.ListReactions(channel.Id, post.Id)).([]*model.Reaction); len(reactions) != 2 ||
|
||||
(*reactions[0] != *reaction && *reactions[1] != *reaction) || (*reactions[0] != *reaction2 && *reactions[1] != *reaction2) {
|
||||
t.Fatal("didn't save multiple reactions correctly")
|
||||
}
|
||||
|
||||
// saving a reaction without a user id
|
||||
reaction3 := &model.Reaction{
|
||||
PostId: post.Id,
|
||||
EmojiName: "smile",
|
||||
}
|
||||
if _, err := Client.SaveReaction(channel.Id, reaction3); err == nil {
|
||||
t.Fatal("should've failed to save reaction without user id")
|
||||
}
|
||||
|
||||
// saving a reaction without a post id
|
||||
reaction4 := &model.Reaction{
|
||||
UserId: user.Id,
|
||||
EmojiName: "smile",
|
||||
}
|
||||
if _, err := Client.SaveReaction(channel.Id, reaction4); err == nil {
|
||||
t.Fatal("should've failed to save reaction without post id")
|
||||
}
|
||||
|
||||
// saving a reaction without a emoji name
|
||||
reaction5 := &model.Reaction{
|
||||
UserId: user.Id,
|
||||
PostId: post.Id,
|
||||
}
|
||||
if _, err := Client.SaveReaction(channel.Id, reaction5); err == nil {
|
||||
t.Fatal("should've failed to save reaction without emoji name")
|
||||
}
|
||||
|
||||
// saving a reaction for another user
|
||||
reaction6 := &model.Reaction{
|
||||
UserId: user2.Id,
|
||||
PostId: post.Id,
|
||||
EmojiName: "smile",
|
||||
}
|
||||
if _, err := Client.SaveReaction(channel.Id, reaction6); err == nil {
|
||||
t.Fatal("should've failed to save reaction for another user")
|
||||
}
|
||||
|
||||
// saving a reaction to a channel we're not a member of
|
||||
th.LoginBasic2()
|
||||
channel2 := th.CreateChannel(th.BasicClient, th.BasicTeam)
|
||||
post2 := th.CreatePost(th.BasicClient, channel2)
|
||||
th.LoginBasic()
|
||||
|
||||
reaction7 := &model.Reaction{
|
||||
UserId: user.Id,
|
||||
PostId: post2.Id,
|
||||
EmojiName: "smile",
|
||||
}
|
||||
if _, err := Client.SaveReaction(channel2.Id, reaction7); err == nil {
|
||||
t.Fatal("should've failed to save reaction to a channel we're not a member of")
|
||||
}
|
||||
|
||||
// saving a reaction to a direct channel
|
||||
directChannel := Client.Must(Client.CreateDirectChannel(user2.Id)).Data.(*model.Channel)
|
||||
directPost := th.CreatePost(th.BasicClient, directChannel)
|
||||
|
||||
reaction8 := &model.Reaction{
|
||||
UserId: user.Id,
|
||||
PostId: directPost.Id,
|
||||
EmojiName: "smile",
|
||||
}
|
||||
if returned, err := Client.SaveReaction(directChannel.Id, reaction8); err != nil {
|
||||
t.Fatal(err)
|
||||
} else {
|
||||
reaction8 = returned
|
||||
}
|
||||
|
||||
if reactions := Client.MustGeneric(Client.ListReactions(directChannel.Id, directPost.Id)).([]*model.Reaction); len(reactions) != 1 || *reactions[0] != *reaction8 {
|
||||
t.Fatal("didn't save reaction correctly")
|
||||
}
|
||||
|
||||
// saving a reaction for a post in the wrong channel
|
||||
reaction9 := &model.Reaction{
|
||||
UserId: user.Id,
|
||||
PostId: directPost.Id,
|
||||
EmojiName: "sad",
|
||||
}
|
||||
if _, err := Client.SaveReaction(channel.Id, reaction9); err == nil {
|
||||
t.Fatal("should've failed to save reaction to a post that isn't in the given channel")
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeleteReaction(t *testing.T) {
|
||||
th := Setup().InitBasic()
|
||||
|
||||
Client := th.BasicClient
|
||||
|
||||
user := th.BasicUser
|
||||
user2 := th.BasicUser2
|
||||
|
||||
channel := th.BasicChannel
|
||||
post := th.BasicPost
|
||||
|
||||
reaction1 := &model.Reaction{
|
||||
UserId: user.Id,
|
||||
PostId: post.Id,
|
||||
EmojiName: "smile",
|
||||
}
|
||||
|
||||
// deleting a reaction that does exist
|
||||
Client.MustGeneric(Client.SaveReaction(channel.Id, reaction1))
|
||||
if err := Client.DeleteReaction(channel.Id, reaction1); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if reactions := Client.MustGeneric(Client.ListReactions(channel.Id, post.Id)).([]*model.Reaction); len(reactions) != 0 {
|
||||
t.Fatal("should've deleted reaction")
|
||||
}
|
||||
|
||||
// deleting one reaction when a post has multiple
|
||||
reaction2 := &model.Reaction{
|
||||
UserId: user.Id,
|
||||
PostId: post.Id,
|
||||
EmojiName: "sad",
|
||||
}
|
||||
reaction1 = Client.MustGeneric(Client.SaveReaction(channel.Id, reaction1)).(*model.Reaction)
|
||||
reaction2 = Client.MustGeneric(Client.SaveReaction(channel.Id, reaction2)).(*model.Reaction)
|
||||
if err := Client.DeleteReaction(channel.Id, reaction2); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if reactions := Client.MustGeneric(Client.ListReactions(channel.Id, post.Id)).([]*model.Reaction); len(reactions) != 1 || *reactions[0] != *reaction1 {
|
||||
t.Fatal("should've deleted only one reaction")
|
||||
}
|
||||
|
||||
// deleting a reaction made by another user
|
||||
reaction3 := &model.Reaction{
|
||||
UserId: user2.Id,
|
||||
PostId: post.Id,
|
||||
EmojiName: "smile",
|
||||
}
|
||||
|
||||
th.LoginBasic2()
|
||||
Client.Must(Client.JoinChannel(channel.Id))
|
||||
reaction3 = Client.MustGeneric(Client.SaveReaction(channel.Id, reaction3)).(*model.Reaction)
|
||||
|
||||
th.LoginBasic()
|
||||
if err := Client.DeleteReaction(channel.Id, reaction3); err == nil {
|
||||
t.Fatal("should've failed to delete another user's reaction")
|
||||
}
|
||||
|
||||
// deleting a reaction for a post we can't see
|
||||
channel2 := th.CreateChannel(th.BasicClient, th.BasicTeam)
|
||||
post2 := th.CreatePost(th.BasicClient, channel2)
|
||||
|
||||
reaction4 := &model.Reaction{
|
||||
UserId: user.Id,
|
||||
PostId: post2.Id,
|
||||
EmojiName: "smile",
|
||||
}
|
||||
|
||||
reaction4 = Client.MustGeneric(Client.SaveReaction(channel2.Id, reaction4)).(*model.Reaction)
|
||||
Client.Must(Client.LeaveChannel(channel2.Id))
|
||||
|
||||
if err := Client.DeleteReaction(channel2.Id, reaction4); err == nil {
|
||||
t.Fatal("should've failed to delete a reaction from a channel we're not in")
|
||||
}
|
||||
|
||||
// deleting a reaction for a post with the wrong channel
|
||||
channel3 := th.CreateChannel(th.BasicClient, th.BasicTeam)
|
||||
|
||||
reaction5 := &model.Reaction{
|
||||
UserId: user.Id,
|
||||
PostId: post.Id,
|
||||
EmojiName: "happy",
|
||||
}
|
||||
if _, err := Client.SaveReaction(channel3.Id, reaction5); err == nil {
|
||||
t.Fatal("should've failed to save reaction to a post that isn't in the given channel")
|
||||
}
|
||||
}
|
||||
|
||||
func TestListReactions(t *testing.T) {
|
||||
th := Setup().InitBasic()
|
||||
|
||||
Client := th.BasicClient
|
||||
|
||||
user := th.BasicUser
|
||||
user2 := th.BasicUser2
|
||||
|
||||
channel := th.BasicChannel
|
||||
|
||||
post := th.BasicPost
|
||||
|
||||
userReactions := []*model.Reaction{
|
||||
{
|
||||
UserId: user.Id,
|
||||
PostId: post.Id,
|
||||
EmojiName: "smile",
|
||||
},
|
||||
{
|
||||
UserId: user.Id,
|
||||
PostId: post.Id,
|
||||
EmojiName: "happy",
|
||||
},
|
||||
{
|
||||
UserId: user.Id,
|
||||
PostId: post.Id,
|
||||
EmojiName: "sad",
|
||||
},
|
||||
}
|
||||
|
||||
for i, reaction := range userReactions {
|
||||
userReactions[i] = Client.MustGeneric(Client.SaveReaction(channel.Id, reaction)).(*model.Reaction)
|
||||
}
|
||||
|
||||
th.LoginBasic2()
|
||||
Client.Must(Client.JoinChannel(channel.Id))
|
||||
|
||||
userReactions2 := []*model.Reaction{
|
||||
{
|
||||
UserId: user2.Id,
|
||||
PostId: post.Id,
|
||||
EmojiName: "smile",
|
||||
},
|
||||
{
|
||||
UserId: user2.Id,
|
||||
PostId: post.Id,
|
||||
EmojiName: "sad",
|
||||
},
|
||||
}
|
||||
|
||||
for i, reaction := range userReactions2 {
|
||||
userReactions2[i] = Client.MustGeneric(Client.SaveReaction(channel.Id, reaction)).(*model.Reaction)
|
||||
}
|
||||
|
||||
if reactions, err := Client.ListReactions(channel.Id, post.Id); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if len(reactions) != 5 {
|
||||
t.Fatal("should've returned 5 reactions")
|
||||
} else {
|
||||
checkForReaction := func(expected *model.Reaction) {
|
||||
found := false
|
||||
|
||||
for _, reaction := range reactions {
|
||||
if *reaction == *expected {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !found {
|
||||
t.Fatalf("didn't return expected reaction %v", *expected)
|
||||
}
|
||||
}
|
||||
|
||||
for _, reaction := range userReactions {
|
||||
checkForReaction(reaction)
|
||||
}
|
||||
|
||||
for _, reaction := range userReactions2 {
|
||||
checkForReaction(reaction)
|
||||
}
|
||||
}
|
||||
}
|
||||
Ссылка в новой задаче
Block a user