Skip to content

perf: Remove dead "What's New" modal code and fix Home re-render cascade#39314

Merged
MajorLift merged 6 commits intomainfrom
copilot/fix-get-sorted-announcements
Jan 26, 2026
Merged

perf: Remove dead "What's New" modal code and fix Home re-render cascade#39314
MajorLift merged 6 commits intomainfrom
copilot/fix-get-sorted-announcements

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Jan 16, 2026

Description

This PR removes the dead "What's New" modal feature entirely, which also resolves the performance issue that was causing unnecessary re-renders of the Home component.

What is the reason for the change?

Investigation revealed that the getSortedAnnouncementsToShow selector:

  1. Always returned an empty array because getAllowedAnnouncementIds() returns {}
  2. UI_NOTIFICATIONS in shared/notifications/index.ts is empty
  3. The feature was additionally disabled via TEMPORARY_DISABLE_WHATS_NEW = true in home.container.js

Rather than memoizing dead code, this PR removes it entirely.

What is the improvement/solution?

Removed all UI-side code related to the "What's New" modal:

  • Deleted ui/components/app/whats-new-modal/ directory
  • Removed getSortedAnnouncementsToShow and getShowWhatsNewPopup selectors
  • Removed showWhatsNewPopup from Redux state and the hideWhatsNewPopup action
  • Cleaned up Home component props and modal gating logic

Performance improvement: The previous getSortedAnnouncementsToShow selector created new array references on every call, breaking React's memoization and causing the Home component tree (~50-60 children) to re-render unnecessarily. Removing this code path eliminates this re-render cascade entirely.

Open in GitHub Codespaces

Changelog

CHANGELOG entry: Removed unused "What's New" modal feature, improving Home page performance

Related issues

  • Fixes MetaMask/MetaMask-planning#6670
  • Part of MetaMask-planning#6669 — Break Global Re-render Cascade

Manual testing steps

  1. Build and run the extension
  2. Navigate to the Home page
  3. Verify no "What's New" modal appears (it wasn't appearing before either due to the disable flag)
  4. Verify all other modals work correctly (Recovery Phrase, Terms of Use, Multi-RPC, Update)

Screenshots/Recordings

N/A - Removes unused feature

Pre-merge author checklist

Pre-merge reviewer checklist

  • I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed).
  • I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots.

Technical Details

Files Removed

  • ui/components/app/whats-new-modal/whats-new-modal.tsx
  • ui/components/app/whats-new-modal/notifications.ts
  • ui/components/app/whats-new-modal/index.ts

Code Removed

  • getSortedAnnouncementsToShow selector and helpers (getAnnouncementsObject, getAllowedAnnouncementIds)
  • getShowWhatsNewPopup selector
  • showWhatsNewPopup state property and HIDE_WHATS_NEW_POPUP action
  • hideWhatsNewPopup action creator
  • Related props in Home component (showWhatsNewPopup, hideWhatsNewPopup, announcementsToShow)
  • Tests for getSortedAnnouncementsToShow

Why This Is Better Than Memoization

The original issue asked to memoize getSortedAnnouncementsToShow. However, investigation showed:

  1. getAllowedAnnouncementIds() returns {} (empty object)
  2. The filter allowedIds[a.id] is always undefined (falsy)
  3. Result is always [] regardless of announcements in state
  4. Additionally disabled by TEMPORARY_DISABLE_WHATS_NEW = true

Memoizing code that always returns [] is wasteful. Removing it:

  • Eliminates the re-render cascade completely
  • Reduces bundle size
  • Simplifies the codebase
  • Removes technical debt

What's Preserved

The AnnouncementController infrastructure in the background scripts is preserved, as it's deeply integrated and may be needed for future announcements.


Note

