MM-31710 Server/API: change total_unread_replies to total_unread_threads (#16663)
Co-authored-by: Mattermod <mattermod@users.noreply.github.com>
Этот коммит содержится в:
коммит произвёл
GitHub
родитель
90952a1bd5
Коммит
911d1f070e
@@ -5549,6 +5549,36 @@ func TestFollowThreads(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func checkThreadListReplies(t *testing.T, th *TestHelper, client *model.Client4, userId string, expectedReplies, expectedThreads int, options *model.GetUserThreadsOpts) (*model.Threads, *model.Response) {
|
||||||
|
opts := model.GetUserThreadsOpts{}
|
||||||
|
if options != nil {
|
||||||
|
opts = *options
|
||||||
|
}
|
||||||
|
u, r := client.GetUserThreads(userId, th.BasicTeam.Id, opts)
|
||||||
|
CheckNoError(t, r)
|
||||||
|
require.Len(t, u.Threads, expectedThreads)
|
||||||
|
|
||||||
|
count := int64(0)
|
||||||
|
sum := int64(0)
|
||||||
|
for _, thr := range u.Threads {
|
||||||
|
if thr.UnreadReplies > 0 {
|
||||||
|
count += 1
|
||||||
|
}
|
||||||
|
sum += thr.UnreadReplies
|
||||||
|
}
|
||||||
|
require.EqualValues(t, expectedReplies, sum)
|
||||||
|
require.Equal(t, count, u.TotalUnreadThreads)
|
||||||
|
|
||||||
|
return u, r
|
||||||
|
}
|
||||||
|
|
||||||
|
func postAndCheck(t *testing.T, client *model.Client4, post *model.Post) (*model.Post, *model.Response) {
|
||||||
|
p, resp := client.CreatePost(post)
|
||||||
|
CheckNoError(t, resp)
|
||||||
|
CheckCreatedStatus(t, resp)
|
||||||
|
return p, resp
|
||||||
|
}
|
||||||
|
|
||||||
func TestMaintainUnreadRepliesInThread(t *testing.T) {
|
func TestMaintainUnreadRepliesInThread(t *testing.T) {
|
||||||
th := Setup(t).InitBasic()
|
th := Setup(t).InitBasic()
|
||||||
defer th.TearDown()
|
defer th.TearDown()
|
||||||
@@ -5564,63 +5594,80 @@ func TestMaintainUnreadRepliesInThread(t *testing.T) {
|
|||||||
defer th.App.Srv().Store.Post().PermanentDeleteByUser(th.SystemAdminUser.Id)
|
defer th.App.Srv().Store.Post().PermanentDeleteByUser(th.SystemAdminUser.Id)
|
||||||
|
|
||||||
// create a post by regular user
|
// create a post by regular user
|
||||||
rpost, resp := Client.CreatePost(&model.Post{ChannelId: th.BasicChannel.Id, Message: "testMsg"})
|
rpost, _ := postAndCheck(t, Client, &model.Post{ChannelId: th.BasicChannel.Id, Message: "testMsg"})
|
||||||
CheckNoError(t, resp)
|
|
||||||
CheckCreatedStatus(t, resp)
|
|
||||||
// reply with another
|
// reply with another
|
||||||
_, resp2 := th.SystemAdminClient.CreatePost(&model.Post{ChannelId: th.BasicChannel.Id, Message: "testReply", RootId: rpost.Id})
|
postAndCheck(t, th.SystemAdminClient, &model.Post{ChannelId: th.BasicChannel.Id, Message: "testReply", RootId: rpost.Id})
|
||||||
CheckNoError(t, resp2)
|
|
||||||
CheckCreatedStatus(t, resp2)
|
|
||||||
|
|
||||||
checkThreadList := func(client *model.Client4, userId string, expectedReplies, expectedThreads int) (*model.Threads, *model.Response) {
|
|
||||||
u, r := client.GetUserThreads(userId, th.BasicTeam.Id, model.GetUserThreadsOpts{
|
|
||||||
Page: 0,
|
|
||||||
PageSize: 30,
|
|
||||||
Deleted: false,
|
|
||||||
})
|
|
||||||
CheckNoError(t, r)
|
|
||||||
require.Len(t, u.Threads, expectedThreads)
|
|
||||||
require.EqualValues(t, expectedReplies, u.Threads[0].UnreadReplies)
|
|
||||||
|
|
||||||
sum := int64(0)
|
|
||||||
for _, thr := range u.Threads {
|
|
||||||
sum += thr.UnreadReplies
|
|
||||||
}
|
|
||||||
require.Equal(t, sum, u.TotalUnreadReplies)
|
|
||||||
|
|
||||||
return u, r
|
|
||||||
}
|
|
||||||
// regular user should have one thread with one reply
|
// regular user should have one thread with one reply
|
||||||
checkThreadList(th.Client, th.BasicUser.Id, 1, 1)
|
checkThreadListReplies(t, th, th.Client, th.BasicUser.Id, 1, 1, nil)
|
||||||
|
|
||||||
// add another reply by regular user
|
// add another reply by regular user
|
||||||
_, resp3 := Client.CreatePost(&model.Post{ChannelId: th.BasicChannel.Id, Message: "testReply2", RootId: rpost.Id})
|
postAndCheck(t, Client, &model.Post{ChannelId: th.BasicChannel.Id, Message: "testReply2", RootId: rpost.Id})
|
||||||
CheckNoError(t, resp3)
|
|
||||||
CheckCreatedStatus(t, resp3)
|
|
||||||
|
|
||||||
// replying to the thread clears reply count, so it should be 0
|
// replying to the thread clears reply count, so it should be 0
|
||||||
checkThreadList(th.Client, th.BasicUser.Id, 0, 1)
|
checkThreadListReplies(t, th, th.Client, th.BasicUser.Id, 0, 1, nil)
|
||||||
|
|
||||||
// the other user should have 2 replies
|
// the other user should have 2 replies
|
||||||
checkThreadList(th.SystemAdminClient, th.SystemAdminUser.Id, 2, 1)
|
checkThreadListReplies(t, th, th.SystemAdminClient, th.SystemAdminUser.Id, 2, 1, nil)
|
||||||
|
|
||||||
// mark all as read for user
|
// mark all as read for user
|
||||||
resp = th.Client.UpdateThreadsReadForUser(th.BasicUser.Id, th.BasicTeam.Id)
|
resp := th.Client.UpdateThreadsReadForUser(th.BasicUser.Id, th.BasicTeam.Id)
|
||||||
CheckNoError(t, resp)
|
CheckNoError(t, resp)
|
||||||
CheckOKStatus(t, resp)
|
CheckOKStatus(t, resp)
|
||||||
|
|
||||||
// reply count should be 0
|
// reply count should be 0
|
||||||
checkThreadList(th.Client, th.BasicUser.Id, 0, 1)
|
checkThreadListReplies(t, th, th.Client, th.BasicUser.Id, 0, 1, nil)
|
||||||
|
|
||||||
// the other user should also have 2
|
// the other user should also have 2
|
||||||
checkThreadList(th.SystemAdminClient, th.SystemAdminUser.Id, 2, 1)
|
checkThreadListReplies(t, th, th.SystemAdminClient, th.SystemAdminUser.Id, 2, 1, nil)
|
||||||
}
|
}
|
||||||
func postAndCheck(t *testing.T, client *model.Client4, post *model.Post) (*model.Post, *model.Response) {
|
|
||||||
p, resp := client.CreatePost(post)
|
func TestThreadCounts(t *testing.T) {
|
||||||
CheckNoError(t, resp)
|
th := Setup(t).InitBasic()
|
||||||
CheckCreatedStatus(t, resp)
|
defer th.TearDown()
|
||||||
return p, resp
|
os.Setenv("MM_FEATUREFLAGS_COLLAPSEDTHREADS", "true")
|
||||||
|
defer os.Unsetenv("MM_FEATUREFLAGS_COLLAPSEDTHREADS")
|
||||||
|
th.App.UpdateConfig(func(cfg *model.Config) {
|
||||||
|
*cfg.ServiceSettings.ThreadAutoFollow = true
|
||||||
|
*cfg.ServiceSettings.CollapsedThreads = model.COLLAPSED_THREADS_DEFAULT_ON
|
||||||
|
})
|
||||||
|
|
||||||
|
Client := th.Client
|
||||||
|
defer th.App.Srv().Store.Post().PermanentDeleteByUser(th.BasicUser.Id)
|
||||||
|
defer th.App.Srv().Store.Post().PermanentDeleteByUser(th.SystemAdminUser.Id)
|
||||||
|
|
||||||
|
// create a post by regular user
|
||||||
|
rpost, _ := postAndCheck(t, Client, &model.Post{ChannelId: th.BasicChannel.Id, Message: "testMsg"})
|
||||||
|
// reply with another
|
||||||
|
time.Sleep(1)
|
||||||
|
postAndCheck(t, th.SystemAdminClient, &model.Post{ChannelId: th.BasicChannel.Id, Message: "testReply", RootId: rpost.Id})
|
||||||
|
|
||||||
|
// create another post by regular user
|
||||||
|
time.Sleep(1)
|
||||||
|
rpost2, _ := postAndCheck(t, Client, &model.Post{ChannelId: th.BasicChannel2.Id, Message: "testMsg1"})
|
||||||
|
// reply with another 2 times
|
||||||
|
time.Sleep(1)
|
||||||
|
postAndCheck(t, th.SystemAdminClient, &model.Post{ChannelId: th.BasicChannel2.Id, Message: "testReply2", RootId: rpost2.Id})
|
||||||
|
postAndCheck(t, th.SystemAdminClient, &model.Post{ChannelId: th.BasicChannel2.Id, Message: "testReply22", RootId: rpost2.Id})
|
||||||
|
|
||||||
|
// regular user should have two threads with 3 replies total
|
||||||
|
checkThreadListReplies(t, th, th.Client, th.BasicUser.Id, 3, 2, &model.GetUserThreadsOpts{
|
||||||
|
Deleted: false,
|
||||||
|
})
|
||||||
|
|
||||||
|
// delete first thread
|
||||||
|
th.App.Srv().Store.Post().Delete(rpost.Id, model.GetMillis(), th.BasicUser.Id)
|
||||||
|
|
||||||
|
// we should now have 1 thread with 2 replies
|
||||||
|
checkThreadListReplies(t, th, th.Client, th.BasicUser.Id, 2, 1, &model.GetUserThreadsOpts{
|
||||||
|
Deleted: false,
|
||||||
|
})
|
||||||
|
// with Deleted we should get the same as before deleting, minus the reply in the deleted thread
|
||||||
|
checkThreadListReplies(t, th, th.Client, th.BasicUser.Id, 3, 2, &model.GetUserThreadsOpts{
|
||||||
|
Deleted: true,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMaintainUnreadMentionsInThread(t *testing.T) {
|
func TestMaintainUnreadMentionsInThread(t *testing.T) {
|
||||||
th := Setup(t).InitBasic()
|
th := Setup(t).InitBasic()
|
||||||
defer th.TearDown()
|
defer th.TearDown()
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ type ThreadResponse struct {
|
|||||||
|
|
||||||
type Threads struct {
|
type Threads struct {
|
||||||
Total int64 `json:"total"`
|
Total int64 `json:"total"`
|
||||||
TotalUnreadReplies int64 `json:"total_unread_replies"`
|
TotalUnreadThreads int64 `json:"total_unread_threads"`
|
||||||
TotalUnreadMentions int64 `json:"total_unread_mentions"`
|
TotalUnreadMentions int64 `json:"total_unread_mentions"`
|
||||||
Threads []*ThreadResponse `json:"threads"`
|
Threads []*ThreadResponse `json:"threads"`
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -120,19 +120,22 @@ func (s *SqlThreadStore) GetThreadsForUser(userId, teamId string, opts model.Get
|
|||||||
model.Post
|
model.Post
|
||||||
}
|
}
|
||||||
|
|
||||||
unreadRepliesQuery := "SELECT COUNT(Posts.Id) From Posts Where Posts.RootId=ThreadMemberships.PostId AND Posts.UpdateAt >= ThreadMemberships.LastViewed AND Posts.DeleteAt=0"
|
unreadRepliesQuery := "SELECT COUNT(Posts.Id) From Posts Where Posts.RootId=ThreadMemberships.PostId AND Posts.UpdateAt >= ThreadMemberships.LastViewed"
|
||||||
fetchConditions := sq.And{
|
fetchConditions := sq.And{
|
||||||
sq.Or{sq.Eq{"Channels.TeamId": teamId}, sq.Eq{"Channels.TeamId": ""}},
|
sq.Or{sq.Eq{"Channels.TeamId": teamId}, sq.Eq{"Channels.TeamId": ""}},
|
||||||
sq.Eq{"ThreadMemberships.UserId": userId},
|
sq.Eq{"ThreadMemberships.UserId": userId},
|
||||||
sq.Eq{"ThreadMemberships.Following": true},
|
sq.Eq{"ThreadMemberships.Following": true},
|
||||||
}
|
}
|
||||||
|
if !opts.Deleted {
|
||||||
|
fetchConditions = sq.And{fetchConditions, sq.Eq{"Posts.DeleteAt": 0}}
|
||||||
|
}
|
||||||
|
|
||||||
pageSize := uint64(30)
|
pageSize := uint64(30)
|
||||||
if opts.PageSize == 0 {
|
if opts.PageSize != 0 {
|
||||||
pageSize = opts.PageSize
|
pageSize = opts.PageSize
|
||||||
}
|
}
|
||||||
|
|
||||||
totalUnreadRepliesChan := make(chan store.StoreResult, 1)
|
totalUnreadThreadsChan := make(chan store.StoreResult, 1)
|
||||||
totalCountChan := make(chan store.StoreResult, 1)
|
totalCountChan := make(chan store.StoreResult, 1)
|
||||||
totalUnreadMentionsChan := make(chan store.StoreResult, 1)
|
totalUnreadMentionsChan := make(chan store.StoreResult, 1)
|
||||||
threadsChan := make(chan store.StoreResult, 1)
|
threadsChan := make(chan store.StoreResult, 1)
|
||||||
@@ -140,20 +143,21 @@ func (s *SqlThreadStore) GetThreadsForUser(userId, teamId string, opts model.Get
|
|||||||
repliesQuery, repliesQueryArgs, _ := s.getQueryBuilder().
|
repliesQuery, repliesQueryArgs, _ := s.getQueryBuilder().
|
||||||
Select("COUNT(Posts.Id)").
|
Select("COUNT(Posts.Id)").
|
||||||
From("Posts").
|
From("Posts").
|
||||||
LeftJoin("ThreadMemberships ON Posts.RootId = ThreadMemberships.PostId").
|
LeftJoin("ThreadMemberships ON Posts.Id = ThreadMemberships.PostId").
|
||||||
LeftJoin("Channels ON Posts.ChannelId = Channels.Id").
|
LeftJoin("Channels ON Posts.ChannelId = Channels.Id").
|
||||||
Where(fetchConditions).
|
Where(fetchConditions).
|
||||||
Where("Posts.UpdateAt >= ThreadMemberships.LastViewed").ToSql()
|
Where("Posts.UpdateAt >= ThreadMemberships.LastViewed").ToSql()
|
||||||
|
|
||||||
totalUnreadReplies, err := s.GetMaster().SelectInt(repliesQuery, repliesQueryArgs...)
|
totalUnreadThreads, err := s.GetMaster().SelectInt(repliesQuery, repliesQueryArgs...)
|
||||||
totalUnreadRepliesChan <- store.StoreResult{Data: totalUnreadReplies, NErr: errors.Wrapf(err, "failed to get count replies on threads for user id=%s", userId)}
|
totalUnreadThreadsChan <- store.StoreResult{Data: totalUnreadThreads, NErr: errors.Wrapf(err, "failed to get count unread on threads for user id=%s", userId)}
|
||||||
close(totalUnreadRepliesChan)
|
close(totalUnreadThreadsChan)
|
||||||
}()
|
}()
|
||||||
go func() {
|
go func() {
|
||||||
threadsQuery, threadsQueryArgs, _ := s.getQueryBuilder().
|
threadsQuery, threadsQueryArgs, _ := s.getQueryBuilder().
|
||||||
Select("COUNT(ThreadMemberships.PostId)").
|
Select("COUNT(ThreadMemberships.PostId)").
|
||||||
LeftJoin("Threads ON Threads.PostId = ThreadMemberships.PostId").
|
LeftJoin("Threads ON Threads.PostId = ThreadMemberships.PostId").
|
||||||
LeftJoin("Channels ON Threads.ChannelId = Channels.Id").
|
LeftJoin("Channels ON Threads.ChannelId = Channels.Id").
|
||||||
|
LeftJoin("Posts ON Posts.Id = ThreadMemberships.PostId").
|
||||||
From("ThreadMemberships").
|
From("ThreadMemberships").
|
||||||
Where(fetchConditions).ToSql()
|
Where(fetchConditions).ToSql()
|
||||||
|
|
||||||
@@ -166,6 +170,7 @@ func (s *SqlThreadStore) GetThreadsForUser(userId, teamId string, opts model.Get
|
|||||||
Select("COALESCE(SUM(ThreadMemberships.UnreadMentions),0)").
|
Select("COALESCE(SUM(ThreadMemberships.UnreadMentions),0)").
|
||||||
From("ThreadMemberships").
|
From("ThreadMemberships").
|
||||||
LeftJoin("Threads ON Threads.PostId = ThreadMemberships.PostId").
|
LeftJoin("Threads ON Threads.PostId = ThreadMemberships.PostId").
|
||||||
|
LeftJoin("Posts ON Posts.Id = ThreadMemberships.PostId").
|
||||||
LeftJoin("Channels ON Threads.ChannelId = Channels.Id").
|
LeftJoin("Channels ON Threads.ChannelId = Channels.Id").
|
||||||
Where(fetchConditions).ToSql()
|
Where(fetchConditions).ToSql()
|
||||||
totalUnreadMentions, err := s.GetMaster().SelectInt(mentionsQuery, mentionsQueryArgs...)
|
totalUnreadMentions, err := s.GetMaster().SelectInt(mentionsQuery, mentionsQueryArgs...)
|
||||||
@@ -174,9 +179,6 @@ func (s *SqlThreadStore) GetThreadsForUser(userId, teamId string, opts model.Get
|
|||||||
}()
|
}()
|
||||||
go func() {
|
go func() {
|
||||||
newFetchConditions := fetchConditions
|
newFetchConditions := fetchConditions
|
||||||
if !opts.Deleted {
|
|
||||||
newFetchConditions = sq.And{fetchConditions, sq.Eq{"Posts.DeleteAt": 0}}
|
|
||||||
}
|
|
||||||
if opts.Since > 0 {
|
if opts.Since > 0 {
|
||||||
newFetchConditions = sq.And{newFetchConditions, sq.GtOrEq{"Threads.LastReplyAt": opts.Since}}
|
newFetchConditions = sq.And{newFetchConditions, sq.GtOrEq{"Threads.LastReplyAt": opts.Since}}
|
||||||
}
|
}
|
||||||
@@ -216,11 +218,11 @@ func (s *SqlThreadStore) GetThreadsForUser(userId, teamId string, opts model.Get
|
|||||||
}
|
}
|
||||||
totalCount := totalCountResult.Data.(int64)
|
totalCount := totalCountResult.Data.(int64)
|
||||||
|
|
||||||
totalUnreadRepliesResult := <-totalUnreadRepliesChan
|
totalUnreadThreadsResult := <-totalUnreadThreadsChan
|
||||||
if totalUnreadRepliesResult.NErr != nil {
|
if totalUnreadThreadsResult.NErr != nil {
|
||||||
return nil, totalUnreadRepliesResult.NErr
|
return nil, totalUnreadThreadsResult.NErr
|
||||||
}
|
}
|
||||||
totalUnreadReplies := totalUnreadRepliesResult.Data.(int64)
|
totalUnreadThreads := totalUnreadThreadsResult.Data.(int64)
|
||||||
|
|
||||||
var userIds []string
|
var userIds []string
|
||||||
userIdMap := map[string]bool{}
|
userIdMap := map[string]bool{}
|
||||||
@@ -249,7 +251,7 @@ func (s *SqlThreadStore) GetThreadsForUser(userId, teamId string, opts model.Get
|
|||||||
Total: totalCount,
|
Total: totalCount,
|
||||||
Threads: nil,
|
Threads: nil,
|
||||||
TotalUnreadMentions: totalUnreadMentions,
|
TotalUnreadMentions: totalUnreadMentions,
|
||||||
TotalUnreadReplies: totalUnreadReplies,
|
TotalUnreadThreads: totalUnreadThreads,
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, thread := range threads {
|
for _, thread := range threads {
|
||||||
|
|||||||
Ссылка в новой задаче
Block a user