From a84032f40a6ab1dadc344914a1ab92a2fb934577 Mon Sep 17 00:00:00 2001 From: Harrison Healey Date: Tue, 2 Jun 2026 01:43:55 -0400 Subject: [PATCH] MM-69053 Log server message when a user has concurrent React enabled (#36837) * MM-69053 Log server message when a user has concurrent React enabled * Add session_id and user_id to server-logged messages sent by the client * Fix linting * Fix test --- server/channels/api4/system.go | 2 ++ .../src/components/root/actions/index.ts | 19 ++++++++++++++++++- webapp/channels/src/components/root/index.ts | 2 ++ .../src/components/root/root.test.tsx | 1 + webapp/channels/src/components/root/root.tsx | 2 ++ 5 files changed, 25 insertions(+), 1 deletion(-) diff --git a/server/channels/api4/system.go b/server/channels/api4/system.go index 7de04c0676d..4b96ba5f45c 100644 --- a/server/channels/api4/system.go +++ b/server/channels/api4/system.go @@ -478,6 +478,8 @@ func postLog(c *Context, w http.ResponseWriter, r *http.Request) { fields := []mlog.Field{ mlog.String("type", "client_message"), mlog.String("user_agent", c.AppContext.UserAgent()), + mlog.String("session_id", c.AppContext.Session().Id), + mlog.String("user_id", c.AppContext.Session().UserId), } if !forceToDebug && lvl == "ERROR" { diff --git a/webapp/channels/src/components/root/actions/index.ts b/webapp/channels/src/components/root/actions/index.ts index a21c8f074bd..cb6766cfa46 100644 --- a/webapp/channels/src/components/root/actions/index.ts +++ b/webapp/channels/src/components/root/actions/index.ts @@ -3,6 +3,7 @@ import type {History} from 'history'; +import {LogLevel} from '@mattermost/types/client4'; import type {ServerError} from '@mattermost/types/errors'; import type {UserProfile} from '@mattermost/types/users'; @@ -25,7 +26,7 @@ import {reloadPage} from 'utils/browser_utils'; import {StoragePrefixes} from 'utils/constants'; import {doesCookieContainsMMUserId} from 'utils/utils'; -import type {ThunkActionFunc} from 'types/store'; +import type {ActionFuncAsync, ThunkActionFunc} from 'types/store'; import type {Translations} from 'types/store/i18n'; export type TranslationPluginFunction = (locale: string) => Translations @@ -156,3 +157,19 @@ export function handleLoginLogoutSignal(e: StorageEvent): ThunkActionFunc } }; } + +export function logIfConcurrentReactEnabled(): ActionFuncAsync { + return async () => { + const concurrentReactEnabled = localStorage.getItem('enable_concurrent_react_experimental') === 'true'; + + if (concurrentReactEnabled) { + Client4.logClientError( + "This user's session is using experimental concurrent React which may cause visual bugs. It can be " + + 'disabled from Settings > Advanced or by clearing their browser storage.', + LogLevel.Debug, + ); + } + + return {data: concurrentReactEnabled}; + }; +} diff --git a/webapp/channels/src/components/root/index.ts b/webapp/channels/src/components/root/index.ts index 6f505e91d3b..1710a8ffefc 100644 --- a/webapp/channels/src/components/root/index.ts +++ b/webapp/channels/src/components/root/index.ts @@ -32,6 +32,7 @@ import { loadConfigAndMe, handleLoginLogoutSignal, redirectToOnboardingOrDefaultTeam, + logIfConcurrentReactEnabled, } from './actions'; import Root from './root'; @@ -77,6 +78,7 @@ function mapDispatchToProps(dispatch: Dispatch) { actions: bindActionCreators({ loadConfigAndMe, loadRecentlyUsedCustomEmojis, + logIfConcurrentReactEnabled, migrateRecentEmojis, initializeProducts, handleLoginLogoutSignal, diff --git a/webapp/channels/src/components/root/root.test.tsx b/webapp/channels/src/components/root/root.test.tsx index 3d0d8103796..c9c26b7fd45 100644 --- a/webapp/channels/src/components/root/root.test.tsx +++ b/webapp/channels/src/components/root/root.test.tsx @@ -71,6 +71,7 @@ describe('components/Root', () => { }); }), loadRecentlyUsedCustomEmojis: jest.fn(), + logIfConcurrentReactEnabled: jest.fn(), migrateRecentEmojis: jest.fn(), initializeProducts: jest.fn(), ...bindActionCreators({ diff --git a/webapp/channels/src/components/root/root.tsx b/webapp/channels/src/components/root/root.tsx index f019498d917..6a3142afb47 100644 --- a/webapp/channels/src/components/root/root.tsx +++ b/webapp/channels/src/components/root/root.tsx @@ -231,6 +231,8 @@ export default class Root extends React.PureComponent { initiateMeRequests = async () => { const {isLoaded, isMeRequested} = await this.props.actions.loadConfigAndMe(); + this.props.actions.logIfConcurrentReactEnabled(); + if (isLoaded) { const isUserAtRootRoute = this.props.location.pathname === '/';