MM-46410: adds urgency on mention counts (#20999)
* MM-46410: adds urgency on mention counts We have introduced priority for posts in https://github.com/mattermost/mattermost-webapp/pull/10951. We do need to color the mention badges in the webapp with a prominent color when a mention is posted in an urgent message. A thread has urgent mentions if the root post is marked as urgent, and the replies contain mentions to the user viewing the thread. This PR adds a column, urgentmentioncount, in channelmembers. Furthermore when asking for team/thread mention counts, we also return urgent mention counts for the user. Adds a new table to hold posts priorities Refactors priority out of the props and into the new table We are nilifying Metadata when post.ForPlugin(), which didn't save Priority for a post when Boards was enabled. This commit copies metadata again to the post, so metadata are reinstated. Co-authored-by: Mattermod <mattermod@users.noreply.github.com> Co-authored-by: Vishal Choudhary <vish9812@gmail.com>
Этот коммит содержится в:
@@ -716,6 +716,8 @@ type AppIface interface {
|
||||
GetPreferenceByCategoryForUser(userID string, category string) (model.Preferences, *model.AppError)
|
||||
GetPreferencesForUser(userID string) (model.Preferences, *model.AppError)
|
||||
GetPrevPostIdFromPostList(postList *model.PostList, collapsedThreads bool) string
|
||||
GetPriorityForPost(postId string) (*model.PostPriority, *model.AppError)
|
||||
GetPriorityForPostList(list *model.PostList) (map[string]*model.PostPriority, *model.AppError)
|
||||
GetPrivateChannelsForTeam(c request.CTX, teamID string, offset int, limit int) (model.ChannelList, *model.AppError)
|
||||
GetProfileImage(user *model.User) ([]byte, bool, *model.AppError)
|
||||
GetPublicChannelsByIdsForTeam(c request.CTX, teamID string, channelIDs []string) (model.ChannelList, *model.AppError)
|
||||
@@ -927,8 +929,8 @@ type AppIface interface {
|
||||
PostUpdateChannelPurposeMessage(c request.CTX, userID string, channel *model.Channel, oldChannelPurpose string, newChannelPurpose string) *model.AppError
|
||||
PostWithProxyAddedToImageURLs(post *model.Post) *model.Post
|
||||
PostWithProxyRemovedFromImageURLs(post *model.Post) *model.Post
|
||||
PreparePostForClient(c request.CTX, originalPost *model.Post, isNewPost, isEditPost bool) *model.Post
|
||||
PreparePostForClientWithEmbedsAndImages(c request.CTX, originalPost *model.Post, isNewPost, isEditPost bool) *model.Post
|
||||
PreparePostForClient(c request.CTX, originalPost *model.Post, isNewPost, isEditPost, includePriority bool) *model.Post
|
||||
PreparePostForClientWithEmbedsAndImages(c request.CTX, originalPost *model.Post, isNewPost, isEditPost, includePriority bool) *model.Post
|
||||
PreparePostListForClient(c request.CTX, originalList *model.PostList) *model.PostList
|
||||
ProcessSlackText(text string) string
|
||||
Publish(message *model.WebSocketEvent)
|
||||
|
||||
@@ -2609,12 +2609,12 @@ func (a *App) MarkChannelAsUnreadFromPost(c request.CTX, postID string, userID s
|
||||
return nil, err
|
||||
}
|
||||
|
||||
unreadMentions, unreadMentionsRoot, err := a.countMentionsFromPost(c, user, post)
|
||||
unreadMentions, unreadMentionsRoot, urgentMentions, err := a.countMentionsFromPost(c, user, post)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
channelUnread, nErr := a.Srv().Store().Channel().UpdateLastViewedAtPost(post, userID, unreadMentions, unreadMentionsRoot, true)
|
||||
channelUnread, nErr := a.Srv().Store().Channel().UpdateLastViewedAtPost(post, userID, unreadMentions, unreadMentionsRoot, urgentMentions, true)
|
||||
if nErr != nil {
|
||||
return channelUnread, model.NewAppError("MarkChannelAsUnreadFromPost", "app.channel.update_last_viewed_at_post.app_error", nil, "", http.StatusInternalServerError).Wrap(nErr)
|
||||
}
|
||||
@@ -2641,7 +2641,7 @@ func (a *App) markChannelAsUnreadFromPostCRTUnsupported(c request.CTX, postID st
|
||||
threadId = post.Id
|
||||
}
|
||||
|
||||
unreadMentions, unreadMentionsRoot, appErr := a.countMentionsFromPost(c, user, post)
|
||||
unreadMentions, unreadMentionsRoot, urgentMentions, appErr := a.countMentionsFromPost(c, user, post)
|
||||
if appErr != nil {
|
||||
return nil, appErr
|
||||
}
|
||||
@@ -2650,7 +2650,7 @@ func (a *App) markChannelAsUnreadFromPostCRTUnsupported(c request.CTX, postID st
|
||||
// In CRT Supported Client: badge on channel only sums mentions in root posts including and below the post that was marked.
|
||||
// In CRT Unsupported Client: badge on channel sums mentions in all posts (root & replies) including and below the post that was marked unread.
|
||||
if post.RootId == "" {
|
||||
channelUnread, nErr := a.Srv().Store().Channel().UpdateLastViewedAtPost(post, userID, unreadMentions, unreadMentionsRoot, true)
|
||||
channelUnread, nErr := a.Srv().Store().Channel().UpdateLastViewedAtPost(post, userID, unreadMentions, unreadMentionsRoot, urgentMentions, true)
|
||||
if nErr != nil {
|
||||
return channelUnread, model.NewAppError("MarkChannelAsUnreadFromPost", "app.channel.update_last_viewed_at_post.app_error", nil, "", http.StatusInternalServerError).Wrap(nErr)
|
||||
}
|
||||
@@ -2706,7 +2706,7 @@ func (a *App) markChannelAsUnreadFromPostCRTUnsupported(c request.CTX, postID st
|
||||
if mErr != nil {
|
||||
return nil, model.NewAppError("MarkChannelAsUnreadFromPost", "app.channel.update_last_viewed_at_post.app_error", nil, "", http.StatusInternalServerError).Wrap(mErr)
|
||||
}
|
||||
thread, mErr := a.Srv().Store().Thread().GetThreadForUser(threadMembership, true)
|
||||
thread, mErr := a.Srv().Store().Thread().GetThreadForUser(threadMembership, true, a.isPostPriorityEnabled())
|
||||
if mErr != nil {
|
||||
return nil, model.NewAppError("MarkChannelAsUnreadFromPost", "app.channel.update_last_viewed_at_post.app_error", nil, "", http.StatusInternalServerError).Wrap(mErr)
|
||||
}
|
||||
@@ -2724,7 +2724,7 @@ func (a *App) markChannelAsUnreadFromPostCRTUnsupported(c request.CTX, postID st
|
||||
}
|
||||
}
|
||||
|
||||
channelUnread, nErr := a.Srv().Store().Channel().UpdateLastViewedAtPost(post, userID, unreadMentions, 0, false)
|
||||
channelUnread, nErr := a.Srv().Store().Channel().UpdateLastViewedAtPost(post, userID, unreadMentions, 0, 0, false)
|
||||
if nErr != nil {
|
||||
return channelUnread, model.NewAppError("MarkChannelAsUnreadFromPost", "app.channel.update_last_viewed_at_post.app_error", nil, "", http.StatusInternalServerError).Wrap(nErr)
|
||||
}
|
||||
@@ -2741,6 +2741,7 @@ func (a *App) sendWebSocketPostUnreadEvent(c request.CTX, channelUnread *model.C
|
||||
}
|
||||
message.Add("mention_count", channelUnread.MentionCount)
|
||||
message.Add("mention_count_root", channelUnread.MentionCountRoot)
|
||||
message.Add("urgent_mention_count", channelUnread.UrgentMentionCount)
|
||||
message.Add("last_viewed_at", channelUnread.LastViewedAt)
|
||||
message.Add("post_id", postID)
|
||||
a.Publish(message)
|
||||
|
||||
@@ -305,7 +305,8 @@ func (a *App) SendNotifications(c request.CTX, post *model.Post, team *model.Tea
|
||||
mentionedUsersList = append(mentionedUsersList, id)
|
||||
}
|
||||
|
||||
nErr := a.Srv().Store().Channel().IncrementMentionCount(post.ChannelId, mentionedUsersList, post.RootId == "")
|
||||
nErr := a.Srv().Store().Channel().IncrementMentionCount(post.ChannelId, mentionedUsersList, post.RootId == "", post.IsUrgent())
|
||||
|
||||
if nErr != nil {
|
||||
mlog.Warn(
|
||||
"Failed to update mention count",
|
||||
@@ -596,7 +597,7 @@ func (a *App) SendNotifications(c request.CTX, post *model.Post, team *model.Tea
|
||||
}
|
||||
threadMembership = tm
|
||||
}
|
||||
userThread, err := a.Srv().Store().Thread().GetThreadForUser(threadMembership, true)
|
||||
userThread, err := a.Srv().Store().Thread().GetThreadForUser(threadMembership, true, a.isPostPriorityEnabled())
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "cannot get thread %q for user %q", post.RootId, uid)
|
||||
}
|
||||
|
||||
@@ -8118,6 +8118,50 @@ func (a *OpenTracingAppLayer) GetPrevPostIdFromPostList(postList *model.PostList
|
||||
return resultVar0
|
||||
}
|
||||
|
||||
func (a *OpenTracingAppLayer) GetPriorityForPost(postId string) (*model.PostPriority, *model.AppError) {
|
||||
origCtx := a.ctx
|
||||
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.GetPriorityForPost")
|
||||
|
||||
a.ctx = newCtx
|
||||
a.app.Srv().Store().SetContext(newCtx)
|
||||
defer func() {
|
||||
a.app.Srv().Store().SetContext(origCtx)
|
||||
a.ctx = origCtx
|
||||
}()
|
||||
|
||||
defer span.Finish()
|
||||
resultVar0, resultVar1 := a.app.GetPriorityForPost(postId)
|
||||
|
||||
if resultVar1 != nil {
|
||||
span.LogFields(spanlog.Error(resultVar1))
|
||||
ext.Error.Set(span, true)
|
||||
}
|
||||
|
||||
return resultVar0, resultVar1
|
||||
}
|
||||
|
||||
func (a *OpenTracingAppLayer) GetPriorityForPostList(list *model.PostList) (map[string]*model.PostPriority, *model.AppError) {
|
||||
origCtx := a.ctx
|
||||
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.GetPriorityForPostList")
|
||||
|
||||
a.ctx = newCtx
|
||||
a.app.Srv().Store().SetContext(newCtx)
|
||||
defer func() {
|
||||
a.app.Srv().Store().SetContext(origCtx)
|
||||
a.ctx = origCtx
|
||||
}()
|
||||
|
||||
defer span.Finish()
|
||||
resultVar0, resultVar1 := a.app.GetPriorityForPostList(list)
|
||||
|
||||
if resultVar1 != nil {
|
||||
span.LogFields(spanlog.Error(resultVar1))
|
||||
ext.Error.Set(span, true)
|
||||
}
|
||||
|
||||
return resultVar0, resultVar1
|
||||
}
|
||||
|
||||
func (a *OpenTracingAppLayer) GetPrivateChannelsForTeam(c request.CTX, teamID string, offset int, limit int) (model.ChannelList, *model.AppError) {
|
||||
origCtx := a.ctx
|
||||
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.GetPrivateChannelsForTeam")
|
||||
@@ -13023,7 +13067,7 @@ func (a *OpenTracingAppLayer) PostWithProxyRemovedFromImageURLs(post *model.Post
|
||||
return resultVar0
|
||||
}
|
||||
|
||||
func (a *OpenTracingAppLayer) PreparePostForClient(c request.CTX, originalPost *model.Post, isNewPost bool, isEditPost bool) *model.Post {
|
||||
func (a *OpenTracingAppLayer) PreparePostForClient(c request.CTX, originalPost *model.Post, isNewPost bool, isEditPost bool, includePriority bool) *model.Post {
|
||||
origCtx := a.ctx
|
||||
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.PreparePostForClient")
|
||||
|
||||
@@ -13035,12 +13079,12 @@ func (a *OpenTracingAppLayer) PreparePostForClient(c request.CTX, originalPost *
|
||||
}()
|
||||
|
||||
defer span.Finish()
|
||||
resultVar0 := a.app.PreparePostForClient(c, originalPost, isNewPost, isEditPost)
|
||||
resultVar0 := a.app.PreparePostForClient(c, originalPost, isNewPost, isEditPost, includePriority)
|
||||
|
||||
return resultVar0
|
||||
}
|
||||
|
||||
func (a *OpenTracingAppLayer) PreparePostForClientWithEmbedsAndImages(c request.CTX, originalPost *model.Post, isNewPost bool, isEditPost bool) *model.Post {
|
||||
func (a *OpenTracingAppLayer) PreparePostForClientWithEmbedsAndImages(c request.CTX, originalPost *model.Post, isNewPost bool, isEditPost bool, includePriority bool) *model.Post {
|
||||
origCtx := a.ctx
|
||||
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.PreparePostForClientWithEmbedsAndImages")
|
||||
|
||||
@@ -13052,7 +13096,7 @@ func (a *OpenTracingAppLayer) PreparePostForClientWithEmbedsAndImages(c request.
|
||||
}()
|
||||
|
||||
defer span.Finish()
|
||||
resultVar0 := a.app.PreparePostForClientWithEmbedsAndImages(c, originalPost, isNewPost, isEditPost)
|
||||
resultVar0 := a.app.PreparePostForClientWithEmbedsAndImages(c, originalPost, isNewPost, isEditPost, includePriority)
|
||||
|
||||
return resultVar0
|
||||
}
|
||||
|
||||
84
app/post.go
84
app/post.go
@@ -259,7 +259,15 @@ func (a *App) CreatePost(c request.CTX, post *model.Post, channel *model.Channel
|
||||
}
|
||||
}
|
||||
|
||||
if !a.isPostPriorityEnabled() && post.GetPriority() != nil {
|
||||
post.Metadata.Priority = nil
|
||||
}
|
||||
|
||||
if pluginsEnvironment := a.GetPluginsEnvironment(); pluginsEnvironment != nil {
|
||||
var metadata *model.PostMetadata
|
||||
if post.Metadata != nil {
|
||||
metadata = post.Metadata.Copy()
|
||||
}
|
||||
var rejectionError *model.AppError
|
||||
pluginContext := pluginContext(c)
|
||||
pluginsEnvironment.RunMultiPluginHook(func(hooks plugin.Hooks) bool {
|
||||
@@ -273,8 +281,12 @@ func (a *App) CreatePost(c request.CTX, post *model.Post, channel *model.Channel
|
||||
return false
|
||||
}
|
||||
if replacementPost != nil {
|
||||
// the original post's metadata (if there ever was any) is lost, and will be rebuilt.
|
||||
post = replacementPost
|
||||
if post.Metadata != nil && metadata != nil {
|
||||
post.Metadata.Priority = metadata.Priority
|
||||
} else {
|
||||
post.Metadata = metadata
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
@@ -343,7 +355,9 @@ func (a *App) CreatePost(c request.CTX, post *model.Post, channel *model.Channel
|
||||
|
||||
// Normally, we would let the API layer call PreparePostForClient, but we do it here since it also needs
|
||||
// to be done when we send the post over the websocket in handlePostEvents
|
||||
rpost = a.PreparePostForClient(c, rpost, true, false)
|
||||
// PS: we don't want to include PostPriority from the db to avoid the replica lag,
|
||||
// so we just return the one that was passed with post
|
||||
rpost = a.PreparePostForClient(c, rpost, true, false, false)
|
||||
|
||||
// Make sure poster is following the thread
|
||||
if *a.Config().ServiceSettings.ThreadAutoFollow && rpost.RootId != "" {
|
||||
@@ -515,7 +529,7 @@ func (a *App) SendEphemeralPost(c request.CTX, userID string, post *model.Post)
|
||||
|
||||
post.GenerateActionIds()
|
||||
message := model.NewWebSocketEvent(model.WebsocketEventEphemeralMessage, "", post.ChannelId, userID, nil, "")
|
||||
post = a.PreparePostForClientWithEmbedsAndImages(c, post, true, false)
|
||||
post = a.PreparePostForClientWithEmbedsAndImages(c, post, true, false, true)
|
||||
post = model.AddPostActionCookies(post, a.PostActionCookieSecret())
|
||||
|
||||
postJSON, jsonErr := post.ToJSON()
|
||||
@@ -538,7 +552,7 @@ func (a *App) UpdateEphemeralPost(c request.CTX, userID string, post *model.Post
|
||||
|
||||
post.GenerateActionIds()
|
||||
message := model.NewWebSocketEvent(model.WebsocketEventPostEdited, "", post.ChannelId, userID, nil, "")
|
||||
post = a.PreparePostForClientWithEmbedsAndImages(c, post, true, false)
|
||||
post = a.PreparePostForClientWithEmbedsAndImages(c, post, true, false, true)
|
||||
post = model.AddPostActionCookies(post, a.PostActionCookieSecret())
|
||||
postJSON, jsonErr := post.ToJSON()
|
||||
if jsonErr != nil {
|
||||
@@ -682,7 +696,7 @@ func (a *App) UpdatePost(c *request.Context, post *model.Post, safeUpdate bool)
|
||||
})
|
||||
}
|
||||
|
||||
rpost = a.PreparePostForClientWithEmbedsAndImages(c, rpost, false, true)
|
||||
rpost = a.PreparePostForClientWithEmbedsAndImages(c, rpost, false, true, true)
|
||||
|
||||
// Ensure IsFollowing is nil since this updated post will be broadcast to all users
|
||||
// and we don't want to have to populate it for every single user and broadcast to each
|
||||
@@ -1705,7 +1719,7 @@ func (a *App) countThreadMentions(c request.CTX, user *model.User, post *model.P
|
||||
|
||||
posts, nErr := a.Srv().Store().Post().GetPostsByThread(post.Id, timestamp)
|
||||
if nErr != nil {
|
||||
return 0, model.NewAppError("countMentionsFromPost", "app.channel.count_posts_since.app_error", nil, "", http.StatusInternalServerError).Wrap(nErr)
|
||||
return 0, model.NewAppError("countThreadMentions", "app.channel.count_posts_since.app_error", nil, "", http.StatusInternalServerError).Wrap(nErr)
|
||||
}
|
||||
|
||||
count := 0
|
||||
@@ -1732,7 +1746,7 @@ func (a *App) countThreadMentions(c request.CTX, user *model.User, post *model.P
|
||||
|
||||
groups, nErr := a.getGroupsAllowedForReferenceInChannel(channel, team)
|
||||
if nErr != nil {
|
||||
return 0, model.NewAppError("countMentionsFromPost", "app.channel.count_posts_since.app_error", nil, "", http.StatusInternalServerError).Wrap(nErr)
|
||||
return 0, model.NewAppError("countThreadMentions", "app.channel.count_posts_since.app_error", nil, "", http.StatusInternalServerError).Wrap(nErr)
|
||||
}
|
||||
|
||||
for _, p := range posts {
|
||||
@@ -1749,25 +1763,33 @@ func (a *App) countThreadMentions(c request.CTX, user *model.User, post *model.P
|
||||
|
||||
// countMentionsFromPost returns the number of posts in the post's channel that mention the user after and including the
|
||||
// given post.
|
||||
func (a *App) countMentionsFromPost(c request.CTX, user *model.User, post *model.Post) (int, int, *model.AppError) {
|
||||
func (a *App) countMentionsFromPost(c request.CTX, user *model.User, post *model.Post) (int, int, int, *model.AppError) {
|
||||
channel, err := a.GetChannel(c, post.ChannelId)
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
return 0, 0, 0, err
|
||||
}
|
||||
|
||||
if channel.Type == model.ChannelTypeDirect {
|
||||
// In a DM channel, every post made by the other user is a mention
|
||||
count, countRoot, nErr := a.Srv().Store().Channel().CountPostsAfter(post.ChannelId, post.CreateAt-1, channel.GetOtherUserIdForDM(user.Id))
|
||||
if nErr != nil {
|
||||
return 0, 0, model.NewAppError("countMentionsFromPost", "app.channel.count_posts_since.app_error", nil, "", http.StatusInternalServerError).Wrap(nErr)
|
||||
return 0, 0, 0, model.NewAppError("countMentionsFromPost", "app.channel.count_posts_since.app_error", nil, "", http.StatusInternalServerError).Wrap(nErr)
|
||||
}
|
||||
|
||||
return count, countRoot, nil
|
||||
var urgentCount int
|
||||
if a.isPostPriorityEnabled() {
|
||||
urgentCount, nErr = a.Srv().Store().Channel().CountUrgentPostsAfter(post.ChannelId, post.CreateAt-1, channel.GetOtherUserIdForDM(user.Id))
|
||||
if nErr != nil {
|
||||
return 0, 0, 0, model.NewAppError("countMentionsFromPost", "app.channel.count_urgent_posts_since.app_error", nil, "", http.StatusInternalServerError).Wrap(nErr)
|
||||
}
|
||||
}
|
||||
|
||||
return count, countRoot, urgentCount, nil
|
||||
}
|
||||
|
||||
channelMember, err := a.GetChannelMember(c, channel.Id, user.Id)
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
return 0, 0, 0, err
|
||||
}
|
||||
|
||||
keywords := addMentionKeywordsForUser(
|
||||
@@ -1785,15 +1807,25 @@ func (a *App) countMentionsFromPost(c request.CTX, user *model.User, post *model
|
||||
|
||||
thread, err := a.GetPostThread(post.Id, model.GetPostsOptions{}, user.Id)
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
return 0, 0, 0, err
|
||||
}
|
||||
|
||||
count := 0
|
||||
countRoot := 0
|
||||
urgentCount := 0
|
||||
if isPostMention(user, post, keywords, thread.Posts, mentionedByThread, checkForCommentMentions) {
|
||||
count += 1
|
||||
if post.RootId == "" {
|
||||
countRoot += 1
|
||||
if a.isPostPriorityEnabled() {
|
||||
priority, err := a.GetPriorityForPost(post.Id)
|
||||
if err != nil {
|
||||
return 0, 0, 0, err
|
||||
}
|
||||
if priority != nil && *priority.Priority == model.PostPriorityUrgent {
|
||||
urgentCount += 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1807,18 +1839,32 @@ func (a *App) countMentionsFromPost(c request.CTX, user *model.User, post *model
|
||||
PerPage: perPage,
|
||||
})
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
return 0, 0, 0, err
|
||||
}
|
||||
|
||||
mentionPostIds := make([]string, 0)
|
||||
for _, postID := range postList.Order {
|
||||
if isPostMention(user, postList.Posts[postID], keywords, postList.Posts, mentionedByThread, checkForCommentMentions) {
|
||||
count += 1
|
||||
if postList.Posts[postID].RootId == "" {
|
||||
mentionPostIds = append(mentionPostIds, postID)
|
||||
countRoot += 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if a.isPostPriorityEnabled() {
|
||||
priorityList, nErr := a.Srv().Store().PostPriority().GetForPosts(mentionPostIds)
|
||||
if err != nil {
|
||||
return 0, 0, 0, model.NewAppError("countMentionsFromPost", "app.channel.get_priority_for_posts.app_error", nil, "", http.StatusInternalServerError).Wrap(nErr)
|
||||
}
|
||||
for _, priority := range priorityList {
|
||||
if *priority.Priority == model.PostPriorityUrgent {
|
||||
urgentCount += 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(postList.Order) < perPage {
|
||||
break
|
||||
}
|
||||
@@ -1826,7 +1872,7 @@ func (a *App) countMentionsFromPost(c request.CTX, user *model.User, post *model
|
||||
page += 1
|
||||
}
|
||||
|
||||
return count, countRoot, nil
|
||||
return count, countRoot, urgentCount, nil
|
||||
}
|
||||
|
||||
func isCommentMention(user *model.User, post *model.Post, otherPosts map[string]*model.Post, mentionedByThread map[string]bool) bool {
|
||||
@@ -2025,7 +2071,7 @@ func (a *App) SetPostReminder(postID, userID string, targetTime int64) *model.Ap
|
||||
}
|
||||
|
||||
message := model.NewWebSocketEvent(model.WebsocketEventEphemeralMessage, "", ephemeralPost.ChannelId, userID, nil, "")
|
||||
ephemeralPost = a.PreparePostForClientWithEmbedsAndImages(request.EmptyContext(a.Log()), ephemeralPost, true, false)
|
||||
ephemeralPost = a.PreparePostForClientWithEmbedsAndImages(request.EmptyContext(a.Log()), ephemeralPost, true, false, true)
|
||||
ephemeralPost = model.AddPostActionCookies(ephemeralPost, a.PostActionCookieSecret())
|
||||
|
||||
postJSON, jsonErr := ephemeralPost.ToJSON()
|
||||
@@ -2107,7 +2153,7 @@ func (a *App) CheckPostReminders() {
|
||||
|
||||
func includeEmbedsAndImages(a *App, c request.CTX, topThreadList *model.TopThreadList, userID string) (*model.TopThreadList, error) {
|
||||
for _, topThread := range topThreadList.Items {
|
||||
topThread.Post = a.PreparePostForClientWithEmbedsAndImages(c, topThread.Post, false, false)
|
||||
topThread.Post = a.PreparePostForClientWithEmbedsAndImages(c, topThread.Post, false, false, true)
|
||||
sanitizedPost, err := a.SanitizePostMetadataForUser(c, topThread.Post, userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -2116,3 +2162,7 @@ func includeEmbedsAndImages(a *App, c request.CTX, topThreadList *model.TopThrea
|
||||
}
|
||||
return topThreadList, nil
|
||||
}
|
||||
|
||||
func (a *App) isPostPriorityEnabled() bool {
|
||||
return a.Config().FeatureFlags.PostPriority && *a.Config().ServiceSettings.PostPriority
|
||||
}
|
||||
|
||||
@@ -56,11 +56,20 @@ func (a *App) PreparePostListForClient(c request.CTX, originalList *model.PostLi
|
||||
}
|
||||
|
||||
for id, originalPost := range originalList.Posts {
|
||||
post := a.PreparePostForClientWithEmbedsAndImages(c, originalPost, false, false)
|
||||
post := a.PreparePostForClientWithEmbedsAndImages(c, originalPost, false, false, false)
|
||||
|
||||
list.Posts[id] = post
|
||||
}
|
||||
|
||||
if a.isPostPriorityEnabled() {
|
||||
priority, _ := a.GetPriorityForPostList(list)
|
||||
for _, id := range list.Order {
|
||||
if _, ok := priority[id]; ok {
|
||||
list.Posts[id].Metadata.Priority = priority[id]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return list
|
||||
}
|
||||
|
||||
@@ -90,7 +99,7 @@ func (a *App) OverrideIconURLIfEmoji(c request.CTX, post *model.Post) {
|
||||
}
|
||||
}
|
||||
|
||||
func (a *App) PreparePostForClient(c request.CTX, originalPost *model.Post, isNewPost, isEditPost bool) *model.Post {
|
||||
func (a *App) PreparePostForClient(c request.CTX, originalPost *model.Post, isNewPost, isEditPost, includePriority bool) *model.Post {
|
||||
post := originalPost.Clone()
|
||||
|
||||
// Proxy image links before constructing metadata so that requests go through the proxy
|
||||
@@ -123,11 +132,20 @@ func (a *App) PreparePostForClient(c request.CTX, originalPost *model.Post, isNe
|
||||
post.Metadata.Files = fileInfos
|
||||
}
|
||||
|
||||
if includePriority && a.isPostPriorityEnabled() && post.RootId == "" {
|
||||
// Post's Priority if any
|
||||
if priority, err := a.GetPriorityForPost(post.Id); err != nil {
|
||||
mlog.Warn("Failed to get post priority for a post", mlog.String("post_id", post.Id), mlog.Err(err))
|
||||
} else {
|
||||
post.Metadata.Priority = priority
|
||||
}
|
||||
}
|
||||
|
||||
return post
|
||||
}
|
||||
|
||||
func (a *App) PreparePostForClientWithEmbedsAndImages(c request.CTX, originalPost *model.Post, isNewPost, isEditPost bool) *model.Post {
|
||||
post := a.PreparePostForClient(c, originalPost, isNewPost, isEditPost)
|
||||
func (a *App) PreparePostForClientWithEmbedsAndImages(c request.CTX, originalPost *model.Post, isNewPost, isEditPost, includePriority bool) *model.Post {
|
||||
post := a.PreparePostForClient(c, originalPost, isNewPost, isEditPost, includePriority)
|
||||
post = a.getEmbedsAndImages(c, post, isNewPost)
|
||||
return post
|
||||
}
|
||||
@@ -562,7 +580,7 @@ func (a *App) getLinkMetadata(c request.CTX, requestURL string, timestamp int64,
|
||||
permalink = &model.Permalink{PreviewPost: model.NewPreviewPost(referencedPost, referencedTeam, referencedChannel)}
|
||||
} else {
|
||||
// referencedPost does not contain a permalink: we get its metadata
|
||||
referencedPostWithMetadata := a.PreparePostForClientWithEmbedsAndImages(c, referencedPost, false, false)
|
||||
referencedPostWithMetadata := a.PreparePostForClientWithEmbedsAndImages(c, referencedPost, false, false, false)
|
||||
permalink = &model.Permalink{PreviewPost: model.NewPreviewPost(referencedPostWithMetadata, referencedTeam, referencedChannel)}
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -125,7 +125,7 @@ func TestPreparePostForClient(t *testing.T) {
|
||||
Message: message,
|
||||
}
|
||||
|
||||
clientPost := th.App.PreparePostForClient(th.Context, post, false, true)
|
||||
clientPost := th.App.PreparePostForClient(th.Context, post, false, true, false)
|
||||
|
||||
t.Run("doesn't mutate provided post", func(t *testing.T) {
|
||||
assert.NotEqual(t, clientPost, post, "should've returned a new post")
|
||||
@@ -151,7 +151,7 @@ func TestPreparePostForClient(t *testing.T) {
|
||||
|
||||
post := th.CreatePost(th.BasicChannel)
|
||||
|
||||
clientPost := th.App.PreparePostForClient(th.Context, post, false, false)
|
||||
clientPost := th.App.PreparePostForClient(th.Context, post, false, false, false)
|
||||
|
||||
assert.False(t, clientPost == post, "should've returned a new post")
|
||||
assert.Equal(t, clientPost, post, "shouldn't have changed any metadata")
|
||||
@@ -167,7 +167,7 @@ func TestPreparePostForClient(t *testing.T) {
|
||||
reaction3 := th.AddReactionToPost(post, th.BasicUser2, "ice_cream")
|
||||
post.HasReactions = true
|
||||
|
||||
clientPost := th.App.PreparePostForClient(th.Context, post, false, false)
|
||||
clientPost := th.App.PreparePostForClient(th.Context, post, false, false, false)
|
||||
|
||||
assert.Len(t, clientPost.Metadata.Reactions, 3, "should've populated Reactions")
|
||||
assert.Equal(t, reaction1, clientPost.Metadata.Reactions[0], "first reaction is incorrect")
|
||||
@@ -194,7 +194,7 @@ func TestPreparePostForClient(t *testing.T) {
|
||||
|
||||
var clientPost *model.Post
|
||||
assert.Eventually(t, func() bool {
|
||||
clientPost = th.App.PreparePostForClient(th.Context, post, false, false)
|
||||
clientPost = th.App.PreparePostForClient(th.Context, post, false, false, false)
|
||||
return assert.ObjectsAreEqual([]*model.FileInfo{fileInfo}, clientPost.Metadata.Files)
|
||||
}, time.Second, 10*time.Millisecond)
|
||||
|
||||
@@ -230,7 +230,7 @@ func TestPreparePostForClient(t *testing.T) {
|
||||
th.AddReactionToPost(post, th.BasicUser2, "angry")
|
||||
post.HasReactions = true
|
||||
|
||||
clientPost := th.App.PreparePostForClient(th.Context, post, false, false)
|
||||
clientPost := th.App.PreparePostForClient(th.Context, post, false, false, false)
|
||||
|
||||
t.Run("populates emojis", func(t *testing.T) {
|
||||
assert.ElementsMatch(t, []*model.Emoji{}, clientPost.Metadata.Emojis, "should've populated empty Emojis")
|
||||
@@ -275,7 +275,7 @@ func TestPreparePostForClient(t *testing.T) {
|
||||
th.AddReactionToPost(post, th.BasicUser2, "angry")
|
||||
post.HasReactions = true
|
||||
|
||||
clientPost := th.App.PreparePostForClient(th.Context, post, false, false)
|
||||
clientPost := th.App.PreparePostForClient(th.Context, post, false, false, false)
|
||||
|
||||
t.Run("populates emojis", func(t *testing.T) {
|
||||
assert.ElementsMatch(t, []*model.Emoji{emoji1, emoji2, emoji3, emoji4}, clientPost.Metadata.Emojis, "should've populated post.Emojis")
|
||||
@@ -307,7 +307,7 @@ func TestPreparePostForClient(t *testing.T) {
|
||||
post.AddProp(model.PostPropsOverrideIconURL, url)
|
||||
post.AddProp(model.PostPropsOverrideIconEmoji, emoji)
|
||||
|
||||
return th.App.PreparePostForClient(th.Context, post, false, false)
|
||||
return th.App.PreparePostForClient(th.Context, post, false, false, false)
|
||||
}
|
||||
|
||||
emoji := "basketball"
|
||||
@@ -361,7 +361,7 @@ func TestPreparePostForClient(t *testing.T) {
|
||||
}, th.BasicChannel, false, true)
|
||||
require.Nil(t, err)
|
||||
|
||||
clientPost := th.App.PreparePostForClient(th.Context, post, false, false)
|
||||
clientPost := th.App.PreparePostForClient(th.Context, post, false, false, false)
|
||||
|
||||
t.Run("populates image dimensions", func(t *testing.T) {
|
||||
imageDimensions := clientPost.Metadata.Images
|
||||
@@ -394,7 +394,7 @@ func TestPreparePostForClient(t *testing.T) {
|
||||
post.AddProp(model.PostPropsOverrideIconEmoji, true)
|
||||
|
||||
require.NotPanics(t, func() {
|
||||
_ = th.App.PreparePostForClient(th.Context, post, false, false)
|
||||
_ = th.App.PreparePostForClient(th.Context, post, false, false, false)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -424,7 +424,7 @@ func TestPreparePostForClient(t *testing.T) {
|
||||
}, th.BasicChannel, false, true)
|
||||
require.Nil(t, err)
|
||||
post.Metadata.Embeds = nil
|
||||
clientPost := th.App.PreparePostForClientWithEmbedsAndImages(th.Context, post, false, false)
|
||||
clientPost := th.App.PreparePostForClientWithEmbedsAndImages(th.Context, post, false, false, false)
|
||||
|
||||
// Reminder that only the first link gets an embed and dimensions
|
||||
|
||||
@@ -459,7 +459,7 @@ func TestPreparePostForClient(t *testing.T) {
|
||||
}, th.BasicChannel, false, true)
|
||||
require.Nil(t, err)
|
||||
|
||||
clientPost := th.App.PreparePostForClient(th.Context, post, false, false)
|
||||
clientPost := th.App.PreparePostForClient(th.Context, post, false, false, false)
|
||||
firstEmbed := clientPost.Metadata.Embeds[0]
|
||||
ogData := firstEmbed.Data.(*opengraph.OpenGraph)
|
||||
|
||||
@@ -502,7 +502,7 @@ func TestPreparePostForClient(t *testing.T) {
|
||||
}, th.BasicChannel, false, true)
|
||||
require.Nil(t, err)
|
||||
post.Metadata.Embeds = nil
|
||||
clientPost := th.App.PreparePostForClientWithEmbedsAndImages(th.Context, post, false, false)
|
||||
clientPost := th.App.PreparePostForClientWithEmbedsAndImages(th.Context, post, false, false, false)
|
||||
|
||||
t.Run("populates embeds", func(t *testing.T) {
|
||||
assert.ElementsMatch(t, []*model.PostEmbed{
|
||||
@@ -547,7 +547,7 @@ func TestPreparePostForClient(t *testing.T) {
|
||||
// DeleteAt isn't set on the post returned by App.DeletePost
|
||||
post.DeleteAt = model.GetMillis()
|
||||
|
||||
clientPost := th.App.PreparePostForClient(th.Context, post, false, false)
|
||||
clientPost := th.App.PreparePostForClient(th.Context, post, false, false, false)
|
||||
|
||||
assert.NotEqual(t, nil, clientPost.Metadata, "should've populated Metadata“")
|
||||
assert.Equal(t, "", clientPost.Message, "should've cleaned post content")
|
||||
@@ -582,7 +582,7 @@ func TestPreparePostForClient(t *testing.T) {
|
||||
}, th.BasicChannel, false, true)
|
||||
require.Nil(t, err)
|
||||
previewPost.Metadata.Embeds = nil
|
||||
clientPost := th.App.PreparePostForClientWithEmbedsAndImages(th.Context, previewPost, false, false)
|
||||
clientPost := th.App.PreparePostForClientWithEmbedsAndImages(th.Context, previewPost, false, false, false)
|
||||
firstEmbed := clientPost.Metadata.Embeds[0]
|
||||
preview := firstEmbed.Data.(*model.PreviewPost)
|
||||
require.Equal(t, referencedPost.Id, preview.PostID)
|
||||
@@ -641,7 +641,7 @@ func TestPreparePostForClient(t *testing.T) {
|
||||
require.Nil(t, err)
|
||||
previewPost.Metadata.Embeds = nil
|
||||
|
||||
clientPost := th.App.PreparePostForClientWithEmbedsAndImages(th.Context, previewPost, false, false)
|
||||
clientPost := th.App.PreparePostForClientWithEmbedsAndImages(th.Context, previewPost, false, false, false)
|
||||
firstEmbed := clientPost.Metadata.Embeds[0]
|
||||
preview := firstEmbed.Data.(*model.PreviewPost)
|
||||
|
||||
@@ -679,7 +679,7 @@ func TestPreparePostForClient(t *testing.T) {
|
||||
require.Nil(t, err)
|
||||
previewPost.Metadata.Embeds = nil
|
||||
|
||||
clientPost := th.App.PreparePostForClientWithEmbedsAndImages(th.Context, previewPost, false, false)
|
||||
clientPost := th.App.PreparePostForClientWithEmbedsAndImages(th.Context, previewPost, false, false, false)
|
||||
firstEmbed := clientPost.Metadata.Embeds[0]
|
||||
preview := firstEmbed.Data.(*model.PreviewPost)
|
||||
referencedPostFirstEmbed := preview.Post.Metadata.Embeds[0]
|
||||
@@ -726,7 +726,7 @@ func TestPreparePostForClient(t *testing.T) {
|
||||
require.Nil(t, err)
|
||||
previewPost.Metadata.Embeds = nil
|
||||
|
||||
clientPost := th.App.PreparePostForClientWithEmbedsAndImages(th.Context, previewPost, false, false)
|
||||
clientPost := th.App.PreparePostForClientWithEmbedsAndImages(th.Context, previewPost, false, false, false)
|
||||
firstEmbed := clientPost.Metadata.Embeds[0]
|
||||
preview := firstEmbed.Data.(*model.PreviewPost)
|
||||
referencedPostMetadata := preview.Post.Metadata
|
||||
@@ -761,7 +761,7 @@ func TestPreparePostForClient(t *testing.T) {
|
||||
}, th.BasicChannel, false, true)
|
||||
require.Nil(t, err)
|
||||
|
||||
clientPost := th.App.PreparePostForClient(th.Context, previewPost, false, false)
|
||||
clientPost := th.App.PreparePostForClient(th.Context, previewPost, false, false, false)
|
||||
firstEmbed := clientPost.Metadata.Embeds[0]
|
||||
preview := firstEmbed.Data.(*model.PreviewPost)
|
||||
require.Equal(t, referencedPost.Id, preview.PostID)
|
||||
@@ -770,13 +770,13 @@ func TestPreparePostForClient(t *testing.T) {
|
||||
*cfg.ServiceSettings.EnablePermalinkPreviews = false
|
||||
})
|
||||
|
||||
th.App.PreparePostForClient(th.Context, previewPost, false, false)
|
||||
th.App.PreparePostForClient(th.Context, previewPost, false, false, false)
|
||||
|
||||
th.App.UpdateConfig(func(cfg *model.Config) {
|
||||
*cfg.ServiceSettings.EnablePermalinkPreviews = true
|
||||
})
|
||||
|
||||
clientPost2 := th.App.PreparePostForClient(th.Context, previewPost, false, false)
|
||||
clientPost2 := th.App.PreparePostForClient(th.Context, previewPost, false, false, false)
|
||||
firstEmbed2 := clientPost2.Metadata.Embeds[0]
|
||||
preview2 := firstEmbed2.Data.(*model.PreviewPost)
|
||||
require.Equal(t, referencedPost.Id, preview2.PostID)
|
||||
@@ -828,7 +828,7 @@ func testProxyLinkedImage(t *testing.T, th *TestHelper, shouldProxy bool) {
|
||||
Message: fmt.Sprintf(postTemplate, imageURL),
|
||||
}
|
||||
|
||||
clientPost := th.App.PreparePostForClient(th.Context, post, false, false)
|
||||
clientPost := th.App.PreparePostForClient(th.Context, post, false, false, false)
|
||||
|
||||
if shouldProxy {
|
||||
assert.Equal(t, fmt.Sprintf(postTemplate, imageURL), post.Message, "should not have mutated original post")
|
||||
@@ -876,7 +876,7 @@ func testProxyOpenGraphImage(t *testing.T, th *TestHelper, shouldProxy bool) {
|
||||
require.Nil(t, err)
|
||||
|
||||
post.Metadata.Embeds = nil
|
||||
embeds := th.App.PreparePostForClientWithEmbedsAndImages(th.Context, post, false, false).Metadata.Embeds
|
||||
embeds := th.App.PreparePostForClientWithEmbedsAndImages(th.Context, post, false, false, false).Metadata.Embeds
|
||||
require.Len(t, embeds, 1, "should have one embed")
|
||||
|
||||
embed := embeds[0]
|
||||
|
||||
34
app/post_priority.go
Обычный файл
34
app/post_priority.go
Обычный файл
@@ -0,0 +1,34 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
package app
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"net/http"
|
||||
|
||||
"github.com/mattermost/mattermost-server/v6/model"
|
||||
)
|
||||
|
||||
func (a *App) GetPriorityForPost(postId string) (*model.PostPriority, *model.AppError) {
|
||||
priority, err := a.Srv().Store().PostPriority().GetForPost(postId)
|
||||
|
||||
if err != nil && err != sql.ErrNoRows {
|
||||
return nil, model.NewAppError("GetPriorityForPost", "app.post_prority.get_for_post.app_error", nil, "", http.StatusInternalServerError).Wrap(err)
|
||||
}
|
||||
return priority, nil
|
||||
}
|
||||
|
||||
func (a *App) GetPriorityForPostList(list *model.PostList) (map[string]*model.PostPriority, *model.AppError) {
|
||||
priority, err := a.Srv().Store().PostPriority().GetForPosts(list.Order)
|
||||
if err != nil {
|
||||
return nil, model.NewAppError("GetPriorityForPost", "app.post_prority.get_for_post.app_error", nil, "", http.StatusInternalServerError).Wrap(err)
|
||||
}
|
||||
|
||||
priorityMap := make(map[string]*model.PostPriority)
|
||||
for _, p := range priority {
|
||||
priorityMap[p.PostId] = p
|
||||
}
|
||||
|
||||
return priorityMap, nil
|
||||
}
|
||||
@@ -1598,7 +1598,7 @@ func TestCountMentionsFromPost(t *testing.T) {
|
||||
}, channel, false, true)
|
||||
require.Nil(t, err)
|
||||
|
||||
count, _, err := th.App.countMentionsFromPost(th.Context, user2, post1)
|
||||
count, _, _, err := th.App.countMentionsFromPost(th.Context, user2, post1)
|
||||
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 0, count)
|
||||
@@ -1637,7 +1637,7 @@ func TestCountMentionsFromPost(t *testing.T) {
|
||||
|
||||
// post1 and post3 should mention the user
|
||||
|
||||
count, _, err := th.App.countMentionsFromPost(th.Context, user2, post1)
|
||||
count, _, _, err := th.App.countMentionsFromPost(th.Context, user2, post1)
|
||||
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 2, count)
|
||||
@@ -1676,7 +1676,7 @@ func TestCountMentionsFromPost(t *testing.T) {
|
||||
|
||||
// post2 and post3 should mention the user
|
||||
|
||||
count, _, err := th.App.countMentionsFromPost(th.Context, user2, post1)
|
||||
count, _, _, err := th.App.countMentionsFromPost(th.Context, user2, post1)
|
||||
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 2, count)
|
||||
@@ -1713,7 +1713,7 @@ func TestCountMentionsFromPost(t *testing.T) {
|
||||
}, channel, false, true)
|
||||
require.Nil(t, err)
|
||||
|
||||
count, _, err := th.App.countMentionsFromPost(th.Context, user2, post1)
|
||||
count, _, _, err := th.App.countMentionsFromPost(th.Context, user2, post1)
|
||||
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 0, count)
|
||||
@@ -1755,7 +1755,7 @@ func TestCountMentionsFromPost(t *testing.T) {
|
||||
}, channel, false, true)
|
||||
require.Nil(t, err)
|
||||
|
||||
count, _, err := th.App.countMentionsFromPost(th.Context, user2, post1)
|
||||
count, _, _, err := th.App.countMentionsFromPost(th.Context, user2, post1)
|
||||
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 0, count)
|
||||
@@ -1809,7 +1809,7 @@ func TestCountMentionsFromPost(t *testing.T) {
|
||||
|
||||
// post2 should mention the user
|
||||
|
||||
count, _, err := th.App.countMentionsFromPost(th.Context, user2, post1)
|
||||
count, _, _, err := th.App.countMentionsFromPost(th.Context, user2, post1)
|
||||
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 1, count)
|
||||
@@ -1863,7 +1863,7 @@ func TestCountMentionsFromPost(t *testing.T) {
|
||||
|
||||
// post2 and post5 should mention the user
|
||||
|
||||
count, _, err := th.App.countMentionsFromPost(th.Context, user2, post1)
|
||||
count, _, _, err := th.App.countMentionsFromPost(th.Context, user2, post1)
|
||||
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 2, count)
|
||||
@@ -1912,7 +1912,7 @@ func TestCountMentionsFromPost(t *testing.T) {
|
||||
|
||||
// should be mentioned by post2 and post3
|
||||
|
||||
count, _, err := th.App.countMentionsFromPost(th.Context, user2, post1)
|
||||
count, _, _, err := th.App.countMentionsFromPost(th.Context, user2, post1)
|
||||
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 2, count)
|
||||
@@ -1942,12 +1942,12 @@ func TestCountMentionsFromPost(t *testing.T) {
|
||||
}, channel, false, true)
|
||||
require.Nil(t, err)
|
||||
|
||||
count, _, err := th.App.countMentionsFromPost(th.Context, user2, post1)
|
||||
count, _, _, err := th.App.countMentionsFromPost(th.Context, user2, post1)
|
||||
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 2, count)
|
||||
|
||||
count, _, err = th.App.countMentionsFromPost(th.Context, user1, post1)
|
||||
count, _, _, err = th.App.countMentionsFromPost(th.Context, user1, post1)
|
||||
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 0, count)
|
||||
@@ -1984,7 +1984,7 @@ func TestCountMentionsFromPost(t *testing.T) {
|
||||
|
||||
// post1 and post3 should mention the user, but we only count post3
|
||||
|
||||
count, _, err := th.App.countMentionsFromPost(th.Context, user2, post2)
|
||||
count, _, _, err := th.App.countMentionsFromPost(th.Context, user2, post2)
|
||||
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 1, count)
|
||||
@@ -2015,7 +2015,7 @@ func TestCountMentionsFromPost(t *testing.T) {
|
||||
|
||||
// post2 should mention the user
|
||||
|
||||
count, _, err := th.App.countMentionsFromPost(th.Context, user2, post1)
|
||||
count, _, _, err := th.App.countMentionsFromPost(th.Context, user2, post1)
|
||||
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 1, count)
|
||||
@@ -2062,7 +2062,7 @@ func TestCountMentionsFromPost(t *testing.T) {
|
||||
|
||||
// post4 should mention the user
|
||||
|
||||
count, _, err := th.App.countMentionsFromPost(th.Context, user2, post3)
|
||||
count, _, _, err := th.App.countMentionsFromPost(th.Context, user2, post3)
|
||||
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 1, count)
|
||||
@@ -2102,7 +2102,7 @@ func TestCountMentionsFromPost(t *testing.T) {
|
||||
|
||||
// post3 should mention the user
|
||||
|
||||
count, _, err := th.App.countMentionsFromPost(th.Context, user2, post1)
|
||||
count, _, _, err := th.App.countMentionsFromPost(th.Context, user2, post1)
|
||||
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 1, count)
|
||||
@@ -2138,11 +2138,70 @@ func TestCountMentionsFromPost(t *testing.T) {
|
||||
|
||||
// Every post should mention the user
|
||||
|
||||
count, _, err := th.App.countMentionsFromPost(th.Context, user2, post1)
|
||||
count, _, _, err := th.App.countMentionsFromPost(th.Context, user2, post1)
|
||||
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, numPosts, count)
|
||||
})
|
||||
|
||||
t.Run("should count urgent mentions", func(t *testing.T) {
|
||||
os.Setenv("MM_FEATUREFLAGS_POSTPRIORITY", "true")
|
||||
defer os.Unsetenv("MM_FEATUREFLAGS_POSTPRIORITY")
|
||||
|
||||
th := Setup(t).InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
th.App.UpdateConfig(func(cfg *model.Config) {
|
||||
*cfg.ServiceSettings.PostPriority = true
|
||||
cfg.FeatureFlags.PostPriority = true
|
||||
})
|
||||
|
||||
user1 := th.BasicUser
|
||||
user2 := th.BasicUser2
|
||||
|
||||
channel := th.CreateChannel(th.Context, th.BasicTeam)
|
||||
th.AddUserToChannel(user2, channel)
|
||||
|
||||
user2.NotifyProps[model.MentionKeysNotifyProp] = "apple"
|
||||
|
||||
post1, err := th.App.CreatePost(th.Context, &model.Post{
|
||||
UserId: user1.Id,
|
||||
ChannelId: channel.Id,
|
||||
Message: fmt.Sprintf("@%s", user2.Username),
|
||||
Metadata: &model.PostMetadata{
|
||||
Priority: &model.PostPriority{
|
||||
Priority: model.NewString(model.PostPriorityUrgent),
|
||||
},
|
||||
},
|
||||
}, channel, false, true)
|
||||
require.Nil(t, err)
|
||||
|
||||
_, err = th.App.CreatePost(th.Context, &model.Post{
|
||||
UserId: user1.Id,
|
||||
ChannelId: channel.Id,
|
||||
Message: fmt.Sprintf("@%s", user2.Username),
|
||||
}, channel, false, true)
|
||||
require.Nil(t, err)
|
||||
|
||||
_, err = th.App.CreatePost(th.Context, &model.Post{
|
||||
UserId: user1.Id,
|
||||
ChannelId: channel.Id,
|
||||
Message: "apple",
|
||||
Metadata: &model.PostMetadata{
|
||||
Priority: &model.PostPriority{
|
||||
Priority: model.NewString(model.PostPriorityUrgent),
|
||||
},
|
||||
},
|
||||
}, channel, false, true)
|
||||
require.Nil(t, err)
|
||||
|
||||
// all posts mention the user but only post1, post3 are urgent
|
||||
|
||||
_, _, count, err := th.App.countMentionsFromPost(th.Context, user2, post1)
|
||||
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 2, count)
|
||||
})
|
||||
}
|
||||
|
||||
func TestFillInPostProps(t *testing.T) {
|
||||
|
||||
18
app/team.go
18
app/team.go
@@ -1741,13 +1741,14 @@ func (a *App) GetTeamsUnreadForUser(excludeTeamId string, userID string, include
|
||||
} else {
|
||||
teamIDs = append(teamIDs, id)
|
||||
membersMap[id] = unreads(data[i], &model.TeamUnread{
|
||||
MsgCount: 0,
|
||||
MentionCount: 0,
|
||||
MentionCountRoot: 0,
|
||||
MsgCountRoot: 0,
|
||||
ThreadCount: 0,
|
||||
ThreadMentionCount: 0,
|
||||
TeamId: id,
|
||||
MsgCount: 0,
|
||||
MentionCount: 0,
|
||||
MentionCountRoot: 0,
|
||||
MsgCountRoot: 0,
|
||||
ThreadCount: 0,
|
||||
ThreadMentionCount: 0,
|
||||
ThreadUrgentMentionCount: 0,
|
||||
TeamId: id,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1755,7 +1756,7 @@ func (a *App) GetTeamsUnreadForUser(excludeTeamId string, userID string, include
|
||||
includeCollapsedThreads = includeCollapsedThreads && *a.Config().ServiceSettings.CollapsedThreads != model.CollapsedThreadsDisabled
|
||||
|
||||
if includeCollapsedThreads {
|
||||
teamUnreads, err := a.Srv().Store().Thread().GetTeamsUnreadForUser(userID, teamIDs)
|
||||
teamUnreads, err := a.Srv().Store().Thread().GetTeamsUnreadForUser(userID, teamIDs, a.isPostPriorityEnabled())
|
||||
if err != nil {
|
||||
return nil, model.NewAppError("GetTeamsUnreadForUser", "app.team.get_unread.app_error", nil, "", http.StatusInternalServerError).Wrap(err)
|
||||
}
|
||||
@@ -1763,6 +1764,7 @@ func (a *App) GetTeamsUnreadForUser(excludeTeamId string, userID string, include
|
||||
if _, ok := teamUnreads[teamID]; ok {
|
||||
member.ThreadCount = teamUnreads[teamID].ThreadCount
|
||||
member.ThreadMentionCount = teamUnreads[teamID].ThreadMentionCount
|
||||
member.ThreadUrgentMentionCount = teamUnreads[teamID].ThreadUrgentMentionCount
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
20
app/user.go
20
app/user.go
@@ -2391,6 +2391,10 @@ func (a *App) ConvertBotToUser(c request.CTX, bot *model.Bot, userPatch *model.U
|
||||
func (a *App) GetThreadsForUser(userID, teamID string, options model.GetUserThreadsOpts) (*model.Threads, *model.AppError) {
|
||||
var result model.Threads
|
||||
var eg errgroup.Group
|
||||
postPriorityIsEnabled := a.isPostPriorityEnabled()
|
||||
if postPriorityIsEnabled {
|
||||
options.IncludeIsUrgent = true
|
||||
}
|
||||
|
||||
if !options.ThreadsOnly {
|
||||
eg.Go(func() error {
|
||||
@@ -2427,6 +2431,18 @@ func (a *App) GetThreadsForUser(userID, teamID string, options model.GetUserThre
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
if postPriorityIsEnabled {
|
||||
eg.Go(func() error {
|
||||
totalUnreadUrgentMentions, err := a.Srv().Store().Thread().GetTotalUnreadUrgentMentions(userID, teamID, options)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "failed to count urgent mentioned threads for user id=%s", userID)
|
||||
}
|
||||
result.TotalUnreadUrgentMentions = totalUnreadUrgentMentions
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if !options.TotalsOnly {
|
||||
@@ -2469,7 +2485,7 @@ func (a *App) GetThreadMembershipForUser(userId, threadId string) (*model.Thread
|
||||
}
|
||||
|
||||
func (a *App) GetThreadForUser(threadMembership *model.ThreadMembership, extended bool) (*model.ThreadResponse, *model.AppError) {
|
||||
thread, err := a.Srv().Store().Thread().GetThreadForUser(threadMembership, extended)
|
||||
thread, err := a.Srv().Store().Thread().GetThreadForUser(threadMembership, extended, a.isPostPriorityEnabled())
|
||||
if err != nil {
|
||||
return nil, model.NewAppError("GetThreadForUser", "app.user.get_threads_for_user.app_error", nil, "", http.StatusInternalServerError).Wrap(err)
|
||||
}
|
||||
@@ -2551,7 +2567,7 @@ func (a *App) UpdateThreadFollowForUserFromChannelAdd(c request.CTX, userID, tea
|
||||
}
|
||||
|
||||
message := model.NewWebSocketEvent(model.WebsocketEventThreadUpdated, teamID, "", userID, nil, "")
|
||||
userThread, err := a.Srv().Store().Thread().GetThreadForUser(tm, true)
|
||||
userThread, err := a.Srv().Store().Thread().GetThreadForUser(tm, true, a.isPostPriorityEnabled())
|
||||
|
||||
if err != nil {
|
||||
var errNotFound *store.ErrNotFound
|
||||
|
||||
Ссылка в новой задаче
Block a user