MM-30970 Add Basic unreadMentions support for collapsed threads (#16407)

Co-authored-by: Mattermod <mattermod@users.noreply.github.com>
Этот коммит содержится в:
Eli Yukelzon
2020-12-01 17:20:23 +02:00
коммит произвёл GitHub
родитель 1bd7dc41bd
Коммит c2036f614e
14 изменённых файлов: 330 добавлений и 66 удалений

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

@@ -7666,7 +7666,7 @@ func (s *OpenTracingLayerThreadStore) CollectThreadsWithNewerReplies(userId stri
return result, err
}
func (s *OpenTracingLayerThreadStore) CreateMembershipIfNeeded(userId string, postId string, following bool) error {
func (s *OpenTracingLayerThreadStore) CreateMembershipIfNeeded(userId string, postId string, following bool, incrementMentions bool, updateFollowing bool) error {
origCtx := s.Root.Store.Context()
span, newCtx := tracing.StartSpanWithParentByContext(s.Root.Store.Context(), "ThreadStore.CreateMembershipIfNeeded")
s.Root.Store.SetContext(newCtx)
@@ -7675,7 +7675,7 @@ func (s *OpenTracingLayerThreadStore) CreateMembershipIfNeeded(userId string, po
}()
defer span.Finish()
err := s.ThreadStore.CreateMembershipIfNeeded(userId, postId, following)
err := s.ThreadStore.CreateMembershipIfNeeded(userId, postId, following, incrementMentions, updateFollowing)
if err != nil {
span.LogFields(spanlog.Error(err))
ext.Error.Set(span, true)
@@ -7774,6 +7774,24 @@ func (s *OpenTracingLayerThreadStore) GetMembershipsForUser(userId string) ([]*m
return result, err
}
func (s *OpenTracingLayerThreadStore) GetPosts(threadId string, since int64) ([]*model.Post, error) {
origCtx := s.Root.Store.Context()
span, newCtx := tracing.StartSpanWithParentByContext(s.Root.Store.Context(), "ThreadStore.GetPosts")
s.Root.Store.SetContext(newCtx)
defer func() {
s.Root.Store.SetContext(origCtx)
}()
defer span.Finish()
result, err := s.ThreadStore.GetPosts(threadId, since)
if err != nil {
span.LogFields(spanlog.Error(err))
ext.Error.Set(span, true)
}
return result, err
}
func (s *OpenTracingLayerThreadStore) GetThreadsForUser(userId string, opts model.GetUserThreadsOpts) (*model.Threads, error) {
origCtx := s.Root.Store.Context()
span, newCtx := tracing.StartSpanWithParentByContext(s.Root.Store.Context(), "ThreadStore.GetThreadsForUser")
@@ -7918,7 +7936,7 @@ func (s *OpenTracingLayerThreadStore) UpdateMembership(membership *model.ThreadM
return result, err
}
func (s *OpenTracingLayerThreadStore) UpdateUnreadsByChannel(userId string, changedThreads []string, timestamp int64) error {
func (s *OpenTracingLayerThreadStore) UpdateUnreadsByChannel(userId string, changedThreads []string, timestamp int64, updateViewedTimestamp bool) error {
origCtx := s.Root.Store.Context()
span, newCtx := tracing.StartSpanWithParentByContext(s.Root.Store.Context(), "ThreadStore.UpdateUnreadsByChannel")
s.Root.Store.SetContext(newCtx)
@@ -7927,7 +7945,7 @@ func (s *OpenTracingLayerThreadStore) UpdateUnreadsByChannel(userId string, chan
}()
defer span.Finish()
err := s.ThreadStore.UpdateUnreadsByChannel(userId, changedThreads, timestamp)
err := s.ThreadStore.UpdateUnreadsByChannel(userId, changedThreads, timestamp, updateViewedTimestamp)
if err != nil {
span.LogFields(spanlog.Error(err))
ext.Error.Set(span, true)

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

@@ -8318,11 +8318,11 @@ func (s *RetryLayerThreadStore) CollectThreadsWithNewerReplies(userId string, ch
}
func (s *RetryLayerThreadStore) CreateMembershipIfNeeded(userId string, postId string, following bool) error {
func (s *RetryLayerThreadStore) CreateMembershipIfNeeded(userId string, postId string, following bool, incrementMentions bool, updateFollowing bool) error {
tries := 0
for {
err := s.ThreadStore.CreateMembershipIfNeeded(userId, postId, following)
err := s.ThreadStore.CreateMembershipIfNeeded(userId, postId, following, incrementMentions, updateFollowing)
if err == nil {
return nil
}
@@ -8438,6 +8438,26 @@ func (s *RetryLayerThreadStore) GetMembershipsForUser(userId string) ([]*model.T
}
func (s *RetryLayerThreadStore) GetPosts(threadId string, since int64) ([]*model.Post, error) {
tries := 0
for {
result, err := s.ThreadStore.GetPosts(threadId, since)
if err == nil {
return result, nil
}
if !isRepeatableError(err) {
return result, err
}
tries++
if tries >= 3 {
err = errors.Wrap(err, "giving up after 3 consecutive repeatable transaction failures")
return result, err
}
}
}
func (s *RetryLayerThreadStore) GetThreadsForUser(userId string, opts model.GetUserThreadsOpts) (*model.Threads, error) {
tries := 0
@@ -8598,11 +8618,11 @@ func (s *RetryLayerThreadStore) UpdateMembership(membership *model.ThreadMembers
}
func (s *RetryLayerThreadStore) UpdateUnreadsByChannel(userId string, changedThreads []string, timestamp int64) error {
func (s *RetryLayerThreadStore) UpdateUnreadsByChannel(userId string, changedThreads []string, timestamp int64, updateViewedTimestamp bool) error {
tries := 0
for {
err := s.ThreadStore.UpdateUnreadsByChannel(userId, changedThreads, timestamp)
err := s.ThreadStore.UpdateUnreadsByChannel(userId, changedThreads, timestamp, updateViewedTimestamp)
if err == nil {
return nil
}

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

@@ -2100,7 +2100,7 @@ func (s SqlChannelStore) UpdateLastViewedAt(channelIds []string, userId string,
times[t.Id] = t.LastPostAt
}
if updateThreads {
s.Thread().UpdateUnreadsByChannel(userId, threadsToUpdate, now)
s.Thread().UpdateUnreadsByChannel(userId, threadsToUpdate, now, true)
}
return times, nil
}
@@ -2136,7 +2136,7 @@ func (s SqlChannelStore) UpdateLastViewedAt(channelIds []string, userId string,
}
if updateThreads {
s.Thread().UpdateUnreadsByChannel(userId, threadsToUpdate, now)
s.Thread().UpdateUnreadsByChannel(userId, threadsToUpdate, now, true)
}
return times, nil
}
@@ -2252,7 +2252,7 @@ func (s SqlChannelStore) UpdateLastViewedAtPost(unreadPost *model.Post, userID s
}
if updateThreads {
s.Thread().UpdateUnreadsByChannel(userID, threadsToUpdate, unreadDate)
s.Thread().UpdateUnreadsByChannel(userID, threadsToUpdate, unreadDate, true)
}
return result, nil
}
@@ -2282,7 +2282,7 @@ func (s SqlChannelStore) IncrementMentionCount(channelId string, userId string,
return errors.Wrapf(err, "failed to Update ChannelMembers with channelId=%s and userId=%s", channelId, userId)
}
if updateThreads {
s.Thread().UpdateUnreadsByChannel(userId, threadsToUpdate, now)
s.Thread().UpdateUnreadsByChannel(userId, threadsToUpdate, now, false)
}
return nil
}

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

@@ -120,7 +120,7 @@ func (s *SqlThreadStore) GetThreadsForUser(userId string, opts model.GetUserThre
var threads []*JoinedThread
fetchConditions := sq.And{
sq.Eq{"Posts.UserId": userId},
sq.Eq{"ThreadMemberships.UserId": userId},
sq.Eq{"ThreadMemberships.Following": true},
}
if !opts.Deleted {
@@ -273,13 +273,18 @@ func (s *SqlThreadStore) DeleteMembershipForUser(userId string, postId string) e
return nil
}
func (s *SqlThreadStore) CreateMembershipIfNeeded(userId, postId string, following bool) error {
func (s *SqlThreadStore) CreateMembershipIfNeeded(userId, postId string, following, incrementMentions, updateFollowing bool) error {
membership, err := s.GetMembershipForUser(userId, postId)
now := utils.MillisFromTime(time.Now())
if err == nil {
if !membership.Following || membership.Following != following {
membership.Following = following
if (updateFollowing && !membership.Following || membership.Following != following) || incrementMentions {
if updateFollowing {
membership.Following = following
}
membership.LastUpdated = now
if incrementMentions {
membership.UnreadMentions += 1
}
_, err = s.UpdateMembership(membership)
}
return err
@@ -290,12 +295,17 @@ func (s *SqlThreadStore) CreateMembershipIfNeeded(userId, postId string, followi
if !errors.As(err, &nfErr) {
return errors.Wrap(err, "failed to get thread membership")
}
mentions := 0
if incrementMentions {
mentions = 1
}
_, err = s.SaveMembership(&model.ThreadMembership{
PostId: postId,
UserId: userId,
Following: following,
LastViewed: 0,
LastUpdated: now,
PostId: postId,
UserId: userId,
Following: following,
LastViewed: 0,
LastUpdated: now,
UnreadMentions: int64(mentions),
})
return err
}
@@ -321,19 +331,38 @@ func (s *SqlThreadStore) CollectThreadsWithNewerReplies(userId string, channelId
return changedThreads, nil
}
func (s *SqlThreadStore) UpdateUnreadsByChannel(userId string, changedThreads []string, timestamp int64) error {
func (s *SqlThreadStore) UpdateUnreadsByChannel(userId string, changedThreads []string, timestamp int64, updateViewedTimestamp bool) error {
if len(changedThreads) == 0 {
return nil
}
updateQuery, updateArgs, _ := s.getQueryBuilder().
qb := s.getQueryBuilder().
Update("ThreadMemberships").
Where(sq.Eq{"UserId": userId, "PostId": changedThreads}).
Set("LastUpdated", timestamp).
Set("LastViewed", timestamp).
ToSql()
Set("LastUpdated", timestamp)
if updateViewedTimestamp {
qb = qb.Set("LastViewed", timestamp)
}
updateQuery, updateArgs, _ := qb.ToSql()
if _, err := s.GetMaster().Exec(updateQuery, updateArgs...); err != nil {
return errors.Wrap(err, "failed to update thread membership")
}
return nil
}
func (s *SqlThreadStore) GetPosts(threadId string, since int64) ([]*model.Post, error) {
query, args, _ := s.getQueryBuilder().
Select("*").
From("Posts").
Where(sq.Eq{"RootId": threadId}).
Where(sq.Eq{"DeleteAt": 0}).
Where(sq.GtOrEq{"UpdateAt": since}).ToSql()
var result []*model.Post
if _, err := s.GetReplica().Select(&result, query, args...); err != nil {
return nil, errors.Wrap(err, "failed to fetch thread posts")
}
return result, nil
}

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

@@ -253,6 +253,7 @@ type ThreadStore interface {
Get(id string) (*model.Thread, error)
GetThreadsForUser(userId string, opts model.GetUserThreadsOpts) (*model.Threads, error)
Delete(postId string) error
GetPosts(threadId string, since int64) ([]*model.Post, error)
MarkAllAsRead(userId string, timestamp int64) error
MarkAsRead(userId, threadId string, timestamp int64) error
@@ -262,9 +263,9 @@ type ThreadStore interface {
GetMembershipsForUser(userId string) ([]*model.ThreadMembership, error)
GetMembershipForUser(userId, postId string) (*model.ThreadMembership, error)
DeleteMembershipForUser(userId, postId string) error
CreateMembershipIfNeeded(userId, postId string, following bool) error
CreateMembershipIfNeeded(userId, postId string, following, incrementMentions, updateFollowing bool) error
CollectThreadsWithNewerReplies(userId string, channelIds []string, timestamp int64) ([]string, error)
UpdateUnreadsByChannel(userId string, changedThreads []string, timestamp int64) error
UpdateUnreadsByChannel(userId string, changedThreads []string, timestamp int64, updateViewedTimestamp bool) error
}
type PostStore interface {

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

@@ -37,13 +37,13 @@ func (_m *ThreadStore) CollectThreadsWithNewerReplies(userId string, channelIds
return r0, r1
}
// CreateMembershipIfNeeded provides a mock function with given fields: userId, postId, following
func (_m *ThreadStore) CreateMembershipIfNeeded(userId string, postId string, following bool) error {
ret := _m.Called(userId, postId, following)
// CreateMembershipIfNeeded provides a mock function with given fields: userId, postId, following, incrementMentions, updateFollowing
func (_m *ThreadStore) CreateMembershipIfNeeded(userId string, postId string, following bool, incrementMentions bool, updateFollowing bool) error {
ret := _m.Called(userId, postId, following, incrementMentions, updateFollowing)
var r0 error
if rf, ok := ret.Get(0).(func(string, string, bool) error); ok {
r0 = rf(userId, postId, following)
if rf, ok := ret.Get(0).(func(string, string, bool, bool, bool) error); ok {
r0 = rf(userId, postId, following, incrementMentions, updateFollowing)
} else {
r0 = ret.Error(0)
}
@@ -148,6 +148,29 @@ func (_m *ThreadStore) GetMembershipsForUser(userId string) ([]*model.ThreadMemb
return r0, r1
}
// GetPosts provides a mock function with given fields: threadId, since
func (_m *ThreadStore) GetPosts(threadId string, since int64) ([]*model.Post, error) {
ret := _m.Called(threadId, since)
var r0 []*model.Post
if rf, ok := ret.Get(0).(func(string, int64) []*model.Post); ok {
r0 = rf(threadId, since)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).([]*model.Post)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(string, int64) error); ok {
r1 = rf(threadId, since)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// GetThreadsForUser provides a mock function with given fields: userId, opts
func (_m *ThreadStore) GetThreadsForUser(userId string, opts model.GetUserThreadsOpts) (*model.Threads, error) {
ret := _m.Called(userId, opts)
@@ -321,13 +344,13 @@ func (_m *ThreadStore) UpdateMembership(membership *model.ThreadMembership) (*mo
return r0, r1
}
// UpdateUnreadsByChannel provides a mock function with given fields: userId, changedThreads, timestamp
func (_m *ThreadStore) UpdateUnreadsByChannel(userId string, changedThreads []string, timestamp int64) error {
ret := _m.Called(userId, changedThreads, timestamp)
// UpdateUnreadsByChannel provides a mock function with given fields: userId, changedThreads, timestamp, updateViewedTimestamp
func (_m *ThreadStore) UpdateUnreadsByChannel(userId string, changedThreads []string, timestamp int64, updateViewedTimestamp bool) error {
ret := _m.Called(userId, changedThreads, timestamp, updateViewedTimestamp)
var r0 error
if rf, ok := ret.Get(0).(func(string, []string, int64) error); ok {
r0 = rf(userId, changedThreads, timestamp)
if rf, ok := ret.Get(0).(func(string, []string, int64, bool) error); ok {
r0 = rf(userId, changedThreads, timestamp, updateViewedTimestamp)
} else {
r0 = ret.Error(0)
}

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

@@ -234,7 +234,7 @@ func testThreadStorePopulation(t *testing.T, ss store.Store) {
t.Run("Thread last updated is changed when channel is updated after UpdateLastViewedAtPost", func(t *testing.T) {
newPosts := makeSomePosts()
require.Nil(t, ss.Thread().CreateMembershipIfNeeded(newPosts[0].UserId, newPosts[0].Id, true))
require.Nil(t, ss.Thread().CreateMembershipIfNeeded(newPosts[0].UserId, newPosts[0].Id, true, false, true))
m, err1 := ss.Thread().GetMembershipForUser(newPosts[0].UserId, newPosts[0].Id)
require.Nil(t, err1)
m.LastUpdated -= 1000
@@ -254,7 +254,7 @@ func testThreadStorePopulation(t *testing.T, ss store.Store) {
t.Run("Thread last updated is changed when channel is updated after IncrementMentionCount", func(t *testing.T) {
newPosts := makeSomePosts()
require.Nil(t, ss.Thread().CreateMembershipIfNeeded(newPosts[0].UserId, newPosts[0].Id, true))
require.Nil(t, ss.Thread().CreateMembershipIfNeeded(newPosts[0].UserId, newPosts[0].Id, true, false, true))
m, err1 := ss.Thread().GetMembershipForUser(newPosts[0].UserId, newPosts[0].Id)
require.Nil(t, err1)
m.LastUpdated -= 1000
@@ -274,7 +274,7 @@ func testThreadStorePopulation(t *testing.T, ss store.Store) {
t.Run("Thread last updated is changed when channel is updated after UpdateLastViewedAt", func(t *testing.T) {
newPosts := makeSomePosts()
require.Nil(t, ss.Thread().CreateMembershipIfNeeded(newPosts[0].UserId, newPosts[0].Id, true))
require.Nil(t, ss.Thread().CreateMembershipIfNeeded(newPosts[0].UserId, newPosts[0].Id, true, false, true))
m, err1 := ss.Thread().GetMembershipForUser(newPosts[0].UserId, newPosts[0].Id)
require.Nil(t, err1)
m.LastUpdated -= 1000
@@ -294,7 +294,7 @@ func testThreadStorePopulation(t *testing.T, ss store.Store) {
t.Run("Thread last updated is changed when channel is updated after UpdateLastViewedAtPost for mark unread", func(t *testing.T) {
newPosts := makeSomePosts()
require.Nil(t, ss.Thread().CreateMembershipIfNeeded(newPosts[0].UserId, newPosts[0].Id, true))
require.Nil(t, ss.Thread().CreateMembershipIfNeeded(newPosts[0].UserId, newPosts[0].Id, true, false, true))
m, err1 := ss.Thread().GetMembershipForUser(newPosts[0].UserId, newPosts[0].Id)
require.Nil(t, err1)
m.LastUpdated += 1000

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

@@ -6920,10 +6920,10 @@ func (s *TimerLayerThreadStore) CollectThreadsWithNewerReplies(userId string, ch
return result, err
}
func (s *TimerLayerThreadStore) CreateMembershipIfNeeded(userId string, postId string, following bool) error {
func (s *TimerLayerThreadStore) CreateMembershipIfNeeded(userId string, postId string, following bool, incrementMentions bool, updateFollowing bool) error {
start := timemodule.Now()
err := s.ThreadStore.CreateMembershipIfNeeded(userId, postId, following)
err := s.ThreadStore.CreateMembershipIfNeeded(userId, postId, following, incrementMentions, updateFollowing)
elapsed := float64(timemodule.Since(start)) / float64(timemodule.Second)
if s.Root.Metrics != nil {
@@ -7016,6 +7016,22 @@ func (s *TimerLayerThreadStore) GetMembershipsForUser(userId string) ([]*model.T
return result, err
}
func (s *TimerLayerThreadStore) GetPosts(threadId string, since int64) ([]*model.Post, error) {
start := timemodule.Now()
result, err := s.ThreadStore.GetPosts(threadId, since)
elapsed := float64(timemodule.Since(start)) / float64(timemodule.Second)
if s.Root.Metrics != nil {
success := "false"
if err == nil {
success = "true"
}
s.Root.Metrics.ObserveStoreMethodDuration("ThreadStore.GetPosts", success, elapsed)
}
return result, err
}
func (s *TimerLayerThreadStore) GetThreadsForUser(userId string, opts model.GetUserThreadsOpts) (*model.Threads, error) {
start := timemodule.Now()
@@ -7144,10 +7160,10 @@ func (s *TimerLayerThreadStore) UpdateMembership(membership *model.ThreadMembers
return result, err
}
func (s *TimerLayerThreadStore) UpdateUnreadsByChannel(userId string, changedThreads []string, timestamp int64) error {
func (s *TimerLayerThreadStore) UpdateUnreadsByChannel(userId string, changedThreads []string, timestamp int64, updateViewedTimestamp bool) error {
start := timemodule.Now()
err := s.ThreadStore.UpdateUnreadsByChannel(userId, changedThreads, timestamp)
err := s.ThreadStore.UpdateUnreadsByChannel(userId, changedThreads, timestamp, updateViewedTimestamp)
elapsed := float64(timemodule.Since(start)) / float64(timemodule.Second)
if s.Root.Metrics != nil {