From f0abe8a596068924caa79d120c614b16f09a69e7 Mon Sep 17 00:00:00 2001 From: "cursor[bot]" <206951365+cursor[bot]@users.noreply.github.com> Date: Wed, 8 Jul 2026 21:18:18 +0000 Subject: [PATCH] [MM-69591] Remove WebSocketEventScope feature flag (#37384) * Remove WebSocketEventScope feature flag gating on server Leave the typing/reaction WebSocket event scoping permanently enabled. Co-authored-by: mattermost-code * Remove WebSocketEventScope feature flag usage in webapp Always update the active channel/team/thread over the WebSocket so the event scoping stays permanently enabled. Co-authored-by: mattermost-code * Remove WebSocketEventScope feature flag from e2e default config Co-authored-by: mattermost-code --------- Co-authored-by: Cursor Agent Co-authored-by: mattermost-code Co-authored-by: Jesse Hallam --- e2e-tests/playwright/lib/src/server/default_config.ts | 2 +- server/channels/app/platform/web_conn.go | 11 +++++------ server/public/model/feature_flags.go | 4 ++-- .../src/components/channel_view/channel_view.test.tsx | 1 - .../src/components/channel_view/channel_view.tsx | 2 +- webapp/channels/src/components/channel_view/index.ts | 2 -- webapp/channels/src/components/team_sidebar/index.ts | 2 -- .../src/components/team_sidebar/team_sidebar.tsx | 2 +- .../src/components/threading/thread_viewer/index.ts | 5 ----- .../threading/thread_viewer/thread_viewer.test.tsx | 1 - .../threading/thread_viewer/thread_viewer.tsx | 7 ++----- webapp/platform/types/src/config.ts | 1 - 12 files changed, 12 insertions(+), 28 deletions(-) diff --git a/e2e-tests/playwright/lib/src/server/default_config.ts b/e2e-tests/playwright/lib/src/server/default_config.ts index 929a3e34a0b..9d8f8b15baf 100644 --- a/e2e-tests/playwright/lib/src/server/default_config.ts +++ b/e2e-tests/playwright/lib/src/server/default_config.ts @@ -800,7 +800,7 @@ const defaultServerConfig: AdminConfig = { EnableExportDirectDownload: false, MoveThreadsEnabled: false, CloudDedicatedExportUI: false, - WebSocketEventScope: true, + NotificationMonitoring: true, ExperimentalAuditSettingsSystemConsoleUI: true, CustomProfileAttributes: true, AttributeBasedAccessControl: true, diff --git a/server/channels/app/platform/web_conn.go b/server/channels/app/platform/web_conn.go index b08cfc1cc1f..af607a11cf3 100644 --- a/server/channels/app/platform/web_conn.go +++ b/server/channels/app/platform/web_conn.go @@ -963,12 +963,11 @@ func (wc *WebConn) ShouldSendEvent(msg *model.WebSocketEvent) bool { if chID := msg.GetBroadcast().ChannelId; chID != "" { // For typing/reaction_added/reaction_removed events, we don't send them to users // who don't have that channel or thread opened. - if wc.Platform.Config().FeatureFlags.WebSocketEventScope && - slices.Contains([]model.WebsocketEventType{ - model.WebsocketEventTyping, - model.WebsocketEventReactionAdded, - model.WebsocketEventReactionRemoved, - }, msg.EventType()) && wc.notInChannel(chID) && wc.notInThread(chID) { + if slices.Contains([]model.WebsocketEventType{ + model.WebsocketEventTyping, + model.WebsocketEventReactionAdded, + model.WebsocketEventReactionRemoved, + }, msg.EventType()) && wc.notInChannel(chID) && wc.notInThread(chID) { return false } diff --git a/server/public/model/feature_flags.go b/server/public/model/feature_flags.go index ff7cfc91de5..839930fa997 100644 --- a/server/public/model/feature_flags.go +++ b/server/public/model/feature_flags.go @@ -38,7 +38,7 @@ type FeatureFlags struct { CloudDedicatedExportUI bool - WebSocketEventScope bool + NotificationMonitoring bool ExperimentalAuditSettingsSystemConsoleUI bool @@ -155,7 +155,7 @@ func (f *FeatureFlags) SetDefaults() { f.EnableExportDirectDownload = false f.MoveThreadsEnabled = false f.CloudDedicatedExportUI = false - f.WebSocketEventScope = true + f.NotificationMonitoring = true f.ExperimentalAuditSettingsSystemConsoleUI = true f.CustomProfileAttributes = true f.AttributeBasedAccessControl = true 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 8839fc4c497..5e76f40dc2b 100644 --- a/webapp/channels/src/components/channel_view/channel_view.test.tsx +++ b/webapp/channels/src/components/channel_view/channel_view.test.tsx @@ -55,7 +55,6 @@ describe('components/channel_view', () => { isCloud: false, goToLastViewedChannel: jest.fn(), isFirstAdmin: false, - enableWebSocketEventScope: false, isChannelBookmarksEnabled: false, missingChannelRole: false, fetchIsRestrictedDM: jest.fn(), diff --git a/webapp/channels/src/components/channel_view/channel_view.tsx b/webapp/channels/src/components/channel_view/channel_view.tsx index c4192aebee8..5a38db5df2b 100644 --- a/webapp/channels/src/components/channel_view/channel_view.tsx +++ b/webapp/channels/src/components/channel_view/channel_view.tsx @@ -100,7 +100,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); } diff --git a/webapp/channels/src/components/channel_view/index.ts b/webapp/channels/src/components/channel_view/index.ts index ec1e2b0dad0..d80d25123db 100644 --- a/webapp/channels/src/components/channel_view/index.ts +++ b/webapp/channels/src/components/channel_view/index.ts @@ -37,7 +37,6 @@ function mapStateToProps(state: GlobalState) { const config = getConfig(state); const enableOnboardingFlow = config.EnableOnboardingFlow === 'true'; - const enableWebSocketEventScope = config.FeatureFlagWebSocketEventScope === 'true'; const missingChannelRole = isMissingChannelRoles(state, channel); @@ -49,7 +48,6 @@ function mapStateToProps(state: GlobalState) { isCloud: getLicense(state).Cloud === 'true', teamUrl: getCurrentRelativeTeamUrl(state), isFirstAdmin: isFirstAdmin(state), - enableWebSocketEventScope, canRestrictDirectMessage: config.RestrictDirectMessage === 'team' && (channel?.type === 'D' || channel?.type === 'G'), restrictDirectMessage: channel ? state.entities.channels.restrictedDMs[channel.id] : false, isChannelBookmarksEnabled: getIsChannelBookmarksEnabled(state), diff --git a/webapp/channels/src/components/team_sidebar/index.ts b/webapp/channels/src/components/team_sidebar/index.ts index 58a1504f9ab..d97695f147e 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 7af8779a9b2..2b27a834e9b 100644 --- a/webapp/channels/src/components/team_sidebar/team_sidebar.tsx +++ b/webapp/channels/src/components/team_sidebar/team_sidebar.tsx @@ -123,7 +123,7 @@ export 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 3ae39962849..274abf87596 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'; @@ -46,8 +44,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; @@ -73,7 +69,6 @@ function makeMapStateToProps() { channel, highlightedPostId, selectedPostFocusedAt, - enableWebSocketEventScope, lastUpdateAt, }; }; 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 a081f91bdfb..e94980028ec 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 @@ -87,7 +87,6 @@ describe('components/threading/ThreadViewer', () => { appsEnabled: true, rootPostId: post.id, isThreadView: true, - enableWebSocketEventScope: false, lastUpdateAt: 1234, }; 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 36f7d9dff18..b3de520b245 100644 --- a/webapp/channels/src/components/threading/thread_viewer/thread_viewer.tsx +++ b/webapp/channels/src/components/threading/thread_viewer/thread_viewer.tsx @@ -55,7 +55,6 @@ export type Props = Attrs & { isThreadView: boolean; inputPlaceholder?: string; rootPostId: string; - enableWebSocketEventScope: boolean; lastUpdateAt: number; }; @@ -85,9 +84,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) { @@ -203,7 +200,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/platform/types/src/config.ts b/webapp/platform/types/src/config.ts index a1f180a1ea8..eb75a80fb18 100644 --- a/webapp/platform/types/src/config.ts +++ b/webapp/platform/types/src/config.ts @@ -133,7 +133,6 @@ export type ClientConfig = { FeatureFlagPermissionPolicies: string; FeatureFlagChannelPermissionPolicies: string; FeatureFlagPolicySimulation: string; - FeatureFlagWebSocketEventScope: string; FeatureFlagContentFlagging: string; FeatureFlagClassificationMarkings: string; FeatureFlagPropertyFieldRank: string;