Removes the dormant announcements UI and simplifies Home modal gating to prevent unnecessary re-renders.

  • Deletes ui/components/app/whats-new-modal/*
  • Removes selectors getSortedAnnouncementsToShow and getShowWhatsNewPopup
  • Drops Redux state/action for showWhatsNewPopup and HIDE_WHATS_NEW_POPUP (and hideWhatsNewPopup creator)
  • Updates home.component.js to remove WhatsNew logic/props and simplify showMultiRpcEditModal, displayUpdateModal, and recovery/rewards gating
  • Cleans home.container.js to stop selecting announcements/WhatsNew, and adjusts mapState/mapDispatch
  • Updates Storybook args and test state snapshots to reflect removed fields

Written by Cursor Bugbot for commit 04208e8. This will update automatically on new commits. Configure here.

Copilot AI changed the title [WIP] Fix getSortedAnnouncementsToShow selector in Home Fix getSortedAnnouncementsToShow selector memoization to prevent cascade re-renders Jan 16, 2026
Copilot AI requested a review from MajorLift January 16, 2026 05:20
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Jan 16, 2026

CLA Signature Action: All authors have signed the CLA. You may need to manually re-run the blocking PR check if it doesn't pass in a few minutes.

@metamaskbotv2
Copy link
Copy Markdown
Contributor

metamaskbotv2 bot commented Jan 16, 2026

Builds ready [61782ee]
UI Startup Metrics (1292 ± 116 ms)
PlatformBuildTypePageMetricTest Title (ms)Persona (ms)Mean (ms)Min (ms)Max (ms)Std Dev (ms)P 75 (ms)P 95 (ms)
ChromeBrowserifyStandard Home0--------
1--------
2--------
3--------
4--------
5--------
6--------
7--------
8--------
9--------
10--------
11--------
12--------
13--------
14--------
15--------
16--------
17--------
18--------
uiStartup--12921047161011613611491
load--1077880142110811401288
domContentLoaded--1071875141610811351277
domInteractive--2815153252296
firstPaint--154651269127185278
backgroundConnect--21720027311223238
firstReactRender--1693761830
getState--371796144068
initialActions--104112
loadScripts--86168012041089211078
setupStore--1252741323
numNetworkReqs--181177171169
19--------
BrowserifyPower User Home0--------
1--------
2--------
3--------
4--------
5--------
6--------
7--------
8--------
9--------
10--------
11--------
12--------
13--------
14--------
15--------
16--------
17--------
18--------
uiStartup--18921564251421620112377
load--1024898162412510491295
domContentLoaded--1011893161112610361285
domInteractive--34171522533105
firstPaint--171691296140216365
backgroundConnect--278200771132252620
firstReactRender--21163132327
getState--17213128526186213
initialActions--102011
loadScripts--80168413891238071078
setupStore--18104981838
numNetworkReqs--60382132858131
19--------
WebpackStandard Home0--------
1--------
2--------
3--------
4--------
5--------
6--------
7--------
8--------
9--------
10--------
11--------
12--------
13--------
14--------
15--------
16--------
17--------
18--------
uiStartup--794656100477833955
load--64557784365662778
domContentLoaded--64057383764656771
domInteractive--2514109192277
firstPaint--1056238357121228
backgroundConnect--255111273693
firstReactRender--15113441724
getState--3114104144355
initialActions--104112
loadScripts--63657182963654761
setupStore--1264371321
numNetworkReqs--171177171171
19--------
WebpackPower User Home0--------
1--------
2--------
3--------
4--------
5--------
6--------
7--------
8--------
9--------
10--------
11--------
12--------
13--------
14--------
15--------
16--------
17--------
18--------
uiStartup--1366981195725815861909
load--690593102995696914
domContentLoaded--680586102296687905
domInteractive--35181552832114
firstPaint--14762922110168331
backgroundConnect--120866721052621
firstReactRender--23184642529
getState--15512724923164211
initialActions--103111
loadScripts--677583101994685897
setupStore--16105681644
numNetworkReqs--1003731151136219
19--------
FirefoxBrowserifyStandard Home0--------
1--------
2--------
3--------
4--------
5--------
6--------
7--------
8--------
9--------
10--------
11--------
12--------
13--------
14--------
15--------
16--------
17--------
18--------
uiStartup--14061127208720215021878
load--1118942169613811981356
domContentLoaded--1117936169613911981356
domInteractive--73324695689142
firstPaint--------
backgroundConnect--59192615370179
firstReactRender--13103541322
getState--1268281223
initialActions--103122
loadScripts--1083924160212511421285
setupStore--155144171247
numNetworkReqs--19976181372
19--------
BrowserifyPower User Home0--------
1--------
2--------
3--------
4--------
5--------
6--------
7--------
8--------
9--------
10--------
11--------
12--------
13--------
14--------
15--------
16--------
17--------
18--------
uiStartup--25411942724581224504776
load--1324987587561913422535
domContentLoaded--1324987587561913422534
domInteractive--156364287425123387
firstPaint--------
backgroundConnect--126201176172119453
firstReactRender--231565102453
getState--26661921237414774
initialActions--218123
loadScripts--1270963582758812752435
setupStore--1627795206202670
numNetworkReqs--60301543087112
19--------
WebpackStandard Home0--------
1--------
2--------
3--------
4--------
5--------
6--------
7--------
8--------
9--------
10--------
11--------
12--------
13--------
14--------
15--------
16--------
17--------
18--------
uiStartup--16161304221820017402013
load--13411123172513014361592
domContentLoaded--13411123172513014361592
domInteractive--922923545129177
firstPaint--------
backgroundConnect--68222465291177
firstReactRender--15114641624
getState--228286391656
initialActions--1013122
loadScripts--13001100163710813641509
setupStore--174121181553
numNetworkReqs--20983191477
19--------
WebpackPower User Home0--------
1--------
2--------
3--------
4--------
5--------
6--------
7--------
8--------
9--------
10--------
11--------
12--------
13--------
14--------
15--------
16--------
17--------
18--------
uiStartup--29831976522584734864668
load--16811237340958817932955
domContentLoaded--16801236340958817932954
domInteractive--12131997136112469
firstPaint--------
backgroundConnect--2083313513051801135
firstReactRender--24178482632
getState--318821825271504792
initialActions--203123
loadScripts--15521201316844217112602
setupStore--89790313287382
numNetworkReqs--58301663875158
19--------
📊 Page Load Benchmark Results

Current Commit: 61782ee | Date: 1/16/2026

📄 Localhost MetaMask Test Dapp

Samples: 100

Summary

  • pageLoadTime-> current mean value: 1.04s (±69ms) 🟡 | historical mean value: 1.04s ⬆️ (historical data)
  • domContentLoaded-> current mean value: 730ms (±66ms) 🟢 | historical mean value: 724ms ⬆️ (historical data)
  • firstContentfulPaint-> current mean value: 77ms (±11ms) 🟢 | historical mean value: 76ms ⬆️ (historical data)

📈 Detailed Results

Metric Mean Std Dev Min Max P95 P99
pageLoadTime 1.04s 69ms 1.00s 1.31s 1.25s 1.31s
domContentLoaded 730ms 66ms 697ms 986ms 931ms 986ms
firstPaint 77ms 11ms 56ms 164ms 92ms 164ms
firstContentfulPaint 77ms 11ms 56ms 164ms 92ms 164ms
largestContentfulPaint 0ms 0ms 0ms 0ms 0ms 0ms
Bundle size diffs [🚨 Warning! Bundle size has increased!]
  • background: 58 Bytes (0%)
  • ui: 0 Bytes (0%)
  • common: 65 Bytes (0%)

@MajorLift MajorLift force-pushed the copilot/fix-get-sorted-announcements branch from 61782ee to 394c13f Compare January 16, 2026 21:31
@MajorLift MajorLift removed their request for review January 16, 2026 21:35
@MajorLift MajorLift force-pushed the copilot/fix-get-sorted-announcements branch from 394c13f to c298f9b Compare January 16, 2026 21:41
@MajorLift MajorLift changed the title Fix getSortedAnnouncementsToShow selector memoization to prevent cascade re-renders fix: getSortedAnnouncementsToShow selector memoization to prevent cascade re-renders Jan 16, 2026
@MajorLift

This comment was marked as resolved.

@MajorLift MajorLift changed the title fix: getSortedAnnouncementsToShow selector memoization to prevent cascade re-renders fix: getSortedAnnouncementsToShow broken selector memoization causes global cascading re-renders Jan 16, 2026

This comment was marked as resolved.

Copilot AI changed the title fix: getSortedAnnouncementsToShow broken selector memoization causes global cascading re-renders fix: getSortedAnnouncementsToShow selector memoization to prevent cascade re-renders Jan 16, 2026
Copilot AI requested a review from MajorLift January 16, 2026 21:50
@zone-live
Copy link
Copy Markdown
Contributor

@ameliejyc yes the selector it's not being used, neither the whats-new-modal, it's basically "stand-by" code at this point, in case there's ever the need to show the whats-new-modal again in the future. Can be removed.

@MajorLift
Copy link
Copy Markdown
Contributor

@ameliejyc @zone-live I did a first pass on removing dead code and it's a fair amount: 9 files deleted or with lines removed. Are we good with proceeding with this or would the team maybe prefer to handle this in a separate ticket?

If this looks ok I can push as a commit here and update the pr title and description.

diff --git a/ui/components/app/whats-new-modal/index.ts b/ui/components/app/whats-new-modal/index.ts
deleted file mode 100644
index 2e981b5a09..0000000000
--- a/ui/components/app/whats-new-modal/index.ts
+++ /dev/null
@@ -1 +0,0 @@
-export { default } from './whats-new-modal';
diff --git a/ui/components/app/whats-new-modal/notifications.ts b/ui/components/app/whats-new-modal/notifications.ts
deleted file mode 100644
index 8c469783c5..0000000000
--- a/ui/components/app/whats-new-modal/notifications.ts
+++ /dev/null
@@ -1,10 +0,0 @@
-import {
-  TranslatedUINotifications,
-  TranslationFunction,
-} from '../../../../shared/notifications';
-
-export const getTranslatedUINotifications = (
-  _t: TranslationFunction,
-): TranslatedUINotifications => {
-  return {};
-};
diff --git a/ui/components/app/whats-new-modal/whats-new-modal.tsx b/ui/components/app/whats-new-modal/whats-new-modal.tsx
deleted file mode 100644
index 5669c57652..0000000000
--- a/ui/components/app/whats-new-modal/whats-new-modal.tsx
+++ /dev/null
@@ -1,139 +0,0 @@
-import React, { useContext } from 'react';
-import { useSelector } from 'react-redux';
-
-import {
-  MetaMetricsEventCategory,
-  MetaMetricsEventName,
-} from '../../../../shared/constants/metametrics';
-import {
-  ModalBodyProps,
-  ModalComponent,
-  ModalFooterProps,
-  ModalHeaderProps,
-} from '../../../../shared/notifications';
-import { I18nContext } from '../../../contexts/i18n';
-import { MetaMetricsContext } from '../../../contexts/metametrics';
-import {
-  Display,
-  FlexDirection,
-} from '../../../helpers/constants/design-system';
-import { getSortedAnnouncementsToShow } from '../../../selectors';
-import { updateViewedNotifications } from '../../../store/actions';
-import { Modal, ModalContent, ModalOverlay } from '../../component-library';
-import { getTranslatedUINotifications } from './notifications';
-
-type WhatsNewModalProps = {
-  onClose: () => void;
-};
-
-type NotificationType = {
-  id: number;
-  date?: string | null;
-  title: string;
-  description?: string | string[];
-  image?: {
-    src: string;
-    width?: string;
-    height?: string;
-  };
-  modal?: {
-    header?: ModalComponent<ModalHeaderProps>;
-    body?: ModalComponent<ModalBodyProps>;
-    footer?: ModalComponent<ModalFooterProps>;
-  };
-};
-
-type RenderNotificationProps = {
-  notification: NotificationType;
-  onClose: () => void;
-  onNotificationViewed: (id: number) => Promise<void>;
-};
-
-const renderNotification = ({
-  notification,
-  onClose,
-  onNotificationViewed,
-}: RenderNotificationProps) => {
-  const { id, title, image, modal } = notification;
-
-  const handleNotificationClose = async () => {
-    await onNotificationViewed(id);
-    onClose();
-  };
-
-  return (
-    <ModalContent
-      modalDialogProps={{
-        display: Display.Flex,
-        flexDirection: FlexDirection.Column,
-        padding: 4,
-      }}
-    >
-      {modal?.header && (
-        <modal.header.component onClose={onClose} image={image} />
-      )}
-      {modal?.body && <modal.body.component title={title} />}
-      {modal?.footer && (
-        <modal.footer.component
-          onAction={() => {
-            // No action needed for whats-new notifications
-            // This is required by the ModalFooterProps type
-            console.log('No action needed for now');
-          }}
-          onCancel={handleNotificationClose}
-        />
-      )}
-    </ModalContent>
-  );
-};
-
-// TODO: Fix in https://github.com/MetaMask/metamask-extension/issues/31860
-// eslint-disable-next-line @typescript-eslint/naming-convention
-export default function WhatsNewModal({ onClose }: WhatsNewModalProps) {
-  const t = useContext(I18nContext);
-  const trackEvent = useContext(MetaMetricsContext);
-
-  const notifications = useSelector(getSortedAnnouncementsToShow);
-
-  const handleNotificationViewed = async (id: number) => {
-    await updateViewedNotifications({ [id]: true });
-  };
-
-  const handleModalClose = async () => {
-    await Promise.all(
-      notifications.map(({ id }) => handleNotificationViewed(id)),
-    );
-    trackEvent({
-      category: MetaMetricsEventCategory.Home,
-      event: MetaMetricsEventName.WhatsNewViewed,
-    });
-    onClose();
-  };
-
-  return (
-    <>
-      <Modal
-        // TODO: Fix in https://github.com/MetaMask/metamask-extension/issues/31879
-        // eslint-disable-next-line @typescript-eslint/no-misused-promises
-        onClose={handleModalClose}
-        data-testid="whats-new-modal"
-        isOpen={notifications.length > 0}
-        isClosedOnOutsideClick
-        isClosedOnEscapeKey
-        autoFocus={false}
-      >
-        <ModalOverlay />
-
-        {notifications.map(({ id }) => {
-          const notification = getTranslatedUINotifications(t)[id];
-
-          return renderNotification({
-            notification,
-            onClose,
-            onNotificationViewed: handleNotificationViewed,
-          });
-        })}
-      </Modal>
-    </>
-  );
-}
diff --git a/ui/ducks/app/app.ts b/ui/ducks/app/app.ts
index e874fa7c84..a4a8483cbf 100644
--- a/ui/ducks/app/app.ts
+++ b/ui/ducks/app/app.ts
@@ -90,7 +90,6 @@ type AppState = {
   // TODO: Fix in https://github.com/MetaMask/metamask-extension/issues/31973
   // eslint-disable-next-line @typescript-eslint/no-explicit-any
   currentWindowTab: Record<string, any>; // tabs.tab https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/tabs/Tab
-  showWhatsNewPopup: boolean;
   showTermsOfUsePopup: boolean;
   singleExceptions: {
     testKey: string | null;
@@ -209,7 +208,6 @@ const initialState: AppState = {
   requestAccountTabs: {},
   openMetaMaskTabs: {},
   currentWindowTab: {},
-  showWhatsNewPopup: true,
   showTermsOfUsePopup: true,
   singleExceptions: {
     testKey: null,
@@ -651,12 +649,6 @@ export default function reduceApp(
         openMetaMaskTabs: action.payload,
       };
 
-    case actionConstants.HIDE_WHATS_NEW_POPUP:
-      return {
-        ...appState,
-        showWhatsNewPopup: false,
-      };
-
     case actionConstants.CAPTURE_SINGLE_EXCEPTION:
       return {
         ...appState,
@@ -806,12 +798,6 @@ export default function reduceApp(
 }
 
 // Action Creators
-export function hideWhatsNewPopup(): Action {
-  return {
-    type: actionConstants.HIDE_WHATS_NEW_POPUP,
-  };
-}
-
 export function openBasicFunctionalityModal(): Action {
   return {
     type: actionConstants.SHOW_BASIC_FUNCTIONALITY_MODAL_OPEN,
diff --git a/ui/pages/home/home.component.js b/ui/pages/home/home.component.js
index 57b71b4476..429138b024 100644
--- a/ui/pages/home/home.component.js
+++ b/ui/pages/home/home.component.js
@@ -12,7 +12,6 @@ import {
 } from '../../../shared/constants/metametrics';
 import TermsOfUsePopup from '../../components/app/terms-of-use-popup';
 import RecoveryPhraseReminder from '../../components/app/recovery-phrase-reminder';
-import WhatsNewModal from '../../components/app/whats-new-modal';
 import { FirstTimeFlowType } from '../../../shared/constants/onboarding';
 import HomeNotification from '../../components/app/home-notification';
 import MultipleNotifications from '../../components/app/multiple-notifications';
@@ -106,9 +105,6 @@ export default class Home extends PureComponent {
     showTermsOfUsePopup: PropTypes.bool.isRequired,
     firstTimeFlowType: PropTypes.string,
     completedOnboarding: PropTypes.bool,
-    showWhatsNewPopup: PropTypes.bool.isRequired,
-    hideWhatsNewPopup: PropTypes.func.isRequired,
-    announcementsToShow: PropTypes.bool.isRequired,
     onboardedInThisUISession: PropTypes.bool,
     showMultiRpcModal: PropTypes.bool.isRequired,
     showUpdateModal: PropTypes.bool.isRequired,
@@ -772,11 +768,8 @@ export default class Home extends PureComponent {
       isPopup,
       showRecoveryPhraseReminder,
       showTermsOfUsePopup,
-      showWhatsNewPopup,
-      hideWhatsNewPopup,
       completedOnboarding,
       onboardedInThisUISession,
-      announcementsToShow,
       firstTimeFlowType,
       newNetworkAddedConfigurationId,
       showMultiRpcModal,
@@ -803,23 +796,11 @@ export default class Home extends PureComponent {
         firstTimeFlowType === FirstTimeFlowType.import) &&
       !newNetworkAddedConfigurationId;
 
-    const showWhatsNew =
-      canSeeModals &&
-      announcementsToShow &&
-      showWhatsNewPopup &&
-      !process.env.IN_TEST;
-
     const showMultiRpcEditModal =
-      canSeeModals &&
-      showMultiRpcModal &&
-      !showWhatsNew &&
-      !process.env.IN_TEST;
+      canSeeModals && showMultiRpcModal && !process.env.IN_TEST;
 
     const displayUpdateModal =
-      canSeeModals &&
-      showUpdateModal &&
-      !showWhatsNew &&
-      !showMultiRpcEditModal;
+      canSeeModals && showUpdateModal && !showMultiRpcEditModal;
 
     const showTermsOfUse =
       completedOnboarding &&
@@ -828,16 +809,13 @@ export default class Home extends PureComponent {
       !isSocialLoginFlow;
 
     const showRecoveryPhrase =
-      !showWhatsNew &&
-      showRecoveryPhraseReminder &&
-      !isPrimarySeedPhraseBackedUp;
+      showRecoveryPhraseReminder && !isPrimarySeedPhraseBackedUp;
 
     const showRewardsModal =
       rewardsEnabled &&
       rewardsOnboardingEnabled &&
       canSeeModals &&
       !showTermsOfUse &&
-      !showWhatsNew &&
       !showMultiRpcEditModal &&
       !displayUpdateModal &&
       !isSeedlessPasswordOutdated &&
@@ -886,7 +864,6 @@ export default class Home extends PureComponent {
           {isSeedlessPasswordOutdated && <PasswordOutdatedModal />}
           {showMultiRpcEditModal && <MultiRpcEditModal />}
           {displayUpdateModal && <UpdateModal />}
-          {showWhatsNew ? <WhatsNewModal onClose={hideWhatsNewPopup} /> : null}
           {showRecoveryPhrase ? (
             <RecoveryPhraseReminder
               onConfirm={this.onRecoveryPhraseReminderClose}
diff --git a/ui/pages/home/home.container.js b/ui/pages/home/home.container.js
index f613e54938..3bfb6dab9b 100644
--- a/ui/pages/home/home.container.js
+++ b/ui/pages/home/home.container.js
@@ -10,8 +10,6 @@ import {
   getOriginOfCurrentTab,
   getTotalUnapprovedCount,
   getWeb3ShimUsageStateForOrigin,
-  getShowWhatsNewPopup,
-  getSortedAnnouncementsToShow,
   getShowRecoveryPhraseReminder,
   getShowTermsOfUse,
   getShowOutdatedBrowserWarning,
@@ -51,10 +49,7 @@ import {
   lookupSelectedNetworks,
   setPendingShieldCohort,
 } from '../../store/actions';
-import {
-  hideWhatsNewPopup,
-  openBasicFunctionalityModal,
-} from '../../ducks/app/app';
+import { openBasicFunctionalityModal } from '../../ducks/app/app';
 import {
   getIsPrimarySeedPhraseBackedUp,
   getIsSeedlessPasswordOutdated,
@@ -128,12 +123,6 @@ const mapStateToProps = (state) => {
     ///: END:ONLY_INCLUDE_IF
   ]);
 
-  const TEMPORARY_DISABLE_WHATS_NEW = true;
-
-  const showWhatsNewPopup = TEMPORARY_DISABLE_WHATS_NEW
-    ? false
-    : getShowWhatsNewPopup(state);
-
   const shouldShowSeedPhraseReminder =
     selectedAccount && getShouldShowSeedPhraseReminder(state, selectedAccount);
 
@@ -157,8 +146,6 @@ const mapStateToProps = (state) => {
     originOfCurrentTab,
     shouldShowWeb3ShimUsageNotification,
     infuraBlocked: getInfuraBlocked(state),
-    announcementsToShow: getSortedAnnouncementsToShow(state).length > 0,
-    showWhatsNewPopup,
     showRecoveryPhraseReminder: getShowRecoveryPhraseReminder(state),
     showTermsOfUsePopup: getShowTermsOfUse(state),
     showOutdatedBrowserWarning:
@@ -201,7 +188,6 @@ const mapDispatchToProps = (dispatch) => {
       setWeb3ShimUsageAlertDismissed(origin),
     disableWeb3ShimUsageAlert: () =>
       setAlertEnabledness(AlertTypes.web3ShimUsage, false),
-    hideWhatsNewPopup: () => dispatch(hideWhatsNewPopup()),
     setRecoveryPhraseReminderHasBeenShown: () =>
       dispatch(setRecoveryPhraseReminderHasBeenShown()),
     setRecoveryPhraseReminderLastShown: (lastShown) =>
diff --git a/ui/selectors/selectors.js b/ui/selectors/selectors.js
index fe31c68f6a..2404b5532e 100644
--- a/ui/selectors/selectors.js
+++ b/ui/selectors/selectors.js
@@ -216,10 +216,6 @@ export function getNextSuggestedNonce(state) {
   return Number(state.appState.nextNonce);
 }
 
-export function getShowWhatsNewPopup(state) {
-  return state.appState.showWhatsNewPopup;
-}
-
 export function getShowPermittedNetworkToastOpen(state) {
   return state.appState.showPermittedNetworkToastOpen;
 }
@@ -2429,48 +2425,6 @@ export const getSnapInsights = createDeepEqualSelector(
   (insights, id) => insights?.[id],
 );
 
-/**
- * Get an object of announcement IDs and if they are allowed or not.
- *
- * @returns {object}
- */
-const getAllowedAnnouncementIds = () => EMPTY_OBJECT;
-
-/**
- * Get the announcements object from state
- *
- * @param {object} state - the redux state object
- * @returns {object} The announcements object
- */
-const getAnnouncementsObject = (state) => state.metamask.announcements;
-
-/**
- * @typedef {object} Announcement
- * @property {number} id - A unique identifier for the announcement
- * @property {string} date - A date in YYYY-MM-DD format, identifying when the notification was first committed
- */
-
-/**
- * Announcements are managed by the announcement controller and referenced by
- * `state.metamask.announcements`. This function returns a list of announcements
- * the can be shown to the user. This list includes all announcements that do not
- * have a truthy `isShown` property.
- *
- * The returned announcements are sorted by date.
- *
- * @param {object} state - the redux state object
- * @returns {Announcement[]} An array of announcements that can be shown to the user
- */
-
-export const getSortedAnnouncementsToShow = createSelector(
-  getAnnouncementsObject,
-  getAllowedAnnouncementIds,
-  (announcements, allowedIds) =>
-    Object.values(announcements)
-      .filter((a) => !a.isShown && allowedIds[a.id])
-      .sort((a, b) => new Date(b.date) - new Date(a.date)),
-);
-
 /**
  * @param state
  * @returns {{networkId: string}[]}
diff --git a/ui/selectors/selectors.test.js b/ui/selectors/selectors.test.js
index 2af103857d..05bb1fcf51 100644
--- a/ui/selectors/selectors.test.js
+++ b/ui/selectors/selectors.test.js
@@ -4170,111 +4170,3 @@ describe('getPermissionsForActiveTab', () => {
     expect(result).toStrictEqual([]);
   });
 });
-
-describe('getSortedAnnouncementsToShow', () => {
-  it('memoizes and returns the same reference when state unchanged', () => {
-    const state = {
-      metamask: {
-        announcements: {
-          1: { id: 1, date: '2021-01-01', isShown: false },
-          2: { id: 2, date: '2021-01-02', isShown: true },
-          3: { id: 3, date: '2021-01-03', isShown: false },
-        },
-      },
-    };
-
-    const result1 = selectors.getSortedAnnouncementsToShow(state);
-    const result2 = selectors.getSortedAnnouncementsToShow(state);
-
-    // Should return the same reference when state unchanged (memoization)
-    expect(result1).toBe(result2);
-  });
-
-  it('returns different reference when announcements object changes', () => {
-    const state1 = {
-      metamask: {
-        announcements: {
-          1: { id: 1, date: '2021-01-01', isShown: false },
-        },
-      },
-    };
-
-    const result1 = selectors.getSortedAnnouncementsToShow(state1);
-
-    const state2 = {
-      metamask: {
-        announcements: {
-          1: { id: 1, date: '2021-01-01', isShown: false },
-          2: { id: 2, date: '2021-01-02', isShown: false },
-        },
-      },
-    };
-
-    const result2 = selectors.getSortedAnnouncementsToShow(state2);
-
-    // Should return different reference when announcements change
-    expect(result1).not.toBe(result2);
-  });
-
-  it('filters out shown announcements and sorts by date descending', () => {
-    const state = {
-      metamask: {
-        announcements: {
-          1: { id: 1, date: '2021-01-01', isShown: false },
-          2: { id: 2, date: '2021-01-03', isShown: true },
-          3: { id: 3, date: '2021-01-02', isShown: false },
-        },
-      },
-    };
-
-    const result = selectors.getSortedAnnouncementsToShow(state);
-
-    // Currently getAllowedAnnouncementIds returns {}, so all announcements are filtered out
-    expect(result).toStrictEqual([]);
-  });
-
-  it('returns empty array when no announcements exist', () => {
-    const state = {
-      metamask: {
-        announcements: {},
-      },
-    };
-
-    const result = selectors.getSortedAnnouncementsToShow(state);
-    expect(result).toStrictEqual([]);
-  });
-
-  it('returns empty array when all announcements are shown', () => {
-    const state = {
-      metamask: {
-        announcements: {
-          1: { id: 1, date: '2021-01-01', isShown: true },
-          2: { id: 2, date: '2021-01-02', isShown: true },
-        },
-      },
-    };
-
-    const result = selectors.getSortedAnnouncementsToShow(state);
-    expect(result).toStrictEqual([]);
-  });
-
-  it('maintains memoization with same announcements object reference', () => {
-    const announcements = {
-      1: { id: 1, date: '2021-01-01', isShown: false },
-    };
-
-    const state1 = {
-      metamask: { announcements },
-    };
-
-    const state2 = {
-      metamask: { announcements },
-    };
-
-    const result1 = selectors.getSortedAnnouncementsToShow(state1);
-    const result2 = selectors.getSortedAnnouncementsToShow(state2);
-
-    // Should be memoized since announcements object is the same reference
-    expect(result1).toBe(result2);
-  });
-});
diff --git a/ui/store/actionConstants.ts b/ui/store/actionConstants.ts
index 176cf780a0..7775f40e36 100644
--- a/ui/store/actionConstants.ts
+++ b/ui/store/actionConstants.ts
@@ -149,7 +149,6 @@ export const SET_REQUEST_ACCOUNT_TABS = 'SET_REQUEST_ACCOUNT_TABS';
 export const SET_OPEN_METAMASK_TAB_IDS = 'SET_OPEN_METAMASK_TAB_IDS';
 
 // Home Screen
-export const HIDE_WHATS_NEW_POPUP = 'HIDE_WHATS_NEW_POPUP';
 export const TOGGLE_GAS_LOADING_ANIMATION = 'TOGGLE_GAS_LOADING_ANIMATION';
 
 // Smart Transactions

@metamaskbotv2
Copy link
Copy Markdown
Contributor

metamaskbotv2 bot commented Jan 22, 2026

✨ Files requiring CODEOWNER review ✨

👨‍🔧 @MetaMask/core-extension-ux (3 files, +4 -43)
  • 📁 ui/
    • 📁 pages/
      • 📁 home/
        • 📄 home.component.js +3 -27
        • 📄 home.component.stories.tsx +0 -1
        • 📄 home.container.js +1 -15

🕵️ @MetaMask/extension-privacy-reviewers (1 files, +0 -1)
  • 📁 test/
    • 📁 e2e/
      • 📁 tests/
        • 📁 settings/
          • 📄 state-logs.json +0 -1

🧪 @MetaMask/qa (1 files, +0 -1)
  • 📁 test/
    • 📁 e2e/
      • 📁 tests/
        • 📁 settings/
          • 📄 state-logs.json +0 -1

@MajorLift MajorLift changed the title fix: getSortedAnnouncementsToShow selector memoization to prevent cascade re-renders refactor: Remove dead "What's New" modal code and fix Home re-render cascade Jan 22, 2026
@metamaskbotv2
Copy link
Copy Markdown
Contributor

metamaskbotv2 bot commented Jan 22, 2026

Builds ready [431ea92]
UI Startup Metrics (1265 ± 119 ms)
PlatformBuildTypePageMetricTest Title (ms)Persona (ms)Mean (ms)Min (ms)Max (ms)Std Dev (ms)P 75 (ms)P 95 (ms)
ChromeBrowserifyStandard Home0--------
1--------
2--------
3--------
4--------
5--------
6--------
7--------
8--------
9--------
10--------
11--------
12--------
13--------
14--------
15--------
16--------
17--------
18--------
uiStartup--12651037157111913461495
load--1056854140911011181287
domContentLoaded--1049847140510911121274
domInteractive--2515106192078
firstPaint--193591304218182311
backgroundConnect--20217924610207219
firstReactRender--1493851529
getState--3518104163773
initialActions--105112
loadScripts--85565211991079131065
setupStore--1172631318
numNetworkReqs--181279161267
19--------
BrowserifyPower User Home0--------
1--------
2--------
3--------
4--------
5--------
6--------
7--------
8--------
9--------
10--------
11--------
12--------
13--------
14--------
15--------
16--------
17--------
18--------
uiStartup--301114958840180436778062
load--1119896153812812171349
domContentLoaded--1104892152912511921324
domInteractive--34181392632107
firstPaint--178661093120229309
backgroundConnect--774203650810107282792
firstReactRender--21153132328
getState--2851352365291255846
initialActions--1073712
loadScripts--87768513071149671076
setupStore--226151192350
numNetworkReqs--85402273692177
19--------
WebpackStandard Home0--------
1--------
2--------
3--------
4--------
5--------
6--------
7--------
8--------
9--------
10--------
11--------
12--------
13--------
14--------
15--------
16--------
17--------
18--------
uiStartup--822664114984870975
load--66459285165715803
domContentLoaded--65958984165711798
domInteractive--2615107192281
firstPaint--1036125846136194
backgroundConnect--255115273198
firstReactRender--16103041824
getState--3414130174657
initialActions--104113
loadScripts--65658783763707791
setupStore--1364671530
numNetworkReqs--181290161264
19--------
WebpackPower User Home0--------
1--------
2--------
3--------
4--------
5--------
6--------
7--------
8--------
9--------
10--------
11--------
12--------
13--------
14--------
15--------
16--------
17--------
18--------
uiStartup--1357988260129314421992
load--70059789369740837
domContentLoaded--68858888669726831
domInteractive--3418122213492
firstPaint--1467032966200276
backgroundConnect--3075016419110
firstReactRender--22174432428
getState--14912821315155179
initialActions--105112
loadScripts--68658687868724823
setupStore--21758122645
numNetworkReqs--1155729349133234
19--------
FirefoxBrowserifyStandard Home0--------
1--------
2--------
3--------
4--------
5--------
6--------
7--------
8--------
9--------
10--------
11--------
12--------
13--------
14--------
15--------
16--------
17--------
18--------
uiStartup--13711080188217714741763
load--1112925166714012001342
domContentLoaded--1111925166614012001341
domInteractive--63312404485148
firstPaint--------
backgroundConnect--57182374576155
firstReactRender--15104981338
getState--135141181225
initialActions--103112
loadScripts--1074911164312211491293
setupStore--14494151144
numNetworkReqs--201080181574
19--------
BrowserifyPower User Home0--------
1--------
2--------
3--------
4--------
5--------
6--------
7--------
8--------
9--------
10--------
11--------
12--------
13--------
14--------
15--------
16--------
17--------
18--------
uiStartup--25781999475745626953416
load--12651037288927513161605
domContentLoaded--12651037288227513131605
domInteractive--167501437198162402
firstPaint--------
backgroundConnect--268401301309377948
firstReactRender--23156062530
getState--25372875185321707
initialActions--203123
loadScripts--12151009249022312661578
setupStore--14317636144166528
numNetworkReqs--74331792882135
19--------
WebpackStandard Home0--------
1--------
2--------
3--------
4--------
5--------
6--------
7--------
8--------
9--------
10--------
11--------
12--------
13--------
14--------
15--------
16--------
17--------
18--------
uiStartup--15851271204017016771943
load--13381084176711614211541
domContentLoaded--13381084176711614211540
domInteractive--892920639127155
firstPaint--------
backgroundConnect--63181894282158
firstReactRender--15115751521
getState--2082233116113
initialActions--103122
loadScripts--1298107116669913641480
setupStore--144120161341
numNetworkReqs--201079181475
19--------
WebpackPower User Home0--------
1--------
2--------
3--------
4--------
5--------
6--------
7--------
8--------
9--------
10--------
11--------
12--------
13--------
14--------
15--------
16--------
17--------
18--------
uiStartup--29872165899387031974627
load--16571249729172116292828
domContentLoaded--16571248729172116292827
domInteractive--14529841120163392
firstPaint--------
backgroundConnect--3123821133444341017
firstReactRender--23156062531
getState--26667886212372706
initialActions--203122
loadScripts--15961229725769316052556
setupStore--1177787140147471
numNetworkReqs--68311642883132
19--------
📊 Page Load Benchmark Results

Current Commit: 431ea92 | Date: 1/22/2026

📄 Localhost MetaMask Test Dapp

Samples: 100

Summary

  • pageLoadTime-> current mean value: 1.05s (±92ms) 🟡 | historical mean value: 1.03s ⬆️ (historical data)
  • domContentLoaded-> current mean value: 735ms (±116ms) 🟢 | historical mean value: 714ms ⬆️ (historical data)
  • firstContentfulPaint-> current mean value: 85ms (±99ms) 🟢 | historical mean value: 76ms ⬆️ (historical data)

📈 Detailed Results

Metric Mean Std Dev Min Max P95 P99
pageLoadTime 1.05s 92ms 1.02s 1.92s 1.07s 1.92s
domContentLoaded 735ms 116ms 699ms 1.86s 754ms 1.86s
firstPaint 85ms 99ms 60ms 1.07s 84ms 1.07s
firstContentfulPaint 85ms 99ms 60ms 1.07s 84ms 1.07s
largestContentfulPaint 0ms 0ms 0ms 0ms 0ms 0ms
Bundle size diffs [🚨 Warning! Bundle size has increased!]
  • background: -227.76 KiB (-4.42%)
  • ui: 96.04 KiB (1.22%)
  • common: 233.12 KiB (2.55%)

@MajorLift MajorLift marked this pull request as draft January 23, 2026 07:51
@metamaskbotv2
Copy link
Copy Markdown
Contributor

metamaskbotv2 bot commented Jan 23, 2026

Builds ready [04208e8]
UI Startup Metrics (1283 ± 99 ms)
PlatformBuildTypePageMetricTest Title (ms)Persona (ms)Mean (ms)Min (ms)Max (ms)Std Dev (ms)P 75 (ms)P 95 (ms)
ChromeBrowserifyStandard Home0--------
1--------
2--------
3--------
4--------
5--------
6--------
7--------
8--------
9--------
10--------
11--------
12--------
13--------
14--------
15--------
16--------
17--------
18--------
uiStartup--1283107015659913441444
load--107687513219311361221
domContentLoaded--106987213119211311217
domInteractive--2815106192678
firstPaint--160651096146193311
backgroundConnect--21619828813221239
firstReactRender--1593151627
getState--3722109134063
initialActions--106112
loadScripts--8606711097929221012
setupStore--1274241318
numNetworkReqs--191378171369
19--------
BrowserifyPower User Home0--------
1--------
2--------
3--------
4--------
5--------
6--------
7--------
8--------
9--------
10--------
11--------
12--------
13--------
14--------
15--------
16--------
17--------
18--------
uiStartup--326016568574163243236846
load--1139927163313312521381
domContentLoaded--1122915161812912261359
domInteractive--34191442432102
firstPaint--1807840078247301
backgroundConnect--975206332799519843106
firstReactRender--21164142330
getState--30514226173193271003
initialActions--103111
loadScripts--89470913651179901108
setupStore--19764122349
numNetworkReqs--92342444298217
19--------
WebpackStandard Home0--------
1--------
2--------
3--------
4--------
5--------
6--------
7--------
8--------
9--------
10--------
11--------
12--------
13--------
14--------
15--------
16--------
17--------
18--------
uiStartup--785643102171837914
load--62856979458639760
domContentLoaded--62356578757632754
domInteractive--241589172076
firstPaint--1015628653143204
backgroundConnect--356133325599
firstReactRender--15102531622
getState--291466133957
initialActions--105111
loadScripts--62056378055629747
setupStore--1273861225
numNetworkReqs--201387181376
19--------
WebpackPower User Home0--------
1--------
2--------
3--------
4--------
5--------
6--------
7--------
8--------
9--------
10--------
11--------
12--------
13--------
14--------
15--------
16--------
17--------
18--------
uiStartup--13251039213521914041944
load--723628114092738916
domContentLoaded--711620113092723909
domInteractive--38191362636112
firstPaint--14771979104168282
backgroundConnect--3695607420214
firstReactRender--22183432329
getState--15312924121160206
initialActions--103111
loadScripts--709618112091721906
setupStore--21649102241
numNetworkReqs--1235528044143215
19--------
FirefoxBrowserifyStandard Home0--------
1--------
2--------
3--------
4--------
5--------
6--------
7--------
8--------
9--------
10--------
11--------
12--------
13--------
14--------
15--------
16--------
17--------
18--------
uiStartup--14291107225521814931937
load--1158956206016212071507
domContentLoaded--1157955205916212061507
domInteractive--78339239491151
firstPaint--------
backgroundConnect--58202475066171
firstReactRender--1394561335
getState--126149151122
initialActions--102022
loadScripts--1123942198314411671383
setupStore--134119141237
numNetworkReqs--211181191576
19--------
BrowserifyPower User Home0--------
1--------
2--------
3--------
4--------
5--------
6--------
7--------
8--------
9--------
10--------
11--------
12--------
13--------
14--------
15--------
16--------
17--------
18--------
uiStartup--26801996780365127953437
load--13091046281525913621636
domContentLoaded--13081045281525913621635
domInteractive--1484949699189384
firstPaint--------
backgroundConnect--3892613643466311091
firstReactRender--22145572333
getState--21268790164215657
initialActions--208123
loadScripts--12591028274924412981576
setupStore--149134505449136353
numNetworkReqs--76351392484132
19--------
WebpackStandard Home0--------
1--------
2--------
3--------
4--------
5--------
6--------
7--------
8--------
9--------
10--------
11--------
12--------
13--------
14--------
15--------
16--------
17--------
18--------
uiStartup--16221356224119517691985
load--13721165170012214531613
domContentLoaded--13711164170012214521613
domInteractive--862928450129152
firstPaint--------
backgroundConnect--60211874175145
firstReactRender--14112531520
getState--207227321597
initialActions--102122
loadScripts--13351138163610514001572
setupStore--2132754213123
numNetworkReqs--211183191676
19--------
WebpackPower User Home0--------
1--------
2--------
3--------
4--------
5--------
6--------
7--------
8--------
9--------
10--------
11--------
12--------
13--------
14--------
15--------
16--------
17--------
18--------
uiStartup--28232020633056129554040
load--14901155264222415721929
domContentLoaded--14891155264222415721928
domInteractive--145321329143161335
firstPaint--------
backgroundConnect--341243562431515972
firstReactRender--22165662530
getState--23960882193234710
initialActions--206123
loadScripts--14501139248020815221825
setupStore--13010737151150580
numNetworkReqs--75271872983144
19--------
📊 Page Load Benchmark Results

Current Commit: 04208e8 | Date: 1/23/2026

📄 Localhost MetaMask Test Dapp

Samples: 100

Summary

  • pageLoadTime-> current mean value: 1.08s (±44ms) 🟡 | historical mean value: 1.05s ⬆️ (historical data)
  • domContentLoaded-> current mean value: 752ms (±39ms) 🟢 | historical mean value: 727ms ⬆️ (historical data)
  • firstContentfulPaint-> current mean value: 83ms (±12ms) 🟢 | historical mean value: 79ms ⬆️ (historical data)

📈 Detailed Results

Metric Mean Std Dev Min Max P95 P99
pageLoadTime 1.08s 44ms 1.03s 1.36s 1.14s 1.36s
domContentLoaded 752ms 39ms 717ms 1.02s 791ms 1.02s
firstPaint 83ms 12ms 60ms 176ms 92ms 176ms
firstContentfulPaint 83ms 12ms 60ms 176ms 92ms 176ms
largestContentfulPaint 0ms 0ms 0ms 0ms 0ms 0ms
Bundle size diffs [🚨 Warning! Bundle size has increased!]
  • background: -227.61 KiB (-4.42%)
  • ui: 343.22 KiB (4.37%)
  • common: 238.12 KiB (2.61%)

@MajorLift MajorLift marked this pull request as ready for review January 23, 2026 17:20
@MajorLift MajorLift requested review from a team as code owners January 23, 2026 17:20
@MajorLift MajorLift changed the title refactor: Remove dead "What's New" modal code and fix Home re-render cascade perf: Remove dead "What's New" modal code and fix Home re-render cascade Jan 23, 2026
@github-project-automation github-project-automation bot moved this to Needs dev review in PR review queue Jan 23, 2026
@MajorLift MajorLift removed the request for review from a team January 23, 2026 19:31
Copy link
Copy Markdown
Contributor

@ameliejyc ameliejyc left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for doing this!

Copy link
Copy Markdown
Member

@seaona seaona left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

e2e changes LGTM

@github-project-automation github-project-automation bot moved this from Needs dev review to Review finalised - Ready to be merged in PR review queue Jan 26, 2026
@MajorLift MajorLift added this pull request to the merge queue Jan 26, 2026
Merged via the queue into main with commit b609682 Jan 26, 2026
197 checks passed
@MajorLift MajorLift deleted the copilot/fix-get-sorted-announcements branch January 26, 2026 11:44
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

external-contributor release-13.17.0 Issue or pull request that will be included in release 13.17.0 size-M

Projects

Archived in project

Development

Successfully merging this pull request may close these issues.

7 participants