Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: routes.component.js and creation of ToastMaster #27735

Merged
merged 34 commits into from
Oct 22, 2024

Conversation

HowardBraham
Copy link
Contributor

@HowardBraham HowardBraham commented Oct 9, 2024

Description

This PR is part of the larger effort to add React.lazy to routes.

I would be very surprised if I did not break functionality somewhere, as this was a large and tough refactor. It needs some good QA, and Harika has been doing some testing that has passed.

Summary of what I did:

  • Pulled all Toast-related logic out of routes.component.js and into the new ToastMaster
  • Pulled everything else I could pull out into routes-helpers.js
  • Moved things out of the general selectors.js and actions.ts into toast-master-selectors.ts
  • Simplification of some Pointless Thunks™

For later PRs:

  • Convert routes.component.js to TypeScript
  • Convert routes.component.js to Hooks
  • Convert routes.component.js to react-router v6
  • Eliminate routes.container.js

Open in GitHub Codespaces

Related issues

Progresses: MetaMask/MetaMask-planning#2898

Manual testing steps

Test all the Routes and all the Toasts

Screenshots/Recordings

Before

After

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.

@HowardBraham HowardBraham added the team-tiger Tiger team (for tech debt reduction + performance improvements) label Oct 9, 2024
@HowardBraham HowardBraham self-assigned this Oct 9, 2024
Copy link
Contributor

github-actions bot commented Oct 9, 2024

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.

Comment on lines 99 to 106
export function setNewPrivacyPolicyToastShownDate(time: number) {
submitRequestToBackground('setNewPrivacyPolicyToastShownDate', [time]);
}

export function setNewPrivacyPolicyToastClickedOrClosed() {
submitRequestToBackground('setNewPrivacyPolicyToastClickedOrClosed');
}
Copy link
Contributor

@MajorLift MajorLift Oct 9, 2024

Choose a reason for hiding this comment

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

I believe submitRequestToBackground would still need to be awaited/thenned even if it's no longer being called inside of a thunk callback.

As I understand it, the motivation for removing this function call from redux would be to turn the thunks it used to be in into synchronous actions, but this doesn't change the fact that submitRequestToBackground itself returns a promise.

Suggested change
export function setNewPrivacyPolicyToastShownDate(time: number) {
submitRequestToBackground('setNewPrivacyPolicyToastShownDate', [time]);
}
export function setNewPrivacyPolicyToastClickedOrClosed() {
submitRequestToBackground('setNewPrivacyPolicyToastClickedOrClosed');
}
export async function setNewPrivacyPolicyToastShownDate(time: number) {
await submitRequestToBackground('setNewPrivacyPolicyToastShownDate', [time]);
}
export async function setNewPrivacyPolicyToastClickedOrClosed() {
await submitRequestToBackground('setNewPrivacyPolicyToastClickedOrClosed');
}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@Gudahtt I don't think there's any reason to wait for this to complete, but what do you think?

Copy link
Member

@matthewwalsh0 matthewwalsh0 Oct 11, 2024

Choose a reason for hiding this comment

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

My understanding is also that any requests to the background rely on the JSON-RPC API over the port streams and are therefore asynchronous, meaning we should await since submitRequestToBackground returns a promise.

I recall a contribution guideline that also recommends awaiting even when returning as it results in better stack traces in the event of errors.

https://github.com/MetaMask/contributor-docs/blob/main/docs/javascript.md#await-promises-before-returning-them

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I did a little research and experimentation on this, screenshots below. My conclusion is that our contributor guidelines may be incorrect, unless someone can show me a counterexample.

In this specific case, we actually do NOT want to wait for this. Waiting would slow down the main thread, and we don't need to wait for anything to happen before continuing.

Fake error in foreground, no async await

image

Fake error in foreground, with async await

image

Fake error in background, no async await

image

Fake error in background, with async await

image

Copy link
Contributor

Choose a reason for hiding this comment

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

By newly introducing floating promises into the codebase, wouldn't we be triggering uncaught exceptions that will crash the process (like the ones shown in the screenshots), when before this wasn't a concern?

