* Make UpdateSidebarCategories return the original categories

* MM-20897 Add category muting

* Prevent muting the DMs category

* Fix muted state not being stored in the database

* Address feedback

* Address some feedback

* Fix unit tests

* MM-20897 Mute/unmute channels in the database in bulk

* Satisfy golangci-lint
Этот коммит содержится в:
Harrison Healey
2020-11-16 15:19:01 -05:00
коммит произвёл GitHub
родитель 7f6b43a15f
Коммит 2ebc8ec90f
20 изменённых файлов: 1189 добавлений и 147 удалений

Просмотреть файл

@@ -953,7 +953,7 @@ type AppIface interface {
TestLdap() *model.AppError
TestSiteURL(siteURL string) *model.AppError
Timezones() *timezones.Timezones
ToggleMuteChannel(channelId string, userId string) *model.ChannelMember
ToggleMuteChannel(channelId, userId string) (*model.ChannelMember, *model.AppError)
TotalWebsocketConnections() int
TriggerWebhook(payload *model.OutgoingWebhookPayload, hook *model.OutgoingWebhook, post *model.Post, channel *model.Channel)
UnregisterPluginCommand(pluginId, teamId, trigger string)

Просмотреть файл

@@ -1097,22 +1097,7 @@ func (a *App) UpdateChannelMemberRoles(channelId string, userId string, newRoles
member.ExplicitRoles = strings.Join(newExplicitRoles, " ")
member, nErr := a.Srv().Store.Channel().UpdateMember(member)
if nErr != nil {
var appErr *model.AppError
var nfErr *store.ErrNotFound
switch {
case errors.As(nErr, &appErr):
return nil, appErr
case errors.As(nErr, &nfErr):
return nil, model.NewAppError("UpdateChannelMemberRoles", MISSING_CHANNEL_MEMBER_ERROR, nil, nfErr.Error(), http.StatusNotFound)
default:
return nil, model.NewAppError("UpdateChannelMemberRoles", "app.channel.get_member.app_error", nil, nErr.Error(), http.StatusInternalServerError)
}
}
a.InvalidateCacheForUser(userId)
return member, nil
return a.updateChannelMember(member)
}
func (a *App) UpdateChannelMemberSchemeRoles(channelId string, userId string, isSchemeGuest bool, isSchemeUser bool, isSchemeAdmin bool) (*model.ChannelMember, *model.AppError) {
@@ -1134,27 +1119,7 @@ func (a *App) UpdateChannelMemberSchemeRoles(channelId string, userId string, is
member.ExplicitRoles = RemoveRoles([]string{model.CHANNEL_GUEST_ROLE_ID, model.CHANNEL_USER_ROLE_ID, model.CHANNEL_ADMIN_ROLE_ID}, member.ExplicitRoles)
}
member, nErr := a.Srv().Store.Channel().UpdateMember(member)
if nErr != nil {
var appErr *model.AppError
var nfErr *store.ErrNotFound
switch {
case errors.As(nErr, &appErr):
return nil, appErr
case errors.As(nErr, &nfErr):
return nil, model.NewAppError("UpdateChannelMemberSchemeRoles", MISSING_CHANNEL_MEMBER_ERROR, nil, nfErr.Error(), http.StatusNotFound)
default:
return nil, model.NewAppError("UpdateChannelMemberSchemeRoles", "app.channel.get_member.app_error", nil, nErr.Error(), http.StatusInternalServerError)
}
}
// Notify the clients that the member notify props changed
message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_CHANNEL_MEMBER_UPDATED, "", "", userId, nil)
message.Add("channelMember", member.ToJson())
a.Publish(message)
a.InvalidateCacheForUser(userId)
return member, nil
return a.updateChannelMember(member)
}
func (a *App) UpdateChannelMemberNotifyProps(data map[string]string, channelId string, userId string) (*model.ChannelMember, *model.AppError) {
@@ -1185,6 +1150,17 @@ func (a *App) UpdateChannelMemberNotifyProps(data map[string]string, channelId s
member.NotifyProps[model.IGNORE_CHANNEL_MENTIONS_NOTIFY_PROP] = ignoreChannelMentions
}
member, err = a.updateChannelMember(member)
if err != nil {
return nil, err
}
a.invalidateCacheForChannelMembersNotifyProps(member.ChannelId)
return member, nil
}
func (a *App) updateChannelMember(member *model.ChannelMember) (*model.ChannelMember, *model.AppError) {
member, nErr := a.Srv().Store.Channel().UpdateMember(member)
if nErr != nil {
var appErr *model.AppError
@@ -1193,18 +1169,19 @@ func (a *App) UpdateChannelMemberNotifyProps(data map[string]string, channelId s
case errors.As(nErr, &appErr):
return nil, appErr
case errors.As(nErr, &nfErr):
return nil, model.NewAppError("UpdateChannelMemberNotifyProps", MISSING_CHANNEL_MEMBER_ERROR, nil, nfErr.Error(), http.StatusNotFound)
return nil, model.NewAppError("updateChannelMember", MISSING_CHANNEL_MEMBER_ERROR, nil, nfErr.Error(), http.StatusNotFound)
default:
return nil, model.NewAppError("UpdateChannelMemberNotifyProps", "app.channel.get_member.app_error", nil, nErr.Error(), http.StatusInternalServerError)
return nil, model.NewAppError("updateChannelMember", "app.channel.get_member.app_error", nil, nErr.Error(), http.StatusInternalServerError)
}
}
a.InvalidateCacheForUser(userId)
a.invalidateCacheForChannelMembersNotifyProps(channelId)
a.InvalidateCacheForUser(member.UserId)
// Notify the clients that the member notify props changed
evt := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_CHANNEL_MEMBER_UPDATED, "", "", userId, nil)
evt := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_CHANNEL_MEMBER_UPDATED, "", "", member.UserId, nil)
evt.Add("channelMember", member.ToJson())
a.Publish(evt)
return member, nil
}
@@ -2788,20 +2765,84 @@ func (a *App) GetPinnedPosts(channelId string) (*model.PostList, *model.AppError
return posts, nil
}
func (a *App) ToggleMuteChannel(channelId string, userId string) *model.ChannelMember {
member, err := a.Srv().Store.Channel().GetMember(channelId, userId)
func (a *App) ToggleMuteChannel(channelId, userId string) (*model.ChannelMember, *model.AppError) {
member, nErr := a.Srv().Store.Channel().GetMember(channelId, userId)
if nErr != nil {
var appErr *model.AppError
var nfErr *store.ErrNotFound
switch {
case errors.As(nErr, &appErr):
return nil, appErr
case errors.As(nErr, &nfErr):
return nil, model.NewAppError("ToggleMuteChannel", MISSING_CHANNEL_MEMBER_ERROR, nil, nfErr.Error(), http.StatusNotFound)
default:
return nil, model.NewAppError("ToggleMuteChannel", "app.channel.get_member.app_error", nil, nErr.Error(), http.StatusInternalServerError)
}
}
member.SetChannelMuted(!member.IsChannelMuted())
member, err := a.updateChannelMember(member)
if err != nil {
return nil
return nil, err
}
if member.NotifyProps[model.MARK_UNREAD_NOTIFY_PROP] == model.CHANNEL_NOTIFY_MENTION {
member.NotifyProps[model.MARK_UNREAD_NOTIFY_PROP] = model.CHANNEL_MARK_UNREAD_ALL
} else {
member.NotifyProps[model.MARK_UNREAD_NOTIFY_PROP] = model.CHANNEL_NOTIFY_MENTION
a.invalidateCacheForChannelMembersNotifyProps(member.ChannelId)
return member, nil
}
func (a *App) setChannelsMuted(channelIds []string, userId string, muted bool) ([]*model.ChannelMember, *model.AppError) {
members, nErr := a.Srv().Store.Channel().GetMembersByChannelIds(channelIds, userId)
if nErr != nil {
var appErr *model.AppError
switch {
case errors.As(nErr, &appErr):
return nil, appErr
default:
return nil, model.NewAppError("setChannelsMuted", "app.channel.get_member.app_error", nil, nErr.Error(), http.StatusInternalServerError)
}
}
a.Srv().Store.Channel().UpdateMember(member)
return member
var membersToUpdate []*model.ChannelMember
for _, member := range *members {
if muted == member.IsChannelMuted() {
continue
}
updatedMember := member
updatedMember.SetChannelMuted(muted)
membersToUpdate = append(membersToUpdate, &updatedMember)
}
if len(membersToUpdate) == 0 {
return nil, nil
}
updated, nErr := a.Srv().Store.Channel().UpdateMultipleMembers(membersToUpdate)
if nErr != nil {
var appErr *model.AppError
var nfErr *store.ErrNotFound
switch {
case errors.As(nErr, &appErr):
return nil, appErr
case errors.As(nErr, &nfErr):
return nil, model.NewAppError("setChannelsMuted", MISSING_CHANNEL_MEMBER_ERROR, nil, nfErr.Error(), http.StatusNotFound)
default:
return nil, model.NewAppError("setChannelsMuted", "app.channel.get_member.app_error", nil, nErr.Error(), http.StatusInternalServerError)
}
}
for _, member := range updated {
a.invalidateCacheForChannelMembersNotifyProps(member.ChannelId)
evt := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_CHANNEL_MEMBER_UPDATED, "", "", member.UserId, nil)
evt.Add("channelMember", member.ToJson())
a.Publish(evt)
}
return updated, nil
}
func (a *App) FillInChannelProps(channel *model.Channel) *model.AppError {

Просмотреть файл

@@ -144,14 +144,122 @@ func (a *App) UpdateSidebarCategoryOrder(userId, teamId string, categoryOrder []
}
func (a *App) UpdateSidebarCategories(userId, teamId string, categories []*model.SidebarCategoryWithChannels) ([]*model.SidebarCategoryWithChannels, *model.AppError) {
result, err := a.Srv().Store.Channel().UpdateSidebarCategories(userId, teamId, categories)
updatedCategories, originalCategories, err := a.Srv().Store.Channel().UpdateSidebarCategories(userId, teamId, categories)
if err != nil {
return nil, model.NewAppError("UpdateSidebarCategories", "app.channel.sidebar_categories.app_error", nil, err.Error(), http.StatusInternalServerError)
}
message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_SIDEBAR_CATEGORY_UPDATED, teamId, "", userId, nil)
a.Publish(message)
return result, nil
a.muteChannelsForUpdatedCategories(userId, updatedCategories, originalCategories)
return updatedCategories, nil
}
func (a *App) muteChannelsForUpdatedCategories(userId string, updatedCategories []*model.SidebarCategoryWithChannels, originalCategories []*model.SidebarCategoryWithChannels) {
var channelsToMute []string
var channelsToUnmute []string
// Mute or unmute all channels in categories that were muted or unmuted
for i, updatedCategory := range updatedCategories {
if i > len(originalCategories)-1 {
// The two slices should be the same length, but double check that to be safe
continue
}
originalCategory := originalCategories[i]
if updatedCategory.Muted && !originalCategory.Muted {
channelsToMute = append(channelsToMute, updatedCategory.Channels...)
} else if !updatedCategory.Muted && originalCategory.Muted {
channelsToUnmute = append(channelsToUnmute, updatedCategory.Channels...)
}
}
// Mute any channels moved from an unmuted category into a muted one and vice versa
channelsDiff := diffChannelsBetweenCategories(updatedCategories, originalCategories)
if len(channelsDiff) != 0 {
makeCategoryMap := func(categories []*model.SidebarCategoryWithChannels) map[string]*model.SidebarCategoryWithChannels {
result := make(map[string]*model.SidebarCategoryWithChannels)
for _, category := range categories {
result[category.Id] = category
}
return result
}
updatedCategoriesById := makeCategoryMap(updatedCategories)
originalCategoriesById := makeCategoryMap(originalCategories)
for channelId, diff := range channelsDiff {
fromCategory := originalCategoriesById[diff.fromCategoryId]
toCategory := updatedCategoriesById[diff.toCategoryId]
if toCategory.Muted && !fromCategory.Muted {
channelsToMute = append(channelsToMute, channelId)
} else if !toCategory.Muted && fromCategory.Muted {
channelsToUnmute = append(channelsToUnmute, channelId)
}
}
}
if len(channelsToMute) > 0 {
_, err := a.setChannelsMuted(channelsToMute, userId, true)
if err != nil {
mlog.Error(
"Failed to mute channels to match category",
mlog.String("user_id", userId),
mlog.Err(err),
)
}
}
if len(channelsToUnmute) > 0 {
_, err := a.setChannelsMuted(channelsToUnmute, userId, false)
if err != nil {
mlog.Error(
"Failed to unmute channels to match category",
mlog.String("user_id", userId),
mlog.Err(err),
)
}
}
}
type categoryChannelDiff struct {
fromCategoryId string
toCategoryId string
}
func diffChannelsBetweenCategories(updatedCategories []*model.SidebarCategoryWithChannels, originalCategories []*model.SidebarCategoryWithChannels) map[string]*categoryChannelDiff {
// mapChannelIdsToCategories returns a map of channel IDs to the IDs of the categories that they're a member of.
mapChannelIdsToCategories := func(categories []*model.SidebarCategoryWithChannels) map[string]string {
result := make(map[string]string)
for _, category := range categories {
for _, channelId := range category.Channels {
result[channelId] = category.Id
}
}
return result
}
updatedChannelIdsMap := mapChannelIdsToCategories(updatedCategories)
originalChannelIdsMap := mapChannelIdsToCategories(originalCategories)
// Check for any channels that have changed categories. Note that we don't worry about any channels that have moved
// outside of these categories since that heavily complicates things and doesn't currently happen in our apps.
channelsDiff := make(map[string]*categoryChannelDiff)
for channelId, originalCategoryId := range originalChannelIdsMap {
updatedCategoryId := updatedChannelIdsMap[channelId]
if originalCategoryId != updatedCategoryId && updatedCategoryId != "" {
channelsDiff[channelId] = &categoryChannelDiff{originalCategoryId, updatedCategoryId}
}
}
return channelsDiff
}
func (a *App) DeleteSidebarCategory(userId, teamId, categoryId string) *model.AppError {

Просмотреть файл

@@ -136,3 +136,524 @@ func TestGetSidebarCategories(t *testing.T) {
assert.Equal(t, "app.channel.sidebar_categories.app_error", appErr.Id)
})
}
func TestUpdateSidebarCategories(t *testing.T) {
t.Run("should mute and unmute all channels in a category when it is muted or unmuted", func(t *testing.T) {
th := Setup(t).InitBasic()
defer th.TearDown()
categories, err := th.App.GetSidebarCategories(th.BasicUser.Id, th.BasicTeam.Id)
require.Nil(t, err)
channelsCategory := categories.Categories[1]
// Create some channels to be part of the channels category
channel1 := th.CreateChannel(th.BasicTeam)
th.AddUserToChannel(th.BasicUser, channel1)
channel2 := th.CreateChannel(th.BasicTeam)
th.AddUserToChannel(th.BasicUser, channel2)
// Mute the category
updated, err := th.App.UpdateSidebarCategories(th.BasicUser.Id, th.BasicTeam.Id, []*model.SidebarCategoryWithChannels{
{
SidebarCategory: model.SidebarCategory{
Id: channelsCategory.Id,
Muted: true,
},
Channels: []string{channel1.Id, channel2.Id},
},
})
require.Nil(t, err)
assert.True(t, updated[0].Muted)
// Confirm that the channels are now muted
member1, err := th.App.GetChannelMember(channel1.Id, th.BasicUser.Id)
require.Nil(t, err)
assert.True(t, member1.IsChannelMuted())
member2, err := th.App.GetChannelMember(channel2.Id, th.BasicUser.Id)
require.Nil(t, err)
assert.True(t, member2.IsChannelMuted())
// Unmute the category
updated, err = th.App.UpdateSidebarCategories(th.BasicUser.Id, th.BasicTeam.Id, []*model.SidebarCategoryWithChannels{
{
SidebarCategory: model.SidebarCategory{
Id: channelsCategory.Id,
Muted: false,
},
Channels: []string{channel1.Id, channel2.Id},
},
})
require.Nil(t, err)
assert.False(t, updated[0].Muted)
// Confirm that the channels are now unmuted
member1, err = th.App.GetChannelMember(channel1.Id, th.BasicUser.Id)
require.Nil(t, err)
assert.False(t, member1.IsChannelMuted())
member2, err = th.App.GetChannelMember(channel2.Id, th.BasicUser.Id)
require.Nil(t, err)
assert.False(t, member2.IsChannelMuted())
})
t.Run("should mute and unmute channels moved from an unmuted category to a muted one and back", func(t *testing.T) {
th := Setup(t).InitBasic()
defer th.TearDown()
// Create some channels
channel1 := th.CreateChannel(th.BasicTeam)
th.AddUserToChannel(th.BasicUser, channel1)
channel2 := th.CreateChannel(th.BasicTeam)
th.AddUserToChannel(th.BasicUser, channel2)
// And some categories
mutedCategory, err := th.App.CreateSidebarCategory(th.BasicUser.Id, th.BasicTeam.Id, &model.SidebarCategoryWithChannels{
SidebarCategory: model.SidebarCategory{
DisplayName: "muted",
Muted: true,
},
})
require.Nil(t, err)
require.True(t, mutedCategory.Muted)
unmutedCategory, err := th.App.CreateSidebarCategory(th.BasicUser.Id, th.BasicTeam.Id, &model.SidebarCategoryWithChannels{
SidebarCategory: model.SidebarCategory{
DisplayName: "unmuted",
Muted: false,
},
Channels: []string{channel1.Id, channel2.Id},
})
require.Nil(t, err)
require.False(t, unmutedCategory.Muted)
// Move the channels
_, err = th.App.UpdateSidebarCategories(th.BasicUser.Id, th.BasicTeam.Id, []*model.SidebarCategoryWithChannels{
{
SidebarCategory: model.SidebarCategory{
Id: mutedCategory.Id,
DisplayName: mutedCategory.DisplayName,
Muted: mutedCategory.Muted,
},
Channels: []string{channel1.Id, channel2.Id},
},
{
SidebarCategory: model.SidebarCategory{
Id: unmutedCategory.Id,
DisplayName: unmutedCategory.DisplayName,
Muted: unmutedCategory.Muted,
},
Channels: []string{},
},
})
require.Nil(t, err)
// Confirm that the channels are now muted
member1, err := th.App.GetChannelMember(channel1.Id, th.BasicUser.Id)
require.Nil(t, err)
assert.True(t, member1.IsChannelMuted())
member2, err := th.App.GetChannelMember(channel2.Id, th.BasicUser.Id)
require.Nil(t, err)
assert.True(t, member2.IsChannelMuted())
// Move the channels back
_, err = th.App.UpdateSidebarCategories(th.BasicUser.Id, th.BasicTeam.Id, []*model.SidebarCategoryWithChannels{
{
SidebarCategory: model.SidebarCategory{
Id: mutedCategory.Id,
DisplayName: mutedCategory.DisplayName,
Muted: mutedCategory.Muted,
},
Channels: []string{},
},
{
SidebarCategory: model.SidebarCategory{
Id: unmutedCategory.Id,
DisplayName: unmutedCategory.DisplayName,
Muted: unmutedCategory.Muted,
},
Channels: []string{channel1.Id, channel2.Id},
},
})
require.Nil(t, err)
// Confirm that the channels are now unmuted
member1, err = th.App.GetChannelMember(channel1.Id, th.BasicUser.Id)
require.Nil(t, err)
assert.False(t, member1.IsChannelMuted())
member2, err = th.App.GetChannelMember(channel2.Id, th.BasicUser.Id)
require.Nil(t, err)
assert.False(t, member2.IsChannelMuted())
})
t.Run("should not mute or unmute channels moved between muted categories", func(t *testing.T) {
th := Setup(t).InitBasic()
defer th.TearDown()
// Create some channels
channel1 := th.CreateChannel(th.BasicTeam)
th.AddUserToChannel(th.BasicUser, channel1)
channel2 := th.CreateChannel(th.BasicTeam)
th.AddUserToChannel(th.BasicUser, channel2)
// And some categories
category1, err := th.App.CreateSidebarCategory(th.BasicUser.Id, th.BasicTeam.Id, &model.SidebarCategoryWithChannels{
SidebarCategory: model.SidebarCategory{
DisplayName: "category1",
Muted: true,
},
})
require.Nil(t, err)
require.True(t, category1.Muted)
category2, err := th.App.CreateSidebarCategory(th.BasicUser.Id, th.BasicTeam.Id, &model.SidebarCategoryWithChannels{
SidebarCategory: model.SidebarCategory{
DisplayName: "category2",
Muted: true,
},
Channels: []string{channel1.Id, channel2.Id},
})
require.Nil(t, err)
require.True(t, category2.Muted)
// Move the unmuted channels
_, err = th.App.UpdateSidebarCategories(th.BasicUser.Id, th.BasicTeam.Id, []*model.SidebarCategoryWithChannels{
{
SidebarCategory: model.SidebarCategory{
Id: category1.Id,
DisplayName: category1.DisplayName,
Muted: category1.Muted,
},
Channels: []string{channel1.Id, channel2.Id},
},
{
SidebarCategory: model.SidebarCategory{
Id: category2.Id,
DisplayName: category2.DisplayName,
Muted: category2.Muted,
},
Channels: []string{},
},
})
require.Nil(t, err)
// Confirm that the channels are still unmuted
member1, err := th.App.GetChannelMember(channel1.Id, th.BasicUser.Id)
require.Nil(t, err)
assert.False(t, member1.IsChannelMuted())
member2, err := th.App.GetChannelMember(channel2.Id, th.BasicUser.Id)
require.Nil(t, err)
assert.False(t, member2.IsChannelMuted())
// Mute the channels manually
_, err = th.App.ToggleMuteChannel(channel1.Id, th.BasicUser.Id)
require.Nil(t, err)
_, err = th.App.ToggleMuteChannel(channel2.Id, th.BasicUser.Id)
require.Nil(t, err)
// Move the muted channels back
_, err = th.App.UpdateSidebarCategories(th.BasicUser.Id, th.BasicTeam.Id, []*model.SidebarCategoryWithChannels{
{
SidebarCategory: model.SidebarCategory{
Id: category1.Id,
DisplayName: category1.DisplayName,
Muted: category1.Muted,
},
Channels: []string{},
},
{
SidebarCategory: model.SidebarCategory{
Id: category2.Id,
DisplayName: category2.DisplayName,
Muted: category2.Muted,
},
Channels: []string{channel1.Id, channel2.Id},
},
})
require.Nil(t, err)
// Confirm that the channels are still muted
member1, err = th.App.GetChannelMember(channel1.Id, th.BasicUser.Id)
require.Nil(t, err)
assert.True(t, member1.IsChannelMuted())
member2, err = th.App.GetChannelMember(channel2.Id, th.BasicUser.Id)
require.Nil(t, err)
assert.True(t, member2.IsChannelMuted())
})
t.Run("should not mute or unmute channels moved between unmuted categories", func(t *testing.T) {
th := Setup(t).InitBasic()
defer th.TearDown()
// Create some channels
channel1 := th.CreateChannel(th.BasicTeam)
th.AddUserToChannel(th.BasicUser, channel1)
channel2 := th.CreateChannel(th.BasicTeam)
th.AddUserToChannel(th.BasicUser, channel2)
// And some categories
category1, err := th.App.CreateSidebarCategory(th.BasicUser.Id, th.BasicTeam.Id, &model.SidebarCategoryWithChannels{
SidebarCategory: model.SidebarCategory{
DisplayName: "category1",
Muted: false,
},
})
require.Nil(t, err)
require.False(t, category1.Muted)
category2, err := th.App.CreateSidebarCategory(th.BasicUser.Id, th.BasicTeam.Id, &model.SidebarCategoryWithChannels{
SidebarCategory: model.SidebarCategory{
DisplayName: "category2",
Muted: false,
},
Channels: []string{channel1.Id, channel2.Id},
})
require.Nil(t, err)
require.False(t, category2.Muted)
// Move the unmuted channels
_, err = th.App.UpdateSidebarCategories(th.BasicUser.Id, th.BasicTeam.Id, []*model.SidebarCategoryWithChannels{
{
SidebarCategory: model.SidebarCategory{
Id: category1.Id,
DisplayName: category1.DisplayName,
Muted: category1.Muted,
},
Channels: []string{channel1.Id, channel2.Id},
},
{
SidebarCategory: model.SidebarCategory{
Id: category2.Id,
DisplayName: category2.DisplayName,
Muted: category2.Muted,
},
Channels: []string{},
},
})
require.Nil(t, err)
// Confirm that the channels are still unmuted
member1, err := th.App.GetChannelMember(channel1.Id, th.BasicUser.Id)
require.Nil(t, err)
assert.False(t, member1.IsChannelMuted())
member2, err := th.App.GetChannelMember(channel2.Id, th.BasicUser.Id)
require.Nil(t, err)
assert.False(t, member2.IsChannelMuted())
// Mute the channels manually
_, err = th.App.ToggleMuteChannel(channel1.Id, th.BasicUser.Id)
require.Nil(t, err)
_, err = th.App.ToggleMuteChannel(channel2.Id, th.BasicUser.Id)
require.Nil(t, err)
// Move the muted channels back
_, err = th.App.UpdateSidebarCategories(th.BasicUser.Id, th.BasicTeam.Id, []*model.SidebarCategoryWithChannels{
{
SidebarCategory: model.SidebarCategory{
Id: category1.Id,
DisplayName: category1.DisplayName,
Muted: category1.Muted,
},
Channels: []string{},
},
{
SidebarCategory: model.SidebarCategory{
Id: category2.Id,
DisplayName: category2.DisplayName,
Muted: category2.Muted,
},
Channels: []string{channel1.Id, channel2.Id},
},
})
require.Nil(t, err)
// Confirm that the channels are still muted
member1, err = th.App.GetChannelMember(channel1.Id, th.BasicUser.Id)
require.Nil(t, err)
assert.True(t, member1.IsChannelMuted())
member2, err = th.App.GetChannelMember(channel2.Id, th.BasicUser.Id)
require.Nil(t, err)
assert.True(t, member2.IsChannelMuted())
})
}
func TestDiffChannelsBetweenCategories(t *testing.T) {
t.Run("should return nothing when the categories contain identical channels", func(t *testing.T) {
originalCategories := []*model.SidebarCategoryWithChannels{
{
SidebarCategory: model.SidebarCategory{
Id: "category1",
DisplayName: "Category One",
},
Channels: []string{"channel1", "channel2", "channel3"},
},
{
SidebarCategory: model.SidebarCategory{
Id: "category2",
DisplayName: "Category Two",
},
Channels: []string{"channel4", "channel5"},
},
{
SidebarCategory: model.SidebarCategory{
Id: "category3",
DisplayName: "Category Three",
},
Channels: []string{},
},
}
updatedCategories := []*model.SidebarCategoryWithChannels{
{
SidebarCategory: model.SidebarCategory{
Id: "category1",
DisplayName: "Category Won",
},
Channels: []string{"channel1", "channel2", "channel3"},
},
{
SidebarCategory: model.SidebarCategory{
Id: "category2",
DisplayName: "Category Too",
},
Channels: []string{"channel4", "channel5"},
},
{
SidebarCategory: model.SidebarCategory{
Id: "category3",
DisplayName: "Category 🌲",
},
Channels: []string{},
},
}
channelsDiff := diffChannelsBetweenCategories(updatedCategories, originalCategories)
assert.Equal(t, map[string]*categoryChannelDiff{}, channelsDiff)
})
t.Run("should return nothing when the categories contain identical channels", func(t *testing.T) {
originalCategories := []*model.SidebarCategoryWithChannels{
{
SidebarCategory: model.SidebarCategory{
Id: "category1",
DisplayName: "Category One",
},
Channels: []string{"channel1", "channel2", "channel3"},
},
{
SidebarCategory: model.SidebarCategory{
Id: "category2",
DisplayName: "Category Two",
},
Channels: []string{"channel4", "channel5"},
},
{
SidebarCategory: model.SidebarCategory{
Id: "category3",
DisplayName: "Category Three",
},
Channels: []string{},
},
}
updatedCategories := []*model.SidebarCategoryWithChannels{
{
SidebarCategory: model.SidebarCategory{
Id: "category1",
DisplayName: "Category Won",
},
Channels: []string{},
},
{
SidebarCategory: model.SidebarCategory{
Id: "category2",
DisplayName: "Category Too",
},
Channels: []string{"channel5", "channel2"},
},
{
SidebarCategory: model.SidebarCategory{
Id: "category3",
DisplayName: "Category 🌲",
},
Channels: []string{"channel4", "channel1", "channel3"},
},
}
channelsDiff := diffChannelsBetweenCategories(updatedCategories, originalCategories)
assert.Equal(
t,
map[string]*categoryChannelDiff{
"channel1": {
fromCategoryId: "category1",
toCategoryId: "category3",
},
"channel2": {
fromCategoryId: "category1",
toCategoryId: "category2",
},
"channel3": {
fromCategoryId: "category1",
toCategoryId: "category3",
},
"channel4": {
fromCategoryId: "category2",
toCategoryId: "category3",
},
},
channelsDiff,
)
})
t.Run("should not return channels that are moved in our out of the categories implicitly", func(t *testing.T) {
// This case could change to actually return the channels in the future, but we don't need to handle it right now
originalCategories := []*model.SidebarCategoryWithChannels{
{
SidebarCategory: model.SidebarCategory{
Id: "category1",
DisplayName: "Category One",
},
Channels: []string{"channel1", "channel2"},
},
{
SidebarCategory: model.SidebarCategory{
Id: "category2",
DisplayName: "Category Two",
},
Channels: []string{"channel3"},
},
}
updatedCategories := []*model.SidebarCategoryWithChannels{
{
SidebarCategory: model.SidebarCategory{
Id: "category1",
DisplayName: "Category Won",
},
Channels: []string{"channel1", "channel3"},
},
{
SidebarCategory: model.SidebarCategory{
Id: "category2",
DisplayName: "Category Too",
},
Channels: []string{"channel4"},
},
}
channelsDiff := diffChannelsBetweenCategories(updatedCategories, originalCategories)
assert.Equal(
t,
map[string]*categoryChannelDiff{
"channel3": {
fromCategoryId: "category2",
toCategoryId: "category1",
},
},
channelsDiff,
)
})
}

