[MM-56904] Reduce the number of api requests made to fetch user information for GMs on page load (#27149)
* use new endpoint to fetch group members
Этот коммит содержится в:
@@ -933,3 +933,89 @@ func TestDeleteSidebarPreferences(t *testing.T) {
|
|||||||
assert.NotContains(t, categories.Categories[1].Channels, channel.Id)
|
assert.NotContains(t, categories.Categories[1].Channels, channel.Id)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestUpdateLimitVisibleDMsGMs(t *testing.T) {
|
||||||
|
t.Run("Update limit_visible_dms_gms to a valid value", func(t *testing.T) {
|
||||||
|
th := Setup(t).InitBasic()
|
||||||
|
defer th.TearDown()
|
||||||
|
client := th.Client
|
||||||
|
|
||||||
|
th.LoginBasic()
|
||||||
|
user := th.BasicUser
|
||||||
|
|
||||||
|
_, err := client.UpdatePreferences(context.Background(), user.Id, model.Preferences{
|
||||||
|
{
|
||||||
|
UserId: user.Id,
|
||||||
|
Category: model.PreferenceCategorySidebarSettings,
|
||||||
|
Name: model.PreferenceLimitVisibleDmsGms,
|
||||||
|
Value: "40",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
pref, _, err := client.GetPreferenceByCategoryAndName(context.Background(), user.Id, model.PreferenceCategorySidebarSettings, model.PreferenceLimitVisibleDmsGms)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
require.Equal(t, "40", pref.Value, "Value was not updated")
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Update limit_visible_dms_gms to a value greater PreferenceMaxLimitVisibleDmsGmsValue", func(t *testing.T) {
|
||||||
|
th := Setup(t).InitBasic()
|
||||||
|
defer th.TearDown()
|
||||||
|
client := th.Client
|
||||||
|
|
||||||
|
th.LoginBasic()
|
||||||
|
user := th.BasicUser
|
||||||
|
|
||||||
|
resp, err := client.UpdatePreferences(context.Background(), user.Id, model.Preferences{
|
||||||
|
{
|
||||||
|
UserId: user.Id,
|
||||||
|
Category: model.PreferenceCategorySidebarSettings,
|
||||||
|
Name: model.PreferenceLimitVisibleDmsGms,
|
||||||
|
Value: "10000",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
require.Error(t, err)
|
||||||
|
CheckBadRequestStatus(t, resp)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Update limit_visible_dms_gms to an invalid value", func(t *testing.T) {
|
||||||
|
th := Setup(t).InitBasic()
|
||||||
|
defer th.TearDown()
|
||||||
|
client := th.Client
|
||||||
|
|
||||||
|
th.LoginBasic()
|
||||||
|
user := th.BasicUser
|
||||||
|
|
||||||
|
resp, err := client.UpdatePreferences(context.Background(), user.Id, model.Preferences{
|
||||||
|
{
|
||||||
|
UserId: user.Id,
|
||||||
|
Category: model.PreferenceCategorySidebarSettings,
|
||||||
|
Name: model.PreferenceLimitVisibleDmsGms,
|
||||||
|
Value: "one thousand",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
require.Error(t, err)
|
||||||
|
CheckBadRequestStatus(t, resp)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Update limit_visible_dms_gms to a negative number", func(t *testing.T) {
|
||||||
|
th := Setup(t).InitBasic()
|
||||||
|
defer th.TearDown()
|
||||||
|
client := th.Client
|
||||||
|
|
||||||
|
th.LoginBasic()
|
||||||
|
user := th.BasicUser
|
||||||
|
|
||||||
|
resp, err := client.UpdatePreferences(context.Background(), user.Id, model.Preferences{
|
||||||
|
{
|
||||||
|
UserId: user.Id,
|
||||||
|
Category: model.PreferenceCategorySidebarSettings,
|
||||||
|
Name: model.PreferenceLimitVisibleDmsGms,
|
||||||
|
Value: "-20",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
require.Error(t, err)
|
||||||
|
CheckBadRequestStatus(t, resp)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
@@ -646,6 +646,27 @@ func (s *Server) doDeleteOrphanDraftsMigration(c request.CTX) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Server) doDeleteDmsPreferencesMigration(c request.CTX) error {
|
||||||
|
// If the migration is already marked as completed, don't do it again.
|
||||||
|
if _, err := s.Store().System().GetByName(model.MigrationKeyDeleteDmsPreferences); err == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
jobs, err := s.Store().Job().GetAllByTypeAndStatus(c, model.JobTypeDeleteDmsPreferencesMigration, model.JobStatusPending)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to get jobs by type and status: %w", err)
|
||||||
|
}
|
||||||
|
if len(jobs) > 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, appErr := s.Jobs.CreateJobOnce(c, model.JobTypeDeleteDmsPreferencesMigration, nil); appErr != nil {
|
||||||
|
return fmt.Errorf("failed to start job for deleting dm preferences: %w", appErr)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (a *App) DoAppMigrations() {
|
func (a *App) DoAppMigrations() {
|
||||||
a.Srv().doAppMigrations()
|
a.Srv().doAppMigrations()
|
||||||
}
|
}
|
||||||
@@ -688,6 +709,7 @@ func (s *Server) doAppMigrations() {
|
|||||||
{"Encode S3 Image Paths Migration", s.doCloudS3PathMigrations},
|
{"Encode S3 Image Paths Migration", s.doCloudS3PathMigrations},
|
||||||
{"Delete Empty Drafts Migration", s.doDeleteEmptyDraftsMigration},
|
{"Delete Empty Drafts Migration", s.doDeleteEmptyDraftsMigration},
|
||||||
{"Delete Orphan Drafts Migration", s.doDeleteOrphanDraftsMigration},
|
{"Delete Orphan Drafts Migration", s.doDeleteOrphanDraftsMigration},
|
||||||
|
{"Delete Invalid Dms Preferences Migration", s.doDeleteDmsPreferencesMigration},
|
||||||
}
|
}
|
||||||
|
|
||||||
c := request.EmptyContext(s.Log())
|
c := request.EmptyContext(s.Log())
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ import (
|
|||||||
"github.com/mattermost/mattermost/server/v8/channels/jobs"
|
"github.com/mattermost/mattermost/server/v8/channels/jobs"
|
||||||
"github.com/mattermost/mattermost/server/v8/channels/jobs/active_users"
|
"github.com/mattermost/mattermost/server/v8/channels/jobs/active_users"
|
||||||
"github.com/mattermost/mattermost/server/v8/channels/jobs/cleanup_desktop_tokens"
|
"github.com/mattermost/mattermost/server/v8/channels/jobs/cleanup_desktop_tokens"
|
||||||
|
"github.com/mattermost/mattermost/server/v8/channels/jobs/delete_dms_preferences_migration"
|
||||||
"github.com/mattermost/mattermost/server/v8/channels/jobs/delete_empty_drafts_migration"
|
"github.com/mattermost/mattermost/server/v8/channels/jobs/delete_empty_drafts_migration"
|
||||||
"github.com/mattermost/mattermost/server/v8/channels/jobs/delete_orphan_drafts_migration"
|
"github.com/mattermost/mattermost/server/v8/channels/jobs/delete_orphan_drafts_migration"
|
||||||
"github.com/mattermost/mattermost/server/v8/channels/jobs/expirynotify"
|
"github.com/mattermost/mattermost/server/v8/channels/jobs/expirynotify"
|
||||||
@@ -1603,6 +1604,11 @@ func (s *Server) initJobs() {
|
|||||||
nil,
|
nil,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
s.Jobs.RegisterJobType(
|
||||||
|
model.JobTypeDeleteDmsPreferencesMigration,
|
||||||
|
delete_dms_preferences_migration.MakeWorker(s.Jobs, s.Store(), New(ServerConnector(s.Channels()))),
|
||||||
|
nil)
|
||||||
|
|
||||||
s.platform.Jobs = s.Jobs
|
s.platform.Jobs = s.Jobs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,43 @@
|
|||||||
|
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||||
|
// See LICENSE.txt for license information.
|
||||||
|
|
||||||
|
package delete_dms_preferences_migration
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/mattermost/mattermost/server/public/model"
|
||||||
|
"github.com/mattermost/mattermost/server/v8/channels/jobs"
|
||||||
|
"github.com/mattermost/mattermost/server/v8/channels/store"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
timeBetweenBatches = 1 * time.Second
|
||||||
|
)
|
||||||
|
|
||||||
|
// MakeWorker creates a batch migration worker to delete empty drafts.
|
||||||
|
func MakeWorker(jobServer *jobs.JobServer, store store.Store, app jobs.BatchMigrationWorkerAppIFace) model.Worker {
|
||||||
|
return jobs.MakeBatchMigrationWorker(
|
||||||
|
jobServer,
|
||||||
|
store,
|
||||||
|
app,
|
||||||
|
model.MigrationKeyDeleteDmsPreferences,
|
||||||
|
timeBetweenBatches,
|
||||||
|
doDeleteDmsPreferencesMigrationBatch,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// doDeleteDmsPreferencesMigrationBatch deletes any limit_visible_dms_gms preferences with a value > 40 and less than 1.
|
||||||
|
func doDeleteDmsPreferencesMigrationBatch(data model.StringMap, store store.Store) (model.StringMap, bool, error) {
|
||||||
|
rowAffected, err := store.Preference().DeleteInvalidVisibleDmsGms()
|
||||||
|
if err != nil {
|
||||||
|
return nil, false, errors.Wrapf(err, "failed to delete invalid limit_visible_dms_gms")
|
||||||
|
}
|
||||||
|
|
||||||
|
if rowAffected == 0 {
|
||||||
|
return nil, true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, false, nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||||
|
// See LICENSE.txt for license information.
|
||||||
|
|
||||||
|
package delete_dms_preferences_migration
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/mattermost/mattermost/server/v8/channels/store/storetest"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestDoDeleteDMsPreferencesMigrationBatch(t *testing.T) {
|
||||||
|
t.Run("failure deleting batch", func(t *testing.T) {
|
||||||
|
mockStore := &storetest.Store{}
|
||||||
|
t.Cleanup(func() {
|
||||||
|
mockStore.AssertExpectations(t)
|
||||||
|
})
|
||||||
|
|
||||||
|
mockStore.PreferenceStore.On("DeleteInvalidVisibleDmsGms").Return(int64(0), errors.New("failure"))
|
||||||
|
|
||||||
|
data, done, err := doDeleteDmsPreferencesMigrationBatch(nil, mockStore)
|
||||||
|
require.EqualError(t, err, "failed to delete invalid limit_visible_dms_gms: failure")
|
||||||
|
assert.False(t, done)
|
||||||
|
assert.Nil(t, data)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("do batches", func(t *testing.T) {
|
||||||
|
mockStore := &storetest.Store{}
|
||||||
|
t.Cleanup(func() {
|
||||||
|
mockStore.AssertExpectations(t)
|
||||||
|
})
|
||||||
|
|
||||||
|
mockStore.PreferenceStore.On("DeleteInvalidVisibleDmsGms").Return(int64(10), nil)
|
||||||
|
|
||||||
|
data, done, err := doDeleteDmsPreferencesMigrationBatch(nil, mockStore)
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.False(t, done)
|
||||||
|
assert.Nil(t, data)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("done batches", func(t *testing.T) {
|
||||||
|
mockStore := &storetest.Store{}
|
||||||
|
t.Cleanup(func() {
|
||||||
|
mockStore.AssertExpectations(t)
|
||||||
|
})
|
||||||
|
|
||||||
|
mockStore.PreferenceStore.On("DeleteInvalidVisibleDmsGms").Return(int64(0), nil)
|
||||||
|
|
||||||
|
data, done, err := doDeleteDmsPreferencesMigrationBatch(nil, mockStore)
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.True(t, done)
|
||||||
|
assert.Nil(t, data)
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -7378,6 +7378,24 @@ func (s *OpenTracingLayerPreferenceStore) DeleteCategoryAndName(category string,
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *OpenTracingLayerPreferenceStore) DeleteInvalidVisibleDmsGms() (int64, error) {
|
||||||
|
origCtx := s.Root.Store.Context()
|
||||||
|
span, newCtx := tracing.StartSpanWithParentByContext(s.Root.Store.Context(), "PreferenceStore.DeleteInvalidVisibleDmsGms")
|
||||||
|
s.Root.Store.SetContext(newCtx)
|
||||||
|
defer func() {
|
||||||
|
s.Root.Store.SetContext(origCtx)
|
||||||
|
}()
|
||||||
|
|
||||||
|
defer span.Finish()
|
||||||
|
result, err := s.PreferenceStore.DeleteInvalidVisibleDmsGms()
|
||||||
|
if err != nil {
|
||||||
|
span.LogFields(spanlog.Error(err))
|
||||||
|
ext.Error.Set(span, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
return result, err
|
||||||
|
}
|
||||||
|
|
||||||
func (s *OpenTracingLayerPreferenceStore) DeleteOrphanedRows(limit int) (int64, error) {
|
func (s *OpenTracingLayerPreferenceStore) DeleteOrphanedRows(limit int) (int64, error) {
|
||||||
origCtx := s.Root.Store.Context()
|
origCtx := s.Root.Store.Context()
|
||||||
span, newCtx := tracing.StartSpanWithParentByContext(s.Root.Store.Context(), "PreferenceStore.DeleteOrphanedRows")
|
span, newCtx := tracing.StartSpanWithParentByContext(s.Root.Store.Context(), "PreferenceStore.DeleteOrphanedRows")
|
||||||
|
|||||||
@@ -8384,6 +8384,27 @@ func (s *RetryLayerPreferenceStore) DeleteCategoryAndName(category string, name
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *RetryLayerPreferenceStore) DeleteInvalidVisibleDmsGms() (int64, error) {
|
||||||
|
|
||||||
|
tries := 0
|
||||||
|
for {
|
||||||
|
result, err := s.PreferenceStore.DeleteInvalidVisibleDmsGms()
|
||||||
|
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
|
||||||
|
}
|
||||||
|
timepkg.Sleep(100 * timepkg.Millisecond)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
func (s *RetryLayerPreferenceStore) DeleteOrphanedRows(limit int) (int64, error) {
|
func (s *RetryLayerPreferenceStore) DeleteOrphanedRows(limit int) (int64, error) {
|
||||||
|
|
||||||
tries := 0
|
tries := 0
|
||||||
|
|||||||
@@ -315,3 +315,54 @@ func (s SqlPreferenceStore) CleanupFlagsBatch(limit int64) (int64, error) {
|
|||||||
|
|
||||||
return rowsAffected, nil
|
return rowsAffected, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Delete preference for limit_visible_dms_gms where their value is greater than "40" or less than "1"
|
||||||
|
func (s SqlPreferenceStore) DeleteInvalidVisibleDmsGms() (int64, error) {
|
||||||
|
var queryString string
|
||||||
|
var args []interface{}
|
||||||
|
var err error
|
||||||
|
|
||||||
|
// We need to pad the value field with zeros when doing comparison's because the value is stored as a string.
|
||||||
|
// Having them the same length allows Postgres/MySQL to compare them correctly.
|
||||||
|
whereClause := sq.And{
|
||||||
|
sq.Eq{"Category": model.PreferenceCategorySidebarSettings},
|
||||||
|
sq.Eq{"Name": model.PreferenceLimitVisibleDmsGms},
|
||||||
|
sq.Or{
|
||||||
|
sq.Gt{"SUBSTRING(CONCAT('000000000000000', Value), LENGTH(Value) + 1, 15)": "000000000000040"},
|
||||||
|
sq.Lt{"SUBSTRING(CONCAT('000000000000000', Value), LENGTH(Value) + 1, 15)": "000000000000001"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
if s.DriverName() == "postgres" {
|
||||||
|
subQuery := s.getQueryBuilder().
|
||||||
|
Select("UserId, Category, Name").
|
||||||
|
From("Preferences").
|
||||||
|
Where(whereClause).
|
||||||
|
Limit(100)
|
||||||
|
queryString, args, err = s.getQueryBuilder().
|
||||||
|
Delete("Preferences").
|
||||||
|
Where(sq.Expr("(userid, category, name) IN (?)", subQuery)).
|
||||||
|
ToSql()
|
||||||
|
if err != nil {
|
||||||
|
return int64(0), errors.Wrap(err, "could not build sql query to delete preference")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
queryString, args, err = s.getQueryBuilder().
|
||||||
|
Delete("Preferences").
|
||||||
|
Where(whereClause).
|
||||||
|
Limit(100).
|
||||||
|
ToSql()
|
||||||
|
if err != nil {
|
||||||
|
return int64(0), errors.Wrap(err, "could not build sql query to delete preference")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result, err := s.GetMasterX().Exec(queryString, args...)
|
||||||
|
if err != nil {
|
||||||
|
return 0, errors.Wrap(err, "failed to delete Preference")
|
||||||
|
}
|
||||||
|
rowsAffected, err := result.RowsAffected()
|
||||||
|
if err != nil {
|
||||||
|
return 0, errors.Wrap(err, "unable to get rows affected")
|
||||||
|
}
|
||||||
|
return rowsAffected, nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func TestPreferenceStore(t *testing.T) {
|
func TestPreferenceStore(t *testing.T) {
|
||||||
StoreTest(t, storetest.TestPreferenceStore)
|
StoreTestWithSqlStore(t, storetest.TestPreferenceStore)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDeleteUnusedFeatures(t *testing.T) {
|
func TestDeleteUnusedFeatures(t *testing.T) {
|
||||||
|
|||||||
@@ -652,6 +652,7 @@ type PreferenceStore interface {
|
|||||||
PermanentDeleteByUser(userID string) error
|
PermanentDeleteByUser(userID string) error
|
||||||
DeleteOrphanedRows(limit int) (deleted int64, err error)
|
DeleteOrphanedRows(limit int) (deleted int64, err error)
|
||||||
CleanupFlagsBatch(limit int64) (int64, error)
|
CleanupFlagsBatch(limit int64) (int64, error)
|
||||||
|
DeleteInvalidVisibleDmsGms() (int64, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type LicenseStore interface {
|
type LicenseStore interface {
|
||||||
|
|||||||
@@ -96,6 +96,34 @@ func (_m *PreferenceStore) DeleteCategoryAndName(category string, name string) e
|
|||||||
return r0
|
return r0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DeleteInvalidVisibleDmsGms provides a mock function with given fields:
|
||||||
|
func (_m *PreferenceStore) DeleteInvalidVisibleDmsGms() (int64, error) {
|
||||||
|
ret := _m.Called()
|
||||||
|
|
||||||
|
if len(ret) == 0 {
|
||||||
|
panic("no return value specified for DeleteInvalidVisibleDmsGms")
|
||||||
|
}
|
||||||
|
|
||||||
|
var r0 int64
|
||||||
|
var r1 error
|
||||||
|
if rf, ok := ret.Get(0).(func() (int64, error)); ok {
|
||||||
|
return rf()
|
||||||
|
}
|
||||||
|
if rf, ok := ret.Get(0).(func() int64); ok {
|
||||||
|
r0 = rf()
|
||||||
|
} else {
|
||||||
|
r0 = ret.Get(0).(int64)
|
||||||
|
}
|
||||||
|
|
||||||
|
if rf, ok := ret.Get(1).(func() error); ok {
|
||||||
|
r1 = rf()
|
||||||
|
} else {
|
||||||
|
r1 = ret.Error(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0, r1
|
||||||
|
}
|
||||||
|
|
||||||
// DeleteOrphanedRows provides a mock function with given fields: limit
|
// DeleteOrphanedRows provides a mock function with given fields: limit
|
||||||
func (_m *PreferenceStore) DeleteOrphanedRows(limit int) (int64, error) {
|
func (_m *PreferenceStore) DeleteOrphanedRows(limit int) (int64, error) {
|
||||||
ret := _m.Called(limit)
|
ret := _m.Called(limit)
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import (
|
|||||||
"github.com/mattermost/mattermost/server/v8/channels/store"
|
"github.com/mattermost/mattermost/server/v8/channels/store"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestPreferenceStore(t *testing.T, rctx request.CTX, ss store.Store) {
|
func TestPreferenceStore(t *testing.T, rctx request.CTX, ss store.Store, s SqlStore) {
|
||||||
t.Run("PreferenceSave", func(t *testing.T) { testPreferenceSave(t, rctx, ss) })
|
t.Run("PreferenceSave", func(t *testing.T) { testPreferenceSave(t, rctx, ss) })
|
||||||
t.Run("PreferenceGet", func(t *testing.T) { testPreferenceGet(t, rctx, ss) })
|
t.Run("PreferenceGet", func(t *testing.T) { testPreferenceGet(t, rctx, ss) })
|
||||||
t.Run("PreferenceGetCategory", func(t *testing.T) { testPreferenceGetCategory(t, rctx, ss) })
|
t.Run("PreferenceGetCategory", func(t *testing.T) { testPreferenceGetCategory(t, rctx, ss) })
|
||||||
@@ -24,6 +24,7 @@ func TestPreferenceStore(t *testing.T, rctx request.CTX, ss store.Store) {
|
|||||||
t.Run("PreferenceDeleteCategory", func(t *testing.T) { testPreferenceDeleteCategory(t, rctx, ss) })
|
t.Run("PreferenceDeleteCategory", func(t *testing.T) { testPreferenceDeleteCategory(t, rctx, ss) })
|
||||||
t.Run("PreferenceDeleteCategoryAndName", func(t *testing.T) { testPreferenceDeleteCategoryAndName(t, rctx, ss) })
|
t.Run("PreferenceDeleteCategoryAndName", func(t *testing.T) { testPreferenceDeleteCategoryAndName(t, rctx, ss) })
|
||||||
t.Run("PreferenceDeleteOrphanedRows", func(t *testing.T) { testPreferenceDeleteOrphanedRows(t, rctx, ss) })
|
t.Run("PreferenceDeleteOrphanedRows", func(t *testing.T) { testPreferenceDeleteOrphanedRows(t, rctx, ss) })
|
||||||
|
t.Run("PreferenceDeleteInvalidVisibleDmsGms", func(t *testing.T) { testDeleteInvalidVisibleDmsGms(t, rctx, ss, s) })
|
||||||
}
|
}
|
||||||
|
|
||||||
func testPreferenceSave(t *testing.T, rctx request.CTX, ss store.Store) {
|
func testPreferenceSave(t *testing.T, rctx request.CTX, ss store.Store) {
|
||||||
@@ -401,3 +402,78 @@ func testPreferenceDeleteOrphanedRows(t *testing.T, rctx request.CTX, ss store.S
|
|||||||
_, nErr = ss.Preference().Get(userId, category, preference2.Name)
|
_, nErr = ss.Preference().Get(userId, category, preference2.Name)
|
||||||
assert.NoError(t, nErr, "newer preference should not have been deleted")
|
assert.NoError(t, nErr, "newer preference should not have been deleted")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func testDeleteInvalidVisibleDmsGms(t *testing.T, rctx request.CTX, ss store.Store, s SqlStore) {
|
||||||
|
userId1 := model.NewId()
|
||||||
|
userId2 := model.NewId()
|
||||||
|
userId3 := model.NewId()
|
||||||
|
userId4 := model.NewId()
|
||||||
|
category := model.PreferenceCategorySidebarSettings
|
||||||
|
name := model.PreferenceLimitVisibleDmsGms
|
||||||
|
|
||||||
|
preferences := model.Preferences{
|
||||||
|
{
|
||||||
|
UserId: userId1,
|
||||||
|
Category: category,
|
||||||
|
Name: name,
|
||||||
|
Value: "10000",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
UserId: userId2,
|
||||||
|
Category: category,
|
||||||
|
Name: name,
|
||||||
|
Value: "40",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
UserId: userId3,
|
||||||
|
Category: category,
|
||||||
|
Name: name,
|
||||||
|
Value: "invalid",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
UserId: model.NewId(),
|
||||||
|
Category: category,
|
||||||
|
Name: name,
|
||||||
|
Value: "-10",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
UserId: model.NewId(),
|
||||||
|
Category: category,
|
||||||
|
Name: name,
|
||||||
|
Value: "0",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
UserId: model.NewId(),
|
||||||
|
Category: category,
|
||||||
|
Name: name,
|
||||||
|
Value: "00000",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
UserId: userId4,
|
||||||
|
Category: category,
|
||||||
|
Name: name,
|
||||||
|
Value: "20",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Can't insert with Save methods because the values are invalid
|
||||||
|
_, execerr := s.GetMasterX().NamedExec(`
|
||||||
|
INSERT INTO
|
||||||
|
Preferences(UserId, Category, Name, Value)
|
||||||
|
VALUES
|
||||||
|
(:UserId, :Category, :Name, :Value);
|
||||||
|
`, preferences)
|
||||||
|
require.NoError(t, execerr)
|
||||||
|
|
||||||
|
count, err := ss.Preference().DeleteInvalidVisibleDmsGms()
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Equal(t, int64(5), count)
|
||||||
|
|
||||||
|
preference, err := ss.Preference().Get(userId2, category, name)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, &preferences[1], preference)
|
||||||
|
|
||||||
|
preference, err = ss.Preference().Get(userId4, category, name)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, &preferences[6], preference)
|
||||||
|
}
|
||||||
|
|||||||
@@ -6665,6 +6665,22 @@ func (s *TimerLayerPreferenceStore) DeleteCategoryAndName(category string, name
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *TimerLayerPreferenceStore) DeleteInvalidVisibleDmsGms() (int64, error) {
|
||||||
|
start := time.Now()
|
||||||
|
|
||||||
|
result, err := s.PreferenceStore.DeleteInvalidVisibleDmsGms()
|
||||||
|
|
||||||
|
elapsed := float64(time.Since(start)) / float64(time.Second)
|
||||||
|
if s.Root.Metrics != nil {
|
||||||
|
success := "false"
|
||||||
|
if err == nil {
|
||||||
|
success = "true"
|
||||||
|
}
|
||||||
|
s.Root.Metrics.ObserveStoreMethodDuration("PreferenceStore.DeleteInvalidVisibleDmsGms", success, elapsed)
|
||||||
|
}
|
||||||
|
return result, err
|
||||||
|
}
|
||||||
|
|
||||||
func (s *TimerLayerPreferenceStore) DeleteOrphanedRows(limit int) (int64, error) {
|
func (s *TimerLayerPreferenceStore) DeleteOrphanedRows(limit int) (int64, error) {
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
|
|
||||||
|
|||||||
@@ -76,6 +76,7 @@ func GetMockStoreForSetupFunctions() *mocks.Store {
|
|||||||
systemStore.On("GetByName", model.MigrationKeyAddIPFilteringPermissions).Return(&model.System{Name: model.MigrationKeyAddIPFilteringPermissions, Value: "true"}, nil)
|
systemStore.On("GetByName", model.MigrationKeyAddIPFilteringPermissions).Return(&model.System{Name: model.MigrationKeyAddIPFilteringPermissions, Value: "true"}, nil)
|
||||||
systemStore.On("GetByName", model.MigrationKeyAddOutgoingOAuthConnectionsPermissions).Return(&model.System{Name: model.MigrationKeyAddOutgoingOAuthConnectionsPermissions, Value: "true"}, nil)
|
systemStore.On("GetByName", model.MigrationKeyAddOutgoingOAuthConnectionsPermissions).Return(&model.System{Name: model.MigrationKeyAddOutgoingOAuthConnectionsPermissions, Value: "true"}, nil)
|
||||||
systemStore.On("GetByName", model.MigrationKeyAddChannelBookmarksPermissions).Return(&model.System{Name: model.MigrationKeyAddChannelBookmarksPermissions, Value: "true"}, nil)
|
systemStore.On("GetByName", model.MigrationKeyAddChannelBookmarksPermissions).Return(&model.System{Name: model.MigrationKeyAddChannelBookmarksPermissions, Value: "true"}, nil)
|
||||||
|
systemStore.On("GetByName", model.MigrationKeyDeleteDmsPreferences).Return(&model.System{Name: model.MigrationKeyDeleteDmsPreferences, Value: "true"}, nil)
|
||||||
systemStore.On("GetByName", model.MigrationKeyAddManageJobAncillaryPermissions).Return(&model.System{Name: model.MigrationKeyAddManageJobAncillaryPermissions, Value: "true"}, nil)
|
systemStore.On("GetByName", model.MigrationKeyAddManageJobAncillaryPermissions).Return(&model.System{Name: model.MigrationKeyAddManageJobAncillaryPermissions, Value: "true"}, nil)
|
||||||
systemStore.On("GetByName", "CustomGroupAdminRoleCreationMigrationComplete").Return(&model.System{Name: model.MigrationKeyAddPlayboosksManageRolesPermissions, Value: "true"}, nil)
|
systemStore.On("GetByName", "CustomGroupAdminRoleCreationMigrationComplete").Return(&model.System{Name: model.MigrationKeyAddPlayboosksManageRolesPermissions, Value: "true"}, nil)
|
||||||
systemStore.On("GetByName", "products_boards").Return(&model.System{Name: "products_boards", Value: "true"}, nil)
|
systemStore.On("GetByName", "products_boards").Return(&model.System{Name: "products_boards", Value: "true"}, nil)
|
||||||
|
|||||||
@@ -9486,6 +9486,10 @@
|
|||||||
"id": "model.preference.is_valid.id.app_error",
|
"id": "model.preference.is_valid.id.app_error",
|
||||||
"translation": "Invalid user id."
|
"translation": "Invalid user id."
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"id": "model.preference.is_valid.limit_visible_dms_gms.app_error",
|
||||||
|
"translation": "Invalid value for limit_visible_dms_gms."
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"id": "model.preference.is_valid.name.app_error",
|
"id": "model.preference.is_valid.name.app_error",
|
||||||
"translation": "Invalid name."
|
"translation": "Invalid name."
|
||||||
|
|||||||
@@ -8,37 +8,38 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
JobTypeDataRetention = "data_retention"
|
JobTypeDataRetention = "data_retention"
|
||||||
JobTypeMessageExport = "message_export"
|
JobTypeMessageExport = "message_export"
|
||||||
JobTypeElasticsearchPostIndexing = "elasticsearch_post_indexing"
|
JobTypeElasticsearchPostIndexing = "elasticsearch_post_indexing"
|
||||||
JobTypeElasticsearchPostAggregation = "elasticsearch_post_aggregation"
|
JobTypeElasticsearchPostAggregation = "elasticsearch_post_aggregation"
|
||||||
JobTypeBlevePostIndexing = "bleve_post_indexing"
|
JobTypeBlevePostIndexing = "bleve_post_indexing"
|
||||||
JobTypeLdapSync = "ldap_sync"
|
JobTypeLdapSync = "ldap_sync"
|
||||||
JobTypeMigrations = "migrations"
|
JobTypeMigrations = "migrations"
|
||||||
JobTypePlugins = "plugins"
|
JobTypePlugins = "plugins"
|
||||||
JobTypeExpiryNotify = "expiry_notify"
|
JobTypeExpiryNotify = "expiry_notify"
|
||||||
JobTypeProductNotices = "product_notices"
|
JobTypeProductNotices = "product_notices"
|
||||||
JobTypeActiveUsers = "active_users"
|
JobTypeActiveUsers = "active_users"
|
||||||
JobTypeImportProcess = "import_process"
|
JobTypeImportProcess = "import_process"
|
||||||
JobTypeImportDelete = "import_delete"
|
JobTypeImportDelete = "import_delete"
|
||||||
JobTypeExportProcess = "export_process"
|
JobTypeExportProcess = "export_process"
|
||||||
JobTypeExportDelete = "export_delete"
|
JobTypeExportDelete = "export_delete"
|
||||||
JobTypeCloud = "cloud"
|
JobTypeCloud = "cloud"
|
||||||
JobTypeResendInvitationEmail = "resend_invitation_email"
|
JobTypeResendInvitationEmail = "resend_invitation_email"
|
||||||
JobTypeExtractContent = "extract_content"
|
JobTypeExtractContent = "extract_content"
|
||||||
JobTypeLastAccessiblePost = "last_accessible_post"
|
JobTypeLastAccessiblePost = "last_accessible_post"
|
||||||
JobTypeLastAccessibleFile = "last_accessible_file"
|
JobTypeLastAccessibleFile = "last_accessible_file"
|
||||||
JobTypeUpgradeNotifyAdmin = "upgrade_notify_admin"
|
JobTypeUpgradeNotifyAdmin = "upgrade_notify_admin"
|
||||||
JobTypeTrialNotifyAdmin = "trial_notify_admin"
|
JobTypeTrialNotifyAdmin = "trial_notify_admin"
|
||||||
JobTypePostPersistentNotifications = "post_persistent_notifications"
|
JobTypePostPersistentNotifications = "post_persistent_notifications"
|
||||||
JobTypeInstallPluginNotifyAdmin = "install_plugin_notify_admin"
|
JobTypeInstallPluginNotifyAdmin = "install_plugin_notify_admin"
|
||||||
JobTypeHostedPurchaseScreening = "hosted_purchase_screening"
|
JobTypeHostedPurchaseScreening = "hosted_purchase_screening"
|
||||||
JobTypeS3PathMigration = "s3_path_migration"
|
JobTypeS3PathMigration = "s3_path_migration"
|
||||||
JobTypeCleanupDesktopTokens = "cleanup_desktop_tokens"
|
JobTypeCleanupDesktopTokens = "cleanup_desktop_tokens"
|
||||||
JobTypeDeleteEmptyDraftsMigration = "delete_empty_drafts_migration"
|
JobTypeDeleteEmptyDraftsMigration = "delete_empty_drafts_migration"
|
||||||
JobTypeRefreshPostStats = "refresh_post_stats"
|
JobTypeRefreshPostStats = "refresh_post_stats"
|
||||||
JobTypeDeleteOrphanDraftsMigration = "delete_orphan_drafts_migration"
|
JobTypeDeleteOrphanDraftsMigration = "delete_orphan_drafts_migration"
|
||||||
JobTypeExportUsersToCSV = "export_users_to_csv"
|
JobTypeExportUsersToCSV = "export_users_to_csv"
|
||||||
|
JobTypeDeleteDmsPreferencesMigration = "delete_dms_preferences_migration"
|
||||||
|
|
||||||
JobStatusPending = "pending"
|
JobStatusPending = "pending"
|
||||||
JobStatusInProgress = "in_progress"
|
JobStatusInProgress = "in_progress"
|
||||||
|
|||||||
@@ -47,5 +47,6 @@ const (
|
|||||||
MigrationKeyAddIPFilteringPermissions = "add_ip_filtering_permissions"
|
MigrationKeyAddIPFilteringPermissions = "add_ip_filtering_permissions"
|
||||||
MigrationKeyAddOutgoingOAuthConnectionsPermissions = "add_outgoing_oauth_connections_permissions"
|
MigrationKeyAddOutgoingOAuthConnectionsPermissions = "add_outgoing_oauth_connections_permissions"
|
||||||
MigrationKeyAddChannelBookmarksPermissions = "add_channel_bookmarks_permissions"
|
MigrationKeyAddChannelBookmarksPermissions = "add_channel_bookmarks_permissions"
|
||||||
|
MigrationKeyDeleteDmsPreferences = "delete_dms_preferences_migration"
|
||||||
MigrationKeyAddManageJobAncillaryPermissions = "add_manage_jobs_ancillary_permissions"
|
MigrationKeyAddManageJobAncillaryPermissions = "add_manage_jobs_ancillary_permissions"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"net/http"
|
"net/http"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"unicode/utf8"
|
"unicode/utf8"
|
||||||
)
|
)
|
||||||
@@ -61,7 +62,9 @@ const (
|
|||||||
PreferenceEmailIntervalHourAsSeconds = "3600"
|
PreferenceEmailIntervalHourAsSeconds = "3600"
|
||||||
PreferenceCloudUserEphemeralInfo = "cloud_user_ephemeral_info"
|
PreferenceCloudUserEphemeralInfo = "cloud_user_ephemeral_info"
|
||||||
|
|
||||||
MaxPreferenceValueLength = 20000
|
PreferenceLimitVisibleDmsGms = "limit_visible_dms_gms"
|
||||||
|
PreferenceMaxLimitVisibleDmsGmsValue = 40
|
||||||
|
MaxPreferenceValueLength = 20000
|
||||||
)
|
)
|
||||||
|
|
||||||
type Preference struct {
|
type Preference struct {
|
||||||
@@ -97,6 +100,13 @@ func (o *Preference) IsValid() *AppError {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if o.Category == PreferenceCategorySidebarSettings && o.Name == PreferenceLimitVisibleDmsGms {
|
||||||
|
visibleDmsGmsValue, convErr := strconv.Atoi(o.Value)
|
||||||
|
if convErr != nil || visibleDmsGmsValue < 1 || visibleDmsGmsValue > PreferenceMaxLimitVisibleDmsGmsValue {
|
||||||
|
return NewAppError("Preference.IsValid", "model.preference.is_valid.limit_visible_dms_gms.app_error", nil, "value="+o.Value, http.StatusBadRequest)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -70,6 +70,34 @@ func TestPreferenceIsValid(t *testing.T) {
|
|||||||
|
|
||||||
require.Nil(t, preference.IsValid())
|
require.Nil(t, preference.IsValid())
|
||||||
})
|
})
|
||||||
|
|
||||||
|
t.Run("limit_visible_dms_gms has a valid value", func(t *testing.T) {
|
||||||
|
preference.Category = PreferenceCategorySidebarSettings
|
||||||
|
preference.Name = PreferenceLimitVisibleDmsGms
|
||||||
|
preference.Value = "40"
|
||||||
|
require.Nil(t, preference.IsValid())
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("limit_visible_dms_gms has a value greater than PreferenceMaxLimitVisibleDmsGmsValue", func(t *testing.T) {
|
||||||
|
preference.Category = PreferenceCategorySidebarSettings
|
||||||
|
preference.Name = PreferenceLimitVisibleDmsGms
|
||||||
|
preference.Value = "10000"
|
||||||
|
require.NotNil(t, preference.IsValid())
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("limit_visible_dms_gms has an invalid value", func(t *testing.T) {
|
||||||
|
preference.Category = PreferenceCategorySidebarSettings
|
||||||
|
preference.Name = PreferenceLimitVisibleDmsGms
|
||||||
|
preference.Value = "one thousand"
|
||||||
|
require.NotNil(t, preference.IsValid())
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("limit_visible_dms_gms has a negative number", func(t *testing.T) {
|
||||||
|
preference.Category = PreferenceCategorySidebarSettings
|
||||||
|
preference.Name = PreferenceLimitVisibleDmsGms
|
||||||
|
preference.Value = "-10"
|
||||||
|
require.NotNil(t, preference.IsValid())
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPreferencePreUpdate(t *testing.T) {
|
func TestPreferencePreUpdate(t *testing.T) {
|
||||||
|
|||||||
@@ -520,11 +520,8 @@ describe('Actions.User', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Should call p-queue APIs on loadProfilesForGM', async () => {
|
test('Should call getProfilesInGroupChannels on loadProfilesForGM', async () => {
|
||||||
const gmChannel = {id: 'gmChannel', type: General.GM_CHANNEL, team_id: '', delete_at: 0};
|
const gmChannel = {id: 'gmChannel', type: General.GM_CHANNEL, team_id: '', delete_at: 0};
|
||||||
UserActions.queue.add = jest.fn().mockReturnValue(jest.fn());
|
|
||||||
UserActions.queue.onEmpty = jest.fn();
|
|
||||||
|
|
||||||
const user = TestHelper.fakeUser();
|
const user = TestHelper.fakeUser();
|
||||||
|
|
||||||
const profiles = {
|
const profiles = {
|
||||||
@@ -555,7 +552,7 @@ describe('Actions.User', () => {
|
|||||||
profiles,
|
profiles,
|
||||||
statuses: {},
|
statuses: {},
|
||||||
profilesInChannel: {
|
profilesInChannel: {
|
||||||
[gmChannel.id]: new Set(['current_user_id']),
|
[gmChannel.id]: new Set([]),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
teams: {
|
teams: {
|
||||||
@@ -605,9 +602,10 @@ describe('Actions.User', () => {
|
|||||||
|
|
||||||
const testStore = mockStore(state);
|
const testStore = mockStore(state);
|
||||||
store.getState.mockImplementation(testStore.getState);
|
store.getState.mockImplementation(testStore.getState);
|
||||||
|
store.dispatch.mockImplementation(testStore.dispatch);
|
||||||
|
const actions = testStore.getActions();
|
||||||
|
|
||||||
await UserActions.loadProfilesForGM();
|
await UserActions.loadProfilesForGM();
|
||||||
expect(UserActions.queue.onEmpty).toHaveBeenCalled();
|
expect(actions).toEqual([{args: [['gmChannel']], type: 'MOCK_GET_PROFILES_IN_GROUP_CHANNELS'}]);
|
||||||
expect(UserActions.queue.add).toHaveBeenCalled();
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||||
// See LICENSE.txt for license information.
|
// See LICENSE.txt for license information.
|
||||||
|
|
||||||
import PQueue from 'p-queue';
|
|
||||||
|
|
||||||
import type {UserAutocomplete} from '@mattermost/types/autocomplete';
|
import type {UserAutocomplete} from '@mattermost/types/autocomplete';
|
||||||
import type {Channel} from '@mattermost/types/channels';
|
import type {Channel} from '@mattermost/types/channels';
|
||||||
import type {UserProfile, UserStatus} from '@mattermost/types/users';
|
import type {UserProfile, UserStatus} from '@mattermost/types/users';
|
||||||
@@ -37,7 +35,6 @@ import * as Utils from 'utils/utils';
|
|||||||
|
|
||||||
import type {GlobalState} from 'types/store';
|
import type {GlobalState} from 'types/store';
|
||||||
|
|
||||||
export const queue = new PQueue({concurrency: 4});
|
|
||||||
const dispatch = store.dispatch;
|
const dispatch = store.dispatch;
|
||||||
const getState = store.getState;
|
const getState = store.getState;
|
||||||
|
|
||||||
@@ -312,9 +309,9 @@ export async function loadProfilesForGM() {
|
|||||||
const collapsedThreads = isCollapsedThreadsEnabled(state);
|
const collapsedThreads = isCollapsedThreadsEnabled(state);
|
||||||
|
|
||||||
const userIdsForLoadingCustomEmojis = new Set();
|
const userIdsForLoadingCustomEmojis = new Set();
|
||||||
|
const channelUsersToLoad: string[] = [];
|
||||||
for (const channel of getGMsForLoading(state)) {
|
for (const channel of getGMsForLoading(state)) {
|
||||||
const userIds = userIdsInChannels[channel.id] || new Set();
|
const userIds = userIdsInChannels[channel.id] || new Set();
|
||||||
|
|
||||||
userIds.forEach((userId) => userIdsForLoadingCustomEmojis.add(userId));
|
userIds.forEach((userId) => userIdsForLoadingCustomEmojis.add(userId));
|
||||||
|
|
||||||
if (userIds.size >= Constants.MIN_USERS_IN_GM) {
|
if (userIds.size >= Constants.MIN_USERS_IN_GM) {
|
||||||
@@ -340,12 +337,14 @@ export async function loadProfilesForGM() {
|
|||||||
value: 'true',
|
value: 'true',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
if (userIds.size === 0) {
|
||||||
const getProfilesAction = UserActions.getProfilesInChannel(channel.id, 0, Constants.MAX_USERS_IN_GM);
|
channelUsersToLoad.push(channel.id);
|
||||||
queue.add(() => dispatch(getProfilesAction));
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
await queue.onEmpty();
|
if (channelUsersToLoad.length > 0) {
|
||||||
|
await dispatch(UserActions.getProfilesInGroupChannels(channelUsersToLoad));
|
||||||
|
}
|
||||||
|
|
||||||
if (userIdsForLoadingCustomEmojis.size > 0) {
|
if (userIdsForLoadingCustomEmojis.size > 0) {
|
||||||
dispatch(loadCustomEmojisForCustomStatusesByUserIds(userIdsForLoadingCustomEmojis));
|
dispatch(loadCustomEmojisForCustomStatusesByUserIds(userIdsForLoadingCustomEmojis));
|
||||||
|
|||||||
@@ -102,17 +102,6 @@ exports[`components/sidebar/sidebar_category/sidebar_category_sorting_menu shoul
|
|||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<MenuItem
|
|
||||||
id="showAllDms-category_id"
|
|
||||||
labels={
|
|
||||||
<Memo(MemoizedFormattedMessage)
|
|
||||||
defaultMessage="All direct messages"
|
|
||||||
id="sidebar.allDirectMessages"
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
onClick={[Function]}
|
|
||||||
/>
|
|
||||||
<MenuItemSeparator />
|
|
||||||
<MenuItem
|
<MenuItem
|
||||||
id="showDmCount-category_id-10"
|
id="showDmCount-category_id-10"
|
||||||
key="showDmCount-category_id-10"
|
key="showDmCount-category_id-10"
|
||||||
|
|||||||
@@ -147,17 +147,6 @@ const SidebarCategorySortingMenu = ({
|
|||||||
)}
|
)}
|
||||||
menuId={`showMessagesCount-${category.id}-menu`}
|
menuId={`showMessagesCount-${category.id}-menu`}
|
||||||
>
|
>
|
||||||
<Menu.Item
|
|
||||||
id={`showAllDms-${category.id}`}
|
|
||||||
labels={(
|
|
||||||
<FormattedMessage
|
|
||||||
id='sidebar.allDirectMessages'
|
|
||||||
defaultMessage='All direct messages'
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
onClick={() => handlelimitVisibleDMsGMs(Constants.HIGHEST_DM_SHOW_COUNT)}
|
|
||||||
/>
|
|
||||||
<Menu.Separator/>
|
|
||||||
{Constants.DM_AND_GM_SHOW_COUNTS.map((dmGmShowCount) => (
|
{Constants.DM_AND_GM_SHOW_COUNTS.map((dmGmShowCount) => (
|
||||||
<Menu.Item
|
<Menu.Item
|
||||||
id={`showDmCount-${category.id}-${dmGmShowCount}`}
|
id={`showDmCount-${category.id}-${dmGmShowCount}`}
|
||||||
|
|||||||
@@ -16,8 +16,6 @@ import SettingItemMax from 'components/setting_item_max';
|
|||||||
import SettingItemMin from 'components/setting_item_min';
|
import SettingItemMin from 'components/setting_item_min';
|
||||||
import type SettingItemMinComponent from 'components/setting_item_min';
|
import type SettingItemMinComponent from 'components/setting_item_min';
|
||||||
|
|
||||||
import {localizeMessage} from 'utils/utils';
|
|
||||||
|
|
||||||
type Limit = {
|
type Limit = {
|
||||||
value: number;
|
value: number;
|
||||||
label: string;
|
label: string;
|
||||||
@@ -44,7 +42,6 @@ type State = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const limits: Limit[] = [
|
const limits: Limit[] = [
|
||||||
{value: 10000, label: localizeMessage('user.settings.sidebar.limitVisibleGMsDMs.allDirectMessages', 'All Direct Messages')},
|
|
||||||
{value: 10, label: '10'},
|
{value: 10, label: '10'},
|
||||||
{value: 15, label: '15'},
|
{value: 15, label: '15'},
|
||||||
{value: 20, label: '20'},
|
{value: 20, label: '20'},
|
||||||
|
|||||||
@@ -4972,7 +4972,6 @@
|
|||||||
"sidebar_right_menu.console": "System Console",
|
"sidebar_right_menu.console": "System Console",
|
||||||
"sidebar_right_menu.flagged": "Saved messages",
|
"sidebar_right_menu.flagged": "Saved messages",
|
||||||
"sidebar_right_menu.recentMentions": "Recent Mentions",
|
"sidebar_right_menu.recentMentions": "Recent Mentions",
|
||||||
"sidebar.allDirectMessages": "All direct messages",
|
|
||||||
"sidebar.createDirectMessage": "Write a direct message",
|
"sidebar.createDirectMessage": "Write a direct message",
|
||||||
"sidebar.createUserGroup": "Create New User Group",
|
"sidebar.createUserGroup": "Create New User Group",
|
||||||
"sidebar.directchannel.you": "{displayname} (you)",
|
"sidebar.directchannel.you": "{displayname} (you)",
|
||||||
@@ -5663,7 +5662,6 @@
|
|||||||
"user.settings.security.viewHistory": "View Access History",
|
"user.settings.security.viewHistory": "View Access History",
|
||||||
"user.settings.security.viewHistory.icon": "Access History Icon",
|
"user.settings.security.viewHistory.icon": "Access History Icon",
|
||||||
"user.settings.sidebar.icon": "Sidebar Settings Icon",
|
"user.settings.sidebar.icon": "Sidebar Settings Icon",
|
||||||
"user.settings.sidebar.limitVisibleGMsDMs.allDirectMessages": "All Direct Messages",
|
|
||||||
"user.settings.sidebar.limitVisibleGMsDMsDesc": "You can also change these settings in the direct messages sidebar menu.",
|
"user.settings.sidebar.limitVisibleGMsDMsDesc": "You can also change these settings in the direct messages sidebar menu.",
|
||||||
"user.settings.sidebar.limitVisibleGMsDMsTitle": "Number of direct messages to show",
|
"user.settings.sidebar.limitVisibleGMsDMsTitle": "Number of direct messages to show",
|
||||||
"user.settings.sidebar.off": "Off",
|
"user.settings.sidebar.off": "Off",
|
||||||
|
|||||||
Ссылка в новой задаче
Block a user