MM-34758 Collapsed Reply Threads without mobile support (#17424)

Summary
added support for legacy clients accessing server
added collapsed_threads_supported param to viewChannel API and setPostUnread API

Ticket Link
https://mattermost.atlassian.net/browse/MM-34758

Related Webapp PR
mattermost/mattermost-webapp#7933
Этот коммит содержится в:
Eli Yukelzon
2021-05-26 18:10:25 +03:00
коммит произвёл GitHub
родитель ebed0c67f7
Коммит 46649292f8
21 изменённых файлов: 417 добавлений и 65 удалений

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

@@ -1330,7 +1330,7 @@ func viewChannel(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
times, err := c.App.ViewChannel(view, c.Params.UserId, c.AppContext.Session().Id)
times, err := c.App.ViewChannel(view, c.Params.UserId, c.AppContext.Session().Id, view.CollapsedThreadsSupported)
if err != nil {
c.Err = err
return

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

@@ -7,6 +7,7 @@ import (
"context"
"fmt"
"net/http"
"os"
"sort"
"strings"
"sync"
@@ -4178,6 +4179,7 @@ func TestMoveChannel(t *testing.T) {
func TestRootMentionsCount(t *testing.T) {
th := Setup(t).InitBasic()
defer th.TearDown()
Client := th.Client
user := th.BasicUser
channel := th.BasicChannel
@@ -4214,3 +4216,44 @@ func TestRootMentionsCount(t *testing.T) {
require.Equal(t, int64(1), counts.MentionCountRoot)
require.Equal(t, int64(2), counts.MentionCount)
}
func TestViewChannelWithoutCollapsedThreads(t *testing.T) {
th := Setup(t).InitBasic()
defer th.TearDown()
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
user := th.BasicUser
team := th.BasicTeam
channel := th.BasicChannel
// mention the user in a root post
post1, resp := th.SystemAdminClient.CreatePost(&model.Post{ChannelId: channel.Id, Message: "hey @" + user.Username})
CheckNoError(t, resp)
// mention the user in a reply post
post2 := &model.Post{ChannelId: channel.Id, Message: "reply at @" + user.Username, RootId: post1.Id}
_, resp = th.SystemAdminClient.CreatePost(post2)
CheckNoError(t, resp)
threads, resp := Client.GetUserThreads(user.Id, team.Id, model.GetUserThreadsOpts{})
CheckNoError(t, resp)
require.EqualValues(t, int64(1), threads.TotalUnreadMentions)
// simulate opening the channel from an old client
_, resp = Client.ViewChannel(user.Id, &model.ChannelView{
ChannelId: channel.Id,
PrevChannelId: "",
CollapsedThreadsSupported: false,
})
CheckNoError(t, resp)
threads, resp = Client.GetUserThreads(user.Id, team.Id, model.GetUserThreadsOpts{})
CheckNoError(t, resp)
require.Zero(t, threads.TotalUnreadMentions)
}

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

@@ -639,11 +639,15 @@ func patchPost(c *Context, w http.ResponseWriter, r *http.Request) {
w.Write([]byte(patchedPost.ToJson()))
}
func setPostUnread(c *Context, w http.ResponseWriter, _ *http.Request) {
func setPostUnread(c *Context, w http.ResponseWriter, r *http.Request) {
c.RequirePostId().RequireUserId()
if c.Err != nil {
return
}
props := model.MapBoolFromJson(r.Body)
collapsedThreadsSupported := props["collapsed_threads_supported"]
if c.AppContext.Session().UserId != c.Params.UserId && !c.App.SessionHasPermissionToUser(*c.AppContext.Session(), c.Params.UserId) {
c.SetPermissionError(model.PERMISSION_EDIT_OTHER_USERS)
return
@@ -653,7 +657,7 @@ func setPostUnread(c *Context, w http.ResponseWriter, _ *http.Request) {
return
}
state, err := c.App.MarkChannelAsUnreadFromPost(c.Params.PostId, c.Params.UserId)
state, err := c.App.MarkChannelAsUnreadFromPost(c.Params.PostId, c.Params.UserId, collapsedThreadsSupported)
if err != nil {
c.Err = err
return

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

@@ -2493,14 +2493,14 @@ func TestSetChannelUnread(t *testing.T) {
unread, err = th.App.GetChannelUnread(c1.Id, u2.Id)
require.Nil(t, err)
require.Equal(t, int64(4), unread.MsgCount)
_, err = th.App.ViewChannel(c1toc2, u2.Id, s2.Id)
_, err = th.App.ViewChannel(c1toc2, u2.Id, s2.Id, false)
require.Nil(t, err)
unread, err = th.App.GetChannelUnread(c1.Id, u2.Id)
require.Nil(t, err)
require.Equal(t, int64(0), unread.MsgCount)
t.Run("Unread last one", func(t *testing.T) {
r := th.Client.SetPostUnread(u1.Id, p2.Id)
r := th.Client.SetPostUnread(u1.Id, p2.Id, true)
checkHTTPStatus(t, r, 200, false)
unread, err := th.App.GetChannelUnread(c1.Id, u1.Id)
require.Nil(t, err)
@@ -2508,12 +2508,12 @@ func TestSetChannelUnread(t *testing.T) {
})
t.Run("Unread on a private channel", func(t *testing.T) {
r := th.Client.SetPostUnread(u1.Id, pp2.Id)
r := th.Client.SetPostUnread(u1.Id, pp2.Id, true)
assert.Equal(t, 200, r.StatusCode)
unread, err := th.App.GetChannelUnread(th.BasicPrivateChannel.Id, u1.Id)
require.Nil(t, err)
assert.Equal(t, int64(1), unread.MsgCount)
r = th.Client.SetPostUnread(u1.Id, pp1.Id)
r = th.Client.SetPostUnread(u1.Id, pp1.Id, true)
assert.Equal(t, 200, r.StatusCode)
unread, err = th.App.GetChannelUnread(th.BasicPrivateChannel.Id, u1.Id)
require.Nil(t, err)
@@ -2521,7 +2521,7 @@ func TestSetChannelUnread(t *testing.T) {
})
t.Run("Can't unread an imaginary post", func(t *testing.T) {
r := th.Client.SetPostUnread(u1.Id, "invalid4ofngungryquinj976y")
r := th.Client.SetPostUnread(u1.Id, "invalid4ofngungryquinj976y", true)
assert.Equal(t, http.StatusForbidden, r.StatusCode)
})
@@ -2531,18 +2531,18 @@ func TestSetChannelUnread(t *testing.T) {
c3.Login(u3.Email, u3.Password)
t.Run("Can't unread channels you don't belong to", func(t *testing.T) {
r := c3.SetPostUnread(u3.Id, pp1.Id)
r := c3.SetPostUnread(u3.Id, pp1.Id, true)
assert.Equal(t, http.StatusForbidden, r.StatusCode)
})
t.Run("Can't unread users you don't have permission to edit", func(t *testing.T) {
r := c3.SetPostUnread(u1.Id, pp1.Id)
r := c3.SetPostUnread(u1.Id, pp1.Id, true)
assert.Equal(t, http.StatusForbidden, r.StatusCode)
})
t.Run("Can't unread if user is not logged in", func(t *testing.T) {
th.Client.Logout()
response := th.Client.SetPostUnread(u1.Id, p2.Id)
response := th.Client.SetPostUnread(u1.Id, p2.Id, true)
checkHTTPStatus(t, response, http.StatusUnauthorized, true)
})
}
@@ -2565,7 +2565,7 @@ func TestMarkUnreadCausesAutofollow(t *testing.T) {
require.Nil(t, appErr)
require.Zero(t, threads.Total)
_, appErr = th.App.MarkChannelAsUnreadFromPost(replyPost.Id, th.BasicUser.Id)
_, appErr = th.App.MarkChannelAsUnreadFromPost(replyPost.Id, th.BasicUser.Id, true)
require.Nil(t, appErr)
threads, appErr = th.App.GetThreadsForUser(th.BasicUser.Id, th.BasicTeam.Id, model.GetUserThreadsOpts{})
@@ -2573,3 +2573,70 @@ func TestMarkUnreadCausesAutofollow(t *testing.T) {
require.NotZero(t, threads.Total)
}
func TestSetPostUnreadWithoutCollapsedThreads(t *testing.T) {
os.Setenv("MM_FEATUREFLAGS_COLLAPSEDTHREADS", "true")
defer os.Unsetenv("MM_FEATUREFLAGS_COLLAPSEDTHREADS")
th := Setup(t).InitBasic()
defer th.TearDown()
th.App.UpdateConfig(func(cfg *model.Config) {
*cfg.ServiceSettings.ThreadAutoFollow = true
*cfg.ServiceSettings.CollapsedThreads = model.COLLAPSED_THREADS_DEFAULT_ON
})
// user2: first root mention @user1
// - user1: hello
// - user2: mention @u1
// - user1: another repoy
// - user2: another mention @u1
// user1: a root post
// user2: Another root mention @u1
user1Mention := " @" + th.BasicUser.Username
rootPost1, appErr := th.App.CreatePost(th.Context, &model.Post{UserId: th.BasicUser2.Id, CreateAt: model.GetMillis(), ChannelId: th.BasicChannel.Id, Message: "first root mention" + user1Mention}, th.BasicChannel, false, false)
require.Nil(t, appErr)
_, appErr = th.App.CreatePost(th.Context, &model.Post{RootId: rootPost1.Id, UserId: th.BasicUser.Id, CreateAt: model.GetMillis(), ChannelId: th.BasicChannel.Id, Message: "hello"}, th.BasicChannel, false, false)
require.Nil(t, appErr)
replyPost1, appErr := th.App.CreatePost(th.Context, &model.Post{RootId: rootPost1.Id, UserId: th.BasicUser2.Id, CreateAt: model.GetMillis(), ChannelId: th.BasicChannel.Id, Message: "mention" + user1Mention}, th.BasicChannel, false, false)
require.Nil(t, appErr)
_, appErr = th.App.CreatePost(th.Context, &model.Post{RootId: rootPost1.Id, UserId: th.BasicUser.Id, CreateAt: model.GetMillis(), ChannelId: th.BasicChannel.Id, Message: "another reply"}, th.BasicChannel, false, false)
require.Nil(t, appErr)
_, appErr = th.App.CreatePost(th.Context, &model.Post{RootId: rootPost1.Id, UserId: th.BasicUser2.Id, CreateAt: model.GetMillis(), ChannelId: th.BasicChannel.Id, Message: "another mention" + user1Mention}, th.BasicChannel, false, false)
require.Nil(t, appErr)
_, appErr = th.App.CreatePost(th.Context, &model.Post{UserId: th.BasicUser.Id, CreateAt: model.GetMillis(), ChannelId: th.BasicChannel.Id, Message: "a root post"}, th.BasicChannel, false, false)
require.Nil(t, appErr)
_, appErr = th.App.CreatePost(th.Context, &model.Post{UserId: th.BasicUser2.Id, CreateAt: model.GetMillis(), ChannelId: th.BasicChannel.Id, Message: "another root mention" + user1Mention}, th.BasicChannel, false, false)
require.Nil(t, appErr)
t.Run("Mark reply post as unread", func(t *testing.T) {
resp := th.Client.SetPostUnread(th.BasicUser.Id, replyPost1.Id, false)
CheckNoError(t, resp)
channelUnread, appErr := th.App.GetChannelUnread(th.BasicChannel.Id, th.BasicUser.Id)
require.Nil(t, appErr)
require.Equal(t, int64(3), channelUnread.MentionCount)
// MentionCountRoot should be zero so that supported clients don't show a mention badge for the channel
require.Equal(t, int64(0), channelUnread.MentionCountRoot)
require.Equal(t, int64(5), channelUnread.MsgCount)
// MentionCountRoot should be zero so that supported clients don't show the channel as unread
require.Equal(t, channelUnread.MsgCountRoot, int64(0))
thread, err := th.App.GetThreadForUser(th.BasicUser.Id, th.BasicTeam.Id, rootPost1.Id, false)
require.Nil(t, err)
require.Equal(t, int64(2), thread.UnreadMentions)
require.Equal(t, int64(3), thread.UnreadReplies)
})
t.Run("Mark root post as unread", func(t *testing.T) {
resp := th.Client.SetPostUnread(th.BasicUser.Id, rootPost1.Id, false)
CheckNoError(t, resp)
channelUnread, appErr := th.App.GetChannelUnread(th.BasicChannel.Id, th.BasicUser.Id)
require.Nil(t, appErr)
require.Equal(t, int64(4), channelUnread.MentionCount)
require.Equal(t, int64(2), channelUnread.MentionCountRoot)
require.Equal(t, int64(7), channelUnread.MsgCount)
require.Equal(t, int64(3), channelUnread.MsgCountRoot)
})
}