From 64e814c3a0df0acbcc37541d96482aa824b1a2af Mon Sep 17 00:00:00 2001 From: Caleb Roseland Date: Thu, 14 Nov 2024 10:00:48 -0600 Subject: [PATCH] MM-61733: Fix system console theme stability (#29257) --- .../src/actions/websocket_actions.jsx | 12 ++++ .../src/components/root/root.test.tsx | 59 ++++++++++++++++--- webapp/channels/src/components/root/root.tsx | 17 +++++- 3 files changed, 76 insertions(+), 12 deletions(-) diff --git a/webapp/channels/src/actions/websocket_actions.jsx b/webapp/channels/src/actions/websocket_actions.jsx index 9e5caa492a..bbd33bdc1c 100644 --- a/webapp/channels/src/actions/websocket_actions.jsx +++ b/webapp/channels/src/actions/websocket_actions.jsx @@ -1503,6 +1503,10 @@ function handleSidebarCategoryCreated(msg) { return (doDispatch, doGetState) => { const state = doGetState(); + if (!msg.broadcast.team_id) { + return; + } + if (msg.broadcast.team_id !== getCurrentTeamId(state)) { // The new category will be loaded when we switch teams. return; @@ -1518,6 +1522,10 @@ function handleSidebarCategoryUpdated(msg) { return (doDispatch, doGetState) => { const state = doGetState(); + if (!msg.broadcast.team_id) { + return; + } + if (msg.broadcast.team_id !== getCurrentTeamId(state)) { // The updated categories will be loaded when we switch teams. return; @@ -1533,6 +1541,10 @@ function handleSidebarCategoryDeleted(msg) { return (doDispatch, doGetState) => { const state = doGetState(); + if (!msg.broadcast.team_id) { + return; + } + if (msg.broadcast.team_id !== getCurrentTeamId(state)) { // The category will be removed when we switch teams. return; diff --git a/webapp/channels/src/components/root/root.test.tsx b/webapp/channels/src/components/root/root.test.tsx index 27f6ed19dd..75705ad226 100644 --- a/webapp/channels/src/components/root/root.test.tsx +++ b/webapp/channels/src/components/root/root.test.tsx @@ -15,6 +15,7 @@ import * as GlobalActions from 'actions/global_actions'; import testConfigureStore from 'packages/mattermost-redux/test/test_store'; import {renderWithContext, waitFor} from 'tests/react_testing_utils'; import {StoragePrefixes} from 'utils/constants'; +import * as Utils from 'utils/utils'; import {handleLoginLogoutSignal, redirectToOnboardingOrDefaultTeam} from './actions'; import type {Props} from './root'; @@ -49,14 +50,9 @@ jest.mock('components/team_sidebar', () => () =>
); jest.mock('components/mobile_view_watcher', () => () =>
); jest.mock('./performance_reporter_controller', () => () =>
); -jest.mock('utils/utils', () => { - const original = jest.requireActual('utils/utils'); - - return { - ...original, - applyTheme: jest.fn(), - }; -}); +jest.mock('utils/utils', () => ({ + applyTheme: jest.fn(), +})); jest.mock('actions/global_actions', () => ({ redirectUserToDefaultTeam: jest.fn(), @@ -74,7 +70,7 @@ describe('components/Root', () => { const store = testConfigureStore(); const baseProps: Props = { - theme: {} as Theme, + theme: {sidebarBg: 'color'} as Theme, isConfigLoaded: true, telemetryEnabled: true, noAccounts: false, @@ -372,6 +368,51 @@ describe('components/Root', () => { }); }); }); + + describe('applyTheme', () => { + test('should apply theme initially and on change', async () => { + const props = { + ...baseProps, + }; + + const {rerender} = renderWithContext(); + + await waitFor(() => { + expect(Utils.applyTheme).toHaveBeenCalledWith(props.theme); + }); + + const props2 = { + ...props, + theme: {sidebarBg: 'color2'} as Theme, + }; + + rerender(); + + expect(Utils.applyTheme).toHaveBeenCalledWith(props2.theme); + }); + + test('should not apply theme in system console', async () => { + const props = { + ...baseProps, + ...{ + location: { + pathname: '/admin_console', + }, + } as RouteComponentProps, + }; + + const {rerender} = renderWithContext(); + + const props2 = { + ...props, + theme: {sidebarBg: 'color2'} as Theme, + }; + + rerender(); + + expect(Utils.applyTheme).not.toHaveBeenCalled(); + }); + }); }); describe('doesRouteBelongToTeamControllerRoutes', () => { diff --git a/webapp/channels/src/components/root/root.tsx b/webapp/channels/src/components/root/root.tsx index 01661006e0..c6c7bf2cc2 100644 --- a/webapp/channels/src/components/root/root.tsx +++ b/webapp/channels/src/components/root/root.tsx @@ -13,6 +13,7 @@ import {setSystemEmojis} from 'mattermost-redux/actions/emojis'; import {setUrl} from 'mattermost-redux/actions/general'; import {Client4} from 'mattermost-redux/client'; import {rudderAnalytics, RudderTelemetryHandler} from 'mattermost-redux/client/rudder'; +import {Preferences} from 'mattermost-redux/constants'; import {measurePageLoadTelemetry, temporarilySetPageLoadContext, trackEvent, trackSelectorMetrics} from 'actions/telemetry_actions.jsx'; import BrowserStore from 'stores/browser_store'; @@ -191,7 +192,7 @@ export default class Root extends React.PureComponent { this.showLandingPageIfNecessary(); - applyTheme(this.props.theme); + this.applyTheme(); }; private showLandingPageIfNecessary = () => { @@ -255,9 +256,19 @@ export default class Root extends React.PureComponent { BrowserStore.setLandingPageSeen(true); }; + applyTheme() { + // don't apply theme when in system console; system console hardcoded to THEMES.denim + // AdminConsole will apply denim on mount re-apply user theme on unmount + if (this.props.location.pathname.startsWith('/admin_console')) { + return; + } + + applyTheme(this.props.theme); + } + componentDidUpdate(prevProps: Props, prevState: State) { if (!deepEqual(prevProps.theme, this.props.theme)) { - applyTheme(this.props.theme); + this.applyTheme(); } if (this.props.location.pathname === '/') { @@ -452,7 +463,7 @@ export default class Root extends React.PureComponent { >