I assume that for these background requests the consistency requirements are so weak that we don't need to log their failures. By the same token, aren't they also unimportant enough that we shouldn't be crashing the app over their failure?

Regarding the screenshots, I'm finding it confusing to understand what the promise chains being tested are. In the experiments above, what would be the equivalents of a, b, c from https://mathiasbynens.be/notes/async-stack-traces?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

From that example, a is setNewPrivacyPolicyToastShownDate, b is submitRequestToBackground, and c is continuing on with function PrivacyPolicyToast(). But c doesn't have to wait for b to finish.

Copy link
Contributor

@MajorLift MajorLift Oct 16, 2024

Choose a reason for hiding this comment

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

I think we might be conflating the following in this discussion:

b.then(() => c());
b.then(() => ...);
c();

In the second example, execution for the b.then() clause does not synchronously block c.

With that in mind, how would you feel about at least removing the possibility of uncaught exceptions by adding error handling without relying on async/await syntax?

export function setNewPrivacyPolicyToastClickedOrClosed() {
  submitRequestToBackground('setNewPrivacyPolicyToastClickedOrClosed')
    .catch((error) => ...);
}

In general, floating promises are forbidden by our eslint config with the @typescript-eslint/no-floating-promises rule, which is also included in the recommended ruleset for @typescript-eslint. The only reason that error isn't showing up here is because extension is using eslint configs that are three years old.

Edit: The reason I'm mentioning the lint rule is because someone will be fixing this code down the line so that it conforms with no-floating-promises. If we want to avoid the code from being altered a comment should be left at the very least, or a catch clause added so this is not flagged as a violation. Adding error handling would be the safe thing to do as well.

Copy link
Contributor

@MajorLift MajorLift Oct 16, 2024

Choose a reason for hiding this comment

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

a is setNewPrivacyPolicyToastShownDate, b is submitRequestToBackground, and c is continuing on with function PrivacyPolicyToast()

Having a bit of trouble understanding this. Wouldn't PrivacyPolicyToast be a since it's the outermost scope that the other two are nested in? a being an async function that contains b, c seems like an unalterable part of the premise discussed in the article.

Maybe the second half of a that follows b could be formulated as c, but then we don't seem to be examining the stack trace for an error in c? Not to mention a isn't an async function since it's a react component.

This discussion probably belongs in a dedicated ticket in contributor-docs. I think having the test code available would be helpful as well.

Comment on lines 99 to 119
export function setNewPrivacyPolicyToastShownDate(time: number) {
submitRequestToBackground('setNewPrivacyPolicyToastShownDate', [time]);
}

export function setNewPrivacyPolicyToastClickedOrClosed() {
submitRequestToBackground('setNewPrivacyPolicyToastClickedOrClosed');
}

export function getNftDetectionEnablementToast(state: State): boolean {
return Boolean(state.appState?.showNftDetectionEnablementToast);
}

export function setShowNftDetectionEnablementToast(
value: boolean,
): PayloadAction<string | ReactFragment | undefined> {
return {
type: SHOW_NFT_DETECTION_ENABLEMENT_TOAST,
payload: value,
};
}
Copy link
Contributor

@MajorLift MajorLift Oct 9, 2024

Choose a reason for hiding this comment

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

The three setters here are actions not selectors. It looks like the first two are no longer returning callbacks for thunks but the last one is still a redux action. Should all three be in this file?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Oh yes, I thought about this one. Do we prefer renaming this file to toast-master-selectors-and-actions.ts or creating a new toast-master-actions.ts?

Copy link
Contributor

@MajorLift MajorLift Oct 9, 2024

Choose a reason for hiding this comment

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

Both would work. I agree with the idea of removing these from the global actions.ts and co-locating them with other toast things.

Maybe a separate toast-master-actions.ts would be just a little bit better for readability.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@Gudahtt I thought about it a little more, and actually, are plain submitRequestToBackground functions actions at all?

Copy link
Member

@matthewwalsh0 matthewwalsh0 Oct 11, 2024

