* Mark category as read

* Fix lint and test

* Fix tests

* Fix test and remove wrong aria

* Address server issues and add mark as read for unreads

* Missing changes

* Fix tests

* fix tests

* Add confirmation popup to mark as read category

* Always use viewMultipleChannels and other fixes

* Remove unneeded code

* Fix test

* Address feedback

* Address feedback

* Fix tests

* Fix test

* Fix tests

* Update aria-haspopup depending on the number of channels to mark as viewed

---------

Co-authored-by: Mattermost Build <build@mattermost.com>
Этот коммит содержится в:
Daniel Espino García
2023-08-14 10:01:02 +02:00
коммит произвёл GitHub
родитель c1c07ba1bb
Коммит e9b3afecc2
60 изменённых файлов: 1151 добавлений и 1557 удалений

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

@@ -2970,57 +2970,32 @@ func (a *App) SearchChannelsUserNotIn(c request.CTX, teamID string, userID strin
}
func (a *App) MarkChannelsAsViewed(c request.CTX, channelIDs []string, userID string, currentSessionId string, collapsedThreadsSupported, isCRTEnabled bool) (map[string]int64, *model.AppError) {
// I start looking for channels with notifications before I mark it as read, to clear the push notifications if needed
channelsToClearPushNotifications := []string{}
if a.canSendPushNotifications() {
for _, channelID := range channelIDs {
channel, errCh := a.Srv().Store().Channel().Get(channelID, true)
if errCh != nil {
c.Logger().Warn("Failed to get channel", mlog.Err(errCh))
continue
}
var err error
member, err := a.Srv().Store().Channel().GetMember(context.Background(), channelID, userID)
if err != nil {
c.Logger().Warn("Failed to get membership", mlog.Err(err))
continue
}
notify := member.NotifyProps[model.PushNotifyProp]
if notify == model.ChannelNotifyDefault {
user, err := a.GetUser(userID)
if err != nil {
c.Logger().Warn("Failed to get user", mlog.String("user_id", userID), mlog.Err(err))
continue
}
notify = user.NotifyProps[model.PushNotifyProp]
}
if notify == model.UserNotifyAll {
if count, err := a.Srv().Store().User().GetAnyUnreadPostCountForChannel(userID, channelID); err == nil {
if count > 0 {
channelsToClearPushNotifications = append(channelsToClearPushNotifications, channelID)
}
}
} else if notify == model.UserNotifyMention || channel.Type == model.ChannelTypeDirect {
if count, err := a.Srv().Store().User().GetUnreadCountForChannel(userID, channelID); err == nil {
if count > 0 {
channelsToClearPushNotifications = append(channelsToClearPushNotifications, channelID)
}
}
}
}
user, err := a.Srv().Store().User().Get(c.Context(), userID)
if err != nil {
return nil, model.NewAppError("MarkChannelsAsViewed", "app.user.get.app_error", nil, "", http.StatusInternalServerError).Wrap(err)
}
// We use channelsToView to later only update those, or early return if no channel is to be read
channelsToView, channelsToClearPushNotifications, times, err := a.Srv().Store().Channel().GetChannelsWithUnreadsAndWithMentions(c.Context(), channelIDs, userID, user.NotifyProps)
if err != nil {
return nil, model.NewAppError("MarkChannelsAsViewed", "app.channel.get_channels_with_unreads_and_with_mentions.app_error", nil, "", http.StatusInternalServerError).Wrap(err)
}
if len(channelsToView) == 0 {
return times, nil
}
var err error
updateThreads := *a.Config().ServiceSettings.ThreadAutoFollow && (!collapsedThreadsSupported || !isCRTEnabled)
if updateThreads {
err = a.Srv().Store().Thread().MarkAllAsReadByChannels(userID, channelIDs)
err = a.Srv().Store().Thread().MarkAllAsReadByChannels(userID, channelsToView)
if err != nil {
return nil, model.NewAppError("MarkChannelsAsViewed", "app.channel.update_last_viewed_at.app_error", nil, "", http.StatusInternalServerError).Wrap(err)
return nil, model.NewAppError("MarkChannelsAsViewed", "app.thread.mark_all_as_read_by_channels.app_error", nil, "", http.StatusInternalServerError).Wrap(err)
}
}
times, err := a.Srv().Store().Channel().UpdateLastViewedAt(channelIDs, userID)
_, err = a.Srv().Store().Channel().UpdateLastViewedAt(channelsToView, userID)
if err != nil {
var invErr *store.ErrInvalidInput
switch {
@@ -3032,19 +3007,18 @@ func (a *App) MarkChannelsAsViewed(c request.CTX, channelIDs []string, userID st
}
if *a.Config().ServiceSettings.EnableChannelViewedMessages {
for _, channelID := range channelIDs {
message := model.NewWebSocketEvent(model.WebsocketEventChannelViewed, "", "", userID, nil, "")
message.Add("channel_id", channelID)
a.Publish(message)
}
message := model.NewWebSocketEvent(model.WebsocketEventMultipleChannelsViewed, "", "", userID, nil, "")
message.Add("channel_times", times)
a.Publish(message)
}
for _, channelID := range channelsToClearPushNotifications {
a.clearPushNotification(currentSessionId, userID, channelID, "")
}
if updateThreads && isCRTEnabled {
timestamp := model.GetMillis()
for _, channelID := range channelIDs {
for _, channelID := range channelsToView {
message := model.NewWebSocketEvent(model.WebsocketEventThreadReadChanged, "", channelID, userID, nil, "")
message.Add("timestamp", timestamp)
a.Publish(message)

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

@@ -7,7 +7,6 @@ import (
"context"
"errors"
"fmt"
"net/http"
"sort"
"strings"
"sync"
@@ -18,7 +17,6 @@ import (
"github.com/stretchr/testify/require"
"github.com/mattermost/mattermost/server/public/model"
"github.com/mattermost/mattermost/server/v8/channels/app/users"
"github.com/mattermost/mattermost/server/v8/channels/store/storetest/mocks"
)
@@ -2107,48 +2105,6 @@ func TestPatchChannelModerationsForChannel(t *testing.T) {
})
}
// TestMarkChannelsAsViewedPanic verifies that returning an error from a.GetUser
// does not cause a panic.
func TestMarkChannelsAsViewedPanic(t *testing.T) {
th := SetupWithStoreMock(t)
defer th.TearDown()
mockStore := th.App.Srv().Store().(*mocks.Store)
mockUserStore := mocks.UserStore{}
mockUserStore.On("Get", context.Background(), "userID").Return(nil, model.NewAppError("SqlUserStore.Get", "app.user.get.app_error", nil, "user_id=userID", http.StatusInternalServerError))
mockChannelStore := mocks.ChannelStore{}
mockChannelStore.On("Get", "channelID", true).Return(&model.Channel{}, nil)
mockChannelStore.On("GetMember", context.Background(), "channelID", "userID").Return(&model.ChannelMember{
NotifyProps: model.StringMap{
model.PushNotifyProp: model.ChannelNotifyDefault,
}}, nil)
times := map[string]int64{
"userID": 1,
}
mockChannelStore.On("UpdateLastViewedAt", []string{"channelID"}, "userID").Return(times, nil)
mockSessionStore := mocks.SessionStore{}
mockOAuthStore := mocks.OAuthStore{}
var err error
th.App.ch.srv.userService, err = users.New(users.ServiceConfig{
UserStore: &mockUserStore,
SessionStore: &mockSessionStore,
OAuthStore: &mockOAuthStore,
ConfigFn: th.App.ch.srv.platform.Config,
LicenseFn: th.App.ch.srv.License,
})
require.NoError(t, err)
mockPreferenceStore := mocks.PreferenceStore{}
mockPreferenceStore.On("Get", mock.AnythingOfType("string"), mock.AnythingOfType("string"), mock.AnythingOfType("string")).Return(&model.Preference{Value: "test"}, nil)
mockStore.On("Channel").Return(&mockChannelStore)
mockStore.On("Preference").Return(&mockPreferenceStore)
mockThreadStore := mocks.ThreadStore{}
mockThreadStore.On("MarkAllAsReadByChannels", "userID", []string{"channelID"}).Return(nil)
mockStore.On("Thread").Return(&mockThreadStore)
_, appErr := th.App.MarkChannelsAsViewed(th.Context, []string{"channelID"}, "userID", th.Context.Session().Id, false, false)
require.Nil(t, appErr)
}
func TestClearChannelMembersCache(t *testing.T) {
th := SetupWithStoreMock(t)
defer th.TearDown()

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

@@ -726,7 +726,7 @@ func (wc *WebConn) ShouldSendEvent(msg *model.WebSocketEvent) bool {
switch msg.EventType() {
case model.WebsocketEventTyping,
model.WebsocketEventStatusChange,
model.WebsocketEventChannelViewed:
model.WebsocketEventMultipleChannelsViewed:
if time.Since(wc.lastLogTimeSlow) > websocketSuppressWarnThreshold {
mlog.Warn(
"websocket.slow: dropping message",

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

@@ -1113,26 +1113,6 @@ func TestCreatePostAsUser(t *testing.T) {
require.Equal(t, channelMemberAfter.LastViewedAt, channelMemberBefore.LastViewedAt)
})
t.Run("logs warning for user not in channel", func(t *testing.T) {
th := Setup(t).InitBasic()
defer th.TearDown()
user := th.CreateUser()
th.LinkUserToTeam(user, th.BasicTeam)
post := &model.Post{
ChannelId: th.BasicChannel.Id,
Message: "test",
UserId: user.Id,
}
_, appErr := th.App.CreatePostAsUser(th.Context, post, "", true)
require.Nil(t, appErr)
require.NoError(t, th.TestLogger.Flush())
testlib.AssertLog(t, th.LogBuffer, mlog.LvlWarn.Name, "Failed to get membership")
})
t.Run("does not log warning for bot user not in channel", func(t *testing.T) {
th := Setup(t).InitBasic()
defer th.TearDown()