Просмотреть файл

@@ -624,6 +624,57 @@ func TestAppUpdateChannelScheme(t *testing.T) {
}
}
func TestSetChannelsMuted(t *testing.T) {
t.Run("should mute and unmute the given channels", func(t *testing.T) {
th := Setup(t).InitBasic()
defer th.TearDown()
channel1 := th.BasicChannel
channel2 := th.CreateChannel(th.BasicTeam)
th.AddUserToChannel(th.BasicUser, channel2)
// Ensure that both channels start unmuted
member1, err := th.App.GetChannelMember(channel1.Id, th.BasicUser.Id)
require.Nil(t, err)
require.False(t, member1.IsChannelMuted())
member2, err := th.App.GetChannelMember(channel2.Id, th.BasicUser.Id)
require.Nil(t, err)
require.False(t, member2.IsChannelMuted())
// Mute both channels
updated, err := th.App.setChannelsMuted([]string{channel1.Id, channel2.Id}, th.BasicUser.Id, true)
require.Nil(t, err)
assert.True(t, updated[0].IsChannelMuted())
assert.True(t, updated[1].IsChannelMuted())
// Verify that the channels are muted in the database
member1, err = th.App.GetChannelMember(channel1.Id, th.BasicUser.Id)
require.Nil(t, err)
require.True(t, member1.IsChannelMuted())
member2, err = th.App.GetChannelMember(channel2.Id, th.BasicUser.Id)
require.Nil(t, err)
require.True(t, member2.IsChannelMuted())
// Unm both channels
updated, err = th.App.setChannelsMuted([]string{channel1.Id, channel2.Id}, th.BasicUser.Id, false)
require.Nil(t, err)
assert.False(t, updated[0].IsChannelMuted())
assert.False(t, updated[1].IsChannelMuted())
// Verify that the channels are muted in the database
member1, err = th.App.GetChannelMember(channel1.Id, th.BasicUser.Id)
require.Nil(t, err)
require.False(t, member1.IsChannelMuted())
member2, err = th.App.GetChannelMember(channel2.Id, th.BasicUser.Id)
require.Nil(t, err)
require.False(t, member2.IsChannelMuted())
})
}
func TestFillInChannelProps(t *testing.T) {
th := Setup(t).InitBasic()
defer th.TearDown()

Просмотреть файл

@@ -14212,7 +14212,7 @@ func (a *OpenTracingAppLayer) TestSiteURL(siteURL string) *model.AppError {
return resultVar0
}
func (a *OpenTracingAppLayer) ToggleMuteChannel(channelId string, userId string) *model.ChannelMember {
func (a *OpenTracingAppLayer) ToggleMuteChannel(channelId string, userId string) (*model.ChannelMember, *model.AppError) {
origCtx := a.ctx
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.ToggleMuteChannel")
@@ -14224,9 +14224,14 @@ func (a *OpenTracingAppLayer) ToggleMuteChannel(channelId string, userId string)
}()
defer span.Finish()
resultVar0 := a.app.ToggleMuteChannel(channelId, userId)
resultVar0, resultVar1 := a.app.ToggleMuteChannel(channelId, userId)
return resultVar0
if resultVar1 != nil {
span.LogFields(spanlog.Error(resultVar1))
ext.Error.Set(span, true)
}
return resultVar0, resultVar1
}
func (a *OpenTracingAppLayer) TotalWebsocketConnections() int {

Просмотреть файл

@@ -61,36 +61,23 @@ func (me *MuteProvider) DoCommand(a *app.App, args *model.CommandArgs, message s
}
}
channelMember := a.ToggleMuteChannel(channel.Id, args.UserId)
if channelMember == nil {
channelMember, err := a.ToggleMuteChannel(channel.Id, args.UserId)
if err != nil {
return &model.CommandResponse{Text: args.T("api.command_mute.not_member.error", map[string]interface{}{"Channel": channelName}), ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL}
}
// Invalidate cache to allow cache lookups while sending notifications
a.Srv().Store.Channel().InvalidateCacheForChannelMembersNotifyProps(channel.Id)
// Direct and Group messages won't have a nice channel title, omit it
if channel.Type == model.CHANNEL_DIRECT || channel.Type == model.CHANNEL_GROUP {
if channelMember.NotifyProps[model.MARK_UNREAD_NOTIFY_PROP] == model.CHANNEL_NOTIFY_MENTION {
publishChannelMemberEvt(a, channelMember, args.UserId)
return &model.CommandResponse{Text: args.T("api.command_mute.success_mute_direct_msg"), ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL}
} else {
publishChannelMemberEvt(a, channelMember, args.UserId)
return &model.CommandResponse{Text: args.T("api.command_mute.success_unmute_direct_msg"), ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL}
}
}
if channelMember.NotifyProps[model.MARK_UNREAD_NOTIFY_PROP] == model.CHANNEL_NOTIFY_MENTION {
publishChannelMemberEvt(a, channelMember, args.UserId)
return &model.CommandResponse{Text: args.T("api.command_mute.success_mute", map[string]interface{}{"Channel": channel.DisplayName}), ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL}
} else {
publishChannelMemberEvt(a, channelMember, args.UserId)
return &model.CommandResponse{Text: args.T("api.command_mute.success_unmute", map[string]interface{}{"Channel": channel.DisplayName}), ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL}
}
}
func publishChannelMemberEvt(a *app.App, channelMember *model.ChannelMember, userId string) {
evt := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_CHANNEL_MEMBER_UPDATED, "", "", userId, nil)
evt.Add("channelMember", channelMember.ToJson())
a.Publish(evt)
}