Choose a reason for hiding this comment

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

Definitely would recommend a separate file for these, but no the submitRequestToBackground ones are not technically Redux actions as we're not dispatching them on a reducer, ultimately just utils.

@metamaskbot
Copy link
Collaborator

Builds ready [0eb601f]
Page Load Metrics (1892 ± 86 ms)
PlatformPageMetricMin (ms)Max (ms)Average (ms)StandardDeviation (ms)MarginOfError (ms)
ChromeHomefirstPaint38321621787347166
domContentLoaded15282425186817684
load15362436189217886
domInteractive14107402110
Bundle size diffs [🚀 Bundle size reduced!]
  • background: 0 Bytes (0.00%)
  • ui: 3.7 KiB (0.05%)
  • common: -2.32 KiB (-0.03%)

@gauthierpetetin gauthierpetetin added area-performance Issues relating to slowness of app, cpu usage, and/or blank screens. type-tech-debt Technical debt labels Oct 9, 2024
@metamaskbot
Copy link
Collaborator

Builds ready [789eb79]
Page Load Metrics (1815 ± 96 ms)
PlatformPageMetricMin (ms)Max (ms)Average (ms)StandardDeviation (ms)MarginOfError (ms)
ChromeHomefirstPaint42625951754377181
domContentLoaded15892362177318488
load16082450181519996
domInteractive267443168
Bundle size diffs [🚀 Bundle size reduced!]
  • background: 0 Bytes (0.00%)
  • ui: 3.66 KiB (0.05%)
  • common: -2.32 KiB (-0.03%)

ui/components/app/toast-master/toast-master.js Outdated Show resolved Hide resolved
ui/components/app/toast-master/toast-master.js Outdated Show resolved Hide resolved
ui/components/app/toast-master/toast-master.js Outdated Show resolved Hide resolved
ui/components/app/toast-master/toast-master.js Outdated Show resolved Hide resolved
ui/components/app/toast-master/toast-master-selectors.ts Outdated Show resolved Hide resolved
ui/components/app/toast-master/toast-master-selectors.ts Outdated Show resolved Hide resolved
ui/pages/routes/routes-helpers.js Outdated Show resolved Hide resolved
ui/components/app/toast-master/toast-master.js Outdated Show resolved Hide resolved
Comment on lines 99 to 119
export function setNewPrivacyPolicyToastShownDate(time: number) {
submitRequestToBackground('setNewPrivacyPolicyToastShownDate', [time]);
}

export function setNewPrivacyPolicyToastClickedOrClosed() {
submitRequestToBackground('setNewPrivacyPolicyToastClickedOrClosed');
}

export function getNftDetectionEnablementToast(state: State): boolean {
return Boolean(state.appState?.showNftDetectionEnablementToast);
}

export function setShowNftDetectionEnablementToast(
value: boolean,
): PayloadAction<string | ReactFragment | undefined> {
return {
type: SHOW_NFT_DETECTION_ENABLEMENT_TOAST,
payload: value,
};
}
Copy link
Member

@matthewwalsh0 matthewwalsh0 Oct 11, 2024

Choose a reason for hiding this comment

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

Definitely would recommend a separate file for these, but no the submitRequestToBackground ones are not technically Redux actions as we're not dispatching them on a reducer, ultimately just utils.

Comment on lines 99 to 106
export function setNewPrivacyPolicyToastShownDate(time: number) {
submitRequestToBackground('setNewPrivacyPolicyToastShownDate', [time]);
}

export function setNewPrivacyPolicyToastClickedOrClosed() {
submitRequestToBackground('setNewPrivacyPolicyToastClickedOrClosed');
}
Copy link
Member

@matthewwalsh0 matthewwalsh0 Oct 11, 2024

Choose a reason for hiding this comment

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

My understanding is also that any requests to the background rely on the JSON-RPC API over the port streams and are therefore asynchronous, meaning we should await since submitRequestToBackground returns a promise.

I recall a contribution guideline that also recommends awaiting even when returning as it results in better stack traces in the event of errors.

