From 6808a1c7339c55dc7dd895d732c35dd503ab690e Mon Sep 17 00:00:00 2001 From: Agniva De Sarker Date: Thu, 22 Feb 2024 19:56:56 +0530 Subject: [PATCH] Revert "MM-56201, MM-56280: Suppress typing and emoji events (#25794)" (#26281) This reverts commit f5ee5463e4967b412c0cc402c56cb17d2d694f2b. --- server/channels/app/platform/web_conn.go | 72 +---- server/channels/app/web_conn_test.go | 60 ----- server/public/model/feature_flags.go | 6 +- webapp/channels/src/actions/views/rhs.test.ts | 13 +- webapp/channels/src/actions/views/rhs.ts | 2 + .../__snapshots__/channel_view.test.tsx.snap | 3 - .../channel_view/channel_view.test.tsx | 1 - .../components/channel_view/channel_view.tsx | 2 +- .../src/components/channel_view/index.ts | 2 - .../src/components/team_sidebar/index.ts | 2 - .../components/team_sidebar/team_sidebar.tsx | 2 +- .../threading/thread_viewer/index.ts | 5 - .../thread_viewer/thread_viewer.test.tsx | 1 - .../threading/thread_viewer/thread_viewer.tsx | 13 +- .../src/action_types/posts.ts | 2 - .../src/actions/posts.test.ts | 152 +++++------ .../mattermost-redux/src/actions/posts.ts | 21 +- .../src/reducers/entities/posts.test.ts | 247 +++++------------- .../src/reducers/entities/posts.ts | 23 +- .../mattermost-redux/test/test_helper.ts | 11 - webapp/channels/src/utils/test_helper.ts | 12 +- webapp/platform/types/src/config.ts | 1 - webapp/platform/types/src/posts.ts | 2 +- 23 files changed, 186 insertions(+), 469 deletions(-) diff --git a/server/channels/app/platform/web_conn.go b/server/channels/app/platform/web_conn.go index 663bee42b1..d3c6778afa 100644 --- a/server/channels/app/platform/web_conn.go +++ b/server/channels/app/platform/web_conn.go @@ -25,7 +25,6 @@ import ( "github.com/mattermost/mattermost/server/public/shared/i18n" "github.com/mattermost/mattermost/server/public/shared/mlog" "github.com/mattermost/mattermost/server/public/shared/request" - "github.com/mattermost/mattermost/server/public/utils" ) const ( @@ -49,10 +48,6 @@ const ( const websocketMessagePluginPrefix = "custom_" -// UnsetPresenceIndicator is the value that gets set initially for active channel/ -// thread/team. This is done to differentiate it from an explicitly set empty value. -const UnsetPresenceIndicator = "<>" - type pluginWSPostedHook struct { connectionID string userID string @@ -110,19 +105,17 @@ type WebConn struct { // a reused connection. // It's theoretically possible for this number to wrap around. But we // leave that as an edge-case. - reuseCount int - sessionToken atomic.Value - session atomic.Pointer[model.Session] - connectionID atomic.Value - + reuseCount int + sessionToken atomic.Value + session atomic.Pointer[model.Session] + connectionID atomic.Value activeChannelID atomic.Value activeTeamID atomic.Value activeRHSThreadChannelID atomic.Value activeThreadViewThreadChannelID atomic.Value - - endWritePump chan struct{} - pumpFinished chan struct{} - pluginPosted chan pluginWSPostedHook + endWritePump chan struct{} + pumpFinished chan struct{} + pluginPosted chan pluginWSPostedHook // These counters are to suppress spammy websocket.slow // and websocket.full logs which happen continuously, if they @@ -243,12 +236,10 @@ func (ps *PlatformService) NewWebConn(cfg *WebConnConfig, suite SuiteIFace, runn wc.SetSessionToken(cfg.Session.Token) wc.SetSessionExpiresAt(cfg.Session.ExpiresAt) wc.SetConnectionID(cfg.ConnectionID) - // <> means unset. This is to differentiate from empty value. - // Because we need to support mobile clients where the value might be unset. - wc.SetActiveChannelID(UnsetPresenceIndicator) - wc.SetActiveTeamID(UnsetPresenceIndicator) - wc.SetActiveRHSThreadChannelID(UnsetPresenceIndicator) - wc.SetActiveThreadViewThreadChannelID(UnsetPresenceIndicator) + wc.SetActiveChannelID("") + wc.SetActiveTeamID("") + wc.SetActiveRHSThreadChannelID("") + wc.SetActiveThreadViewThreadChannelID("") ps.Go(func() { runner.RunMultiHook(func(hooks plugin.Hooks) bool { @@ -314,9 +305,6 @@ func (wc *WebConn) SetActiveChannelID(id string) { // GetActiveChannelID returns the active channel id of the connection. func (wc *WebConn) GetActiveChannelID() string { - if wc.activeChannelID.Load() == nil { - return UnsetPresenceIndicator - } return wc.activeChannelID.Load().(string) } @@ -327,17 +315,11 @@ func (wc *WebConn) SetActiveTeamID(id string) { // GetActiveTeamID returns the active team id of the connection. func (wc *WebConn) GetActiveTeamID() string { - if wc.activeTeamID.Load() == nil { - return UnsetPresenceIndicator - } return wc.activeTeamID.Load().(string) } // GetActiveRHSThreadChannelID returns the channel id of the active thread of the connection. func (wc *WebConn) GetActiveRHSThreadChannelID() string { - if wc.activeRHSThreadChannelID.Load() == nil { - return UnsetPresenceIndicator - } return wc.activeRHSThreadChannelID.Load().(string) } @@ -348,9 +330,6 @@ func (wc *WebConn) SetActiveRHSThreadChannelID(id string) { // GetActiveThreadViewThreadChannelID returns the channel id of the active thread of the connection. func (wc *WebConn) GetActiveThreadViewThreadChannelID() string { - if wc.activeThreadViewThreadChannelID.Load() == nil { - return UnsetPresenceIndicator - } return wc.activeThreadViewThreadChannelID.Load().(string) } @@ -359,11 +338,6 @@ func (wc *WebConn) SetActiveThreadViewThreadChannelID(id string) { wc.activeThreadViewThreadChannelID.Store(id) } -// isSet is a helper to check if a value is unset or not. -func (wc *WebConn) isSet(val string) bool { - return val != UnsetPresenceIndicator -} - // areAllInactive returns whether all of the connections // are inactive or not. func areAllInactive(conns []*WebConn) bool { @@ -873,18 +847,7 @@ func (wc *WebConn) ShouldSendEvent(msg *model.WebSocketEvent) bool { } // Only report events to users who are in the channel for the event - if chID := msg.GetBroadcast().ChannelId; chID != "" { - // For typing events, we don't send them to users who don't have - // that channel or thread opened. - if wc.Platform.Config().FeatureFlags.WebSocketEventScope && - utils.Contains([]model.WebsocketEventType{ - model.WebsocketEventTyping, - model.WebsocketEventReactionAdded, - model.WebsocketEventReactionRemoved, - }, msg.EventType()) && wc.notInChannel(chID) && wc.notInThread(chID) { - return false - } - + if msg.GetBroadcast().ChannelId != "" { if model.GetMillis()-wc.lastAllChannelMembersTime > webConnMemberCacheTime { wc.allChannelMembers = nil wc.lastAllChannelMembersTime = 0 @@ -900,7 +863,7 @@ func (wc *WebConn) ShouldSendEvent(msg *model.WebSocketEvent) bool { wc.lastAllChannelMembersTime = model.GetMillis() } - if _, ok := wc.allChannelMembers[chID]; ok { + if _, ok := wc.allChannelMembers[msg.GetBroadcast().ChannelId]; ok { return true } return false @@ -918,15 +881,6 @@ func (wc *WebConn) ShouldSendEvent(msg *model.WebSocketEvent) bool { return true } -func (wc *WebConn) notInChannel(val string) bool { - return (wc.isSet(wc.GetActiveChannelID()) && val != wc.GetActiveChannelID()) -} - -func (wc *WebConn) notInThread(val string) bool { - return (wc.isSet(wc.GetActiveRHSThreadChannelID()) && val != wc.GetActiveRHSThreadChannelID()) && - (wc.isSet(wc.GetActiveThreadViewThreadChannelID()) && val != wc.GetActiveThreadViewThreadChannelID()) -} - // IsMemberOfTeam returns whether the user of the WebConn // is a member of the given teamID or not. func (wc *WebConn) isMemberOfTeam(teamID string) bool { diff --git a/server/channels/app/web_conn_test.go b/server/channels/app/web_conn_test.go index e0e9988bd8..f2fbcd23e9 100644 --- a/server/channels/app/web_conn_test.go +++ b/server/channels/app/web_conn_test.go @@ -4,7 +4,6 @@ package app import ( - "os" "testing" "github.com/stretchr/testify/assert" @@ -16,8 +15,6 @@ import ( ) func TestWebConnShouldSendEvent(t *testing.T) { - os.Setenv("MM_FEATUREFLAGS_WEBSOCKETEVENTSCOPE", "true") - defer os.Unsetenv("MM_FEATUREFLAGS_WEBSOCKETEVENTSCOPE") th := Setup(t).InitBasic() defer th.TearDown() session, err := th.App.CreateSession(th.Context, &model.Session{UserId: th.BasicUser.Id, Roles: th.BasicUser.GetRawRoles(), TeamMembers: []*model.TeamMember{ @@ -165,63 +162,6 @@ func TestWebConnShouldSendEvent(t *testing.T) { assert.False(t, adminUserWc.ShouldSendEvent(event), "did not expect admin") }) - t.Run("should not send typing event unless in scope", func(t *testing.T) { - event2 := model.NewWebSocketEvent(model.WebsocketEventTyping, "", th.BasicChannel.Id, "", nil, "") - // Basic, unset case - basicUserWc.SetActiveChannelID(platform.UnsetPresenceIndicator) - basicUserWc.SetActiveRHSThreadChannelID(platform.UnsetPresenceIndicator) - basicUserWc.SetActiveThreadViewThreadChannelID(platform.UnsetPresenceIndicator) - assert.True(t, basicUserWc.ShouldSendEvent(event2)) - - // Active channel is set to something else, thread unset - basicUserWc.SetActiveChannelID("ch1") - basicUserWc.SetActiveRHSThreadChannelID(platform.UnsetPresenceIndicator) - basicUserWc.SetActiveThreadViewThreadChannelID(platform.UnsetPresenceIndicator) - assert.True(t, basicUserWc.ShouldSendEvent(event2)) - - // Active channel is unset, thread set - basicUserWc.SetActiveChannelID(platform.UnsetPresenceIndicator) - basicUserWc.SetActiveRHSThreadChannelID("ch1") - basicUserWc.SetActiveThreadViewThreadChannelID("ch2") - assert.True(t, basicUserWc.ShouldSendEvent(event2)) - - // both are set to correct channel - basicUserWc.SetActiveChannelID(th.BasicChannel.Id) - basicUserWc.SetActiveRHSThreadChannelID(th.BasicChannel.Id) - basicUserWc.SetActiveThreadViewThreadChannelID(th.BasicChannel.Id) - assert.True(t, basicUserWc.ShouldSendEvent(event2)) - - // channel is correct, thread is something else. - basicUserWc.SetActiveChannelID(th.BasicChannel.Id) - basicUserWc.SetActiveRHSThreadChannelID("ch1") - basicUserWc.SetActiveThreadViewThreadChannelID("ch2") - assert.True(t, basicUserWc.ShouldSendEvent(event2)) - - // channel is wrong, thread is correct. - basicUserWc.SetActiveChannelID("ch1") - basicUserWc.SetActiveRHSThreadChannelID(th.BasicChannel.Id) - basicUserWc.SetActiveThreadViewThreadChannelID(th.BasicChannel.Id) - assert.True(t, basicUserWc.ShouldSendEvent(event2)) - - // FINALLY, both are set to something else. - basicUserWc.SetActiveChannelID("ch1") - basicUserWc.SetActiveRHSThreadChannelID("ch1") - basicUserWc.SetActiveThreadViewThreadChannelID("ch2") - assert.False(t, basicUserWc.ShouldSendEvent(event2)) - - // Different threads and channel - basicUserWc.SetActiveChannelID("ch1") - basicUserWc.SetActiveRHSThreadChannelID("ch2") - basicUserWc.SetActiveThreadViewThreadChannelID("ch3") - assert.False(t, basicUserWc.ShouldSendEvent(event2)) - - // Other channel. Thread unset explicitly. - basicUserWc.SetActiveChannelID("ch1") - basicUserWc.SetActiveRHSThreadChannelID("") - basicUserWc.SetActiveThreadViewThreadChannelID("") - assert.False(t, basicUserWc.ShouldSendEvent(event2)) - }) - t.Run("should send to basic user and admin in channel2", func(t *testing.T) { event = event.SetBroadcast(&model.WebsocketBroadcast{ChannelId: channel2.Id}) diff --git a/server/public/model/feature_flags.go b/server/public/model/feature_flags.go index 8b562ab4be..bc4ac21ec9 100644 --- a/server/public/model/feature_flags.go +++ b/server/public/model/feature_flags.go @@ -49,9 +49,7 @@ type FeatureFlags struct { CloudIPFiltering bool ConsumePostHook bool - CloudAnnualRenewals bool - OutgoingOAuthConnections bool - WebSocketEventScope bool + CloudAnnualRenewals bool } func (f *FeatureFlags) SetDefaults() { @@ -71,8 +69,6 @@ func (f *FeatureFlags) SetDefaults() { f.CloudIPFiltering = false f.ConsumePostHook = false f.CloudAnnualRenewals = false - f.OutgoingOAuthConnections = false - f.WebSocketEventScope = false } // ToMap returns the feature flags as a map[string]string diff --git a/webapp/channels/src/actions/views/rhs.test.ts b/webapp/channels/src/actions/views/rhs.test.ts index ec8cfb48e0..e06826f33b 100644 --- a/webapp/channels/src/actions/views/rhs.test.ts +++ b/webapp/channels/src/actions/views/rhs.test.ts @@ -11,6 +11,7 @@ import type {UserProfile} from '@mattermost/types/users'; import type {IDMappedObjects} from '@mattermost/types/utilities'; import {SearchTypes} from 'mattermost-redux/action_types'; +import * as PostActions from 'mattermost-redux/actions/posts'; import * as SearchActions from 'mattermost-redux/actions/search'; import {trackEvent} from 'actions/telemetry_actions.jsx'; @@ -170,6 +171,15 @@ describe('rhs view actions', () => { root_id: 'root123', } as Post; + test('it dispatches PostActions.getPostThread correctly', () => { + store.dispatch(selectPostFromRightHandSideSearch(post)); + + const compareStore = mockStore(initialState); + compareStore.dispatch(PostActions.getPostThread(post.root_id)); + + expect(store.getActions()[0]).toEqual(compareStore.getActions()[0]); + }); + describe(`it dispatches ${ActionTypes.SELECT_POST} correctly`, () => { it('with mocked date', async () => { store = mockStore({ @@ -192,7 +202,7 @@ describe('rhs view actions', () => { timestamp: POST_CREATED_TIME, }; - expect(store.getActions()[0]).toEqual(action); + expect(store.getActions()[1]).toEqual(action); }); }); }); @@ -782,6 +792,7 @@ describe('rhs view actions', () => { await store.dispatch(openAtPrevious({selectedPostId: previousSelectedPost.id, previousRhsState: previousState})); const compareStore = mockStore(initialState); + compareStore.dispatch(PostActions.getPostThread(previousSelectedPost.root_id)); compareStore.dispatch({ type: ActionTypes.SELECT_POST, postId: previousSelectedPost.root_id, diff --git a/webapp/channels/src/actions/views/rhs.ts b/webapp/channels/src/actions/views/rhs.ts index fa9ca69552..64af7f69ca 100644 --- a/webapp/channels/src/actions/views/rhs.ts +++ b/webapp/channels/src/actions/views/rhs.ts @@ -9,6 +9,7 @@ import type {Post} from '@mattermost/types/posts'; import {SearchTypes} from 'mattermost-redux/action_types'; import {getChannel} from 'mattermost-redux/actions/channels'; +import * as PostActions from 'mattermost-redux/actions/posts'; import {getPostsByIds, getPost as fetchPost} from 'mattermost-redux/actions/posts'; import { clearSearch, @@ -39,6 +40,7 @@ import type {RhsState} from 'types/store/rhs'; function selectPostFromRightHandSideSearchWithPreviousState(post: Post, previousRhsState?: RhsState): ActionFuncAsync { return async (dispatch, getState) => { const postRootId = post.root_id || post.id; + await dispatch(PostActions.getPostThread(postRootId)); const state = getState(); dispatch({ diff --git a/webapp/channels/src/components/channel_view/__snapshots__/channel_view.test.tsx.snap b/webapp/channels/src/components/channel_view/__snapshots__/channel_view.test.tsx.snap index 897c40be2a..79d2c195f6 100644 --- a/webapp/channels/src/components/channel_view/__snapshots__/channel_view.test.tsx.snap +++ b/webapp/channels/src/components/channel_view/__snapshots__/channel_view.test.tsx.snap @@ -13,7 +13,6 @@ exports[`components/channel_view Should match snapshot if channel is archived 1` channelIsArchived={true} deactivatedChannel={false} enableOnboardingFlow={true} - enableWebSocketEventScope={false} goToLastViewedChannel={[MockFunction]} history={Object {}} isCloud={false} @@ -70,7 +69,6 @@ exports[`components/channel_view Should match snapshot if channel is deactivated channelIsArchived={false} deactivatedChannel={true} enableOnboardingFlow={true} - enableWebSocketEventScope={false} goToLastViewedChannel={[MockFunction]} history={Object {}} isCloud={false} @@ -126,7 +124,6 @@ exports[`components/channel_view Should match snapshot with base props 1`] = ` channelIsArchived={false} deactivatedChannel={false} enableOnboardingFlow={true} - enableWebSocketEventScope={false} goToLastViewedChannel={[MockFunction]} history={Object {}} isCloud={false} diff --git a/webapp/channels/src/components/channel_view/channel_view.test.tsx b/webapp/channels/src/components/channel_view/channel_view.test.tsx index fbae300258..6303676704 100644 --- a/webapp/channels/src/components/channel_view/channel_view.test.tsx +++ b/webapp/channels/src/components/channel_view/channel_view.test.tsx @@ -24,7 +24,6 @@ describe('components/channel_view', () => { isCloud: false, goToLastViewedChannel: jest.fn(), isFirstAdmin: false, - enableWebSocketEventScope: false, }; it('Should match snapshot with base props', () => { diff --git a/webapp/channels/src/components/channel_view/channel_view.tsx b/webapp/channels/src/components/channel_view/channel_view.tsx index 11f7c53989..d66caa1c42 100644 --- a/webapp/channels/src/components/channel_view/channel_view.tsx +++ b/webapp/channels/src/components/channel_view/channel_view.tsx @@ -89,7 +89,7 @@ export default class ChannelView extends React.PureComponent { componentDidUpdate(prevProps: Props) { // TODO: debounce - if (prevProps.channelId !== this.props.channelId && this.props.enableWebSocketEventScope) { + if (prevProps.channelId !== this.props.channelId) { WebSocketClient.updateActiveChannel(this.props.channelId); } if (prevProps.channelId !== this.props.channelId || prevProps.channelIsArchived !== this.props.channelIsArchived) { diff --git a/webapp/channels/src/components/channel_view/index.ts b/webapp/channels/src/components/channel_view/index.ts index af3f6e55b4..1bcd38faab 100644 --- a/webapp/channels/src/components/channel_view/index.ts +++ b/webapp/channels/src/components/channel_view/index.ts @@ -29,7 +29,6 @@ function mapStateToProps(state: GlobalState) { const viewArchivedChannels = config.ExperimentalViewArchivedChannels === 'true'; const enableOnboardingFlow = config.EnableOnboardingFlow === 'true'; - const enableWebSocketEventScope = config.FeatureFlagWebSocketEventScope === 'true'; return { channelId: channel ? channel.id : '', @@ -40,7 +39,6 @@ function mapStateToProps(state: GlobalState) { isCloud: getLicense(state).Cloud === 'true', teamUrl: getCurrentRelativeTeamUrl(state), isFirstAdmin: isFirstAdmin(state), - enableWebSocketEventScope, }; } diff --git a/webapp/channels/src/components/team_sidebar/index.ts b/webapp/channels/src/components/team_sidebar/index.ts index 58a1504f9a..d97695f147 100644 --- a/webapp/channels/src/components/team_sidebar/index.ts +++ b/webapp/channels/src/components/team_sidebar/index.ts @@ -38,7 +38,6 @@ function mapStateToProps(state: GlobalState) { const products = state.plugins.components.Product || []; const [unreadTeamsSet, mentionsInTeamMap, teamHasUrgentMap] = getTeamsUnreadStatuses(state); - const enableWebSocketEventScope = config.FeatureFlagWebSocketEventScope === 'true'; return { currentTeamId: getCurrentTeamId(state), @@ -52,7 +51,6 @@ function mapStateToProps(state: GlobalState) { unreadTeamsSet, mentionsInTeamMap, teamHasUrgentMap, - enableWebSocketEventScope, }; } diff --git a/webapp/channels/src/components/team_sidebar/team_sidebar.tsx b/webapp/channels/src/components/team_sidebar/team_sidebar.tsx index d18eee013a..78b97f643d 100644 --- a/webapp/channels/src/components/team_sidebar/team_sidebar.tsx +++ b/webapp/channels/src/components/team_sidebar/team_sidebar.tsx @@ -149,7 +149,7 @@ export default class TeamSidebar extends React.PureComponent { componentDidUpdate(prevProps: Props) { // TODO: debounce - if (prevProps.currentTeamId !== this.props.currentTeamId && this.props.enableWebSocketEventScope) { + if (prevProps.currentTeamId !== this.props.currentTeamId) { WebSocketClient.updateActiveTeam(this.props.currentTeamId); } } diff --git a/webapp/channels/src/components/threading/thread_viewer/index.ts b/webapp/channels/src/components/threading/thread_viewer/index.ts index ed95fad076..f7d3a3d761 100644 --- a/webapp/channels/src/components/threading/thread_viewer/index.ts +++ b/webapp/channels/src/components/threading/thread_viewer/index.ts @@ -6,7 +6,6 @@ import {bindActionCreators} from 'redux'; import type {Dispatch} from 'redux'; import type {Channel} from '@mattermost/types/channels'; -import type {ClientConfig} from '@mattermost/types/config'; import type {UserThread} from '@mattermost/types/threads'; import {fetchRHSAppsBindings} from 'mattermost-redux/actions/apps'; @@ -14,7 +13,6 @@ import {getNewestPostThread, getPostThread} from 'mattermost-redux/actions/posts import {getThread as fetchThread, updateThreadRead} from 'mattermost-redux/actions/threads'; import {appsEnabled} from 'mattermost-redux/selectors/entities/apps'; import {makeGetChannel} from 'mattermost-redux/selectors/entities/channels'; -import {getConfig} from 'mattermost-redux/selectors/entities/general'; import {getPost, makeGetPostIdsForThread} from 'mattermost-redux/selectors/entities/posts'; import {isCollapsedThreadsEnabled} from 'mattermost-redux/selectors/entities/preferences'; import {getCurrentTeamId} from 'mattermost-redux/selectors/entities/teams'; @@ -45,8 +43,6 @@ function makeMapStateToProps() { const socketStatus = getSocketStatus(state); const highlightedPostId = getHighlightedPostId(state); const selectedPostFocusedAt = getSelectedPostFocussedAt(state); - const config: Partial = getConfig(state); - const enableWebSocketEventScope = config.FeatureFlagWebSocketEventScope === 'true'; let postIds: string[] = []; let userThread: UserThread | null = null; @@ -70,7 +66,6 @@ function makeMapStateToProps() { channel, highlightedPostId, selectedPostFocusedAt, - enableWebSocketEventScope, }; }; } diff --git a/webapp/channels/src/components/threading/thread_viewer/thread_viewer.test.tsx b/webapp/channels/src/components/threading/thread_viewer/thread_viewer.test.tsx index f2a1a284c0..b39c9ba970 100644 --- a/webapp/channels/src/components/threading/thread_viewer/thread_viewer.test.tsx +++ b/webapp/channels/src/components/threading/thread_viewer/thread_viewer.test.tsx @@ -68,7 +68,6 @@ describe('components/threading/ThreadViewer', () => { appsEnabled: true, rootPostId: post.id, isThreadView: true, - enableWebSocketEventScope: false, }; test('should match snapshot', async () => { diff --git a/webapp/channels/src/components/threading/thread_viewer/thread_viewer.tsx b/webapp/channels/src/components/threading/thread_viewer/thread_viewer.tsx index 2b6dc25a12..4c99a82f0e 100644 --- a/webapp/channels/src/components/threading/thread_viewer/thread_viewer.tsx +++ b/webapp/channels/src/components/threading/thread_viewer/thread_viewer.tsx @@ -53,7 +53,6 @@ export type Props = Attrs & { inputPlaceholder?: string; rootPostId: string; fromSuppressed?: boolean; - enableWebSocketEventScope: boolean; }; type State = { @@ -82,9 +81,7 @@ export default class ThreadViewer extends React.PureComponent { } public componentWillUnmount() { - if (this.props.enableWebSocketEventScope) { - WebSocketClient.updateActiveThread(this.props.isThreadView, ''); - } + WebSocketClient.updateActiveThread(this.props.isThreadView, ''); } public componentDidUpdate(prevProps: Props) { @@ -173,7 +170,11 @@ export default class ThreadViewer extends React.PureComponent { // scrolls to either bottom or new messages line private onInit = async (reconnected = false): Promise => { this.setState({isLoading: !reconnected}); - await this.props.actions.getPostThread(this.props.selected?.id || this.props.rootPostId, !reconnected); + if (reconnected || this.morePostsToFetch()) { + await this.props.actions.getPostThread(this.props.selected?.id || this.props.rootPostId, !reconnected); + } else { + await this.props.actions.getNewestPostThread(this.props.selected?.id || this.props.rootPostId); + } if ( this.props.isCollapsedThreadsEnabled && @@ -182,7 +183,7 @@ export default class ThreadViewer extends React.PureComponent { await this.fetchThread(); } - if (this.props.channel && this.props.enableWebSocketEventScope) { + if (this.props.channel) { WebSocketClient.updateActiveThread(this.props.isThreadView, this.props.channel?.id); } this.setState({isLoading: false}); diff --git a/webapp/channels/src/packages/mattermost-redux/src/action_types/posts.ts b/webapp/channels/src/packages/mattermost-redux/src/action_types/posts.ts index 2d1bca6eb4..7581af82e0 100644 --- a/webapp/channels/src/packages/mattermost-redux/src/action_types/posts.ts +++ b/webapp/channels/src/packages/mattermost-redux/src/action_types/posts.ts @@ -41,8 +41,6 @@ export default keyMirror({ POST_DELETED: null, POST_REMOVED: null, - POST_PINNED_CHANGED: null, - RECEIVED_FOCUSED_POST: null, RECEIVED_EDIT_POST: null, RECEIVED_REACTION: null, diff --git a/webapp/channels/src/packages/mattermost-redux/src/actions/posts.test.ts b/webapp/channels/src/packages/mattermost-redux/src/actions/posts.test.ts index 325880d0b2..6bd5ab6e4f 100644 --- a/webapp/channels/src/packages/mattermost-redux/src/actions/posts.test.ts +++ b/webapp/channels/src/packages/mattermost-redux/src/actions/posts.test.ts @@ -1131,115 +1131,87 @@ describe('Actions.Posts', () => { expect(state.entities.teams.myMembers[teamId].mention_count).toBe(1); }); - describe('pinPost', () => { - test('should update post and channel stats', async () => { - nock(Client4.getBaseRoute()). - get(`/channels/${TestHelper.basicChannel!.id}/stats?exclude_files_count=true`). - reply(200, {channel_id: TestHelper.basicChannel!.id, member_count: 1, pinnedpost_count: 0}); - await store.dispatch(getChannelStats(TestHelper.basicChannel!.id)); + it('pinPost', async () => { + const {dispatch, getState} = store; - const post = TestHelper.fakePostWithId(TestHelper.basicChannel!.id); - store.dispatch(Actions.receivedPost(post)); + nock(Client4.getBaseRoute()). + get(`/channels/${TestHelper.basicChannel!.id}/stats?exclude_files_count=true`). + reply(200, {channel_id: TestHelper.basicChannel!.id, member_count: 1, pinnedpost_count: 0}); - nock(Client4.getBaseRoute()). - post(`/posts/${post.id}/pin`). - reply(200, OK_RESPONSE); + await dispatch(getChannelStats(TestHelper.basicChannel!.id)); - const result = await store.dispatch(Actions.pinPost(post.id)); - expect(result.error).toBeUndefined(); + nock(Client4.getBaseRoute()). + post('/posts'). + reply(201, TestHelper.fakePostWithId(TestHelper.basicChannel!.id)); + const post1 = await Client4.createPost( + TestHelper.fakePost(TestHelper.basicChannel!.id), + ); - const state = store.getState(); - expect(state.entities.posts.posts[post.id].is_pinned).toBe(true); - expect(state.entities.channels.stats[TestHelper.basicChannel!.id].pinnedpost_count).toBe(1); - }); + const postList = {order: [post1.id], posts: {}} as PostList; + postList.posts[post1.id] = post1; - test('MM-14115 should not clobber reactions on pinned post', async () => { - const post = TestHelper.getPostMock({ - id: TestHelper.generateId(), - metadata: { - embeds: [], - emojis: [], - files: [], - images: {}, - reactions: [ - TestHelper.getReactionMock({emoji_name: 'test'}), - ], - }, - }); + nock(Client4.getBaseRoute()). + get(`/posts/${post1.id}/thread?skipFetchThreads=false&collapsedThreads=true&collapsedThreadsExtended=false&direction=down&perPage=60`). + reply(200, postList); + await dispatch(Actions.getPostThread(post1.id)); - store.dispatch(Actions.receivedPost(post)); + nock(Client4.getBaseRoute()). + post(`/posts/${post1.id}/pin`). + reply(200, OK_RESPONSE); + await dispatch(Actions.pinPost(post1.id)); - let state = store.getState(); - expect(state.entities.posts.posts[post.id].is_pinned).toBe(false); - expect(Object.keys(state.entities.posts.reactions[post.id])).toHaveLength(1); + const state = getState(); + const {stats} = state.entities.channels; + const post = state.entities.posts.posts[post1.id]; + const pinnedPostCount = stats[TestHelper.basicChannel!.id].pinnedpost_count; - nock(Client4.getBaseRoute()). - post(`/posts/${post.id}/pin`). - reply(200, OK_RESPONSE); - - const result = await store.dispatch(Actions.pinPost(post.id)); - expect(result.error).toBeUndefined(); - - state = store.getState(); - expect(state.entities.posts.posts[post.id].is_pinned).toBe(true); - expect(Object.keys(state.entities.posts.reactions[post.id])).toHaveLength(1); - }); + expect(post).toBeTruthy(); + expect(post.is_pinned === true).toBeTruthy(); + expect(pinnedPostCount === 1).toBeTruthy(); }); - describe('unpinPost', () => { - test('should update post and channel stats', async () => { - nock(Client4.getBaseRoute()). - get(`/channels/${TestHelper.basicChannel!.id}/stats?exclude_files_count=true`). - reply(200, {channel_id: TestHelper.basicChannel!.id, member_count: 1, pinnedpost_count: 1}); - await store.dispatch(getChannelStats(TestHelper.basicChannel!.id)); + it('unpinPost', async () => { + const {dispatch, getState} = store; - const post = TestHelper.fakePostWithId(TestHelper.basicChannel!.id); - store.dispatch(Actions.receivedPost(post)); + nock(Client4.getBaseRoute()). + get(`/channels/${TestHelper.basicChannel!.id}/stats?exclude_files_count=true`). + reply(200, {channel_id: TestHelper.basicChannel!.id, member_count: 1, pinnedpost_count: 0}); - nock(Client4.getBaseRoute()). - post(`/posts/${post.id}/unpin`). - reply(200, OK_RESPONSE); + await dispatch(getChannelStats(TestHelper.basicChannel!.id)); - const result = await store.dispatch(Actions.unpinPost(post.id)); - expect(result.error).toBeUndefined(); + nock(Client4.getBaseRoute()). + post('/posts'). + reply(201, TestHelper.fakePostWithId(TestHelper.basicChannel!.id)); + const post1 = await Client4.createPost( + TestHelper.fakePost(TestHelper.basicChannel!.id), + ); - const state = store.getState(); - expect(state.entities.posts.posts[post.id].is_pinned).toBe(false); - expect(state.entities.channels.stats[TestHelper.basicChannel!.id].pinnedpost_count).toBe(0); - }); + const postList = {order: [post1.id], posts: {}} as PostList; + postList.posts[post1.id] = post1; - test('MM-14115 should not clobber reactions on pinned post', async () => { - const post = TestHelper.getPostMock({ - id: TestHelper.generateId(), - is_pinned: true, - metadata: { - embeds: [], - emojis: [], - files: [], - images: {}, - reactions: [ - TestHelper.getReactionMock({emoji_name: 'test'}), - ], - }, - }); + nock(Client4.getBaseRoute()). + get(`/posts/${post1.id}/thread?skipFetchThreads=false&collapsedThreads=true&collapsedThreadsExtended=false&direction=down&perPage=60`). + reply(200, postList); + await dispatch(Actions.getPostThread(post1.id)); - store.dispatch(Actions.receivedPost(post)); + nock(Client4.getBaseRoute()). + post(`/posts/${post1.id}/pin`). + reply(200, OK_RESPONSE); + await dispatch(Actions.pinPost(post1.id)); - let state = store.getState(); - expect(state.entities.posts.posts[post.id].is_pinned).toBe(true); - expect(Object.keys(state.entities.posts.reactions[post.id])).toHaveLength(1); + nock(Client4.getBaseRoute()). + post(`/posts/${post1.id}/unpin`). + reply(200, OK_RESPONSE); + await dispatch(Actions.unpinPost(post1.id)); - nock(Client4.getBaseRoute()). - post(`/posts/${post.id}/unpin`). - reply(200, OK_RESPONSE); + const state = getState(); + const {stats} = state.entities.channels; + const post = state.entities.posts.posts[post1.id]; + const pinnedPostCount = stats[TestHelper.basicChannel!.id].pinnedpost_count; - const result = await store.dispatch(Actions.unpinPost(post.id)); - expect(result.error).toBeUndefined(); - - state = store.getState(); - expect(state.entities.posts.posts[post.id].is_pinned).toBe(false); - expect(Object.keys(state.entities.posts.reactions[post.id])).toHaveLength(1); - }); + expect(post).toBeTruthy(); + expect(post.is_pinned === false).toBeTruthy(); + expect(pinnedPostCount === 0).toBeTruthy(); }); it('addReaction', async () => { diff --git a/webapp/channels/src/packages/mattermost-redux/src/actions/posts.ts b/webapp/channels/src/packages/mattermost-redux/src/actions/posts.ts index d0c35430c4..540e02ac19 100644 --- a/webapp/channels/src/packages/mattermost-redux/src/actions/posts.ts +++ b/webapp/channels/src/packages/mattermost-redux/src/actions/posts.ts @@ -137,15 +137,6 @@ export function postRemoved(post: Post) { }; } -export function postPinnedChanged(postId: string, isPinned: boolean, updateAt = Date.now()) { - return { - type: PostTypes.POST_PINNED_CHANGED, - postId, - isPinned, - updateAt, - }; -} - export function getPost(postId: string): ActionFuncAsync { return async (dispatch, getState) => { let post; @@ -534,7 +525,11 @@ export function pinPost(postId: string): ActionFuncAsync { const post = PostSelectors.getPost(state, postId); if (post) { actions.push( - postPinnedChanged(postId, true, Date.now()), + receivedPost({ + ...post, + is_pinned: true, + update_at: Date.now(), + }, isCollapsedThreadsEnabled(state)), { type: ChannelTypes.INCREMENT_PINNED_POST_COUNT, id: post.channel_id, @@ -582,7 +577,11 @@ export function unpinPost(postId: string): ActionFuncAsync { const post = PostSelectors.getPost(state, postId); if (post) { actions.push( - postPinnedChanged(postId, false, Date.now()), + receivedPost({ + ...post, + is_pinned: false, + update_at: Date.now(), + }, isCollapsedThreadsEnabled(state)), decrementPinnedPostCount(post.channel_id), ); } diff --git a/webapp/channels/src/packages/mattermost-redux/src/reducers/entities/posts.test.ts b/webapp/channels/src/packages/mattermost-redux/src/reducers/entities/posts.test.ts index fe4293dcbc..1283b5c591 100644 --- a/webapp/channels/src/packages/mattermost-redux/src/reducers/entities/posts.test.ts +++ b/webapp/channels/src/packages/mattermost-redux/src/reducers/entities/posts.test.ts @@ -3615,116 +3615,79 @@ describe('reactions', () => { PostTypes.RECEIVED_POST, ]) { describe(`single post received (${actionType})`, () => { - it('should not store anything for a post first received without metadata', () => { - // This shouldn't occur based on our type definitions, but it is possible - const post = TestHelper.getPostMock({ - id: 'post', - }); - (post as any).metadata = undefined; - + it('no post metadata', () => { const state = deepFreeze({}); const action = { type: actionType, - data: post, - }; - - const nextState = reducers.reactions(state, action); - - expect(nextState).toBe(state); - }); - - it('should not change stored state for a post received without metadata', () => { - // This shouldn't occur based on our type definitions, but it is possible - const post = TestHelper.getPostMock({ - id: 'post', - }); - (post as any).metadata = undefined; - - const state = deepFreeze({ - post: { - 'user-taco': TestHelper.getReactionMock({user_id: 'user', emoji_name: 'taco'}), + data: { + id: 'post', }, - }); - const action = { - type: actionType, - data: post, }; const nextState = reducers.reactions(state, action); - expect(nextState).toBe(state); + expect(nextState).toEqual(state); }); - it('should store when a post is first received without reactions', () => { - const post = TestHelper.getPostMock({ - id: 'post', - }); - post.metadata.reactions = undefined; - + it('no reactions in post metadata', () => { const state = deepFreeze({}); const action = { type: actionType, - data: post, + data: { + id: 'post', + metadata: {reactions: []}, + }, }; const nextState = reducers.reactions(state, action); - expect(nextState).not.toBe(state); + expect(nextState).not.toEqual(state); expect(nextState).toEqual({ post: {}, }); }); - it('should remove existing reactions when a post is received without reactions', () => { - const post = TestHelper.getPostMock({ - id: 'post', - }); - post.metadata.reactions = undefined; - - const state = deepFreeze({ - post: { - 'user-taco': TestHelper.getReactionMock({user_id: 'abcd', emoji_name: '+1'}), - }, - }); + it('should not clobber reactions when metadata empty', () => { + const state = deepFreeze({post: {name: 'smiley', post_id: 'post'}}); const action = { type: actionType, - data: post, + data: { + id: 'post', + metadata: {}, + }, }; const nextState = reducers.reactions(state, action); - expect(nextState).not.toBe(state); expect(nextState).toEqual({ - post: {}, + post: {name: 'smiley', post_id: 'post'}, }); }); it('should save reactions', () => { - const reactions = [ - TestHelper.getReactionMock({user_id: 'abcd', emoji_name: '+1'}), - TestHelper.getReactionMock({user_id: 'efgh', emoji_name: '+1'}), - TestHelper.getReactionMock({user_id: 'abcd', emoji_name: '-1'}), - ]; - const state = deepFreeze({}); const action = { type: actionType, - data: TestHelper.getPostMock({ + data: { id: 'post', metadata: { - reactions, + reactions: [ + {user_id: 'abcd', emoji_name: '+1'}, + {user_id: 'efgh', emoji_name: '+1'}, + {user_id: 'abcd', emoji_name: '-1'}, + ], }, - }), + }, }; const nextState = reducers.reactions(state, action); - expect(nextState).not.toBe(state); + expect(nextState).not.toEqual(state); expect(nextState).toEqual({ post: { - 'abcd-+1': reactions[0], - 'efgh-+1': reactions[1], - 'abcd--1': reactions[2], + 'abcd-+1': {user_id: 'abcd', emoji_name: '+1'}, + 'efgh-+1': {user_id: 'efgh', emoji_name: '+1'}, + 'abcd--1': {user_id: 'abcd', emoji_name: '-1'}, }, }); }); @@ -3733,10 +3696,10 @@ describe('reactions', () => { const state = deepFreeze({}); const action = { type: actionType, - data: TestHelper.getPostMock({ + data: { id: 'post', - delete_at: 1571366424287, - }), + delete_at: '1571366424287', + }, }; const nextState = reducers.reactions(state, action); @@ -3747,218 +3710,150 @@ describe('reactions', () => { } describe('receiving multiple posts', () => { - it('should not store anything for a post first received without metadata', () => { - // This shouldn't occur based on our type definitions, but it is possible - const post = TestHelper.getPostMock({ - id: 'post', - }); - (post as any).metadata = undefined; - + it('no post metadata', () => { const state = deepFreeze({}); const action = { type: PostTypes.RECEIVED_POSTS, data: { posts: { - post, + post: { + id: 'post', + }, }, }, }; const nextState = reducers.reactions(state, action); - expect(state).toBe(nextState); + expect(nextState).toEqual(state); }); - it('should not change stored state for a post received without metadata', () => { - // This shouldn't occur based on our type definitions, but it is possible - const post = TestHelper.getPostMock({ - id: 'post', - }); - (post as any).metadata = undefined; - - const state = deepFreeze({ - post: { - 'user-taco': TestHelper.getReactionMock({user_id: 'abcd', emoji_name: '+1'}), - }, - }); - const action = { - type: PostTypes.RECEIVED_POSTS, - data: { - posts: { - post, - }, - }, - }; - - const nextState = reducers.reactions(state, action); - - expect(state).toBe(nextState); - }); - - it('should store when a post is first received without reactions', () => { - const post = TestHelper.getPostMock({ - id: 'post', - }); - post.metadata.reactions = undefined; - + it('no reactions in post metadata', () => { const state = deepFreeze({}); const action = { type: PostTypes.RECEIVED_POSTS, data: { posts: { - post, + post: { + id: 'post', + metadata: {reactions: []}, + }, }, }, }; const nextState = reducers.reactions(state, action); - expect(nextState).not.toBe(state); - expect(nextState).toEqual({ - post: {}, - }); - }); - - it('should remove existing reactions when a post is received without reactions', () => { - const post = TestHelper.getPostMock({ - id: 'post', - }); - post.metadata.reactions = undefined; - - const state = deepFreeze({ - post: { - 'user-taco': TestHelper.getReactionMock({user_id: 'abcd', emoji_name: '+1'}), - }, - }); - const action = { - type: PostTypes.RECEIVED_POSTS, - data: { - posts: { - post, - }, - }, - }; - - const nextState = reducers.reactions(state, action); - - expect(nextState).not.toBe(state); + expect(nextState).not.toEqual(state); expect(nextState).toEqual({ post: {}, }); }); it('should save reactions', () => { - const reactions = [ - TestHelper.getReactionMock({user_id: 'abcd', emoji_name: '+1'}), - TestHelper.getReactionMock({user_id: 'efgh', emoji_name: '+1'}), - TestHelper.getReactionMock({user_id: 'abcd', emoji_name: '-1'}), - ]; - const state = deepFreeze({}); const action = { type: PostTypes.RECEIVED_POSTS, data: { posts: { - post: TestHelper.getPostMock({ + post: { id: 'post', metadata: { - reactions, + reactions: [ + {user_id: 'abcd', emoji_name: '+1'}, + {user_id: 'efgh', emoji_name: '+1'}, + {user_id: 'abcd', emoji_name: '-1'}, + ], }, - }), + }, }, }, }; const nextState = reducers.reactions(state, action); - expect(nextState).not.toBe(state); + expect(nextState).not.toEqual(state); expect(nextState).toEqual({ post: { - 'abcd-+1': reactions[0], - 'efgh-+1': reactions[1], - 'abcd--1': reactions[2], + 'abcd-+1': {user_id: 'abcd', emoji_name: '+1'}, + 'efgh-+1': {user_id: 'efgh', emoji_name: '+1'}, + 'abcd--1': {user_id: 'abcd', emoji_name: '-1'}, }, }); }); it('should save reactions for multiple posts', () => { - const reaction1 = TestHelper.getReactionMock({user_id: 'abcd', emoji_name: '+1'}); - const reaction2 = TestHelper.getReactionMock({user_id: 'abcd', emoji_name: '-1'}); - const state = deepFreeze({}); const action = { type: PostTypes.RECEIVED_POSTS, data: { posts: { - post1: TestHelper.getPostMock({ + post1: { id: 'post1', metadata: { reactions: [ - reaction1, + {user_id: 'abcd', emoji_name: '+1'}, ], }, - }), - post2: TestHelper.getPostMock({ + }, + post2: { id: 'post2', metadata: { reactions: [ - reaction2, + {user_id: 'abcd', emoji_name: '-1'}, ], }, - }), + }, }, }, }; const nextState = reducers.reactions(state, action); - expect(nextState).not.toBe(state); + expect(nextState).not.toEqual(state); expect(nextState).toEqual({ post1: { - 'abcd-+1': reaction1, + 'abcd-+1': {user_id: 'abcd', emoji_name: '+1'}, }, post2: { - 'abcd--1': reaction2, + 'abcd--1': {user_id: 'abcd', emoji_name: '-1'}, }, }); }); it('should save reactions for multiple posts except deleted posts', () => { - const reaction1 = TestHelper.getReactionMock({user_id: 'abcd', emoji_name: '+1'}); - const reaction2 = TestHelper.getReactionMock({user_id: 'abcd', emoji_name: '-1'}); - const state = deepFreeze({}); const action = { type: PostTypes.RECEIVED_POSTS, data: { posts: { - post1: TestHelper.getPostMock({ + post1: { id: 'post1', metadata: { reactions: [ - reaction1, + {user_id: 'abcd', emoji_name: '+1'}, ], }, - }), - post2: TestHelper.getPostMock({ + }, + post2: { id: 'post2', - delete_at: 1571366424287, + delete_at: '1571366424287', metadata: { reactions: [ - reaction2, + {user_id: 'abcd', emoji_name: '-1'}, ], }, - }), + }, }, }, }; const nextState = reducers.reactions(state, action); - expect(nextState).not.toBe(state); + expect(nextState).not.toEqual(state); expect(nextState).toEqual({ post1: { - 'abcd-+1': reaction1, + 'abcd-+1': {user_id: 'abcd', emoji_name: '+1'}, }, }); }); diff --git a/webapp/channels/src/packages/mattermost-redux/src/reducers/entities/posts.ts b/webapp/channels/src/packages/mattermost-redux/src/reducers/entities/posts.ts index cba1279a11..c17f50c9bf 100644 --- a/webapp/channels/src/packages/mattermost-redux/src/reducers/entities/posts.ts +++ b/webapp/channels/src/packages/mattermost-redux/src/reducers/entities/posts.ts @@ -158,7 +158,7 @@ export function nextPostsReplies(state: {[x in Post['id']]: number} = {}, action } } -export function handlePosts(state: IDMappedObjects = {}, action: AnyAction) { +export function handlePosts(state: RelationOneToOne = {}, action: AnyAction) { switch (action.type) { case PostTypes.RECEIVED_POST: case PostTypes.RECEIVED_NEW_POST: { @@ -263,23 +263,6 @@ export function handlePosts(state: IDMappedObjects = {}, action: AnyAction return nextState; } - case PostTypes.POST_PINNED_CHANGED: { - const {postId, isPinned, updateAt} = action; - - if (!state[postId]) { - return state; - } - - return { - ...state, - [postId]: { - ...state[postId], - is_pinned: isPinned, - last_update_at: updateAt, - }, - }; - } - case ChannelTypes.RECEIVED_CHANNEL_DELETED: case ChannelTypes.DELETE_CHANNEL_SUCCESS: case ChannelTypes.LEAVE_CHANNEL: { @@ -1307,8 +1290,8 @@ export function acknowledgements(state: RelationOneToOne>, post: Post) { - if (!post.metadata || post.delete_at > 0) { +function storeReactionsForPost(state: any, post: Post) { + if (!post.metadata || !post.metadata.reactions || post.delete_at > 0) { return state; } diff --git a/webapp/channels/src/packages/mattermost-redux/test/test_helper.ts b/webapp/channels/src/packages/mattermost-redux/test/test_helper.ts index d795fbaa45..8f3590217e 100644 --- a/webapp/channels/src/packages/mattermost-redux/test/test_helper.ts +++ b/webapp/channels/src/packages/mattermost-redux/test/test_helper.ts @@ -12,7 +12,6 @@ import type {FileInfo} from '@mattermost/types/files'; import type {Group} from '@mattermost/types/groups'; import type {Command, DialogElement, OAuthApp} from '@mattermost/types/integrations'; import type {Post, PostMetadata} from '@mattermost/types/posts'; -import type {Reaction} from '@mattermost/types/reactions'; import type {Role} from '@mattermost/types/roles'; import type {Scheme} from '@mattermost/types/schemes'; import type {Team, TeamMembership} from '@mattermost/types/teams'; @@ -724,16 +723,6 @@ class TestHelper { }; } - getReactionMock(override: Partial = {}): Reaction { - return { - user_id: '', - post_id: '', - emoji_name: '', - create_at: 0, - ...override, - }; - } - mockLogin = () => { const clientBaseRoute = this.basicClient4!.getBaseRoute(); nock(clientBaseRoute). diff --git a/webapp/channels/src/utils/test_helper.ts b/webapp/channels/src/utils/test_helper.ts index 153eeb729b..ccca026651 100644 --- a/webapp/channels/src/utils/test_helper.ts +++ b/webapp/channels/src/utils/test_helper.ts @@ -319,7 +319,7 @@ export class TestHelper { return Object.assign({}, defaultOutgoingWebhook, override); } - public static getPostMock(override: Omit, 'metadata'> & {metadata?: Partial} = {}): Post { + public static getPostMock(override: Partial = {}): Post { const defaultPost: Post = { edit_at: 0, original_id: '', @@ -345,15 +345,7 @@ export class TestHelper { update_at: 0, user_id: 'user_id', }; - - return { - ...defaultPost, - ...override, - metadata: { - ...defaultPost.metadata, - ...override.metadata, - }, - }; + return Object.assign({}, defaultPost, override); } public static getFileInfoMock(override: Partial = {}): FileInfo { diff --git a/webapp/platform/types/src/config.ts b/webapp/platform/types/src/config.ts index cb9eabebf8..d9a8312fa3 100644 --- a/webapp/platform/types/src/config.ts +++ b/webapp/platform/types/src/config.ts @@ -121,7 +121,6 @@ export type ClientConfig = { FileLevel: string; FeatureFlagAppsEnabled: string; FeatureFlagCallsEnabled: string; - FeatureFlagWebSocketEventScope: string; ForgotPasswordLink: string; GiphySdkKey: string; GoogleDeveloperKey: string; diff --git a/webapp/platform/types/src/posts.ts b/webapp/platform/types/src/posts.ts index 520169dbb8..f02456e376 100644 --- a/webapp/platform/types/src/posts.ts +++ b/webapp/platform/types/src/posts.ts @@ -66,7 +66,7 @@ export type PostMetadata = { emojis: CustomEmoji[]; files: FileInfo[]; images: Record; - reactions?: Reaction[]; + reactions: Reaction[]; priority?: PostPriorityMetadata; acknowledgements?: PostAcknowledgement[]; };