https://github.com/MetaMask/contributor-docs/blob/main/docs/javascript.md#await-promises-before-returning-them

@HowardBraham HowardBraham force-pushed the refactor-routes branch 2 times, most recently from ab9ab05 to 553bbe2 Compare October 12, 2024 06:05
@metamaskbot
Copy link
Collaborator

Builds ready [553bbe2]
Page Load Metrics (1858 ± 70 ms)
PlatformPageMetricMin (ms)Max (ms)Average (ms)StandardDeviation (ms)MarginOfError (ms)
ChromeHomefirstPaint30021461774359172
domContentLoaded1601199118029646
load16252327185814770
domInteractive1884522010
Bundle size diffs [🚀 Bundle size reduced!]
  • background: 0 Bytes (0.00%)
  • ui: 3.66 KiB (0.05%)
  • common: -2.31 KiB (-0.03%)

@HowardBraham HowardBraham force-pushed the refactor-routes branch 3 times, most recently from 965772e to 7a54b36 Compare October 14, 2024 10:03
@metamaskbot
Copy link
Collaborator

Builds ready [7a54b36]
Page Load Metrics (2010 ± 314 ms)
PlatformPageMetricMin (ms)Max (ms)Average (ms)StandardDeviation (ms)MarginOfError (ms)
ChromeHomefirstPaint48046071876703338
domContentLoaded156444361978650312
load161444972010653314
domInteractive26143502914
Bundle size diffs [🚀 Bundle size reduced!]
  • background: 0 Bytes (0.00%)
  • ui: 6.1 KiB (0.08%)
  • common: -2.68 KiB (-0.04%)

@metamaskbot
Copy link
Collaborator

Builds ready [2aad6bf]
Page Load Metrics (1779 ± 41 ms)
PlatformPageMetricMin (ms)Max (ms)Average (ms)StandardDeviation (ms)MarginOfError (ms)
ChromeHomefirstPaint1655197617879144
domContentLoaded1644195317567838
load1656196117798541
domInteractive247845189
Bundle size diffs [🚀 Bundle size reduced!]
  • background: 0 Bytes (0.00%)
  • ui: 5.36 KiB (0.07%)
  • common: -2.68 KiB (-0.04%)

@metamaskbot
Copy link
Collaborator

Builds ready [e82745e]
Page Load Metrics (1794 ± 74 ms)
PlatformPageMetricMin (ms)Max (ms)Average (ms)StandardDeviation (ms)MarginOfError (ms)
ChromeHomefirstPaint35423861659461221
domContentLoaded16512143176110651
load16602385179415574
domInteractive179741188
backgroundConnect9205345024
firstReactRender46211984220
getState494212713
initialActions00000
loadScripts1188154612897837
setupStore10100352814
uiStartup180730412056302145

Copy link
Contributor

@MajorLift MajorLift left a comment

Choose a reason for hiding this comment

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

LGTM!

@HowardBraham HowardBraham added this pull request to the merge queue Oct 22, 2024
@github-merge-queue github-merge-queue bot removed this pull request from the merge queue due to failed status checks Oct 22, 2024
@HowardBraham HowardBraham added this pull request to the merge queue Oct 22, 2024
@github-merge-queue github-merge-queue bot removed this pull request from the merge queue due to failed status checks Oct 22, 2024
@HowardBraham HowardBraham added this pull request to the merge queue Oct 22, 2024
Merged via the queue into develop with commit a1a62aa Oct 22, 2024
75 of 76 checks passed
@HowardBraham HowardBraham deleted the refactor-routes branch October 22, 2024 20:56
@github-actions github-actions bot locked and limited conversation to collaborators Oct 22, 2024
@metamaskbot metamaskbot added the release-12.7.0 Issue or pull request that will be included in release 12.7.0 label Oct 22, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area-performance Issues relating to slowness of app, cpu usage, and/or blank screens. release-12.7.0 Issue or pull request that will be included in release 12.7.0 team-tiger Tiger team (for tech debt reduction + performance improvements) type-tech-debt Technical debt
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants