From 0928ee9bdf57034ed0494348b10184e31997c5a1 Mon Sep 17 00:00:00 2001 From: Howard Braham Date: Thu, 3 Oct 2024 03:52:58 -0700 Subject: [PATCH 01/34] kinda stable but some layouts are off --- ui/pages/routes/isolated.js | 242 ++++++++++++++ ui/pages/routes/routes.component.js | 474 +--------------------------- ui/pages/routes/toasts.js | 202 ++++++++++++ 3 files changed, 459 insertions(+), 459 deletions(-) create mode 100644 ui/pages/routes/isolated.js create mode 100644 ui/pages/routes/toasts.js diff --git a/ui/pages/routes/isolated.js b/ui/pages/routes/isolated.js new file mode 100644 index 000000000000..80e2dd395bb8 --- /dev/null +++ b/ui/pages/routes/isolated.js @@ -0,0 +1,242 @@ +import { matchPath } from 'react-router-dom'; +import { getEnvironmentType } from '../../../app/scripts/lib/util'; +import { + ENVIRONMENT_TYPE_NOTIFICATION, + ENVIRONMENT_TYPE_POPUP, +} from '../../../shared/constants/app'; +import { + CONFIRM_TRANSACTION_ROUTE, + CONFIRMATION_V_NEXT_ROUTE, + CONNECT_ROUTE, + CONNECTIONS, + DEFAULT_ROUTE, + NOTIFICATIONS_ROUTE, + ONBOARDING_ROUTE, + ONBOARDING_UNLOCK_ROUTE, + PERMISSIONS, + REVIEW_PERMISSIONS, + SEND_ROUTE, + SNAPS_VIEW_ROUTE, + SWAPS_ROUTE, +} from '../../helpers/constants/routes'; + +export function isConfirmTransactionRoute(pathname) { + return Boolean( + matchPath(pathname, { + path: CONFIRM_TRANSACTION_ROUTE, + exact: false, + }), + ); +} + +export function getTheme() { + const { theme } = this.props; + if (theme === ThemeType.os) { + if (window?.matchMedia('(prefers-color-scheme: dark)')?.matches) { + return ThemeType.dark; + } + return ThemeType.light; + } + return theme; +} + +export function setTheme(theme) { + // const theme = useSelector((state) => getThemeSelector(state)); + + console.log('theme', theme); + + document.documentElement.setAttribute('data-theme', theme); +} + +export function onSwapsPage() { + const { location } = this.props; + return Boolean( + matchPath(location.pathname, { path: SWAPS_ROUTE, exact: false }), + ); +} + +export function onHomeScreen() { + const { location } = this.props; + return location.pathname === DEFAULT_ROUTE; +} + +export function onConfirmPage() { + const { location } = this.props; + return Boolean( + matchPath(location.pathname, { + path: CONFIRM_TRANSACTION_ROUTE, + exact: false, + }), + ); +} + +export function onInitializationUnlockPage() { + const { location } = this.props; + return Boolean( + matchPath(location.pathname, { + path: ONBOARDING_UNLOCK_ROUTE, + exact: true, + }), + ); +} + +export function showOnboardingHeader(location) { + return Boolean( + matchPath(location.pathname, { + path: ONBOARDING_ROUTE, + exact: false, + }), + ); +} + +export function toggleMetamaskActive() { + if (this.props.isUnlocked) { + // currently active: deactivate + this.props.lockMetaMask(); + } else { + // currently inactive: redirect to password box + const passwordBox = document.querySelector('input[type=password]'); + if (!passwordBox) { + return; + } + passwordBox.focus(); + } +} + +export function getConnectingLabel(loadingMessage) { + if (loadingMessage) { + return loadingMessage; + } + const { providerType, providerId } = this.props; + const { t } = this.context; + + switch (providerType) { + case NETWORK_TYPES.MAINNET: + return t('connectingToMainnet'); + case NETWORK_TYPES.GOERLI: + return t('connectingToGoerli'); + case NETWORK_TYPES.SEPOLIA: + return t('connectingToSepolia'); + case NETWORK_TYPES.LINEA_GOERLI: + return t('connectingToLineaGoerli'); + case NETWORK_TYPES.LINEA_SEPOLIA: + return t('connectingToLineaSepolia'); + case NETWORK_TYPES.LINEA_MAINNET: + return t('connectingToLineaMainnet'); + default: + return t('connectingTo', [providerId]); + } +} + +export function hideAppHeader(props) { + const { location } = props; + + const isNotificationsPage = Boolean( + matchPath(location.pathname, { + path: `${NOTIFICATIONS_ROUTE}`, + exact: false, + }), + ); + + if (isNotificationsPage) { + return true; + } + + const isInitializing = Boolean( + matchPath(location.pathname, { + path: ONBOARDING_ROUTE, + exact: false, + }), + ); + + if (isInitializing && !this.onInitializationUnlockPage()) { + return true; + } + + const windowType = getEnvironmentType(); + + if (windowType === ENVIRONMENT_TYPE_NOTIFICATION) { + return true; + } + + const isPermissionsPage = Boolean( + matchPath(location.pathname, { + path: PERMISSIONS, + exact: false, + }), + ); + + if (isPermissionsPage) { + return true; + } + + const isConnectionsPage = Boolean( + matchPath(location.pathname, { + path: CONNECTIONS, + exact: false, + }), + ); + + if (isConnectionsPage) { + return true; + } + + const isReviewPermissionsPgae = Boolean( + matchPath(location.pathname, { + path: REVIEW_PERMISSIONS, + exact: false, + }), + ); + + if (isReviewPermissionsPgae) { + return true; + } + + if (windowType === ENVIRONMENT_TYPE_POPUP && this.onConfirmPage()) { + return true; + } + + const isHandlingPermissionsRequest = Boolean( + matchPath(location.pathname, { + path: CONNECT_ROUTE, + exact: false, + }), + ); + + const isMultichainSend = Boolean( + matchPath(location.pathname, { + path: SEND_ROUTE, + exact: false, + }), + ); + if (isMultichainSend) { + return true; + } + + const isSnapsHome = Boolean( + matchPath(location.pathname, { + path: SNAPS_VIEW_ROUTE, + exact: false, + }), + ); + if (isSnapsHome) { + return true; + } + + const isHandlingAddEthereumChainRequest = Boolean( + matchPath(location.pathname, { + path: CONFIRMATION_V_NEXT_ROUTE, + exact: false, + }), + ); + + return ( + isHandlingPermissionsRequest || + isHandlingAddEthereumChainRequest || + isConfirmTransactionRoute(location.pathname) + ); +} + +export function getShowAutoNetworkSwitchTest(props) { + return props.switchedNetworkDetails && !props.neverShowSwitchedNetworkMessage; +} diff --git a/ui/pages/routes/routes.component.js b/ui/pages/routes/routes.component.js index d59c4b29e0c1..8bfb89da87b2 100644 --- a/ui/pages/routes/routes.component.js +++ b/ui/pages/routes/routes.component.js @@ -38,7 +38,6 @@ import { ToastContainer, Toast, } from '../../components/multichain'; -import { SurveyToast } from '../../components/ui/survey-toast'; import UnlockPage from '../unlock-page'; import Alerts from '../../components/app/alerts'; import Asset from '../asset'; @@ -137,14 +136,14 @@ import { MultichainMetaFoxLogo } from '../../components/multichain/app-header/mu import NetworkConfirmationPopover from '../../components/multichain/network-list-menu/network-confirmation-popover/network-confirmation-popover'; import NftFullImage from '../../components/app/assets/nfts/nft-details/nft-full-image'; import CrossChainSwap from '../bridge'; - -const isConfirmTransactionRoute = (pathname) => - Boolean( - matchPath(pathname, { - path: CONFIRM_TRANSACTION_ROUTE, - exact: false, - }), - ); +import { + getShowAutoNetworkSwitchTest, + hideAppHeader, + isConfirmTransactionRoute, + setTheme, + showOnboardingHeader, +} from './isolated'; +import { updateNewPrivacyPolicyToastDate } from './toasts'; export default class Routes extends Component { static propTypes = { @@ -235,24 +234,8 @@ export default class Routes extends Component { hideConnectAccountToast: false, }; - getTheme() { - const { theme } = this.props; - if (theme === ThemeType.os) { - if (window?.matchMedia('(prefers-color-scheme: dark)')?.matches) { - return ThemeType.dark; - } - return ThemeType.light; - } - return theme; - } - - setTheme() { - const theme = this.getTheme(); - document.documentElement.setAttribute('data-theme', theme); - } - componentDidMount() { - this.updateNewPrivacyPolicyToastDate(); + updateNewPrivacyPolicyToastDate(this.props); } componentDidUpdate(prevProps) { @@ -267,7 +250,7 @@ export default class Routes extends Component { currentExtensionPopupId, } = this.props; if (theme !== prevProps.theme) { - this.setTheme(); + setTheme(theme); } if (prevProps.account?.address !== account?.address) { @@ -326,7 +309,7 @@ export default class Routes extends Component { } }); - this.setTheme(); + setTheme(this.props.theme); } renderRoutes() { @@ -457,394 +440,6 @@ export default class Routes extends Component { return routes; } - onInitializationUnlockPage() { - const { location } = this.props; - return Boolean( - matchPath(location.pathname, { - path: ONBOARDING_UNLOCK_ROUTE, - exact: true, - }), - ); - } - - onConfirmPage() { - const { location } = this.props; - return Boolean( - matchPath(location.pathname, { - path: CONFIRM_TRANSACTION_ROUTE, - exact: false, - }), - ); - } - - onEditTransactionPage() { - return ( - this.props.sendStage === SEND_STAGES.EDIT || - this.props.sendStage === SEND_STAGES.DRAFT || - this.props.sendStage === SEND_STAGES.ADD_RECIPIENT - ); - } - - onSwapsPage() { - const { location } = this.props; - return Boolean( - matchPath(location.pathname, { path: SWAPS_ROUTE, exact: false }), - ); - } - - onHomeScreen() { - const { location } = this.props; - return location.pathname === DEFAULT_ROUTE; - } - - hideAppHeader() { - const { location } = this.props; - - const isCrossChainSwapsPage = Boolean( - matchPath(location.pathname, { - path: `${CROSS_CHAIN_SWAP_ROUTE}`, - exact: false, - }), - ); - - if (isCrossChainSwapsPage) { - return true; - } - - const isNotificationsPage = Boolean( - matchPath(location.pathname, { - path: `${NOTIFICATIONS_ROUTE}`, - exact: false, - }), - ); - - if (isNotificationsPage) { - return true; - } - - const isInitializing = Boolean( - matchPath(location.pathname, { - path: ONBOARDING_ROUTE, - exact: false, - }), - ); - - if (isInitializing && !this.onInitializationUnlockPage()) { - return true; - } - - const windowType = getEnvironmentType(); - - if (windowType === ENVIRONMENT_TYPE_NOTIFICATION) { - return true; - } - - const isPermissionsPage = Boolean( - matchPath(location.pathname, { - path: PERMISSIONS, - exact: false, - }), - ); - - if (isPermissionsPage) { - return true; - } - - const isConnectionsPage = Boolean( - matchPath(location.pathname, { - path: CONNECTIONS, - exact: false, - }), - ); - - if (isConnectionsPage) { - return true; - } - - const isReviewPermissionsPgae = Boolean( - matchPath(location.pathname, { - path: REVIEW_PERMISSIONS, - exact: false, - }), - ); - - if (isReviewPermissionsPgae) { - return true; - } - - if (windowType === ENVIRONMENT_TYPE_POPUP && this.onConfirmPage()) { - return true; - } - - const isHandlingPermissionsRequest = Boolean( - matchPath(location.pathname, { - path: CONNECT_ROUTE, - exact: false, - }), - ); - - const isMultichainSend = Boolean( - matchPath(location.pathname, { - path: SEND_ROUTE, - exact: false, - }), - ); - if (isMultichainSend) { - return true; - } - - const isSnapsHome = Boolean( - matchPath(location.pathname, { - path: SNAPS_VIEW_ROUTE, - exact: false, - }), - ); - if (isSnapsHome) { - return true; - } - - const isHandlingAddEthereumChainRequest = Boolean( - matchPath(location.pathname, { - path: CONFIRMATION_V_NEXT_ROUTE, - exact: false, - }), - ); - - return ( - isHandlingPermissionsRequest || - isHandlingAddEthereumChainRequest || - isConfirmTransactionRoute(this.pathname) - ); - } - - showOnboardingHeader() { - const { location } = this.props; - - return Boolean( - matchPath(location.pathname, { - path: ONBOARDING_ROUTE, - exact: false, - }), - ); - } - - onAppHeaderClick = async () => { - const { prepareToLeaveSwaps } = this.props; - if (this.onSwapsPage()) { - await prepareToLeaveSwaps(); - } - }; - - renderToasts() { - const { t } = this.context; - const { - account, - activeTabOrigin, - addPermittedAccount, - showSurveyToast, - showConnectAccountToast, - showPrivacyPolicyToast, - newPrivacyPolicyToastShownDate, - clearSwitchedNetworkDetails, - setSurveyLinkLastClickedOrClosed, - setNewPrivacyPolicyToastClickedOrClosed, - setSwitchedNetworkNeverShowMessage, - switchedNetworkDetails, - useNftDetection, - showNftEnablementToast, - setHideNftEnablementToast, - isPermittedNetworkToastOpen, - currentNetwork, - } = this.props; - - const showAutoNetworkSwitchToast = this.getShowAutoNetworkSwitchTest(); - const isPrivacyToastRecent = this.getIsPrivacyToastRecent(); - const isPrivacyToastNotShown = !newPrivacyPolicyToastShownDate; - const isEvmAccount = isEvmAccountType(account?.type); - const autoHideToastDelay = 5 * SECOND; - const safeEncodedHost = encodeURIComponent(activeTabOrigin); - - const onAutoHideToast = () => { - setHideNftEnablementToast(false); - }; - if (!this.onHomeScreen()) { - return null; - } - - return ( - - - {showConnectAccountToast && - !this.state.hideConnectAccountToast && - isEvmAccount ? ( - - } - text={this.context.t('accountIsntConnectedToastText', [ - account?.metadata?.name, - getURLHost(activeTabOrigin), - ])} - actionText={this.context.t('connectAccount')} - onActionClick={() => { - // Connect this account - addPermittedAccount(activeTabOrigin, account.address); - // Use setTimeout to prevent React re-render from - // hiding the tooltip - setTimeout(() => { - // Trigger a mouseenter on the header's connection icon - // to display the informative connection tooltip - document - .querySelector( - '[data-testid="connection-menu"] [data-tooltipped]', - ) - ?.dispatchEvent(new CustomEvent('mouseenter', {})); - }, 250 * MILLISECOND); - }} - onClose={() => this.setState({ hideConnectAccountToast: true })} - /> - ) : null} - {showSurveyToast && ( - - } - text={t('surveyTitle')} - actionText={t('surveyConversion')} - onActionClick={() => { - global.platform.openTab({ - url: SURVEY_LINK, - }); - setSurveyLinkLastClickedOrClosed(Date.now()); - }} - onClose={() => { - setSurveyLinkLastClickedOrClosed(Date.now()); - }} - /> - )} - {showPrivacyPolicyToast && - (isPrivacyToastRecent || isPrivacyToastNotShown) && ( - - } - text={t('newPrivacyPolicyTitle')} - actionText={t('newPrivacyPolicyActionButton')} - onActionClick={() => { - global.platform.openTab({ - url: PRIVACY_POLICY_LINK, - }); - setNewPrivacyPolicyToastClickedOrClosed(); - }} - onClose={() => { - setNewPrivacyPolicyToastClickedOrClosed(); - }} - /> - )} - {showAutoNetworkSwitchToast ? ( - - } - text={this.context.t('switchedNetworkToastMessage', [ - switchedNetworkDetails.nickname, - getURLHost(switchedNetworkDetails.origin), - ])} - actionText={this.context.t('switchedNetworkToastDecline')} - onActionClick={() => setSwitchedNetworkNeverShowMessage()} - onClose={() => clearSwitchedNetworkDetails()} - /> - ) : null} - {showNftEnablementToast && useNftDetection ? ( - - } - text={this.context.t('nftAutoDetectionEnabled')} - borderRadius={BorderRadius.LG} - textVariant={TextVariant.bodyMd} - autoHideTime={autoHideToastDelay} - onAutoHideToast={onAutoHideToast} - /> - ) : null} - - {isPermittedNetworkToastOpen ? ( - - } - text={this.context.t('permittedChainToastUpdate', [ - getURLHost(activeTabOrigin), - currentNetwork?.nickname, - ])} - actionText={this.context.t('editPermissions')} - onActionClick={() => { - this.props.hidePermittedNetworkToast(); - this.props.history.push( - `${REVIEW_PERMISSIONS}/${safeEncodedHost}`, - ); - }} - onClose={() => this.props.hidePermittedNetworkToast()} - /> - ) : null} - - ); - } - - updateNewPrivacyPolicyToastDate() { - const { - showPrivacyPolicyToast, - newPrivacyPolicyToastShownDate, - setNewPrivacyPolicyToastShownDate, - } = this.props; - - if (showPrivacyPolicyToast && !newPrivacyPolicyToastShownDate) { - setNewPrivacyPolicyToastShownDate(Date.now()); - } - } - - getIsPrivacyToastRecent() { - const { newPrivacyPolicyToastShownDate } = this.props; - - const currentDate = new Date(); - const oneDayInMilliseconds = 24 * 60 * 60 * 1000; - const newPrivacyPolicyToastShownDateObj = new Date( - newPrivacyPolicyToastShownDate, - ); - const toastWasShownLessThanADayAgo = - currentDate - newPrivacyPolicyToastShownDateObj < oneDayInMilliseconds; - - return toastWasShownLessThanADayAgo; - } - - getShowAutoNetworkSwitchTest() { - return ( - this.props.switchedNetworkDetails && - !this.props.neverShowSwitchedNetworkMessage - ); - } - render() { const { isLoading, @@ -928,7 +523,7 @@ export default class Routes extends Component { ); ///: END:ONLY_INCLUDE_IF - const showAutoNetworkSwitchToast = this.getShowAutoNetworkSwitchTest(); + const showAutoNetworkSwitchToast = getShowAutoNetworkSwitchTest(this.props); return (
- {!this.hideAppHeader() && } + {!hideAppHeader(this.props) && } {isConfirmTransactionRoute(this.pathname) && } - {this.showOnboardingHeader() && } + {showOnboardingHeader(location) && } { ///: BEGIN:ONLY_INCLUDE_IF(build-mmi) isUnlocked ? : null @@ -1007,47 +602,8 @@ export default class Routes extends Component { {this.renderRoutes()} {isUnlocked ? : null} - {this.renderToasts()} + {/* TODO: put back {this.renderToasts()} */}
); } - - toggleMetamaskActive() { - if (this.props.isUnlocked) { - // currently active: deactivate - this.props.lockMetaMask(); - } else { - // currently inactive: redirect to password box - const passwordBox = document.querySelector('input[type=password]'); - if (!passwordBox) { - return; - } - passwordBox.focus(); - } - } - - getConnectingLabel(loadingMessage) { - if (loadingMessage) { - return loadingMessage; - } - const { providerType, providerId } = this.props; - const { t } = this.context; - - switch (providerType) { - case NETWORK_TYPES.MAINNET: - return t('connectingToMainnet'); - case NETWORK_TYPES.GOERLI: - return t('connectingToGoerli'); - case NETWORK_TYPES.SEPOLIA: - return t('connectingToSepolia'); - case NETWORK_TYPES.LINEA_GOERLI: - return t('connectingToLineaGoerli'); - case NETWORK_TYPES.LINEA_SEPOLIA: - return t('connectingToLineaSepolia'); - case NETWORK_TYPES.LINEA_MAINNET: - return t('connectingToLineaMainnet'); - default: - return t('connectingTo', [providerId]); - } - } } diff --git a/ui/pages/routes/toasts.js b/ui/pages/routes/toasts.js new file mode 100644 index 000000000000..dfe464966aad --- /dev/null +++ b/ui/pages/routes/toasts.js @@ -0,0 +1,202 @@ +import { SurveyToast } from '../../components/ui/survey-toast'; + +export function renderToasts() { + const { t } = this.context; + const { + account, + activeTabOrigin, + addPermittedAccount, + showSurveyToast, + showConnectAccountToast, + showPrivacyPolicyToast, + newPrivacyPolicyToastShownDate, + clearSwitchedNetworkDetails, + setSurveyLinkLastClickedOrClosed, + setNewPrivacyPolicyToastClickedOrClosed, + setSwitchedNetworkNeverShowMessage, + switchedNetworkDetails, + useNftDetection, + showNftEnablementToast, + setHideNftEnablementToast, + isPermittedNetworkToastOpen, + currentNetwork, + } = this.props; + + const showAutoNetworkSwitchToast = getShowAutoNetworkSwitchTest(); + const isPrivacyToastRecent = this.getIsPrivacyToastRecent(); + const isPrivacyToastNotShown = !newPrivacyPolicyToastShownDate; + const isEvmAccount = isEvmAccountType(account?.type); + const autoHideToastDelay = 5 * SECOND; + const safeEncodedHost = encodeURIComponent(activeTabOrigin); + + const onAutoHideToast = () => { + setHideNftEnablementToast(false); + }; + if (!this.onHomeScreen()) { + return null; + } + + return ( + + + {showConnectAccountToast && + !this.state.hideConnectAccountToast && + isEvmAccount ? ( + + } + text={this.context.t('accountIsntConnectedToastText', [ + account?.metadata?.name, + getURLHost(activeTabOrigin), + ])} + actionText={this.context.t('connectAccount')} + onActionClick={() => { + // Connect this account + addPermittedAccount(activeTabOrigin, account.address); + // Use setTimeout to prevent React re-render from + // hiding the tooltip + setTimeout(() => { + // Trigger a mouseenter on the header's connection icon + // to display the informative connection tooltip + document + .querySelector( + '[data-testid="connection-menu"] [data-tooltipped]', + ) + ?.dispatchEvent(new CustomEvent('mouseenter', {})); + }, 250 * MILLISECOND); + }} + onClose={() => this.setState({ hideConnectAccountToast: true })} + /> + ) : null} + {showSurveyToast && ( + + } + text={t('surveyTitle')} + actionText={t('surveyConversion')} + onActionClick={() => { + global.platform.openTab({ + url: SURVEY_LINK, + }); + setSurveyLinkLastClickedOrClosed(Date.now()); + }} + onClose={() => { + setSurveyLinkLastClickedOrClosed(Date.now()); + }} + /> + )} + {showPrivacyPolicyToast && + (isPrivacyToastRecent || isPrivacyToastNotShown) && ( + + } + text={t('newPrivacyPolicyTitle')} + actionText={t('newPrivacyPolicyActionButton')} + onActionClick={() => { + global.platform.openTab({ + url: PRIVACY_POLICY_LINK, + }); + setNewPrivacyPolicyToastClickedOrClosed(); + }} + onClose={() => { + setNewPrivacyPolicyToastClickedOrClosed(); + }} + /> + )} + {showAutoNetworkSwitchToast ? ( + + } + text={this.context.t('switchedNetworkToastMessage', [ + switchedNetworkDetails.nickname, + getURLHost(switchedNetworkDetails.origin), + ])} + actionText={this.context.t('switchedNetworkToastDecline')} + onActionClick={() => setSwitchedNetworkNeverShowMessage()} + onClose={() => clearSwitchedNetworkDetails()} + /> + ) : null} + {showNftEnablementToast && useNftDetection ? ( + + } + text={this.context.t('nftAutoDetectionEnabled')} + borderRadius={BorderRadius.LG} + textVariant={TextVariant.bodyMd} + autoHideTime={autoHideToastDelay} + onAutoHideToast={onAutoHideToast} + /> + ) : null} + + {process.env.CHAIN_PERMISSIONS && isPermittedNetworkToastOpen ? ( + + } + text={this.context.t('permittedChainToastUpdate', [ + getURLHost(activeTabOrigin), + currentNetwork?.nickname, + ])} + actionText={this.context.t('editPermissions')} + onActionClick={() => { + this.props.hidePermittedNetworkToast(); + this.props.history.push(`${REVIEW_PERMISSIONS}/${safeEncodedHost}`); + }} + onClose={() => this.props.hidePermittedNetworkToast()} + /> + ) : null} + + ); +} + +export function updateNewPrivacyPolicyToastDate(props) { + const { + showPrivacyPolicyToast, + newPrivacyPolicyToastShownDate, + setNewPrivacyPolicyToastShownDate, + } = props; + + if (showPrivacyPolicyToast && !newPrivacyPolicyToastShownDate) { + setNewPrivacyPolicyToastShownDate(Date.now()); + } +} + +export function getIsPrivacyToastRecent() { + const { newPrivacyPolicyToastShownDate } = this.props; + + const currentDate = new Date(); + const oneDayInMilliseconds = 24 * 60 * 60 * 1000; + const newPrivacyPolicyToastShownDateObj = new Date( + newPrivacyPolicyToastShownDate, + ); + const toastWasShownLessThanADayAgo = + currentDate - newPrivacyPolicyToastShownDateObj < oneDayInMilliseconds; + + return toastWasShownLessThanADayAgo; +} From d0895c34f5de4306f0b8d5ab17bc0eb4a2648840 Mon Sep 17 00:00:00 2001 From: Howard Braham Date: Thu, 3 Oct 2024 14:36:30 -0700 Subject: [PATCH 02/34] stable, fixing some toasts --- ui/pages/routes/isolated.js | 20 ++++------- ui/pages/routes/routes.component.js | 4 +-- ui/pages/routes/toasts.js | 55 +++++++++++++++++++---------- 3 files changed, 45 insertions(+), 34 deletions(-) diff --git a/ui/pages/routes/isolated.js b/ui/pages/routes/isolated.js index 80e2dd395bb8..36fe19140562 100644 --- a/ui/pages/routes/isolated.js +++ b/ui/pages/routes/isolated.js @@ -1,15 +1,16 @@ import { matchPath } from 'react-router-dom'; +// eslint-disable-next-line import/no-restricted-paths import { getEnvironmentType } from '../../../app/scripts/lib/util'; import { ENVIRONMENT_TYPE_NOTIFICATION, ENVIRONMENT_TYPE_POPUP, } from '../../../shared/constants/app'; +import { ThemeType } from '../../../shared/constants/preferences'; import { CONFIRM_TRANSACTION_ROUTE, CONFIRMATION_V_NEXT_ROUTE, CONNECT_ROUTE, CONNECTIONS, - DEFAULT_ROUTE, NOTIFICATIONS_ROUTE, ONBOARDING_ROUTE, ONBOARDING_UNLOCK_ROUTE, @@ -29,8 +30,7 @@ export function isConfirmTransactionRoute(pathname) { ); } -export function getTheme() { - const { theme } = this.props; +export function getThemeFromRawTheme(theme) { if (theme === ThemeType.os) { if (window?.matchMedia('(prefers-color-scheme: dark)')?.matches) { return ThemeType.dark; @@ -41,11 +41,10 @@ export function getTheme() { } export function setTheme(theme) { - // const theme = useSelector((state) => getThemeSelector(state)); - - console.log('theme', theme); - - document.documentElement.setAttribute('data-theme', theme); + document.documentElement.setAttribute( + 'data-theme', + getThemeFromRawTheme(theme), + ); } export function onSwapsPage() { @@ -55,11 +54,6 @@ export function onSwapsPage() { ); } -export function onHomeScreen() { - const { location } = this.props; - return location.pathname === DEFAULT_ROUTE; -} - export function onConfirmPage() { const { location } = this.props; return Boolean( diff --git a/ui/pages/routes/routes.component.js b/ui/pages/routes/routes.component.js index 8bfb89da87b2..c11f654a86fd 100644 --- a/ui/pages/routes/routes.component.js +++ b/ui/pages/routes/routes.component.js @@ -143,7 +143,7 @@ import { setTheme, showOnboardingHeader, } from './isolated'; -import { updateNewPrivacyPolicyToastDate } from './toasts'; +import { renderToasts, updateNewPrivacyPolicyToastDate } from './toasts'; export default class Routes extends Component { static propTypes = { @@ -602,7 +602,7 @@ export default class Routes extends Component { {this.renderRoutes()} {isUnlocked ? : null} - {/* TODO: put back {this.renderToasts()} */} + {renderToasts(this.props, this.context)} ); } diff --git a/ui/pages/routes/toasts.js b/ui/pages/routes/toasts.js index dfe464966aad..3dcec27fb8c1 100644 --- a/ui/pages/routes/toasts.js +++ b/ui/pages/routes/toasts.js @@ -1,7 +1,19 @@ +import { isEvmAccountType } from '@metamask/keyring-api'; +import React from 'react'; +import { MILLISECOND, SECOND } from '../../../shared/constants/time'; +import { Toast, ToastContainer } from '../../components/multichain'; import { SurveyToast } from '../../components/ui/survey-toast'; +import { DEFAULT_ROUTE } from '../../helpers/constants/routes'; +import { getShowAutoNetworkSwitchTest } from './isolated'; +import { + AvatarAccount, + AvatarAccountSize, +} from '../../components/component-library'; +import { BorderColor } from '../../helpers/constants/design-system'; +import { getURLHost } from '../../helpers/utils/util'; -export function renderToasts() { - const { t } = this.context; +export function renderToasts(props, context) { + const { t } = context; const { account, activeTabOrigin, @@ -20,10 +32,12 @@ export function renderToasts() { setHideNftEnablementToast, isPermittedNetworkToastOpen, currentNetwork, - } = this.props; + } = props; - const showAutoNetworkSwitchToast = getShowAutoNetworkSwitchTest(); - const isPrivacyToastRecent = this.getIsPrivacyToastRecent(); + const showAutoNetworkSwitchToast = getShowAutoNetworkSwitchTest(props); + const isPrivacyToastRecent = getIsPrivacyToastRecent( + props.newPrivacyPolicyToastShownDate, + ); const isPrivacyToastNotShown = !newPrivacyPolicyToastShownDate; const isEvmAccount = isEvmAccountType(account?.type); const autoHideToastDelay = 5 * SECOND; @@ -32,7 +46,7 @@ export function renderToasts() { const onAutoHideToast = () => { setHideNftEnablementToast(false); }; - if (!this.onHomeScreen()) { + if (!onHomeScreen(props)) { return null; } @@ -52,11 +66,11 @@ export function renderToasts() { borderColor={BorderColor.transparent} /> } - text={this.context.t('accountIsntConnectedToastText', [ + text={t('accountIsntConnectedToastText', [ account?.metadata?.name, getURLHost(activeTabOrigin), ])} - actionText={this.context.t('connectAccount')} + actionText={t('connectAccount')} onActionClick={() => { // Connect this account addPermittedAccount(activeTabOrigin, account.address); @@ -125,11 +139,11 @@ export function renderToasts() { name={switchedNetworkDetails?.nickname} /> } - text={this.context.t('switchedNetworkToastMessage', [ + text={t('switchedNetworkToastMessage', [ switchedNetworkDetails.nickname, getURLHost(switchedNetworkDetails.origin), ])} - actionText={this.context.t('switchedNetworkToastDecline')} + actionText={t('switchedNetworkToastDecline')} onActionClick={() => setSwitchedNetworkNeverShowMessage()} onClose={() => clearSwitchedNetworkDetails()} /> @@ -140,7 +154,7 @@ export function renderToasts() { startAdornment={ } - text={this.context.t('nftAutoDetectionEnabled')} + text={t('nftAutoDetectionEnabled')} borderRadius={BorderRadius.LG} textVariant={TextVariant.bodyMd} autoHideTime={autoHideToastDelay} @@ -159,16 +173,16 @@ export function renderToasts() { name={currentNetwork?.nickname} /> } - text={this.context.t('permittedChainToastUpdate', [ + text={t('permittedChainToastUpdate', [ getURLHost(activeTabOrigin), currentNetwork?.nickname, ])} - actionText={this.context.t('editPermissions')} + actionText={t('editPermissions')} onActionClick={() => { - this.props.hidePermittedNetworkToast(); - this.props.history.push(`${REVIEW_PERMISSIONS}/${safeEncodedHost}`); + props.hidePermittedNetworkToast(); + props.history.push(`${REVIEW_PERMISSIONS}/${safeEncodedHost}`); }} - onClose={() => this.props.hidePermittedNetworkToast()} + onClose={() => props.hidePermittedNetworkToast()} /> ) : null} @@ -187,9 +201,7 @@ export function updateNewPrivacyPolicyToastDate(props) { } } -export function getIsPrivacyToastRecent() { - const { newPrivacyPolicyToastShownDate } = this.props; - +export function getIsPrivacyToastRecent(newPrivacyPolicyToastShownDate) { const currentDate = new Date(); const oneDayInMilliseconds = 24 * 60 * 60 * 1000; const newPrivacyPolicyToastShownDateObj = new Date( @@ -200,3 +212,8 @@ export function getIsPrivacyToastRecent() { return toastWasShownLessThanADayAgo; } + +function onHomeScreen(props) { + const { location } = props; + return location.pathname === DEFAULT_ROUTE; +} From f9695dd4f6dc745a64aa0d0e8a3f17d4d5e12144 Mon Sep 17 00:00:00 2001 From: Howard Braham Date: Fri, 4 Oct 2024 20:07:45 -0700 Subject: [PATCH 03/34] lint --- ui/pages/routes/isolated.js | 43 +++++++++++++++-------------- ui/pages/routes/routes.component.js | 7 ++++- ui/pages/routes/toasts.js | 24 ++++++++++++---- 3 files changed, 48 insertions(+), 26 deletions(-) diff --git a/ui/pages/routes/isolated.js b/ui/pages/routes/isolated.js index 36fe19140562..21072b380241 100644 --- a/ui/pages/routes/isolated.js +++ b/ui/pages/routes/isolated.js @@ -5,6 +5,7 @@ import { ENVIRONMENT_TYPE_NOTIFICATION, ENVIRONMENT_TYPE_POPUP, } from '../../../shared/constants/app'; +import { NETWORK_TYPES } from '../../../shared/constants/network'; import { ThemeType } from '../../../shared/constants/preferences'; import { CONFIRM_TRANSACTION_ROUTE, @@ -18,7 +19,6 @@ import { REVIEW_PERMISSIONS, SEND_ROUTE, SNAPS_VIEW_ROUTE, - SWAPS_ROUTE, } from '../../helpers/constants/routes'; export function isConfirmTransactionRoute(pathname) { @@ -47,15 +47,18 @@ export function setTheme(theme) { ); } -export function onSwapsPage() { - const { location } = this.props; - return Boolean( - matchPath(location.pathname, { path: SWAPS_ROUTE, exact: false }), - ); -} - -export function onConfirmPage() { - const { location } = this.props; +// function onSwapsPage(props) { +// const { location } = props; +// return Boolean( +// matchPath(location.pathname, { +// path: SWAPS_ROUTE, +// exact: false, +// }), +// ); +// } + +function onConfirmPage(props) { + const { location } = props; return Boolean( matchPath(location.pathname, { path: CONFIRM_TRANSACTION_ROUTE, @@ -64,8 +67,8 @@ export function onConfirmPage() { ); } -export function onInitializationUnlockPage() { - const { location } = this.props; +function onInitializationUnlockPage(props) { + const { location } = props; return Boolean( matchPath(location.pathname, { path: ONBOARDING_UNLOCK_ROUTE, @@ -83,10 +86,10 @@ export function showOnboardingHeader(location) { ); } -export function toggleMetamaskActive() { - if (this.props.isUnlocked) { +export function toggleMetamaskActive(props) { + if (props.isUnlocked) { // currently active: deactivate - this.props.lockMetaMask(); + props.lockMetaMask(); } else { // currently inactive: redirect to password box const passwordBox = document.querySelector('input[type=password]'); @@ -97,12 +100,12 @@ export function toggleMetamaskActive() { } } -export function getConnectingLabel(loadingMessage) { +export function getConnectingLabel(loadingMessage, props, context) { if (loadingMessage) { return loadingMessage; } - const { providerType, providerId } = this.props; - const { t } = this.context; + const { providerType, providerId } = props; + const { t } = context; switch (providerType) { case NETWORK_TYPES.MAINNET: @@ -143,7 +146,7 @@ export function hideAppHeader(props) { }), ); - if (isInitializing && !this.onInitializationUnlockPage()) { + if (isInitializing && !onInitializationUnlockPage(props)) { return true; } @@ -186,7 +189,7 @@ export function hideAppHeader(props) { return true; } - if (windowType === ENVIRONMENT_TYPE_POPUP && this.onConfirmPage()) { + if (windowType === ENVIRONMENT_TYPE_POPUP && onConfirmPage(props)) { return true; } diff --git a/ui/pages/routes/routes.component.js b/ui/pages/routes/routes.component.js index c11f654a86fd..d8644d38d4ff 100644 --- a/ui/pages/routes/routes.component.js +++ b/ui/pages/routes/routes.component.js @@ -1,3 +1,7 @@ +/* eslint-disable no-unused-vars -- TODO remove */ +/* eslint-disable react/no-unused-prop-types -- TODO remove */ +/* eslint-disable react/no-unused-state -- TODO remove */ + import classnames from 'classnames'; import PropTypes from 'prop-types'; import React, { Component } from 'react'; @@ -137,6 +141,7 @@ import NetworkConfirmationPopover from '../../components/multichain/network-list import NftFullImage from '../../components/app/assets/nfts/nft-details/nft-full-image'; import CrossChainSwap from '../bridge'; import { + getConnectingLabel, getShowAutoNetworkSwitchTest, hideAppHeader, isConfirmTransactionRoute, @@ -484,7 +489,7 @@ export default class Routes extends Component { const loadMessage = loadingMessage || isNetworkLoading - ? this.getConnectingLabel(loadingMessage) + ? getConnectingLabel(loadingMessage, this.props, this.context) : null; // Conditions for displaying the Send route diff --git a/ui/pages/routes/toasts.js b/ui/pages/routes/toasts.js index 3dcec27fb8c1..c1dd8c94c054 100644 --- a/ui/pages/routes/toasts.js +++ b/ui/pages/routes/toasts.js @@ -1,16 +1,30 @@ +/* eslint-disable @babel/no-invalid-this -- TODO remove */ + import { isEvmAccountType } from '@metamask/keyring-api'; import React from 'react'; import { MILLISECOND, SECOND } from '../../../shared/constants/time'; -import { Toast, ToastContainer } from '../../components/multichain'; -import { SurveyToast } from '../../components/ui/survey-toast'; -import { DEFAULT_ROUTE } from '../../helpers/constants/routes'; -import { getShowAutoNetworkSwitchTest } from './isolated'; +import { PRIVACY_POLICY_LINK, SURVEY_LINK } from '../../../shared/lib/ui-utils'; import { AvatarAccount, AvatarAccountSize, + AvatarNetwork, + Icon, + IconName, } from '../../components/component-library'; -import { BorderColor } from '../../helpers/constants/design-system'; +import { Toast, ToastContainer } from '../../components/multichain'; +import { SurveyToast } from '../../components/ui/survey-toast'; +import { + BorderColor, + BorderRadius, + IconColor, + TextVariant, +} from '../../helpers/constants/design-system'; +import { + DEFAULT_ROUTE, + REVIEW_PERMISSIONS, +} from '../../helpers/constants/routes'; import { getURLHost } from '../../helpers/utils/util'; +import { getShowAutoNetworkSwitchTest } from './isolated'; export function renderToasts(props, context) { const { t } = context; From 66c10fd3997acbf789a32a6078176274173506c5 Mon Sep 17 00:00:00 2001 From: Howard Braham Date: Mon, 7 Oct 2024 22:49:58 -0700 Subject: [PATCH 04/34] getShowSurveyToast --- ui/pages/routes/routes.component.js | 5 ++-- ui/pages/routes/routes.container.js | 2 -- .../routes/{toasts.js => toast-master.js} | 29 +++++++++++++++++-- ui/selectors/selectors.js | 19 ------------ 4 files changed, 29 insertions(+), 26 deletions(-) rename ui/pages/routes/{toasts.js => toast-master.js} (88%) diff --git a/ui/pages/routes/routes.component.js b/ui/pages/routes/routes.component.js index d8644d38d4ff..21604a5d3100 100644 --- a/ui/pages/routes/routes.component.js +++ b/ui/pages/routes/routes.component.js @@ -148,7 +148,7 @@ import { setTheme, showOnboardingHeader, } from './isolated'; -import { renderToasts, updateNewPrivacyPolicyToastDate } from './toasts'; +import { ToastMaster, updateNewPrivacyPolicyToastDate } from './toast-master'; export default class Routes extends Component { static propTypes = { @@ -216,7 +216,6 @@ export default class Routes extends Component { totalUnapprovedConfirmationCount: PropTypes.number.isRequired, currentExtensionPopupId: PropTypes.number, useRequestQueue: PropTypes.bool, - showSurveyToast: PropTypes.bool.isRequired, showPrivacyPolicyToast: PropTypes.bool.isRequired, newPrivacyPolicyToastShownDate: PropTypes.number, setSurveyLinkLastClickedOrClosed: PropTypes.func.isRequired, @@ -607,7 +606,7 @@ export default class Routes extends Component { {this.renderRoutes()} {isUnlocked ? : null} - {renderToasts(this.props, this.context)} + ); } diff --git a/ui/pages/routes/routes.container.js b/ui/pages/routes/routes.container.js index 2c26f0daa0b5..efd8ad5b970d 100644 --- a/ui/pages/routes/routes.container.js +++ b/ui/pages/routes/routes.container.js @@ -22,7 +22,6 @@ import { getNeverShowSwitchedNetworkMessage, getNetworkToAutomaticallySwitchTo, getNumberOfAllUnapprovedTransactionsAndMessages, - getShowSurveyToast, getNewPrivacyPolicyToastShownDate, getShowPrivacyPolicyToast, getUseRequestQueue, @@ -143,7 +142,6 @@ function mapStateToProps(state) { useRequestQueue: getUseRequestQueue(state), newPrivacyPolicyToastShownDate: getNewPrivacyPolicyToastShownDate(state), showPrivacyPolicyToast: getShowPrivacyPolicyToast(state), - showSurveyToast: getShowSurveyToast(state), ///: BEGIN:ONLY_INCLUDE_IF(keyring-snaps) isShowKeyringSnapRemovalResultModal: state.appState.showKeyringRemovalSnapModal, diff --git a/ui/pages/routes/toasts.js b/ui/pages/routes/toast-master.js similarity index 88% rename from ui/pages/routes/toasts.js rename to ui/pages/routes/toast-master.js index c1dd8c94c054..93aaa46d2321 100644 --- a/ui/pages/routes/toasts.js +++ b/ui/pages/routes/toast-master.js @@ -25,14 +25,19 @@ import { } from '../../helpers/constants/routes'; import { getURLHost } from '../../helpers/utils/util'; import { getShowAutoNetworkSwitchTest } from './isolated'; +import { useSelector } from 'react-redux'; +import { + SURVEY_DATE, + SURVEY_END_TIME, + SURVEY_START_TIME, +} from '../../helpers/constants/survey'; -export function renderToasts(props, context) { +export function ToastMaster({ props, context }) { const { t } = context; const { account, activeTabOrigin, addPermittedAccount, - showSurveyToast, showConnectAccountToast, showPrivacyPolicyToast, newPrivacyPolicyToastShownDate, @@ -57,6 +62,8 @@ export function renderToasts(props, context) { const autoHideToastDelay = 5 * SECOND; const safeEncodedHost = encodeURIComponent(activeTabOrigin); + const showSurveyToast = useSelector(getShowSurveyToast); + const onAutoHideToast = () => { setHideNftEnablementToast(false); }; @@ -231,3 +238,21 @@ function onHomeScreen(props) { const { location } = props; return location.pathname === DEFAULT_ROUTE; } + +/** + * Determines if the survey toast should be shown based on the current time, survey start and end times, and whether the survey link was last clicked or closed. + * + * @param {*} state - The application state containing the necessary survey data. + * @returns {boolean} True if the current time is between the survey start and end times and the survey link was not last clicked or closed. False otherwise. + */ +function getShowSurveyToast(state) { + if (state.metamask.surveyLinkLastClickedOrClosed) { + return false; + } + + const startTime = new Date(`${SURVEY_DATE} ${SURVEY_START_TIME}`).getTime(); + const endTime = new Date(`${SURVEY_DATE} ${SURVEY_END_TIME}`).getTime(); + const now = Date.now(); + + return now > startTime && now < endTime; +} diff --git a/ui/selectors/selectors.js b/ui/selectors/selectors.js index 09c062012731..8c77bc3119db 100644 --- a/ui/selectors/selectors.js +++ b/ui/selectors/selectors.js @@ -97,11 +97,6 @@ import { } from '../../shared/modules/conversion.utils'; import { BackgroundColor } from '../helpers/constants/design-system'; import { NOTIFICATION_DROP_LEDGER_FIREFOX } from '../../shared/notifications'; -import { - SURVEY_DATE, - SURVEY_END_TIME, - SURVEY_START_TIME, -} from '../helpers/constants/survey'; import { PRIVACY_POLICY_DATE } from '../helpers/constants/privacy-policy'; import { ENVIRONMENT_TYPE_POPUP } from '../../shared/constants/app'; import { MultichainNativeAssets } from '../../shared/constants/multichain/assets'; @@ -1989,20 +1984,6 @@ export function getShowTermsOfUse(state) { ); } -/** - * Determines if the survey toast should be shown based on the current time, survey start and end times, and whether the survey link was last clicked or closed. - * - * @param {*} state - The application state containing the necessary survey data. - * @returns {boolean} True if the current time is between the survey start and end times and the survey link was not last clicked or closed. False otherwise. - */ -export function getShowSurveyToast(state) { - const { surveyLinkLastClickedOrClosed } = state.metamask; - const startTime = new Date(`${SURVEY_DATE} ${SURVEY_START_TIME}`).getTime(); - const endTime = new Date(`${SURVEY_DATE} ${SURVEY_END_TIME}`).getTime(); - const now = Date.now(); - return now > startTime && now < endTime && !surveyLinkLastClickedOrClosed; -} - /** * Determines if the privacy policy toast should be shown based on the current date and whether the new privacy policy toast was clicked or closed. * From 28a379aef778f6ca60c81cc77ef267092d553b88 Mon Sep 17 00:00:00 2001 From: Howard Braham Date: Mon, 7 Oct 2024 23:51:08 -0700 Subject: [PATCH 05/34] moving things to toast-master.js --- ui/pages/routes/routes.component.js | 10 ----- ui/pages/routes/routes.container.js | 12 ------ ui/pages/routes/toast-master.js | 62 +++++++++++++++++++++++------ 3 files changed, 50 insertions(+), 34 deletions(-) diff --git a/ui/pages/routes/routes.component.js b/ui/pages/routes/routes.component.js index 21604a5d3100..b9aa8f912a70 100644 --- a/ui/pages/routes/routes.component.js +++ b/ui/pages/routes/routes.component.js @@ -1,6 +1,5 @@ /* eslint-disable no-unused-vars -- TODO remove */ /* eslint-disable react/no-unused-prop-types -- TODO remove */ -/* eslint-disable react/no-unused-state -- TODO remove */ import classnames from 'classnames'; import PropTypes from 'prop-types'; @@ -155,7 +154,6 @@ export default class Routes extends Component { currentCurrency: PropTypes.string, account: PropTypes.object, activeTabOrigin: PropTypes.string, - showConnectAccountToast: PropTypes.bool.isRequired, setCurrentCurrencyToUSD: PropTypes.func, isLoading: PropTypes.bool, loadingMessage: PropTypes.string, @@ -234,10 +232,6 @@ export default class Routes extends Component { metricsEvent: PropTypes.func, }; - state = { - hideConnectAccountToast: false, - }; - componentDidMount() { updateNewPrivacyPolicyToastDate(this.props); } @@ -257,10 +251,6 @@ export default class Routes extends Component { setTheme(theme); } - if (prevProps.account?.address !== account?.address) { - this.setState({ hideConnectAccountToast: false }); - } - // Automatically switch the network if the user // no longer has unapproved transactions and they // should be on a different network for the diff --git a/ui/pages/routes/routes.container.js b/ui/pages/routes/routes.container.js index efd8ad5b970d..f9af2f467363 100644 --- a/ui/pages/routes/routes.container.js +++ b/ui/pages/routes/routes.container.js @@ -17,7 +17,6 @@ import { ///: END:ONLY_INCLUDE_IF getShowExtensionInFullSizeView, getSelectedAccount, - getPermittedAccountsForCurrentTab, getSwitchedNetworkDetails, getNeverShowSwitchedNetworkMessage, getNetworkToAutomaticallySwitchTo, @@ -57,7 +56,6 @@ import { pageChanged } from '../../ducks/history/history'; import { prepareToLeaveSwaps } from '../../ducks/swaps/swaps'; import { getSendStage } from '../../ducks/send'; import { - getAlertEnabledness, getIsUnlocked, getProviderConfig, } from '../../ducks/metamask/metamask'; @@ -73,18 +71,9 @@ function mapStateToProps(state) { // If there is more than one connected account to activeTabOrigin, // *BUT* the current account is not one of them, show the banner - const allowShowAccountSetting = getAlertEnabledness(state).unconnectedAccount; const account = getSelectedAccount(state); const activeTabOrigin = activeTab?.origin; - const connectedAccounts = getPermittedAccountsForCurrentTab(state); const currentNetwork = getCurrentNetwork(state); - const showConnectAccountToast = Boolean( - allowShowAccountSetting && - account && - activeTabOrigin && - connectedAccounts.length > 0 && - !connectedAccounts.find((address) => address === account.address), - ); const networkToAutomaticallySwitchTo = getNetworkToAutomaticallySwitchTo(state); @@ -97,7 +86,6 @@ function mapStateToProps(state) { alertOpen, alertMessage, account, - showConnectAccountToast, activeTabOrigin, textDirection: state.metamask.textDirection, isLoading, diff --git a/ui/pages/routes/toast-master.js b/ui/pages/routes/toast-master.js index 93aaa46d2321..4e3d6fbaaa50 100644 --- a/ui/pages/routes/toast-master.js +++ b/ui/pages/routes/toast-master.js @@ -1,7 +1,8 @@ -/* eslint-disable @babel/no-invalid-this -- TODO remove */ +/* eslint-disable react/prop-types -- TODO: upgrade to TypeScript */ import { isEvmAccountType } from '@metamask/keyring-api'; -import React from 'react'; +import React, { useState } from 'react'; +import { useSelector } from 'react-redux'; import { MILLISECOND, SECOND } from '../../../shared/constants/time'; import { PRIVACY_POLICY_LINK, SURVEY_LINK } from '../../../shared/lib/ui-utils'; import { @@ -24,21 +25,27 @@ import { REVIEW_PERMISSIONS, } from '../../helpers/constants/routes'; import { getURLHost } from '../../helpers/utils/util'; -import { getShowAutoNetworkSwitchTest } from './isolated'; -import { useSelector } from 'react-redux'; import { SURVEY_DATE, SURVEY_END_TIME, SURVEY_START_TIME, } from '../../helpers/constants/survey'; +import { getAlertEnabledness } from '../../ducks/metamask/metamask'; +import { + getPermittedAccountsForCurrentTab, + getSelectedAccount, +} from '../../selectors'; +import { getShowAutoNetworkSwitchTest } from './isolated'; + +// Allow comparison with a previous value, in order to detect changes +// (This pattern only works if ToastMaster is a singleton) +let prevAccountAddress; export function ToastMaster({ props, context }) { const { t } = context; const { - account, activeTabOrigin, addPermittedAccount, - showConnectAccountToast, showPrivacyPolicyToast, newPrivacyPolicyToastShownDate, clearSwitchedNetworkDetails, @@ -54,16 +61,31 @@ export function ToastMaster({ props, context }) { } = props; const showAutoNetworkSwitchToast = getShowAutoNetworkSwitchTest(props); + console.log('switchedNetworkDetails', switchedNetworkDetails); + console.log('showAutoNetworkSwitchToast', showAutoNetworkSwitchToast); + const isPrivacyToastRecent = getIsPrivacyToastRecent( props.newPrivacyPolicyToastShownDate, ); const isPrivacyToastNotShown = !newPrivacyPolicyToastShownDate; - const isEvmAccount = isEvmAccountType(account?.type); const autoHideToastDelay = 5 * SECOND; const safeEncodedHost = encodeURIComponent(activeTabOrigin); const showSurveyToast = useSelector(getShowSurveyToast); + const [hideConnectAccountToast, setHideConnectAccountToast] = useState(false); + const account = useSelector(getSelectedAccount); + + // If the account has changed, allow the connect account toast again + if (account.address !== prevAccountAddress) { + prevAccountAddress = account.address; + setHideConnectAccountToast(false); + } + + const showConnectAccountToast = useSelector((state) => + getShowConnectAccountToast(state, account), + ); + const onAutoHideToast = () => { setHideNftEnablementToast(false); }; @@ -74,9 +96,7 @@ export function ToastMaster({ props, context }) { return ( - {showConnectAccountToast && - !this.state.hideConnectAccountToast && - isEvmAccount ? ( + {!hideConnectAccountToast && showConnectAccountToast && ( this.setState({ hideConnectAccountToast: true })} + onClose={() => setHideConnectAccountToast(true)} /> - ) : null} + )} {showSurveyToast && ( )} + {/* TODO fix showAutoNetworkSwitchToast */} {showAutoNetworkSwitchToast ? ( startTime && now < endTime; } + +// If there is more than one connected account to activeTabOrigin, +// *BUT* the current account is not one of them, show the banner +function getShowConnectAccountToast(state, account) { + const allowShowAccountSetting = getAlertEnabledness(state).unconnectedAccount; + const connectedAccounts = getPermittedAccountsForCurrentTab(state); + const isEvmAccount = isEvmAccountType(account?.type); + + return ( + allowShowAccountSetting && + account && + state.activeTab?.origin && + isEvmAccount && + connectedAccounts.length > 0 && + !connectedAccounts.some((address) => address === account.address) + ); +} From 4a8232a517415ad8b7eb00828896db651508e210 Mon Sep 17 00:00:00 2001 From: Howard Braham Date: Tue, 8 Oct 2024 01:46:30 -0700 Subject: [PATCH 06/34] moved the test --- ui/pages/routes/toast-master.js | 3 +- ui/pages/routes/toast-master.test.ts | 54 ++++++++++++++++++++++++++++ ui/selectors/selectors.test.js | 52 --------------------------- 3 files changed, 56 insertions(+), 53 deletions(-) create mode 100644 ui/pages/routes/toast-master.test.ts diff --git a/ui/pages/routes/toast-master.js b/ui/pages/routes/toast-master.js index 4e3d6fbaaa50..413bb45b5c07 100644 --- a/ui/pages/routes/toast-master.js +++ b/ui/pages/routes/toast-master.js @@ -262,11 +262,12 @@ function onHomeScreen(props) { /** * Determines if the survey toast should be shown based on the current time, survey start and end times, and whether the survey link was last clicked or closed. + * (This function is exported only so the test can access it.) * * @param {*} state - The application state containing the necessary survey data. * @returns {boolean} True if the current time is between the survey start and end times and the survey link was not last clicked or closed. False otherwise. */ -function getShowSurveyToast(state) { +export function getShowSurveyToast(state) { if (state.metamask.surveyLinkLastClickedOrClosed) { return false; } diff --git a/ui/pages/routes/toast-master.test.ts b/ui/pages/routes/toast-master.test.ts new file mode 100644 index 000000000000..090727035cf3 --- /dev/null +++ b/ui/pages/routes/toast-master.test.ts @@ -0,0 +1,54 @@ +import { SURVEY_DATE, SURVEY_GMT } from '../../helpers/constants/survey'; +import { getShowSurveyToast } from './toast-master'; + +describe('#getShowSurveyToast', () => { + const realDateNow = Date.now; + + afterEach(() => { + Date.now = realDateNow; + }); + + it('shows the survey link when not yet seen and within time bounds', () => { + Date.now = () => + new Date(`${SURVEY_DATE} 12:25:00 ${SURVEY_GMT}`).getTime(); + const result = getShowSurveyToast({ + metamask: { + surveyLinkLastClickedOrClosed: null, + }, + }); + expect(result).toStrictEqual(true); + }); + + it('does not show the survey link when seen and within time bounds', () => { + Date.now = () => + new Date(`${SURVEY_DATE} 12:25:00 ${SURVEY_GMT}`).getTime(); + const result = getShowSurveyToast({ + metamask: { + surveyLinkLastClickedOrClosed: 123456789, + }, + }); + expect(result).toStrictEqual(false); + }); + + it('does not show the survey link before time bounds', () => { + Date.now = () => + new Date(`${SURVEY_DATE} 11:25:00 ${SURVEY_GMT}`).getTime(); + const result = getShowSurveyToast({ + metamask: { + surveyLinkLastClickedOrClosed: null, + }, + }); + expect(result).toStrictEqual(false); + }); + + it('does not show the survey link after time bounds', () => { + Date.now = () => + new Date(`${SURVEY_DATE} 14:25:00 ${SURVEY_GMT}`).getTime(); + const result = getShowSurveyToast({ + metamask: { + surveyLinkLastClickedOrClosed: null, + }, + }); + expect(result).toStrictEqual(false); + }); +}); diff --git a/ui/selectors/selectors.test.js b/ui/selectors/selectors.test.js index 8d71048e0924..11b67a4f44b2 100644 --- a/ui/selectors/selectors.test.js +++ b/ui/selectors/selectors.test.js @@ -1406,58 +1406,6 @@ describe('Selectors', () => { }); }); - describe('#getShowSurveyToast', () => { - const realDateNow = Date.now; - - afterEach(() => { - Date.now = realDateNow; - }); - - it('shows the survey link when not yet seen and within time bounds', () => { - Date.now = () => - new Date(`${SURVEY_DATE} 12:25:00 ${SURVEY_GMT}`).getTime(); - const result = selectors.getShowSurveyToast({ - metamask: { - surveyLinkLastClickedOrClosed: null, - }, - }); - expect(result).toStrictEqual(true); - }); - - it('does not show the survey link when seen and within time bounds', () => { - Date.now = () => - new Date(`${SURVEY_DATE} 12:25:00 ${SURVEY_GMT}`).getTime(); - const result = selectors.getShowSurveyToast({ - metamask: { - surveyLinkLastClickedOrClosed: 123456789, - }, - }); - expect(result).toStrictEqual(false); - }); - - it('does not show the survey link before time bounds', () => { - Date.now = () => - new Date(`${SURVEY_DATE} 11:25:00 ${SURVEY_GMT}`).getTime(); - const result = selectors.getShowSurveyToast({ - metamask: { - surveyLinkLastClickedOrClosed: null, - }, - }); - expect(result).toStrictEqual(false); - }); - - it('does not show the survey link after time bounds', () => { - Date.now = () => - new Date(`${SURVEY_DATE} 14:25:00 ${SURVEY_GMT}`).getTime(); - const result = selectors.getShowSurveyToast({ - metamask: { - surveyLinkLastClickedOrClosed: null, - }, - }); - expect(result).toStrictEqual(false); - }); - }); - describe('#getShowPrivacyPolicyToast', () => { let dateNowSpy; From b56dd9cb6f9b4a48f8cfa2987bcfa2211ee84ff8 Mon Sep 17 00:00:00 2001 From: Howard Braham Date: Tue, 8 Oct 2024 19:43:58 -0700 Subject: [PATCH 07/34] showPrivacyPolicyToast --- ui/pages/routes/routes.component.js | 10 +- ui/pages/routes/routes.container.js | 10 -- ui/pages/routes/toast-master-selectors.js | 91 ++++++++++++++ ui/pages/routes/toast-master.js | 109 +++++------------ ui/pages/routes/toast-master.test.ts | 142 +++++++++++++++++++++- ui/selectors/selectors.js | 26 ---- ui/selectors/selectors.test.js | 136 --------------------- ui/store/actions.ts | 14 --- 8 files changed, 265 insertions(+), 273 deletions(-) create mode 100644 ui/pages/routes/toast-master-selectors.js diff --git a/ui/pages/routes/routes.component.js b/ui/pages/routes/routes.component.js index b9aa8f912a70..253bfdd31768 100644 --- a/ui/pages/routes/routes.component.js +++ b/ui/pages/routes/routes.component.js @@ -147,7 +147,7 @@ import { setTheme, showOnboardingHeader, } from './isolated'; -import { ToastMaster, updateNewPrivacyPolicyToastDate } from './toast-master'; +import { ToastMaster } from './toast-master'; export default class Routes extends Component { static propTypes = { @@ -214,12 +214,8 @@ export default class Routes extends Component { totalUnapprovedConfirmationCount: PropTypes.number.isRequired, currentExtensionPopupId: PropTypes.number, useRequestQueue: PropTypes.bool, - showPrivacyPolicyToast: PropTypes.bool.isRequired, - newPrivacyPolicyToastShownDate: PropTypes.number, setSurveyLinkLastClickedOrClosed: PropTypes.func.isRequired, - setNewPrivacyPolicyToastShownDate: PropTypes.func.isRequired, clearEditedNetwork: PropTypes.func.isRequired, - setNewPrivacyPolicyToastClickedOrClosed: PropTypes.func.isRequired, ///: BEGIN:ONLY_INCLUDE_IF(keyring-snaps) isShowKeyringSnapRemovalResultModal: PropTypes.bool.isRequired, hideShowKeyringSnapRemovalResultModal: PropTypes.func.isRequired, @@ -232,10 +228,6 @@ export default class Routes extends Component { metricsEvent: PropTypes.func, }; - componentDidMount() { - updateNewPrivacyPolicyToastDate(this.props); - } - componentDidUpdate(prevProps) { const { theme, diff --git a/ui/pages/routes/routes.container.js b/ui/pages/routes/routes.container.js index f9af2f467363..2d797e7b220c 100644 --- a/ui/pages/routes/routes.container.js +++ b/ui/pages/routes/routes.container.js @@ -21,8 +21,6 @@ import { getNeverShowSwitchedNetworkMessage, getNetworkToAutomaticallySwitchTo, getNumberOfAllUnapprovedTransactionsAndMessages, - getNewPrivacyPolicyToastShownDate, - getShowPrivacyPolicyToast, getUseRequestQueue, getUseNftDetection, getNftDetectionEnablementToast, @@ -40,8 +38,6 @@ import { hideDeprecatedNetworkModal, addPermittedAccount, setSurveyLinkLastClickedOrClosed, - setNewPrivacyPolicyToastClickedOrClosed, - setNewPrivacyPolicyToastShownDate, automaticallySwitchNetwork, clearSwitchedNetworkDetails, neverShowSwitchedNetworkMessage, @@ -128,8 +124,6 @@ function mapStateToProps(state) { neverShowSwitchedNetworkMessage: getNeverShowSwitchedNetworkMessage(state), currentExtensionPopupId: state.metamask.currentExtensionPopupId, useRequestQueue: getUseRequestQueue(state), - newPrivacyPolicyToastShownDate: getNewPrivacyPolicyToastShownDate(state), - showPrivacyPolicyToast: getShowPrivacyPolicyToast(state), ///: BEGIN:ONLY_INCLUDE_IF(keyring-snaps) isShowKeyringSnapRemovalResultModal: state.appState.showKeyringRemovalSnapModal, @@ -161,10 +155,6 @@ function mapDispatchToProps(dispatch) { dispatch(automaticallySwitchNetwork(networkId, selectedTabOrigin)), setSurveyLinkLastClickedOrClosed: (time) => dispatch(setSurveyLinkLastClickedOrClosed(time)), - setNewPrivacyPolicyToastClickedOrClosed: () => - dispatch(setNewPrivacyPolicyToastClickedOrClosed()), - setNewPrivacyPolicyToastShownDate: (date) => - dispatch(setNewPrivacyPolicyToastShownDate(date)), clearEditedNetwork: () => dispatch(setEditedNetwork()), ///: BEGIN:ONLY_INCLUDE_IF(keyring-snaps) hideShowKeyringSnapRemovalResultModal: () => diff --git a/ui/pages/routes/toast-master-selectors.js b/ui/pages/routes/toast-master-selectors.js new file mode 100644 index 000000000000..d74ddc4e833a --- /dev/null +++ b/ui/pages/routes/toast-master-selectors.js @@ -0,0 +1,91 @@ +import { PRIVACY_POLICY_DATE } from '../../helpers/constants/privacy-policy'; +import { + SURVEY_DATE, + SURVEY_START_TIME, + SURVEY_END_TIME, +} from '../../helpers/constants/survey'; +import { + callBackgroundMethod, + submitRequestToBackground, +} from '../../store/background-connection'; + +/** + * Determines if the survey toast should be shown based on the current time, survey start and end times, and whether the survey link was last clicked or closed. + * + * @param {*} state - The application state containing the necessary survey data. + * @returns {boolean} True if the current time is between the survey start and end times and the survey link was not last clicked or closed. False otherwise. + */ +export function getShowSurveyToast(state) { + if (state.metamask.surveyLinkLastClickedOrClosed) { + return false; + } + + const startTime = new Date(`${SURVEY_DATE} ${SURVEY_START_TIME}`).getTime(); + const endTime = new Date(`${SURVEY_DATE} ${SURVEY_END_TIME}`).getTime(); + const now = Date.now(); + + return now > startTime && now < endTime; +} + +/** + * Determines if the privacy policy toast should be shown based on the current date and whether the new privacy policy toast was clicked or closed. + * + * @param {*} state - The application state containing the privacy policy data. + * @returns {boolean, number} Boolean is True if the toast should be shown, and the number is the date the toast was last shown. + */ +export function getShowPrivacyPolicyToast(state) { + const { newPrivacyPolicyToastClickedOrClosed, onboardingDate } = + state.metamask; + const newPrivacyPolicyDate = new Date(PRIVACY_POLICY_DATE); + const currentDate = new Date(Date.now()); + + const newPrivacyPolicyToastShownDate = + state.metamask.newPrivacyPolicyToastShownDate; + + const showPrivacyPolicyToast = + !newPrivacyPolicyToastClickedOrClosed && + currentDate >= newPrivacyPolicyDate && + getIsPrivacyToastRecent(newPrivacyPolicyToastShownDate) && + // users who onboarded before the privacy policy date should see the notice + // and + // old users who don't have onboardingDate set should see the notice + (onboardingDate < newPrivacyPolicyDate || !onboardingDate); + + return { showPrivacyPolicyToast, newPrivacyPolicyToastShownDate }; +} + +/** + * Returns true if the privacy policy toast was shown either never, or less than a day ago. + * + * @param {number} newPrivacyPolicyToastShownDate + * @returns {boolean} true if the privacy policy toast was shown either never, or less than a day ago + */ +function getIsPrivacyToastRecent(newPrivacyPolicyToastShownDate) { + if (!newPrivacyPolicyToastShownDate) { + return true; + } + + const currentDate = new Date(); + const oneDayInMilliseconds = 24 * 60 * 60 * 1000; + const newPrivacyPolicyToastShownDateObj = new Date( + newPrivacyPolicyToastShownDate, + ); + const toastWasShownLessThanADayAgo = + currentDate - newPrivacyPolicyToastShownDateObj < oneDayInMilliseconds; + + console.log( + 'newPrivacyPolicyToastShownDateObj', + newPrivacyPolicyToastShownDateObj, + ); + console.log('toastWasShownLessThanADayAgo', toastWasShownLessThanADayAgo); + + return toastWasShownLessThanADayAgo; +} + +export function setNewPrivacyPolicyToastShownDate(time) { + submitRequestToBackground('setNewPrivacyPolicyToastShownDate', [time]); +} + +export function setNewPrivacyPolicyToastClickedOrClosed() { + submitRequestToBackground('setNewPrivacyPolicyToastClickedOrClosed'); +} diff --git a/ui/pages/routes/toast-master.js b/ui/pages/routes/toast-master.js index 413bb45b5c07..3bda506f917b 100644 --- a/ui/pages/routes/toast-master.js +++ b/ui/pages/routes/toast-master.js @@ -14,6 +14,7 @@ import { } from '../../components/component-library'; import { Toast, ToastContainer } from '../../components/multichain'; import { SurveyToast } from '../../components/ui/survey-toast'; +import { getAlertEnabledness } from '../../ducks/metamask/metamask'; import { BorderColor, BorderRadius, @@ -25,17 +26,17 @@ import { REVIEW_PERMISSIONS, } from '../../helpers/constants/routes'; import { getURLHost } from '../../helpers/utils/util'; -import { - SURVEY_DATE, - SURVEY_END_TIME, - SURVEY_START_TIME, -} from '../../helpers/constants/survey'; -import { getAlertEnabledness } from '../../ducks/metamask/metamask'; import { getPermittedAccountsForCurrentTab, getSelectedAccount, } from '../../selectors'; import { getShowAutoNetworkSwitchTest } from './isolated'; +import { + getShowPrivacyPolicyToast, + getShowSurveyToast, + setNewPrivacyPolicyToastClickedOrClosed, + setNewPrivacyPolicyToastShownDate, +} from './toast-master-selectors'; // Allow comparison with a previous value, in order to detect changes // (This pattern only works if ToastMaster is a singleton) @@ -46,11 +47,8 @@ export function ToastMaster({ props, context }) { const { activeTabOrigin, addPermittedAccount, - showPrivacyPolicyToast, - newPrivacyPolicyToastShownDate, clearSwitchedNetworkDetails, setSurveyLinkLastClickedOrClosed, - setNewPrivacyPolicyToastClickedOrClosed, setSwitchedNetworkNeverShowMessage, switchedNetworkDetails, useNftDetection, @@ -64,10 +62,9 @@ export function ToastMaster({ props, context }) { console.log('switchedNetworkDetails', switchedNetworkDetails); console.log('showAutoNetworkSwitchToast', showAutoNetworkSwitchToast); - const isPrivacyToastRecent = getIsPrivacyToastRecent( - props.newPrivacyPolicyToastShownDate, - ); - const isPrivacyToastNotShown = !newPrivacyPolicyToastShownDate; + const { showPrivacyPolicyToast, newPrivacyPolicyToastShownDate } = + useSelector(getShowPrivacyPolicyToast); + const autoHideToastDelay = 5 * SECOND; const safeEncodedHost = encodeURIComponent(activeTabOrigin); @@ -93,6 +90,11 @@ export function ToastMaster({ props, context }) { return null; } + // If the privacy policy toast is shown, and there is no date set, set it + if (showPrivacyPolicyToast && !newPrivacyPolicyToastShownDate) { + setNewPrivacyPolicyToastShownDate(Date.now()); + } + return ( @@ -149,27 +151,23 @@ export function ToastMaster({ props, context }) { }} /> )} - {showPrivacyPolicyToast && - (isPrivacyToastRecent || isPrivacyToastNotShown) && ( - - } - text={t('newPrivacyPolicyTitle')} - actionText={t('newPrivacyPolicyActionButton')} - onActionClick={() => { - global.platform.openTab({ - url: PRIVACY_POLICY_LINK, - }); - setNewPrivacyPolicyToastClickedOrClosed(); - }} - onClose={() => { - setNewPrivacyPolicyToastClickedOrClosed(); - }} - /> - )} - {/* TODO fix showAutoNetworkSwitchToast */} + {showPrivacyPolicyToast && ( + + } + text={t('newPrivacyPolicyTitle')} + actionText={t('newPrivacyPolicyActionButton')} + onActionClick={() => { + global.platform.openTab({ + url: PRIVACY_POLICY_LINK, + }); + setNewPrivacyPolicyToastClickedOrClosed(); + }} + onClose={setNewPrivacyPolicyToastClickedOrClosed} + /> + )} {showAutoNetworkSwitchToast ? ( startTime && now < endTime; -} - // If there is more than one connected account to activeTabOrigin, // *BUT* the current account is not one of them, show the banner function getShowConnectAccountToast(state, account) { diff --git a/ui/pages/routes/toast-master.test.ts b/ui/pages/routes/toast-master.test.ts index 090727035cf3..31a57307fe60 100644 --- a/ui/pages/routes/toast-master.test.ts +++ b/ui/pages/routes/toast-master.test.ts @@ -1,5 +1,9 @@ +import { PRIVACY_POLICY_DATE } from '../../helpers/constants/privacy-policy'; import { SURVEY_DATE, SURVEY_GMT } from '../../helpers/constants/survey'; -import { getShowSurveyToast } from './toast-master'; +import { + getShowSurveyToast, + getShowPrivacyPolicyToast, +} from './toast-master-selectors'; describe('#getShowSurveyToast', () => { const realDateNow = Date.now; @@ -52,3 +56,139 @@ describe('#getShowSurveyToast', () => { expect(result).toStrictEqual(false); }); }); + +describe('#getShowPrivacyPolicyToast', () => { + let dateNowSpy: jest.SpyInstance; + + describe('mock one day after', () => { + beforeEach(() => { + const dayAfterPolicyDate = new Date(PRIVACY_POLICY_DATE); + dayAfterPolicyDate.setDate(dayAfterPolicyDate.getDate() + 1); + + dateNowSpy = jest + .spyOn(Date, 'now') + .mockReturnValue(dayAfterPolicyDate.getTime()); + }); + + afterEach(() => { + dateNowSpy.mockRestore(); + }); + + it('shows the privacy policy toast when not yet seen, on or after the policy date, and onboardingDate is before the policy date', () => { + const result = getShowPrivacyPolicyToast({ + metamask: { + newPrivacyPolicyToastClickedOrClosed: null, + onboardingDate: new Date(PRIVACY_POLICY_DATE).setDate( + new Date(PRIVACY_POLICY_DATE).getDate() - 2, + ), + }, + }); + expect(result).toBe(true); + }); + + it('does not show the privacy policy toast when seen, even if on or after the policy date and onboardingDate is before the policy date', () => { + const result = getShowPrivacyPolicyToast({ + metamask: { + newPrivacyPolicyToastClickedOrClosed: true, + onboardingDate: new Date(PRIVACY_POLICY_DATE).setDate( + new Date(PRIVACY_POLICY_DATE).getDate() - 2, + ), + }, + }); + expect(result).toBe(false); + }); + + it('shows the privacy policy toast when not yet seen, on or after the policy date, and onboardingDate is not set', () => { + const result = getShowPrivacyPolicyToast({ + metamask: { + newPrivacyPolicyToastClickedOrClosed: null, + onboardingDate: null, + }, + }); + expect(result).toBe(true); + }); + }); + + describe('mock same day', () => { + beforeEach(() => { + dateNowSpy = jest + .spyOn(Date, 'now') + .mockReturnValue(new Date(PRIVACY_POLICY_DATE).getTime()); + }); + + afterEach(() => { + dateNowSpy.mockRestore(); + }); + + it('shows the privacy policy toast when not yet seen, on or after the policy date, and onboardingDate is before the policy date', () => { + const result = getShowPrivacyPolicyToast({ + metamask: { + newPrivacyPolicyToastClickedOrClosed: null, + onboardingDate: new Date(PRIVACY_POLICY_DATE).setDate( + new Date(PRIVACY_POLICY_DATE).getDate() - 2, + ), + }, + }); + expect(result).toBe(true); + }); + + it('does not show the privacy policy toast when seen, even if on or after the policy date and onboardingDate is before the policy date', () => { + const result = getShowPrivacyPolicyToast({ + metamask: { + newPrivacyPolicyToastClickedOrClosed: true, + onboardingDate: new Date(PRIVACY_POLICY_DATE).setDate( + new Date(PRIVACY_POLICY_DATE).getDate() - 2, + ), + }, + }); + expect(result).toBe(false); + }); + + it('shows the privacy policy toast when not yet seen, on or after the policy date, and onboardingDate is not set', () => { + const result = getShowPrivacyPolicyToast({ + metamask: { + newPrivacyPolicyToastClickedOrClosed: null, + onboardingDate: null, + }, + }); + expect(result).toBe(true); + }); + }); + + describe('mock day before', () => { + beforeEach(() => { + const dayBeforePolicyDate = new Date(PRIVACY_POLICY_DATE); + dayBeforePolicyDate.setDate(dayBeforePolicyDate.getDate() - 1); + + dateNowSpy = jest + .spyOn(Date, 'now') + .mockReturnValue(dayBeforePolicyDate.getTime()); + }); + + afterEach(() => { + dateNowSpy.mockRestore(); + }); + + it('does not show the privacy policy toast before the policy date', () => { + const result = getShowPrivacyPolicyToast({ + metamask: { + newPrivacyPolicyToastClickedOrClosed: null, + onboardingDate: new Date(PRIVACY_POLICY_DATE).setDate( + new Date(PRIVACY_POLICY_DATE).getDate() - 2, + ), + }, + }); + expect(result).toBe(false); + }); + + it('does not show the privacy policy toast before the policy date even if onboardingDate is not set', () => { + const result = getShowPrivacyPolicyToast({ + metamask: { + newPrivacyPolicyToastClickedOrClosed: null, + onboardingDate: null, + }, + }); + expect(result).toBe(false); + }); + }); +}); diff --git a/ui/selectors/selectors.js b/ui/selectors/selectors.js index 8c77bc3119db..ece767e594ef 100644 --- a/ui/selectors/selectors.js +++ b/ui/selectors/selectors.js @@ -97,7 +97,6 @@ import { } from '../../shared/modules/conversion.utils'; import { BackgroundColor } from '../helpers/constants/design-system'; import { NOTIFICATION_DROP_LEDGER_FIREFOX } from '../../shared/notifications'; -import { PRIVACY_POLICY_DATE } from '../helpers/constants/privacy-policy'; import { ENVIRONMENT_TYPE_POPUP } from '../../shared/constants/app'; import { MultichainNativeAssets } from '../../shared/constants/multichain/assets'; // TODO: Remove restricted import @@ -1984,27 +1983,6 @@ export function getShowTermsOfUse(state) { ); } -/** - * Determines if the privacy policy toast should be shown based on the current date and whether the new privacy policy toast was clicked or closed. - * - * @param {*} state - The application state containing the privacy policy data. - * @returns {boolean} True if the current date is on or after the new privacy policy date and the privacy policy toast was not clicked or closed. False otherwise. - */ -export function getShowPrivacyPolicyToast(state) { - const { newPrivacyPolicyToastClickedOrClosed, onboardingDate } = - state.metamask; - const newPrivacyPolicyDate = new Date(PRIVACY_POLICY_DATE); - const currentDate = new Date(Date.now()); - return ( - !newPrivacyPolicyToastClickedOrClosed && - currentDate >= newPrivacyPolicyDate && - // users who onboarded before the privacy policy date should see the notice - // and - // old users who don't have onboardingDate set should see the notice - (onboardingDate < newPrivacyPolicyDate || !onboardingDate) - ); -} - export function getLastViewedUserSurvey(state) { return state.metamask.lastViewedUserSurvey; } @@ -2018,10 +1996,6 @@ export function getShowOutdatedBrowserWarning(state) { return currentTime - outdatedBrowserWarningLastShown >= DAY * 2; } -export function getNewPrivacyPolicyToastShownDate(state) { - return state.metamask.newPrivacyPolicyToastShownDate; -} - export function getOnboardingDate(state) { return state.metamask.onboardingDate; } diff --git a/ui/selectors/selectors.test.js b/ui/selectors/selectors.test.js index 11b67a4f44b2..e55862930df0 100644 --- a/ui/selectors/selectors.test.js +++ b/ui/selectors/selectors.test.js @@ -1406,142 +1406,6 @@ describe('Selectors', () => { }); }); - describe('#getShowPrivacyPolicyToast', () => { - let dateNowSpy; - - describe('mock one day after', () => { - beforeEach(() => { - const dayAfterPolicyDate = new Date(PRIVACY_POLICY_DATE); - dayAfterPolicyDate.setDate(dayAfterPolicyDate.getDate() + 1); - - dateNowSpy = jest - .spyOn(Date, 'now') - .mockReturnValue(dayAfterPolicyDate.getTime()); - }); - - afterEach(() => { - dateNowSpy.mockRestore(); - }); - - it('shows the privacy policy toast when not yet seen, on or after the policy date, and onboardingDate is before the policy date', () => { - const result = selectors.getShowPrivacyPolicyToast({ - metamask: { - newPrivacyPolicyToastClickedOrClosed: null, - onboardingDate: new Date(PRIVACY_POLICY_DATE).setDate( - new Date(PRIVACY_POLICY_DATE).getDate() - 2, - ), - }, - }); - expect(result).toBe(true); - }); - - it('does not show the privacy policy toast when seen, even if on or after the policy date and onboardingDate is before the policy date', () => { - const result = selectors.getShowPrivacyPolicyToast({ - metamask: { - newPrivacyPolicyToastClickedOrClosed: true, - onboardingDate: new Date(PRIVACY_POLICY_DATE).setDate( - new Date(PRIVACY_POLICY_DATE).getDate() - 2, - ), - }, - }); - expect(result).toBe(false); - }); - - it('shows the privacy policy toast when not yet seen, on or after the policy date, and onboardingDate is not set', () => { - const result = selectors.getShowPrivacyPolicyToast({ - metamask: { - newPrivacyPolicyToastClickedOrClosed: null, - onboardingDate: null, - }, - }); - expect(result).toBe(true); - }); - }); - - describe('mock same day', () => { - beforeEach(() => { - dateNowSpy = jest - .spyOn(Date, 'now') - .mockReturnValue(new Date(PRIVACY_POLICY_DATE).getTime()); - }); - - afterEach(() => { - dateNowSpy.mockRestore(); - }); - - it('shows the privacy policy toast when not yet seen, on or after the policy date, and onboardingDate is before the policy date', () => { - const result = selectors.getShowPrivacyPolicyToast({ - metamask: { - newPrivacyPolicyToastClickedOrClosed: null, - onboardingDate: new Date(PRIVACY_POLICY_DATE).setDate( - new Date(PRIVACY_POLICY_DATE).getDate() - 2, - ), - }, - }); - expect(result).toBe(true); - }); - - it('does not show the privacy policy toast when seen, even if on or after the policy date and onboardingDate is before the policy date', () => { - const result = selectors.getShowPrivacyPolicyToast({ - metamask: { - newPrivacyPolicyToastClickedOrClosed: true, - onboardingDate: new Date(PRIVACY_POLICY_DATE).setDate( - new Date(PRIVACY_POLICY_DATE).getDate() - 2, - ), - }, - }); - expect(result).toBe(false); - }); - - it('shows the privacy policy toast when not yet seen, on or after the policy date, and onboardingDate is not set', () => { - const result = selectors.getShowPrivacyPolicyToast({ - metamask: { - newPrivacyPolicyToastClickedOrClosed: null, - onboardingDate: null, - }, - }); - expect(result).toBe(true); - }); - }); - - describe('mock day before', () => { - beforeEach(() => { - const dayBeforePolicyDate = new Date(PRIVACY_POLICY_DATE); - dayBeforePolicyDate.setDate(dayBeforePolicyDate.getDate() - 1); - - dateNowSpy = jest - .spyOn(Date, 'now') - .mockReturnValue(dayBeforePolicyDate.getTime()); - }); - - afterEach(() => { - dateNowSpy.mockRestore(); - }); - - it('does not show the privacy policy toast before the policy date', () => { - const result = selectors.getShowPrivacyPolicyToast({ - metamask: { - newPrivacyPolicyToastClickedOrClosed: null, - onboardingDate: new Date(PRIVACY_POLICY_DATE).setDate( - new Date(PRIVACY_POLICY_DATE).getDate() - 2, - ), - }, - }); - expect(result).toBe(false); - }); - - it('does not show the privacy policy toast before the policy date even if onboardingDate is not set', () => { - const result = selectors.getShowPrivacyPolicyToast({ - metamask: { - newPrivacyPolicyToastClickedOrClosed: null, - onboardingDate: null, - }, - }); - expect(result).toBe(false); - }); - }); - }); - it('#getUpdatedAndSortedAccounts', () => { const pinnedAccountState = { ...mockState, diff --git a/ui/store/actions.ts b/ui/store/actions.ts index 3433a798a9d9..65d0a0a2967c 100644 --- a/ui/store/actions.ts +++ b/ui/store/actions.ts @@ -4257,12 +4257,6 @@ export function setSurveyLinkLastClickedOrClosed(time: number) { }; } -export function setNewPrivacyPolicyToastClickedOrClosed() { - return async () => { - await submitRequestToBackground('setNewPrivacyPolicyToastClickedOrClosed'); - }; -} - export function setLastViewedUserSurvey(id: number) { return async () => { await submitRequestToBackground('setLastViewedUserSurvey', [id]); @@ -4275,14 +4269,6 @@ export function setOnboardingDate() { }; } -export function setNewPrivacyPolicyToastShownDate(time: number) { - return async () => { - await submitRequestToBackground('setNewPrivacyPolicyToastShownDate', [ - time, - ]); - }; -} - export function setOutdatedBrowserWarningLastShown(lastShown: number) { return async () => { await submitRequestToBackground('setOutdatedBrowserWarningLastShown', [ From f4b3232790826cdebe6cf7196ed933525a19fb11 Mon Sep 17 00:00:00 2001 From: Howard Braham Date: Tue, 8 Oct 2024 20:05:31 -0700 Subject: [PATCH 08/34] showNftEnablementToast --- ui/pages/routes/routes.component.js | 2 -- ui/pages/routes/routes.container.js | 6 ------ ui/pages/routes/toast-master-selectors.js | 21 +++++++++++++++++++++ ui/pages/routes/toast-master.js | 19 +++++++++---------- ui/selectors/selectors.js | 4 ---- ui/store/actions.ts | 9 --------- 6 files changed, 30 insertions(+), 31 deletions(-) diff --git a/ui/pages/routes/routes.component.js b/ui/pages/routes/routes.component.js index 253bfdd31768..8465d964e655 100644 --- a/ui/pages/routes/routes.component.js +++ b/ui/pages/routes/routes.component.js @@ -204,8 +204,6 @@ export default class Routes extends Component { switchedNetworkDetails: PropTypes.object, useNftDetection: PropTypes.bool, currentNetwork: PropTypes.object, - showNftEnablementToast: PropTypes.bool, - setHideNftEnablementToast: PropTypes.func.isRequired, clearSwitchedNetworkDetails: PropTypes.func.isRequired, setSwitchedNetworkNeverShowMessage: PropTypes.func.isRequired, networkToAutomaticallySwitchTo: PropTypes.object, diff --git a/ui/pages/routes/routes.container.js b/ui/pages/routes/routes.container.js index 2d797e7b220c..ed0b20d55f8a 100644 --- a/ui/pages/routes/routes.container.js +++ b/ui/pages/routes/routes.container.js @@ -23,7 +23,6 @@ import { getNumberOfAllUnapprovedTransactionsAndMessages, getUseRequestQueue, getUseNftDetection, - getNftDetectionEnablementToast, getCurrentNetwork, } from '../../selectors'; import { @@ -41,7 +40,6 @@ import { automaticallySwitchNetwork, clearSwitchedNetworkDetails, neverShowSwitchedNetworkMessage, - setShowNftDetectionEnablementToast, ///: BEGIN:ONLY_INCLUDE_IF(keyring-snaps) hideKeyringRemovalResultModal, ///: END:ONLY_INCLUDE_IF @@ -76,7 +74,6 @@ function mapStateToProps(state) { const switchedNetworkDetails = getSwitchedNetworkDetails(state); const useNftDetection = getUseNftDetection(state); - const showNftEnablementToast = getNftDetectionEnablementToast(state); return { alertOpen, @@ -116,7 +113,6 @@ function mapStateToProps(state) { isPermittedNetworkToastOpen: state.appState.showPermittedNetworkToastOpen, switchedNetworkDetails, useNftDetection, - showNftEnablementToast, networkToAutomaticallySwitchTo, currentNetwork, totalUnapprovedConfirmationCount: @@ -160,8 +156,6 @@ function mapDispatchToProps(dispatch) { hideShowKeyringSnapRemovalResultModal: () => dispatch(hideKeyringRemovalResultModal()), ///: END:ONLY_INCLUDE_IF - setHideNftEnablementToast: (value) => - dispatch(setShowNftDetectionEnablementToast(value)), }; } diff --git a/ui/pages/routes/toast-master-selectors.js b/ui/pages/routes/toast-master-selectors.js index d74ddc4e833a..7d85fe8966e3 100644 --- a/ui/pages/routes/toast-master-selectors.js +++ b/ui/pages/routes/toast-master-selectors.js @@ -4,6 +4,7 @@ import { SURVEY_START_TIME, SURVEY_END_TIME, } from '../../helpers/constants/survey'; +import { SHOW_NFT_DETECTION_ENABLEMENT_TOAST } from '../../store/actionConstants'; import { callBackgroundMethod, submitRequestToBackground, @@ -89,3 +90,23 @@ export function setNewPrivacyPolicyToastShownDate(time) { export function setNewPrivacyPolicyToastClickedOrClosed() { submitRequestToBackground('setNewPrivacyPolicyToastClickedOrClosed'); } + +export function getNftDetectionEnablementToast(state) { + return state.appState.showNftDetectionEnablementToast; +} + +export function setShowNftDetectionEnablementToast(value) { + return { + type: SHOW_NFT_DETECTION_ENABLEMENT_TOAST, + payload: value, + }; +} + +// export function setShowNftDetectionEnablementToast( +// value: boolean, +// ): PayloadAction { +// return { +// type: actionConstants.SHOW_NFT_DETECTION_ENABLEMENT_TOAST, +// payload: value, +// }; +// } diff --git a/ui/pages/routes/toast-master.js b/ui/pages/routes/toast-master.js index 3bda506f917b..6267d16724e8 100644 --- a/ui/pages/routes/toast-master.js +++ b/ui/pages/routes/toast-master.js @@ -32,10 +32,12 @@ import { } from '../../selectors'; import { getShowAutoNetworkSwitchTest } from './isolated'; import { + getNftDetectionEnablementToast, getShowPrivacyPolicyToast, getShowSurveyToast, setNewPrivacyPolicyToastClickedOrClosed, setNewPrivacyPolicyToastShownDate, + setShowNftDetectionEnablementToast, } from './toast-master-selectors'; // Allow comparison with a previous value, in order to detect changes @@ -52,8 +54,6 @@ export function ToastMaster({ props, context }) { setSwitchedNetworkNeverShowMessage, switchedNetworkDetails, useNftDetection, - showNftEnablementToast, - setHideNftEnablementToast, isPermittedNetworkToastOpen, currentNetwork, } = props; @@ -83,9 +83,8 @@ export function ToastMaster({ props, context }) { getShowConnectAccountToast(state, account), ); - const onAutoHideToast = () => { - setHideNftEnablementToast(false); - }; + const showNftEnablementToast = useSelector(getNftDetectionEnablementToast); + if (!onHomeScreen(props)) { return null; } @@ -168,7 +167,7 @@ export function ToastMaster({ props, context }) { onClose={setNewPrivacyPolicyToastClickedOrClosed} /> )} - {showAutoNetworkSwitchToast ? ( + {showAutoNetworkSwitchToast && ( setSwitchedNetworkNeverShowMessage()} onClose={() => clearSwitchedNetworkDetails()} /> - ) : null} - {showNftEnablementToast && useNftDetection ? ( + )} + {showNftEnablementToast && useNftDetection && ( setShowNftDetectionEnablementToast(false)} /> - ) : null} + )} {process.env.CHAIN_PERMISSIONS && isPermittedNetworkToastOpen ? ( { - return { - type: actionConstants.SHOW_NFT_DETECTION_ENABLEMENT_TOAST, - payload: value, - }; -} - export function setHardwareWalletDefaultHdPath({ device, path, From c1f69ca513eddcd8663043ccc85830592a2ddd1e Mon Sep 17 00:00:00 2001 From: Howard Braham Date: Tue, 8 Oct 2024 20:21:32 -0700 Subject: [PATCH 09/34] useNftDetection --- ui/pages/routes/routes.component.js | 1 - ui/pages/routes/routes.container.js | 4 ---- ui/pages/routes/toast-master-selectors.js | 10 +++------- ui/pages/routes/toast-master.js | 11 ++++++++--- ui/selectors/selectors.test.js | 12 +++++------- 5 files changed, 16 insertions(+), 22 deletions(-) diff --git a/ui/pages/routes/routes.component.js b/ui/pages/routes/routes.component.js index 8465d964e655..ff94d2b6c717 100644 --- a/ui/pages/routes/routes.component.js +++ b/ui/pages/routes/routes.component.js @@ -202,7 +202,6 @@ export default class Routes extends Component { hideDeprecatedNetworkModal: PropTypes.func.isRequired, addPermittedAccount: PropTypes.func.isRequired, switchedNetworkDetails: PropTypes.object, - useNftDetection: PropTypes.bool, currentNetwork: PropTypes.object, clearSwitchedNetworkDetails: PropTypes.func.isRequired, setSwitchedNetworkNeverShowMessage: PropTypes.func.isRequired, diff --git a/ui/pages/routes/routes.container.js b/ui/pages/routes/routes.container.js index ed0b20d55f8a..27395eb5f68d 100644 --- a/ui/pages/routes/routes.container.js +++ b/ui/pages/routes/routes.container.js @@ -22,7 +22,6 @@ import { getNetworkToAutomaticallySwitchTo, getNumberOfAllUnapprovedTransactionsAndMessages, getUseRequestQueue, - getUseNftDetection, getCurrentNetwork, } from '../../selectors'; import { @@ -73,8 +72,6 @@ function mapStateToProps(state) { getNetworkToAutomaticallySwitchTo(state); const switchedNetworkDetails = getSwitchedNetworkDetails(state); - const useNftDetection = getUseNftDetection(state); - return { alertOpen, alertMessage, @@ -112,7 +109,6 @@ function mapStateToProps(state) { isIpfsModalOpen: state.appState.showIpfsModalOpen, isPermittedNetworkToastOpen: state.appState.showPermittedNetworkToastOpen, switchedNetworkDetails, - useNftDetection, networkToAutomaticallySwitchTo, currentNetwork, totalUnapprovedConfirmationCount: diff --git a/ui/pages/routes/toast-master-selectors.js b/ui/pages/routes/toast-master-selectors.js index 7d85fe8966e3..1c1df75048e4 100644 --- a/ui/pages/routes/toast-master-selectors.js +++ b/ui/pages/routes/toast-master-selectors.js @@ -1,14 +1,11 @@ import { PRIVACY_POLICY_DATE } from '../../helpers/constants/privacy-policy'; import { SURVEY_DATE, - SURVEY_START_TIME, SURVEY_END_TIME, + SURVEY_START_TIME, } from '../../helpers/constants/survey'; import { SHOW_NFT_DETECTION_ENABLEMENT_TOAST } from '../../store/actionConstants'; -import { - callBackgroundMethod, - submitRequestToBackground, -} from '../../store/background-connection'; +import { submitRequestToBackground } from '../../store/background-connection'; /** * Determines if the survey toast should be shown based on the current time, survey start and end times, and whether the survey link was last clicked or closed. @@ -40,8 +37,7 @@ export function getShowPrivacyPolicyToast(state) { const newPrivacyPolicyDate = new Date(PRIVACY_POLICY_DATE); const currentDate = new Date(Date.now()); - const newPrivacyPolicyToastShownDate = - state.metamask.newPrivacyPolicyToastShownDate; + const { newPrivacyPolicyToastShownDate } = state.metamask; const showPrivacyPolicyToast = !newPrivacyPolicyToastClickedOrClosed && diff --git a/ui/pages/routes/toast-master.js b/ui/pages/routes/toast-master.js index 6267d16724e8..b5c8094dfe47 100644 --- a/ui/pages/routes/toast-master.js +++ b/ui/pages/routes/toast-master.js @@ -2,7 +2,7 @@ import { isEvmAccountType } from '@metamask/keyring-api'; import React, { useState } from 'react'; -import { useSelector } from 'react-redux'; +import { useDispatch, useSelector } from 'react-redux'; import { MILLISECOND, SECOND } from '../../../shared/constants/time'; import { PRIVACY_POLICY_LINK, SURVEY_LINK } from '../../../shared/lib/ui-utils'; import { @@ -29,6 +29,7 @@ import { getURLHost } from '../../helpers/utils/util'; import { getPermittedAccountsForCurrentTab, getSelectedAccount, + getUseNftDetection, } from '../../selectors'; import { getShowAutoNetworkSwitchTest } from './isolated'; import { @@ -53,11 +54,12 @@ export function ToastMaster({ props, context }) { setSurveyLinkLastClickedOrClosed, setSwitchedNetworkNeverShowMessage, switchedNetworkDetails, - useNftDetection, isPermittedNetworkToastOpen, currentNetwork, } = props; + const dispatch = useDispatch(); + const showAutoNetworkSwitchToast = getShowAutoNetworkSwitchTest(props); console.log('switchedNetworkDetails', switchedNetworkDetails); console.log('showAutoNetworkSwitchToast', showAutoNetworkSwitchToast); @@ -84,6 +86,7 @@ export function ToastMaster({ props, context }) { ); const showNftEnablementToast = useSelector(getNftDetectionEnablementToast); + const useNftDetection = useSelector(getUseNftDetection); if (!onHomeScreen(props)) { return null; @@ -197,7 +200,9 @@ export function ToastMaster({ props, context }) { borderRadius={BorderRadius.LG} textVariant={TextVariant.bodyMd} autoHideTime={autoHideToastDelay} - onAutoHideToast={() => setShowNftDetectionEnablementToast(false)} + onAutoHideToast={() => + dispatch(setShowNftDetectionEnablementToast(false)) + } /> )} diff --git a/ui/selectors/selectors.test.js b/ui/selectors/selectors.test.js index e55862930df0..e29831d043f1 100644 --- a/ui/selectors/selectors.test.js +++ b/ui/selectors/selectors.test.js @@ -1,21 +1,19 @@ -import { deepClone } from '@metamask/snaps-utils'; import { ApprovalType } from '@metamask/controller-utils'; import { BtcAccountType, EthAccountType, EthMethod, } from '@metamask/keyring-api'; +import { deepClone } from '@metamask/snaps-utils'; import { TransactionStatus } from '@metamask/transaction-controller'; -import mockState from '../../test/data/mock-state.json'; +import { ETH_EOA_METHODS } from '../../shared/constants/eth-methods'; import { KeyringType } from '../../shared/constants/keyring'; +import { DeleteRegulationStatus } from '../../shared/constants/metametrics'; import { CHAIN_IDS, NETWORK_TYPES } from '../../shared/constants/network'; -import { SURVEY_DATE, SURVEY_GMT } from '../helpers/constants/survey'; -import { PRIVACY_POLICY_DATE } from '../helpers/constants/privacy-policy'; +import mockState from '../../test/data/mock-state.json'; import { createMockInternalAccount } from '../../test/jest/mocks'; -import { ETH_EOA_METHODS } from '../../shared/constants/eth-methods'; -import { getProviderConfig } from '../ducks/metamask/metamask'; import { mockNetworkState } from '../../test/stub/networks'; -import { DeleteRegulationStatus } from '../../shared/constants/metametrics'; +import { getProviderConfig } from '../ducks/metamask/metamask'; import * as selectors from './selectors'; jest.mock('../../app/scripts/lib/util', () => ({ From 7a86c88e48218d1142158637499f24eb0a2bab3f Mon Sep 17 00:00:00 2001 From: Howard Braham Date: Tue, 8 Oct 2024 23:18:55 -0700 Subject: [PATCH 10/34] PermittedNetworkToast --- ui/pages/routes/routes.component.js | 2 -- ui/pages/routes/routes.container.js | 3 --- ui/pages/routes/toast-master.js | 15 +++++++++------ 3 files changed, 9 insertions(+), 11 deletions(-) diff --git a/ui/pages/routes/routes.component.js b/ui/pages/routes/routes.component.js index ff94d2b6c717..a4921537fe28 100644 --- a/ui/pages/routes/routes.component.js +++ b/ui/pages/routes/routes.component.js @@ -191,8 +191,6 @@ export default class Routes extends Component { accountDetailsAddress: PropTypes.string, isImportNftsModalOpen: PropTypes.bool.isRequired, hideImportNftsModal: PropTypes.func.isRequired, - isPermittedNetworkToastOpen: PropTypes.bool.isRequired, - hidePermittedNetworkToast: PropTypes.func.isRequired, isIpfsModalOpen: PropTypes.bool.isRequired, isBasicConfigurationModalOpen: PropTypes.bool.isRequired, hideIpfsModal: PropTypes.func.isRequired, diff --git a/ui/pages/routes/routes.container.js b/ui/pages/routes/routes.container.js index 27395eb5f68d..7c2c46b55ad0 100644 --- a/ui/pages/routes/routes.container.js +++ b/ui/pages/routes/routes.container.js @@ -43,7 +43,6 @@ import { hideKeyringRemovalResultModal, ///: END:ONLY_INCLUDE_IF setEditedNetwork, - hidePermittedNetworkToast, } from '../../store/actions'; import { pageChanged } from '../../ducks/history/history'; import { prepareToLeaveSwaps } from '../../ducks/swaps/swaps'; @@ -107,7 +106,6 @@ function mapStateToProps(state) { accountDetailsAddress: state.appState.accountDetailsAddress, isImportNftsModalOpen: state.appState.importNftsModal.open, isIpfsModalOpen: state.appState.showIpfsModalOpen, - isPermittedNetworkToastOpen: state.appState.showPermittedNetworkToastOpen, switchedNetworkDetails, networkToAutomaticallySwitchTo, currentNetwork, @@ -135,7 +133,6 @@ function mapDispatchToProps(dispatch) { toggleNetworkMenu: () => dispatch(toggleNetworkMenu()), hideImportNftsModal: () => dispatch(hideImportNftsModal()), hideIpfsModal: () => dispatch(hideIpfsModal()), - hidePermittedNetworkToast: () => dispatch(hidePermittedNetworkToast()), hideImportTokensModal: () => dispatch(hideImportTokensModal()), hideDeprecatedNetworkModal: () => dispatch(hideDeprecatedNetworkModal()), addPermittedAccount: (activeTabOrigin, address) => diff --git a/ui/pages/routes/toast-master.js b/ui/pages/routes/toast-master.js index b5c8094dfe47..4c90d0b17cf9 100644 --- a/ui/pages/routes/toast-master.js +++ b/ui/pages/routes/toast-master.js @@ -40,6 +40,7 @@ import { setNewPrivacyPolicyToastShownDate, setShowNftDetectionEnablementToast, } from './toast-master-selectors'; +import { hidePermittedNetworkToast } from '../../store/actions'; // Allow comparison with a previous value, in order to detect changes // (This pattern only works if ToastMaster is a singleton) @@ -54,7 +55,6 @@ export function ToastMaster({ props, context }) { setSurveyLinkLastClickedOrClosed, setSwitchedNetworkNeverShowMessage, switchedNetworkDetails, - isPermittedNetworkToastOpen, currentNetwork, } = props; @@ -97,6 +97,10 @@ export function ToastMaster({ props, context }) { setNewPrivacyPolicyToastShownDate(Date.now()); } + const isPermittedNetworkToastOpen = useSelector( + (state) => state.appState.showPermittedNetworkToastOpen, + ); + return ( @@ -205,8 +209,7 @@ export function ToastMaster({ props, context }) { } /> )} - - {process.env.CHAIN_PERMISSIONS && isPermittedNetworkToastOpen ? ( + {process.env.CHAIN_PERMISSIONS && isPermittedNetworkToastOpen && ( { - props.hidePermittedNetworkToast(); + dispatch(hidePermittedNetworkToast()); props.history.push(`${REVIEW_PERMISSIONS}/${safeEncodedHost}`); }} - onClose={() => props.hidePermittedNetworkToast()} + onClose={() => dispatch(hidePermittedNetworkToast())} /> - ) : null} + )} ); } From beedaa11131a1c005d25243bf3f37d3333f09290 Mon Sep 17 00:00:00 2001 From: Howard Braham Date: Tue, 8 Oct 2024 23:19:23 -0700 Subject: [PATCH 11/34] remove console --- ui/pages/routes/toast-master-selectors.js | 6 ------ ui/pages/routes/toast-master.js | 2 -- 2 files changed, 8 deletions(-) diff --git a/ui/pages/routes/toast-master-selectors.js b/ui/pages/routes/toast-master-selectors.js index 1c1df75048e4..6f8752c0efa8 100644 --- a/ui/pages/routes/toast-master-selectors.js +++ b/ui/pages/routes/toast-master-selectors.js @@ -70,12 +70,6 @@ function getIsPrivacyToastRecent(newPrivacyPolicyToastShownDate) { const toastWasShownLessThanADayAgo = currentDate - newPrivacyPolicyToastShownDateObj < oneDayInMilliseconds; - console.log( - 'newPrivacyPolicyToastShownDateObj', - newPrivacyPolicyToastShownDateObj, - ); - console.log('toastWasShownLessThanADayAgo', toastWasShownLessThanADayAgo); - return toastWasShownLessThanADayAgo; } diff --git a/ui/pages/routes/toast-master.js b/ui/pages/routes/toast-master.js index 4c90d0b17cf9..a69a8481e362 100644 --- a/ui/pages/routes/toast-master.js +++ b/ui/pages/routes/toast-master.js @@ -61,8 +61,6 @@ export function ToastMaster({ props, context }) { const dispatch = useDispatch(); const showAutoNetworkSwitchToast = getShowAutoNetworkSwitchTest(props); - console.log('switchedNetworkDetails', switchedNetworkDetails); - console.log('showAutoNetworkSwitchToast', showAutoNetworkSwitchToast); const { showPrivacyPolicyToast, newPrivacyPolicyToastShownDate } = useSelector(getShowPrivacyPolicyToast); From a1d0fe1b1991bd76610daca16c40c1a0a032e8d5 Mon Sep 17 00:00:00 2001 From: Howard Braham Date: Tue, 8 Oct 2024 23:19:28 -0700 Subject: [PATCH 12/34] fix hooks --- ui/pages/routes/toast-master.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/ui/pages/routes/toast-master.js b/ui/pages/routes/toast-master.js index a69a8481e362..35f9e59892a0 100644 --- a/ui/pages/routes/toast-master.js +++ b/ui/pages/routes/toast-master.js @@ -74,7 +74,7 @@ export function ToastMaster({ props, context }) { const account = useSelector(getSelectedAccount); // If the account has changed, allow the connect account toast again - if (account.address !== prevAccountAddress) { + if (account?.address !== prevAccountAddress) { prevAccountAddress = account.address; setHideConnectAccountToast(false); } @@ -86,10 +86,6 @@ export function ToastMaster({ props, context }) { const showNftEnablementToast = useSelector(getNftDetectionEnablementToast); const useNftDetection = useSelector(getUseNftDetection); - if (!onHomeScreen(props)) { - return null; - } - // If the privacy policy toast is shown, and there is no date set, set it if (showPrivacyPolicyToast && !newPrivacyPolicyToastShownDate) { setNewPrivacyPolicyToastShownDate(Date.now()); @@ -99,6 +95,10 @@ export function ToastMaster({ props, context }) { (state) => state.appState.showPermittedNetworkToastOpen, ); + if (!onHomeScreen(props)) { + return null; + } + return ( From 322832e63e6e6e1a3f7c71cedd063be7c12ba69a Mon Sep 17 00:00:00 2001 From: Howard Braham Date: Fri, 11 Oct 2024 23:05:07 -0700 Subject: [PATCH 13/34] removed eslint-disable --- ui/pages/routes/routes.component.js | 40 +---------------------------- ui/pages/routes/toast-master.js | 4 +-- 2 files changed, 3 insertions(+), 41 deletions(-) diff --git a/ui/pages/routes/routes.component.js b/ui/pages/routes/routes.component.js index a4921537fe28..e8fcb0c82c78 100644 --- a/ui/pages/routes/routes.component.js +++ b/ui/pages/routes/routes.component.js @@ -1,12 +1,8 @@ -/* eslint-disable no-unused-vars -- TODO remove */ -/* eslint-disable react/no-unused-prop-types -- TODO remove */ - import classnames from 'classnames'; import PropTypes from 'prop-types'; import React, { Component } from 'react'; import { matchPath, Route, Switch } from 'react-router-dom'; import IdleTimer from 'react-idle-timer'; -import { isEvmAccountType } from '@metamask/keyring-api'; import Swaps from '../swaps'; import ConfirmTransaction from '../confirmations/confirm-transaction'; @@ -30,7 +26,6 @@ import Loading from '../../components/ui/loading-screen'; import LoadingNetwork from '../../components/app/loading-network-screen'; import { Modal } from '../../components/app/modals'; import Alert from '../../components/ui/alert'; -import { SURVEY_LINK, PRIVACY_POLICY_LINK } from '../../../shared/lib/ui-utils'; import { AppHeader, AccountListMenu, @@ -38,8 +33,6 @@ import { AccountDetails, ImportNftsModal, ImportTokensModal, - ToastContainer, - Toast, } from '../../components/multichain'; import UnlockPage from '../unlock-page'; import Alerts from '../../components/app/alerts'; @@ -75,7 +68,6 @@ import { UNLOCK_ROUTE, CONFIRMATION_V_NEXT_ROUTE, ONBOARDING_ROUTE, - ONBOARDING_UNLOCK_ROUTE, CONNECTIONS, PERMISSIONS, REVIEW_PERMISSIONS, @@ -100,25 +92,15 @@ import { SNAP_MANAGE_ACCOUNTS_CONFIRMATION_TYPES, ///: END:ONLY_INCLUDE_IF } from '../../../shared/constants/app'; -import { NETWORK_TYPES } from '../../../shared/constants/network'; // TODO: Remove restricted import // eslint-disable-next-line import/no-restricted-paths import { getEnvironmentType } from '../../../app/scripts/lib/util'; import ConfirmationPage from '../confirmations/confirmation'; import OnboardingFlow from '../onboarding-flow/onboarding-flow'; import QRHardwarePopover from '../../components/app/qr-hardware-popover'; -import { SEND_STAGES } from '../../ducks/send'; import DeprecatedNetworks from '../../components/ui/deprecated-networks/deprecated-networks'; import NewNetworkInfo from '../../components/ui/new-network-info/new-network-info'; -import { ThemeType } from '../../../shared/constants/preferences'; -import { - AvatarAccount, - AvatarAccountSize, - AvatarNetwork, - Box, - Icon, - IconName, -} from '../../components/component-library'; +import { Box } from '../../components/component-library'; import { ToggleIpfsModal } from '../../components/app/assets/nfts/nft-default-image/toggle-ipfs-modal'; import { BasicConfigurationModal } from '../../components/app/basic-configuration-modal'; ///: BEGIN:ONLY_INCLUDE_IF(keyring-snaps) @@ -127,14 +109,6 @@ import KeyringSnapRemovalResult from '../../components/app/modals/keyring-snap-r import { SendPage } from '../../components/multichain/pages/send'; import { DeprecatedNetworkModal } from '../settings/deprecated-network-modal/DeprecatedNetworkModal'; -import { getURLHost } from '../../helpers/utils/util'; -import { - BorderColor, - BorderRadius, - IconColor, - TextVariant, -} from '../../helpers/constants/design-system'; -import { MILLISECOND, SECOND } from '../../../shared/constants/time'; import { MultichainMetaFoxLogo } from '../../components/multichain/app-header/multichain-meta-fox-logo'; import NetworkConfirmationPopover from '../../components/multichain/network-list-menu/network-confirmation-popover/network-confirmation-popover'; import NftFullImage from '../../components/app/assets/nfts/nft-details/nft-full-image'; @@ -152,7 +126,6 @@ import { ToastMaster } from './toast-master'; export default class Routes extends Component { static propTypes = { currentCurrency: PropTypes.string, - account: PropTypes.object, activeTabOrigin: PropTypes.string, setCurrentCurrencyToUSD: PropTypes.func, isLoading: PropTypes.bool, @@ -165,16 +138,11 @@ export default class Routes extends Component { setLastActiveTime: PropTypes.func, history: PropTypes.object, location: PropTypes.object, - lockMetaMask: PropTypes.func, - providerId: PropTypes.string, - providerType: PropTypes.string, autoLockTimeLimit: PropTypes.number, pageChanged: PropTypes.func.isRequired, - prepareToLeaveSwaps: PropTypes.func, browserEnvironmentOs: PropTypes.string, browserEnvironmentBrowser: PropTypes.string, theme: PropTypes.string, - sendStage: PropTypes.string, isNetworkUsed: PropTypes.bool, allAccountsOnNetworkAreEmpty: PropTypes.bool, isTestNet: PropTypes.bool, @@ -198,18 +166,13 @@ export default class Routes extends Component { hideImportTokensModal: PropTypes.func.isRequired, isDeprecatedNetworkModalOpen: PropTypes.bool.isRequired, hideDeprecatedNetworkModal: PropTypes.func.isRequired, - addPermittedAccount: PropTypes.func.isRequired, switchedNetworkDetails: PropTypes.object, - currentNetwork: PropTypes.object, clearSwitchedNetworkDetails: PropTypes.func.isRequired, - setSwitchedNetworkNeverShowMessage: PropTypes.func.isRequired, networkToAutomaticallySwitchTo: PropTypes.object, - neverShowSwitchedNetworkMessage: PropTypes.bool.isRequired, automaticallySwitchNetwork: PropTypes.func.isRequired, totalUnapprovedConfirmationCount: PropTypes.number.isRequired, currentExtensionPopupId: PropTypes.number, useRequestQueue: PropTypes.bool, - setSurveyLinkLastClickedOrClosed: PropTypes.func.isRequired, clearEditedNetwork: PropTypes.func.isRequired, ///: BEGIN:ONLY_INCLUDE_IF(keyring-snaps) isShowKeyringSnapRemovalResultModal: PropTypes.bool.isRequired, @@ -226,7 +189,6 @@ export default class Routes extends Component { componentDidUpdate(prevProps) { const { theme, - account, networkToAutomaticallySwitchTo, activeTabOrigin, totalUnapprovedConfirmationCount, diff --git a/ui/pages/routes/toast-master.js b/ui/pages/routes/toast-master.js index 35f9e59892a0..6c81c4d32223 100644 --- a/ui/pages/routes/toast-master.js +++ b/ui/pages/routes/toast-master.js @@ -31,7 +31,7 @@ import { getSelectedAccount, getUseNftDetection, } from '../../selectors'; -import { getShowAutoNetworkSwitchTest } from './isolated'; +import { hidePermittedNetworkToast } from '../../store/actions'; import { getNftDetectionEnablementToast, getShowPrivacyPolicyToast, @@ -40,7 +40,7 @@ import { setNewPrivacyPolicyToastShownDate, setShowNftDetectionEnablementToast, } from './toast-master-selectors'; -import { hidePermittedNetworkToast } from '../../store/actions'; +import { getShowAutoNetworkSwitchTest } from './isolated'; // Allow comparison with a previous value, in order to detect changes // (This pattern only works if ToastMaster is a singleton) From ba03d8fc642e4085a96cf22720ab77eedb9fe01c Mon Sep 17 00:00:00 2001 From: Howard Braham Date: Wed, 9 Oct 2024 00:00:54 -0700 Subject: [PATCH 14/34] fixed import --- .../nfts-detection-notice-nfts-tab.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/components/app/assets/nfts/nfts-detection-notice-nfts-tab/nfts-detection-notice-nfts-tab.js b/ui/components/app/assets/nfts/nfts-detection-notice-nfts-tab/nfts-detection-notice-nfts-tab.js index 3069b6cc5168..41b0d7249861 100644 --- a/ui/components/app/assets/nfts/nfts-detection-notice-nfts-tab/nfts-detection-notice-nfts-tab.js +++ b/ui/components/app/assets/nfts/nfts-detection-notice-nfts-tab/nfts-detection-notice-nfts-tab.js @@ -5,10 +5,10 @@ import { useI18nContext } from '../../../../../hooks/useI18nContext'; import { detectNfts, setOpenSeaEnabled, - setShowNftDetectionEnablementToast, setUseNftDetection, } from '../../../../../store/actions'; import { getOpenSeaEnabled } from '../../../../../selectors'; +import { setShowNftDetectionEnablementToast } from '../../../../../pages/routes/toast-master-selectors'; export default function NFTsDetectionNoticeNFTsTab() { const t = useI18nContext(); From cb00324062c3fbcf845b628967acc19244ca3aff Mon Sep 17 00:00:00 2001 From: Howard Braham Date: Wed, 9 Oct 2024 08:37:06 -0700 Subject: [PATCH 15/34] setOnboardingDate --- ui/pages/onboarding-flow/onboarding-flow.js | 9 ++++++--- ui/store/actions.ts | 6 ------ 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/ui/pages/onboarding-flow/onboarding-flow.js b/ui/pages/onboarding-flow/onboarding-flow.js index 01ea177eb851..bf40089476de 100644 --- a/ui/pages/onboarding-flow/onboarding-flow.js +++ b/ui/pages/onboarding-flow/onboarding-flow.js @@ -28,7 +28,6 @@ import { createNewVaultAndGetSeedPhrase, unlockAndGetSeedPhrase, createNewVaultAndRestore, - setOnboardingDate, } from '../../store/actions'; import { getFirstTimeFlowTypeRouteAfterUnlock } from '../../selectors'; import { MetaMetricsContext } from '../../contexts/metametrics'; @@ -72,8 +71,8 @@ export default function OnboardingFlow() { const trackEvent = useContext(MetaMetricsContext); useEffect(() => { - dispatch(setOnboardingDate()); - }, [dispatch]); + setOnboardingDate(); + }, []); useEffect(() => { if (completedOnboarding && !isFromReminder) { @@ -240,3 +239,7 @@ export default function OnboardingFlow() { ); } + +function setOnboardingDate() { + submitRequestToBackground('setOnboardingDate'); +} diff --git a/ui/store/actions.ts b/ui/store/actions.ts index 75987a983f62..abf047476595 100644 --- a/ui/store/actions.ts +++ b/ui/store/actions.ts @@ -4254,12 +4254,6 @@ export function setLastViewedUserSurvey(id: number) { }; } -export function setOnboardingDate() { - return async () => { - await submitRequestToBackground('setOnboardingDate'); - }; -} - export function setOutdatedBrowserWarningLastShown(lastShown: number) { return async () => { await submitRequestToBackground('setOutdatedBrowserWarningLastShown', [ From 04c5ef9274874fce3b0c07755b01e41a52eb426c Mon Sep 17 00:00:00 2001 From: Howard Braham Date: Wed, 9 Oct 2024 09:02:43 -0700 Subject: [PATCH 16/34] convert toast-master-selectors to TS --- ...selectors.js => toast-master-selectors.ts} | 58 ++++++++++++------- 1 file changed, 36 insertions(+), 22 deletions(-) rename ui/pages/routes/{toast-master-selectors.js => toast-master-selectors.ts} (58%) diff --git a/ui/pages/routes/toast-master-selectors.js b/ui/pages/routes/toast-master-selectors.ts similarity index 58% rename from ui/pages/routes/toast-master-selectors.js rename to ui/pages/routes/toast-master-selectors.ts index 6f8752c0efa8..d7532db6c051 100644 --- a/ui/pages/routes/toast-master-selectors.js +++ b/ui/pages/routes/toast-master-selectors.ts @@ -1,3 +1,6 @@ +import { PayloadAction } from '@reduxjs/toolkit'; +import { ReactFragment } from 'react'; +import { AppSliceState } from '../../ducks/app/app'; import { PRIVACY_POLICY_DATE } from '../../helpers/constants/privacy-policy'; import { SURVEY_DATE, @@ -6,14 +9,26 @@ import { } from '../../helpers/constants/survey'; import { SHOW_NFT_DETECTION_ENABLEMENT_TOAST } from '../../store/actionConstants'; import { submitRequestToBackground } from '../../store/background-connection'; +import { MetaMaskReduxState } from '../../store/store'; + +// TODO: get this into one of the larger definitions of state type +type State = AppSliceState & { + metamask: MetaMaskReduxState & { + newPrivacyPolicyToastClickedOrClosed: boolean; + newPrivacyPolicyToastShownDate: number; + onboardingDate: Date; + showNftDetectionEnablementToast: boolean; + surveyLinkLastClickedOrClosed: number; + }; +}; /** * Determines if the survey toast should be shown based on the current time, survey start and end times, and whether the survey link was last clicked or closed. * - * @param {*} state - The application state containing the necessary survey data. - * @returns {boolean} True if the current time is between the survey start and end times and the survey link was not last clicked or closed. False otherwise. + * @param state - The application state containing the necessary survey data. + * @returns True if the current time is between the survey start and end times and the survey link was not last clicked or closed. False otherwise. */ -export function getShowSurveyToast(state) { +export function getShowSurveyToast(state: State): boolean { if (state.metamask.surveyLinkLastClickedOrClosed) { return false; } @@ -28,10 +43,13 @@ export function getShowSurveyToast(state) { /** * Determines if the privacy policy toast should be shown based on the current date and whether the new privacy policy toast was clicked or closed. * - * @param {*} state - The application state containing the privacy policy data. - * @returns {boolean, number} Boolean is True if the toast should be shown, and the number is the date the toast was last shown. + * @param state - The application state containing the privacy policy data. + * @returns Boolean is True if the toast should be shown, and the number is the date the toast was last shown. */ -export function getShowPrivacyPolicyToast(state) { +export function getShowPrivacyPolicyToast(state: State): { + showPrivacyPolicyToast: boolean; + newPrivacyPolicyToastShownDate: number; +} { const { newPrivacyPolicyToastClickedOrClosed, onboardingDate } = state.metamask; const newPrivacyPolicyDate = new Date(PRIVACY_POLICY_DATE); @@ -54,10 +72,12 @@ export function getShowPrivacyPolicyToast(state) { /** * Returns true if the privacy policy toast was shown either never, or less than a day ago. * - * @param {number} newPrivacyPolicyToastShownDate - * @returns {boolean} true if the privacy policy toast was shown either never, or less than a day ago + * @param newPrivacyPolicyToastShownDate + * @returns true if the privacy policy toast was shown either never, or less than a day ago */ -function getIsPrivacyToastRecent(newPrivacyPolicyToastShownDate) { +function getIsPrivacyToastRecent( + newPrivacyPolicyToastShownDate: number, +): boolean { if (!newPrivacyPolicyToastShownDate) { return true; } @@ -68,12 +88,13 @@ function getIsPrivacyToastRecent(newPrivacyPolicyToastShownDate) { newPrivacyPolicyToastShownDate, ); const toastWasShownLessThanADayAgo = - currentDate - newPrivacyPolicyToastShownDateObj < oneDayInMilliseconds; + currentDate.valueOf() - newPrivacyPolicyToastShownDateObj.valueOf() < + oneDayInMilliseconds; return toastWasShownLessThanADayAgo; } -export function setNewPrivacyPolicyToastShownDate(time) { +export function setNewPrivacyPolicyToastShownDate(time: number) { submitRequestToBackground('setNewPrivacyPolicyToastShownDate', [time]); } @@ -81,22 +102,15 @@ export function setNewPrivacyPolicyToastClickedOrClosed() { submitRequestToBackground('setNewPrivacyPolicyToastClickedOrClosed'); } -export function getNftDetectionEnablementToast(state) { +export function getNftDetectionEnablementToast(state: State): boolean { return state.appState.showNftDetectionEnablementToast; } -export function setShowNftDetectionEnablementToast(value) { +export function setShowNftDetectionEnablementToast( + value: boolean, +): PayloadAction { return { type: SHOW_NFT_DETECTION_ENABLEMENT_TOAST, payload: value, }; } - -// export function setShowNftDetectionEnablementToast( -// value: boolean, -// ): PayloadAction { -// return { -// type: actionConstants.SHOW_NFT_DETECTION_ENABLEMENT_TOAST, -// payload: value, -// }; -// } From e92846e32bab3bcd64ef0b418805fe2caab985ff Mon Sep 17 00:00:00 2001 From: Howard Braham Date: Wed, 9 Oct 2024 09:12:22 -0700 Subject: [PATCH 17/34] cleanup isolated.js --- ui/pages/routes/isolated.js | 26 +------------------------- 1 file changed, 1 insertion(+), 25 deletions(-) diff --git a/ui/pages/routes/isolated.js b/ui/pages/routes/isolated.js index 21072b380241..25e044f0d5b9 100644 --- a/ui/pages/routes/isolated.js +++ b/ui/pages/routes/isolated.js @@ -30,7 +30,7 @@ export function isConfirmTransactionRoute(pathname) { ); } -export function getThemeFromRawTheme(theme) { +function getThemeFromRawTheme(theme) { if (theme === ThemeType.os) { if (window?.matchMedia('(prefers-color-scheme: dark)')?.matches) { return ThemeType.dark; @@ -47,16 +47,6 @@ export function setTheme(theme) { ); } -// function onSwapsPage(props) { -// const { location } = props; -// return Boolean( -// matchPath(location.pathname, { -// path: SWAPS_ROUTE, -// exact: false, -// }), -// ); -// } - function onConfirmPage(props) { const { location } = props; return Boolean( @@ -86,20 +76,6 @@ export function showOnboardingHeader(location) { ); } -export function toggleMetamaskActive(props) { - if (props.isUnlocked) { - // currently active: deactivate - props.lockMetaMask(); - } else { - // currently inactive: redirect to password box - const passwordBox = document.querySelector('input[type=password]'); - if (!passwordBox) { - return; - } - passwordBox.focus(); - } -} - export function getConnectingLabel(loadingMessage, props, context) { if (loadingMessage) { return loadingMessage; From 39ededb472aa7ad565a04fb63be7c9df4c94807d Mon Sep 17 00:00:00 2001 From: Howard Braham Date: Wed, 9 Oct 2024 09:17:39 -0700 Subject: [PATCH 18/34] rename isolated.js to routes-helpers.js --- ui/pages/routes/{isolated.js => routes-helpers.js} | 0 ui/pages/routes/routes.component.js | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename ui/pages/routes/{isolated.js => routes-helpers.js} (100%) diff --git a/ui/pages/routes/isolated.js b/ui/pages/routes/routes-helpers.js similarity index 100% rename from ui/pages/routes/isolated.js rename to ui/pages/routes/routes-helpers.js diff --git a/ui/pages/routes/routes.component.js b/ui/pages/routes/routes.component.js index e8fcb0c82c78..79a1a64867f2 100644 --- a/ui/pages/routes/routes.component.js +++ b/ui/pages/routes/routes.component.js @@ -120,7 +120,7 @@ import { isConfirmTransactionRoute, setTheme, showOnboardingHeader, -} from './isolated'; +} from './routes-helpers'; import { ToastMaster } from './toast-master'; export default class Routes extends Component { From 0f96be8b55987e947973d54be2e319e08f343fb0 Mon Sep 17 00:00:00 2001 From: Howard Braham Date: Wed, 9 Oct 2024 09:21:48 -0700 Subject: [PATCH 19/34] move toast-master to its own folder --- .../nfts-detection-notice-nfts-tab.js | 2 +- .../toast-master}/toast-master-selectors.ts | 12 +++--- .../app/toast-master}/toast-master.js | 37 ++++++++++--------- .../app/toast-master}/toast-master.test.ts | 6 +-- ui/pages/routes/routes.component.js | 2 +- 5 files changed, 31 insertions(+), 28 deletions(-) rename ui/{pages/routes => components/app/toast-master}/toast-master-selectors.ts (90%) rename ui/{pages/routes => components/app/toast-master}/toast-master.js (91%) rename ui/{pages/routes => components/app/toast-master}/toast-master.test.ts (97%) diff --git a/ui/components/app/assets/nfts/nfts-detection-notice-nfts-tab/nfts-detection-notice-nfts-tab.js b/ui/components/app/assets/nfts/nfts-detection-notice-nfts-tab/nfts-detection-notice-nfts-tab.js index 41b0d7249861..c86c44fe8257 100644 --- a/ui/components/app/assets/nfts/nfts-detection-notice-nfts-tab/nfts-detection-notice-nfts-tab.js +++ b/ui/components/app/assets/nfts/nfts-detection-notice-nfts-tab/nfts-detection-notice-nfts-tab.js @@ -8,7 +8,7 @@ import { setUseNftDetection, } from '../../../../../store/actions'; import { getOpenSeaEnabled } from '../../../../../selectors'; -import { setShowNftDetectionEnablementToast } from '../../../../../pages/routes/toast-master-selectors'; +import { setShowNftDetectionEnablementToast } from '../../../toast-master/toast-master-selectors'; export default function NFTsDetectionNoticeNFTsTab() { const t = useI18nContext(); diff --git a/ui/pages/routes/toast-master-selectors.ts b/ui/components/app/toast-master/toast-master-selectors.ts similarity index 90% rename from ui/pages/routes/toast-master-selectors.ts rename to ui/components/app/toast-master/toast-master-selectors.ts index d7532db6c051..a4d8c4f48807 100644 --- a/ui/pages/routes/toast-master-selectors.ts +++ b/ui/components/app/toast-master/toast-master-selectors.ts @@ -1,15 +1,15 @@ import { PayloadAction } from '@reduxjs/toolkit'; import { ReactFragment } from 'react'; -import { AppSliceState } from '../../ducks/app/app'; -import { PRIVACY_POLICY_DATE } from '../../helpers/constants/privacy-policy'; +import { AppSliceState } from '../../../ducks/app/app'; +import { PRIVACY_POLICY_DATE } from '../../../helpers/constants/privacy-policy'; import { SURVEY_DATE, SURVEY_END_TIME, SURVEY_START_TIME, -} from '../../helpers/constants/survey'; -import { SHOW_NFT_DETECTION_ENABLEMENT_TOAST } from '../../store/actionConstants'; -import { submitRequestToBackground } from '../../store/background-connection'; -import { MetaMaskReduxState } from '../../store/store'; +} from '../../../helpers/constants/survey'; +import { SHOW_NFT_DETECTION_ENABLEMENT_TOAST } from '../../../store/actionConstants'; +import { submitRequestToBackground } from '../../../store/background-connection'; +import { MetaMaskReduxState } from '../../../store/store'; // TODO: get this into one of the larger definitions of state type type State = AppSliceState & { diff --git a/ui/pages/routes/toast-master.js b/ui/components/app/toast-master/toast-master.js similarity index 91% rename from ui/pages/routes/toast-master.js rename to ui/components/app/toast-master/toast-master.js index 6c81c4d32223..e3a9e6ce075f 100644 --- a/ui/pages/routes/toast-master.js +++ b/ui/components/app/toast-master/toast-master.js @@ -3,35 +3,39 @@ import { isEvmAccountType } from '@metamask/keyring-api'; import React, { useState } from 'react'; import { useDispatch, useSelector } from 'react-redux'; -import { MILLISECOND, SECOND } from '../../../shared/constants/time'; -import { PRIVACY_POLICY_LINK, SURVEY_LINK } from '../../../shared/lib/ui-utils'; +import { MILLISECOND, SECOND } from '../../../../shared/constants/time'; import { - AvatarAccount, - AvatarAccountSize, - AvatarNetwork, - Icon, - IconName, -} from '../../components/component-library'; -import { Toast, ToastContainer } from '../../components/multichain'; -import { SurveyToast } from '../../components/ui/survey-toast'; -import { getAlertEnabledness } from '../../ducks/metamask/metamask'; + PRIVACY_POLICY_LINK, + SURVEY_LINK, +} from '../../../../shared/lib/ui-utils'; +import { getAlertEnabledness } from '../../../ducks/metamask/metamask'; import { BorderColor, BorderRadius, IconColor, TextVariant, -} from '../../helpers/constants/design-system'; +} from '../../../helpers/constants/design-system'; import { DEFAULT_ROUTE, REVIEW_PERMISSIONS, -} from '../../helpers/constants/routes'; -import { getURLHost } from '../../helpers/utils/util'; +} from '../../../helpers/constants/routes'; +import { getURLHost } from '../../../helpers/utils/util'; +import { getShowAutoNetworkSwitchTest } from '../../../pages/routes/routes-helpers'; import { getPermittedAccountsForCurrentTab, getSelectedAccount, getUseNftDetection, -} from '../../selectors'; -import { hidePermittedNetworkToast } from '../../store/actions'; +} from '../../../selectors'; +import { hidePermittedNetworkToast } from '../../../store/actions'; +import { + AvatarAccount, + AvatarAccountSize, + AvatarNetwork, + Icon, + IconName, +} from '../../component-library'; +import { Toast, ToastContainer } from '../../multichain'; +import { SurveyToast } from '../../ui/survey-toast'; import { getNftDetectionEnablementToast, getShowPrivacyPolicyToast, @@ -40,7 +44,6 @@ import { setNewPrivacyPolicyToastShownDate, setShowNftDetectionEnablementToast, } from './toast-master-selectors'; -import { getShowAutoNetworkSwitchTest } from './isolated'; // Allow comparison with a previous value, in order to detect changes // (This pattern only works if ToastMaster is a singleton) diff --git a/ui/pages/routes/toast-master.test.ts b/ui/components/app/toast-master/toast-master.test.ts similarity index 97% rename from ui/pages/routes/toast-master.test.ts rename to ui/components/app/toast-master/toast-master.test.ts index 31a57307fe60..5050a2652ed6 100644 --- a/ui/pages/routes/toast-master.test.ts +++ b/ui/components/app/toast-master/toast-master.test.ts @@ -1,8 +1,8 @@ -import { PRIVACY_POLICY_DATE } from '../../helpers/constants/privacy-policy'; -import { SURVEY_DATE, SURVEY_GMT } from '../../helpers/constants/survey'; +import { PRIVACY_POLICY_DATE } from '../../../helpers/constants/privacy-policy'; +import { SURVEY_DATE, SURVEY_GMT } from '../../../helpers/constants/survey'; import { - getShowSurveyToast, getShowPrivacyPolicyToast, + getShowSurveyToast, } from './toast-master-selectors'; describe('#getShowSurveyToast', () => { diff --git a/ui/pages/routes/routes.component.js b/ui/pages/routes/routes.component.js index 79a1a64867f2..8af192e5ba1e 100644 --- a/ui/pages/routes/routes.component.js +++ b/ui/pages/routes/routes.component.js @@ -121,7 +121,7 @@ import { setTheme, showOnboardingHeader, } from './routes-helpers'; -import { ToastMaster } from './toast-master'; +import { ToastMaster } from '../../components/app/toast-master/toast-master'; export default class Routes extends Component { static propTypes = { From 4122c85ae7a3ef23ab9712a1d58d486276a7deac Mon Sep 17 00:00:00 2001 From: Howard Braham Date: Wed, 9 Oct 2024 09:55:18 -0700 Subject: [PATCH 20/34] fixed unit tests --- .../toast-master/toast-master-selectors.ts | 38 +++++++++--------- .../app/toast-master/toast-master.test.ts | 40 +++++++++---------- ui/pages/routes/routes.component.js | 2 +- 3 files changed, 41 insertions(+), 39 deletions(-) diff --git a/ui/components/app/toast-master/toast-master-selectors.ts b/ui/components/app/toast-master/toast-master-selectors.ts index a4d8c4f48807..b827abe82bab 100644 --- a/ui/components/app/toast-master/toast-master-selectors.ts +++ b/ui/components/app/toast-master/toast-master-selectors.ts @@ -1,6 +1,5 @@ import { PayloadAction } from '@reduxjs/toolkit'; import { ReactFragment } from 'react'; -import { AppSliceState } from '../../../ducks/app/app'; import { PRIVACY_POLICY_DATE } from '../../../helpers/constants/privacy-policy'; import { SURVEY_DATE, @@ -9,16 +8,18 @@ import { } from '../../../helpers/constants/survey'; import { SHOW_NFT_DETECTION_ENABLEMENT_TOAST } from '../../../store/actionConstants'; import { submitRequestToBackground } from '../../../store/background-connection'; -import { MetaMaskReduxState } from '../../../store/store'; // TODO: get this into one of the larger definitions of state type -type State = AppSliceState & { - metamask: MetaMaskReduxState & { - newPrivacyPolicyToastClickedOrClosed: boolean; - newPrivacyPolicyToastShownDate: number; - onboardingDate: Date; - showNftDetectionEnablementToast: boolean; - surveyLinkLastClickedOrClosed: number; +type State = { + appState?: { + showNftDetectionEnablementToast?: boolean; + }; + metamask?: { + newPrivacyPolicyToastClickedOrClosed?: boolean; + newPrivacyPolicyToastShownDate?: number; + onboardingDate?: number; + showNftDetectionEnablementToast?: boolean; + surveyLinkLastClickedOrClosed?: number; }; }; @@ -29,7 +30,7 @@ type State = AppSliceState & { * @returns True if the current time is between the survey start and end times and the survey link was not last clicked or closed. False otherwise. */ export function getShowSurveyToast(state: State): boolean { - if (state.metamask.surveyLinkLastClickedOrClosed) { + if (state.metamask?.surveyLinkLastClickedOrClosed) { return false; } @@ -48,15 +49,16 @@ export function getShowSurveyToast(state: State): boolean { */ export function getShowPrivacyPolicyToast(state: State): { showPrivacyPolicyToast: boolean; - newPrivacyPolicyToastShownDate: number; + newPrivacyPolicyToastShownDate?: number; } { - const { newPrivacyPolicyToastClickedOrClosed, onboardingDate } = - state.metamask; + const { + newPrivacyPolicyToastClickedOrClosed, + newPrivacyPolicyToastShownDate, + onboardingDate, + } = state.metamask || {}; const newPrivacyPolicyDate = new Date(PRIVACY_POLICY_DATE); const currentDate = new Date(Date.now()); - const { newPrivacyPolicyToastShownDate } = state.metamask; - const showPrivacyPolicyToast = !newPrivacyPolicyToastClickedOrClosed && currentDate >= newPrivacyPolicyDate && @@ -64,7 +66,7 @@ export function getShowPrivacyPolicyToast(state: State): { // users who onboarded before the privacy policy date should see the notice // and // old users who don't have onboardingDate set should see the notice - (onboardingDate < newPrivacyPolicyDate || !onboardingDate); + (!onboardingDate || onboardingDate < newPrivacyPolicyDate.valueOf()); return { showPrivacyPolicyToast, newPrivacyPolicyToastShownDate }; } @@ -76,7 +78,7 @@ export function getShowPrivacyPolicyToast(state: State): { * @returns true if the privacy policy toast was shown either never, or less than a day ago */ function getIsPrivacyToastRecent( - newPrivacyPolicyToastShownDate: number, + newPrivacyPolicyToastShownDate?: number, ): boolean { if (!newPrivacyPolicyToastShownDate) { return true; @@ -103,7 +105,7 @@ export function setNewPrivacyPolicyToastClickedOrClosed() { } export function getNftDetectionEnablementToast(state: State): boolean { - return state.appState.showNftDetectionEnablementToast; + return Boolean(state.appState?.showNftDetectionEnablementToast); } export function setShowNftDetectionEnablementToast( diff --git a/ui/components/app/toast-master/toast-master.test.ts b/ui/components/app/toast-master/toast-master.test.ts index 5050a2652ed6..ab866ee1a7d2 100644 --- a/ui/components/app/toast-master/toast-master.test.ts +++ b/ui/components/app/toast-master/toast-master.test.ts @@ -17,7 +17,7 @@ describe('#getShowSurveyToast', () => { new Date(`${SURVEY_DATE} 12:25:00 ${SURVEY_GMT}`).getTime(); const result = getShowSurveyToast({ metamask: { - surveyLinkLastClickedOrClosed: null, + surveyLinkLastClickedOrClosed: undefined, }, }); expect(result).toStrictEqual(true); @@ -39,7 +39,7 @@ describe('#getShowSurveyToast', () => { new Date(`${SURVEY_DATE} 11:25:00 ${SURVEY_GMT}`).getTime(); const result = getShowSurveyToast({ metamask: { - surveyLinkLastClickedOrClosed: null, + surveyLinkLastClickedOrClosed: undefined, }, }); expect(result).toStrictEqual(false); @@ -50,7 +50,7 @@ describe('#getShowSurveyToast', () => { new Date(`${SURVEY_DATE} 14:25:00 ${SURVEY_GMT}`).getTime(); const result = getShowSurveyToast({ metamask: { - surveyLinkLastClickedOrClosed: null, + surveyLinkLastClickedOrClosed: undefined, }, }); expect(result).toStrictEqual(false); @@ -77,13 +77,13 @@ describe('#getShowPrivacyPolicyToast', () => { it('shows the privacy policy toast when not yet seen, on or after the policy date, and onboardingDate is before the policy date', () => { const result = getShowPrivacyPolicyToast({ metamask: { - newPrivacyPolicyToastClickedOrClosed: null, + newPrivacyPolicyToastClickedOrClosed: false, onboardingDate: new Date(PRIVACY_POLICY_DATE).setDate( new Date(PRIVACY_POLICY_DATE).getDate() - 2, ), }, }); - expect(result).toBe(true); + expect(result.showPrivacyPolicyToast).toBe(true); }); it('does not show the privacy policy toast when seen, even if on or after the policy date and onboardingDate is before the policy date', () => { @@ -95,17 +95,17 @@ describe('#getShowPrivacyPolicyToast', () => { ), }, }); - expect(result).toBe(false); + expect(result.showPrivacyPolicyToast).toBe(false); }); it('shows the privacy policy toast when not yet seen, on or after the policy date, and onboardingDate is not set', () => { const result = getShowPrivacyPolicyToast({ metamask: { - newPrivacyPolicyToastClickedOrClosed: null, - onboardingDate: null, + newPrivacyPolicyToastClickedOrClosed: false, + onboardingDate: undefined, }, }); - expect(result).toBe(true); + expect(result.showPrivacyPolicyToast).toBe(true); }); }); @@ -123,13 +123,13 @@ describe('#getShowPrivacyPolicyToast', () => { it('shows the privacy policy toast when not yet seen, on or after the policy date, and onboardingDate is before the policy date', () => { const result = getShowPrivacyPolicyToast({ metamask: { - newPrivacyPolicyToastClickedOrClosed: null, + newPrivacyPolicyToastClickedOrClosed: false, onboardingDate: new Date(PRIVACY_POLICY_DATE).setDate( new Date(PRIVACY_POLICY_DATE).getDate() - 2, ), }, }); - expect(result).toBe(true); + expect(result.showPrivacyPolicyToast).toBe(true); }); it('does not show the privacy policy toast when seen, even if on or after the policy date and onboardingDate is before the policy date', () => { @@ -141,17 +141,17 @@ describe('#getShowPrivacyPolicyToast', () => { ), }, }); - expect(result).toBe(false); + expect(result.showPrivacyPolicyToast).toBe(false); }); it('shows the privacy policy toast when not yet seen, on or after the policy date, and onboardingDate is not set', () => { const result = getShowPrivacyPolicyToast({ metamask: { - newPrivacyPolicyToastClickedOrClosed: null, - onboardingDate: null, + newPrivacyPolicyToastClickedOrClosed: false, + onboardingDate: undefined, }, }); - expect(result).toBe(true); + expect(result.showPrivacyPolicyToast).toBe(true); }); }); @@ -172,23 +172,23 @@ describe('#getShowPrivacyPolicyToast', () => { it('does not show the privacy policy toast before the policy date', () => { const result = getShowPrivacyPolicyToast({ metamask: { - newPrivacyPolicyToastClickedOrClosed: null, + newPrivacyPolicyToastClickedOrClosed: false, onboardingDate: new Date(PRIVACY_POLICY_DATE).setDate( new Date(PRIVACY_POLICY_DATE).getDate() - 2, ), }, }); - expect(result).toBe(false); + expect(result.showPrivacyPolicyToast).toBe(false); }); it('does not show the privacy policy toast before the policy date even if onboardingDate is not set', () => { const result = getShowPrivacyPolicyToast({ metamask: { - newPrivacyPolicyToastClickedOrClosed: null, - onboardingDate: null, + newPrivacyPolicyToastClickedOrClosed: false, + onboardingDate: undefined, }, }); - expect(result).toBe(false); + expect(result.showPrivacyPolicyToast).toBe(false); }); }); }); diff --git a/ui/pages/routes/routes.component.js b/ui/pages/routes/routes.component.js index 8af192e5ba1e..4f365309627a 100644 --- a/ui/pages/routes/routes.component.js +++ b/ui/pages/routes/routes.component.js @@ -113,6 +113,7 @@ import { MultichainMetaFoxLogo } from '../../components/multichain/app-header/mu import NetworkConfirmationPopover from '../../components/multichain/network-list-menu/network-confirmation-popover/network-confirmation-popover'; import NftFullImage from '../../components/app/assets/nfts/nft-details/nft-full-image'; import CrossChainSwap from '../bridge'; +import { ToastMaster } from '../../components/app/toast-master/toast-master'; import { getConnectingLabel, getShowAutoNetworkSwitchTest, @@ -121,7 +122,6 @@ import { setTheme, showOnboardingHeader, } from './routes-helpers'; -import { ToastMaster } from '../../components/app/toast-master/toast-master'; export default class Routes extends Component { static propTypes = { From f1f39d3be6fbfb0821cb18358d681abc0f885855 Mon Sep 17 00:00:00 2001 From: Howard Braham Date: Wed, 9 Oct 2024 10:17:33 -0700 Subject: [PATCH 21/34] import { submitRequestToBackground } --- ui/pages/onboarding-flow/onboarding-flow.js | 1 + 1 file changed, 1 insertion(+) diff --git a/ui/pages/onboarding-flow/onboarding-flow.js b/ui/pages/onboarding-flow/onboarding-flow.js index bf40089476de..ecda90c05d53 100644 --- a/ui/pages/onboarding-flow/onboarding-flow.js +++ b/ui/pages/onboarding-flow/onboarding-flow.js @@ -45,6 +45,7 @@ import ExperimentalArea from '../../components/app/flask/experimental-area'; import OnboardingSuccessful from '../institutional/onboarding-successful/onboarding-successful'; import { RemindSRP } from '../institutional/remind-srp/remind-srp'; ///: END:ONLY_INCLUDE_IF +import { submitRequestToBackground } from '../../store/background-connection'; import OnboardingFlowSwitch from './onboarding-flow-switch/onboarding-flow-switch'; import CreatePassword from './create-password/create-password'; import ReviewRecoveryPhrase from './recovery-phrase/review-recovery-phrase'; From 454f3307f7165e509c107e46eba1802770eee3c3 Mon Sep 17 00:00:00 2001 From: Howard Braham Date: Wed, 9 Oct 2024 12:23:45 -0700 Subject: [PATCH 22/34] state suggestion from @MajorLift --- .../app/toast-master/toast-master-selectors.ts | 7 ++++--- ui/components/app/toast-master/toast-master.test.ts | 12 ++++++++++++ 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/ui/components/app/toast-master/toast-master-selectors.ts b/ui/components/app/toast-master/toast-master-selectors.ts index b827abe82bab..af7353f484b0 100644 --- a/ui/components/app/toast-master/toast-master-selectors.ts +++ b/ui/components/app/toast-master/toast-master-selectors.ts @@ -8,13 +8,14 @@ import { } from '../../../helpers/constants/survey'; import { SHOW_NFT_DETECTION_ENABLEMENT_TOAST } from '../../../store/actionConstants'; import { submitRequestToBackground } from '../../../store/background-connection'; +import { MetaMaskReduxState } from '../../../store/store'; // TODO: get this into one of the larger definitions of state type -type State = { - appState?: { +type State = Omit & { + appState: { showNftDetectionEnablementToast?: boolean; }; - metamask?: { + metamask: { newPrivacyPolicyToastClickedOrClosed?: boolean; newPrivacyPolicyToastShownDate?: number; onboardingDate?: number; diff --git a/ui/components/app/toast-master/toast-master.test.ts b/ui/components/app/toast-master/toast-master.test.ts index ab866ee1a7d2..ee5a6e2e7aa4 100644 --- a/ui/components/app/toast-master/toast-master.test.ts +++ b/ui/components/app/toast-master/toast-master.test.ts @@ -16,6 +16,7 @@ describe('#getShowSurveyToast', () => { Date.now = () => new Date(`${SURVEY_DATE} 12:25:00 ${SURVEY_GMT}`).getTime(); const result = getShowSurveyToast({ + // @ts-expect-error: intentionally passing incomplete input metamask: { surveyLinkLastClickedOrClosed: undefined, }, @@ -27,6 +28,7 @@ describe('#getShowSurveyToast', () => { Date.now = () => new Date(`${SURVEY_DATE} 12:25:00 ${SURVEY_GMT}`).getTime(); const result = getShowSurveyToast({ + // @ts-expect-error: intentionally passing incomplete input metamask: { surveyLinkLastClickedOrClosed: 123456789, }, @@ -38,6 +40,7 @@ describe('#getShowSurveyToast', () => { Date.now = () => new Date(`${SURVEY_DATE} 11:25:00 ${SURVEY_GMT}`).getTime(); const result = getShowSurveyToast({ + // @ts-expect-error: intentionally passing incomplete input metamask: { surveyLinkLastClickedOrClosed: undefined, }, @@ -49,6 +52,7 @@ describe('#getShowSurveyToast', () => { Date.now = () => new Date(`${SURVEY_DATE} 14:25:00 ${SURVEY_GMT}`).getTime(); const result = getShowSurveyToast({ + // @ts-expect-error: intentionally passing incomplete input metamask: { surveyLinkLastClickedOrClosed: undefined, }, @@ -76,6 +80,7 @@ describe('#getShowPrivacyPolicyToast', () => { it('shows the privacy policy toast when not yet seen, on or after the policy date, and onboardingDate is before the policy date', () => { const result = getShowPrivacyPolicyToast({ + // @ts-expect-error: intentionally passing incomplete input metamask: { newPrivacyPolicyToastClickedOrClosed: false, onboardingDate: new Date(PRIVACY_POLICY_DATE).setDate( @@ -88,6 +93,7 @@ describe('#getShowPrivacyPolicyToast', () => { it('does not show the privacy policy toast when seen, even if on or after the policy date and onboardingDate is before the policy date', () => { const result = getShowPrivacyPolicyToast({ + // @ts-expect-error: intentionally passing incomplete input metamask: { newPrivacyPolicyToastClickedOrClosed: true, onboardingDate: new Date(PRIVACY_POLICY_DATE).setDate( @@ -100,6 +106,7 @@ describe('#getShowPrivacyPolicyToast', () => { it('shows the privacy policy toast when not yet seen, on or after the policy date, and onboardingDate is not set', () => { const result = getShowPrivacyPolicyToast({ + // @ts-expect-error: intentionally passing incomplete input metamask: { newPrivacyPolicyToastClickedOrClosed: false, onboardingDate: undefined, @@ -122,6 +129,7 @@ describe('#getShowPrivacyPolicyToast', () => { it('shows the privacy policy toast when not yet seen, on or after the policy date, and onboardingDate is before the policy date', () => { const result = getShowPrivacyPolicyToast({ + // @ts-expect-error: intentionally passing incomplete input metamask: { newPrivacyPolicyToastClickedOrClosed: false, onboardingDate: new Date(PRIVACY_POLICY_DATE).setDate( @@ -134,6 +142,7 @@ describe('#getShowPrivacyPolicyToast', () => { it('does not show the privacy policy toast when seen, even if on or after the policy date and onboardingDate is before the policy date', () => { const result = getShowPrivacyPolicyToast({ + // @ts-expect-error: intentionally passing incomplete input metamask: { newPrivacyPolicyToastClickedOrClosed: true, onboardingDate: new Date(PRIVACY_POLICY_DATE).setDate( @@ -146,6 +155,7 @@ describe('#getShowPrivacyPolicyToast', () => { it('shows the privacy policy toast when not yet seen, on or after the policy date, and onboardingDate is not set', () => { const result = getShowPrivacyPolicyToast({ + // @ts-expect-error: intentionally passing incomplete input metamask: { newPrivacyPolicyToastClickedOrClosed: false, onboardingDate: undefined, @@ -171,6 +181,7 @@ describe('#getShowPrivacyPolicyToast', () => { it('does not show the privacy policy toast before the policy date', () => { const result = getShowPrivacyPolicyToast({ + // @ts-expect-error: intentionally passing incomplete input metamask: { newPrivacyPolicyToastClickedOrClosed: false, onboardingDate: new Date(PRIVACY_POLICY_DATE).setDate( @@ -183,6 +194,7 @@ describe('#getShowPrivacyPolicyToast', () => { it('does not show the privacy policy toast before the policy date even if onboardingDate is not set', () => { const result = getShowPrivacyPolicyToast({ + // @ts-expect-error: intentionally passing incomplete input metamask: { newPrivacyPolicyToastClickedOrClosed: false, onboardingDate: undefined, From 112ade58c7b5fe4357f9945d3dd4fa4ba122b8ca Mon Sep 17 00:00:00 2001 From: Howard Braham Date: Thu, 10 Oct 2024 09:40:36 -0700 Subject: [PATCH 23/34] fixed merge conflict from #27561 --- ui/components/app/toast-master/toast-master.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/components/app/toast-master/toast-master.js b/ui/components/app/toast-master/toast-master.js index e3a9e6ce075f..bd4218cde0b0 100644 --- a/ui/components/app/toast-master/toast-master.js +++ b/ui/components/app/toast-master/toast-master.js @@ -210,7 +210,7 @@ export function ToastMaster({ props, context }) { } /> )} - {process.env.CHAIN_PERMISSIONS && isPermittedNetworkToastOpen && ( + {isPermittedNetworkToastOpen && ( Date: Sun, 13 Oct 2024 22:47:31 -0700 Subject: [PATCH 24/34] moved getShowConnectAccountToast() --- .../toast-master/toast-master-selectors.ts | 23 ++++++++++++++++ .../app/toast-master/toast-master.js | 26 ++----------------- 2 files changed, 25 insertions(+), 24 deletions(-) diff --git a/ui/components/app/toast-master/toast-master-selectors.ts b/ui/components/app/toast-master/toast-master-selectors.ts index af7353f484b0..39b833b660f5 100644 --- a/ui/components/app/toast-master/toast-master-selectors.ts +++ b/ui/components/app/toast-master/toast-master-selectors.ts @@ -1,11 +1,14 @@ +import { InternalAccount, isEvmAccountType } from '@metamask/keyring-api'; import { PayloadAction } from '@reduxjs/toolkit'; import { ReactFragment } from 'react'; +import { getAlertEnabledness } from '../../../ducks/metamask/metamask'; import { PRIVACY_POLICY_DATE } from '../../../helpers/constants/privacy-policy'; import { SURVEY_DATE, SURVEY_END_TIME, SURVEY_START_TIME, } from '../../../helpers/constants/survey'; +import { getPermittedAccountsForCurrentTab } from '../../../selectors'; import { SHOW_NFT_DETECTION_ENABLEMENT_TOAST } from '../../../store/actionConstants'; import { submitRequestToBackground } from '../../../store/background-connection'; import { MetaMaskReduxState } from '../../../store/store'; @@ -117,3 +120,23 @@ export function setShowNftDetectionEnablementToast( payload: value, }; } + +// If there is more than one connected account to activeTabOrigin, +// *BUT* the current account is not one of them, show the banner +export function getShowConnectAccountToast( + state: State, + account: InternalAccount, +): boolean { + const allowShowAccountSetting = getAlertEnabledness(state).unconnectedAccount; + const connectedAccounts = getPermittedAccountsForCurrentTab(state); + const isEvmAccount = isEvmAccountType(account?.type); + + return ( + allowShowAccountSetting && + account && + state.activeTab?.origin && + isEvmAccount && + connectedAccounts.length > 0 && + !connectedAccounts.some((address) => address === account.address) + ); +} diff --git a/ui/components/app/toast-master/toast-master.js b/ui/components/app/toast-master/toast-master.js index bd4218cde0b0..05ae00b7b337 100644 --- a/ui/components/app/toast-master/toast-master.js +++ b/ui/components/app/toast-master/toast-master.js @@ -1,6 +1,5 @@ /* eslint-disable react/prop-types -- TODO: upgrade to TypeScript */ -import { isEvmAccountType } from '@metamask/keyring-api'; import React, { useState } from 'react'; import { useDispatch, useSelector } from 'react-redux'; import { MILLISECOND, SECOND } from '../../../../shared/constants/time'; @@ -8,7 +7,6 @@ import { PRIVACY_POLICY_LINK, SURVEY_LINK, } from '../../../../shared/lib/ui-utils'; -import { getAlertEnabledness } from '../../../ducks/metamask/metamask'; import { BorderColor, BorderRadius, @@ -21,11 +19,7 @@ import { } from '../../../helpers/constants/routes'; import { getURLHost } from '../../../helpers/utils/util'; import { getShowAutoNetworkSwitchTest } from '../../../pages/routes/routes-helpers'; -import { - getPermittedAccountsForCurrentTab, - getSelectedAccount, - getUseNftDetection, -} from '../../../selectors'; +import { getSelectedAccount, getUseNftDetection } from '../../../selectors'; import { hidePermittedNetworkToast } from '../../../store/actions'; import { AvatarAccount, @@ -38,6 +32,7 @@ import { Toast, ToastContainer } from '../../multichain'; import { SurveyToast } from '../../ui/survey-toast'; import { getNftDetectionEnablementToast, + getShowConnectAccountToast, getShowPrivacyPolicyToast, getShowSurveyToast, setNewPrivacyPolicyToastClickedOrClosed, @@ -241,20 +236,3 @@ function onHomeScreen(props) { const { location } = props; return location.pathname === DEFAULT_ROUTE; } - -// If there is more than one connected account to activeTabOrigin, -// *BUT* the current account is not one of them, show the banner -function getShowConnectAccountToast(state, account) { - const allowShowAccountSetting = getAlertEnabledness(state).unconnectedAccount; - const connectedAccounts = getPermittedAccountsForCurrentTab(state); - const isEvmAccount = isEvmAccountType(account?.type); - - return ( - allowShowAccountSetting && - account && - state.activeTab?.origin && - isEvmAccount && - connectedAccounts.length > 0 && - !connectedAccounts.some((address) => address === account.address) - ); -} From 36c5f1cc70443b1166bfa6458da8de26af220132 Mon Sep 17 00:00:00 2001 From: Howard Braham Date: Sun, 13 Oct 2024 22:59:08 -0700 Subject: [PATCH 25/34] renamed get to select --- .../toast-master/toast-master-selectors.ts | 8 +++--- .../app/toast-master/toast-master.js | 16 +++++------ .../app/toast-master/toast-master.test.ts | 28 +++++++++---------- 3 files changed, 26 insertions(+), 26 deletions(-) diff --git a/ui/components/app/toast-master/toast-master-selectors.ts b/ui/components/app/toast-master/toast-master-selectors.ts index 39b833b660f5..0f47f0c6c146 100644 --- a/ui/components/app/toast-master/toast-master-selectors.ts +++ b/ui/components/app/toast-master/toast-master-selectors.ts @@ -33,7 +33,7 @@ type State = Omit & { * @param state - The application state containing the necessary survey data. * @returns True if the current time is between the survey start and end times and the survey link was not last clicked or closed. False otherwise. */ -export function getShowSurveyToast(state: State): boolean { +export function selectShowSurveyToast(state: State): boolean { if (state.metamask?.surveyLinkLastClickedOrClosed) { return false; } @@ -51,7 +51,7 @@ export function getShowSurveyToast(state: State): boolean { * @param state - The application state containing the privacy policy data. * @returns Boolean is True if the toast should be shown, and the number is the date the toast was last shown. */ -export function getShowPrivacyPolicyToast(state: State): { +export function selectShowPrivacyPolicyToast(state: State): { showPrivacyPolicyToast: boolean; newPrivacyPolicyToastShownDate?: number; } { @@ -108,7 +108,7 @@ export function setNewPrivacyPolicyToastClickedOrClosed() { submitRequestToBackground('setNewPrivacyPolicyToastClickedOrClosed'); } -export function getNftDetectionEnablementToast(state: State): boolean { +export function selectNftDetectionEnablementToast(state: State): boolean { return Boolean(state.appState?.showNftDetectionEnablementToast); } @@ -123,7 +123,7 @@ export function setShowNftDetectionEnablementToast( // If there is more than one connected account to activeTabOrigin, // *BUT* the current account is not one of them, show the banner -export function getShowConnectAccountToast( +export function selectShowConnectAccountToast( state: State, account: InternalAccount, ): boolean { diff --git a/ui/components/app/toast-master/toast-master.js b/ui/components/app/toast-master/toast-master.js index 05ae00b7b337..99b85560fe05 100644 --- a/ui/components/app/toast-master/toast-master.js +++ b/ui/components/app/toast-master/toast-master.js @@ -31,10 +31,10 @@ import { import { Toast, ToastContainer } from '../../multichain'; import { SurveyToast } from '../../ui/survey-toast'; import { - getNftDetectionEnablementToast, - getShowConnectAccountToast, - getShowPrivacyPolicyToast, - getShowSurveyToast, + selectNftDetectionEnablementToast, + selectShowConnectAccountToast, + selectShowPrivacyPolicyToast, + selectShowSurveyToast, setNewPrivacyPolicyToastClickedOrClosed, setNewPrivacyPolicyToastShownDate, setShowNftDetectionEnablementToast, @@ -61,12 +61,12 @@ export function ToastMaster({ props, context }) { const showAutoNetworkSwitchToast = getShowAutoNetworkSwitchTest(props); const { showPrivacyPolicyToast, newPrivacyPolicyToastShownDate } = - useSelector(getShowPrivacyPolicyToast); + useSelector(selectShowPrivacyPolicyToast); const autoHideToastDelay = 5 * SECOND; const safeEncodedHost = encodeURIComponent(activeTabOrigin); - const showSurveyToast = useSelector(getShowSurveyToast); + const showSurveyToast = useSelector(selectShowSurveyToast); const [hideConnectAccountToast, setHideConnectAccountToast] = useState(false); const account = useSelector(getSelectedAccount); @@ -78,10 +78,10 @@ export function ToastMaster({ props, context }) { } const showConnectAccountToast = useSelector((state) => - getShowConnectAccountToast(state, account), + selectShowConnectAccountToast(state, account), ); - const showNftEnablementToast = useSelector(getNftDetectionEnablementToast); + const showNftEnablementToast = useSelector(selectNftDetectionEnablementToast); const useNftDetection = useSelector(getUseNftDetection); // If the privacy policy toast is shown, and there is no date set, set it diff --git a/ui/components/app/toast-master/toast-master.test.ts b/ui/components/app/toast-master/toast-master.test.ts index ee5a6e2e7aa4..490ff6ac61dc 100644 --- a/ui/components/app/toast-master/toast-master.test.ts +++ b/ui/components/app/toast-master/toast-master.test.ts @@ -1,8 +1,8 @@ import { PRIVACY_POLICY_DATE } from '../../../helpers/constants/privacy-policy'; import { SURVEY_DATE, SURVEY_GMT } from '../../../helpers/constants/survey'; import { - getShowPrivacyPolicyToast, - getShowSurveyToast, + selectShowPrivacyPolicyToast, + selectShowSurveyToast, } from './toast-master-selectors'; describe('#getShowSurveyToast', () => { @@ -15,7 +15,7 @@ describe('#getShowSurveyToast', () => { it('shows the survey link when not yet seen and within time bounds', () => { Date.now = () => new Date(`${SURVEY_DATE} 12:25:00 ${SURVEY_GMT}`).getTime(); - const result = getShowSurveyToast({ + const result = selectShowSurveyToast({ // @ts-expect-error: intentionally passing incomplete input metamask: { surveyLinkLastClickedOrClosed: undefined, @@ -27,7 +27,7 @@ describe('#getShowSurveyToast', () => { it('does not show the survey link when seen and within time bounds', () => { Date.now = () => new Date(`${SURVEY_DATE} 12:25:00 ${SURVEY_GMT}`).getTime(); - const result = getShowSurveyToast({ + const result = selectShowSurveyToast({ // @ts-expect-error: intentionally passing incomplete input metamask: { surveyLinkLastClickedOrClosed: 123456789, @@ -39,7 +39,7 @@ describe('#getShowSurveyToast', () => { it('does not show the survey link before time bounds', () => { Date.now = () => new Date(`${SURVEY_DATE} 11:25:00 ${SURVEY_GMT}`).getTime(); - const result = getShowSurveyToast({ + const result = selectShowSurveyToast({ // @ts-expect-error: intentionally passing incomplete input metamask: { surveyLinkLastClickedOrClosed: undefined, @@ -51,7 +51,7 @@ describe('#getShowSurveyToast', () => { it('does not show the survey link after time bounds', () => { Date.now = () => new Date(`${SURVEY_DATE} 14:25:00 ${SURVEY_GMT}`).getTime(); - const result = getShowSurveyToast({ + const result = selectShowSurveyToast({ // @ts-expect-error: intentionally passing incomplete input metamask: { surveyLinkLastClickedOrClosed: undefined, @@ -79,7 +79,7 @@ describe('#getShowPrivacyPolicyToast', () => { }); it('shows the privacy policy toast when not yet seen, on or after the policy date, and onboardingDate is before the policy date', () => { - const result = getShowPrivacyPolicyToast({ + const result = selectShowPrivacyPolicyToast({ // @ts-expect-error: intentionally passing incomplete input metamask: { newPrivacyPolicyToastClickedOrClosed: false, @@ -92,7 +92,7 @@ describe('#getShowPrivacyPolicyToast', () => { }); it('does not show the privacy policy toast when seen, even if on or after the policy date and onboardingDate is before the policy date', () => { - const result = getShowPrivacyPolicyToast({ + const result = selectShowPrivacyPolicyToast({ // @ts-expect-error: intentionally passing incomplete input metamask: { newPrivacyPolicyToastClickedOrClosed: true, @@ -105,7 +105,7 @@ describe('#getShowPrivacyPolicyToast', () => { }); it('shows the privacy policy toast when not yet seen, on or after the policy date, and onboardingDate is not set', () => { - const result = getShowPrivacyPolicyToast({ + const result = selectShowPrivacyPolicyToast({ // @ts-expect-error: intentionally passing incomplete input metamask: { newPrivacyPolicyToastClickedOrClosed: false, @@ -128,7 +128,7 @@ describe('#getShowPrivacyPolicyToast', () => { }); it('shows the privacy policy toast when not yet seen, on or after the policy date, and onboardingDate is before the policy date', () => { - const result = getShowPrivacyPolicyToast({ + const result = selectShowPrivacyPolicyToast({ // @ts-expect-error: intentionally passing incomplete input metamask: { newPrivacyPolicyToastClickedOrClosed: false, @@ -141,7 +141,7 @@ describe('#getShowPrivacyPolicyToast', () => { }); it('does not show the privacy policy toast when seen, even if on or after the policy date and onboardingDate is before the policy date', () => { - const result = getShowPrivacyPolicyToast({ + const result = selectShowPrivacyPolicyToast({ // @ts-expect-error: intentionally passing incomplete input metamask: { newPrivacyPolicyToastClickedOrClosed: true, @@ -154,7 +154,7 @@ describe('#getShowPrivacyPolicyToast', () => { }); it('shows the privacy policy toast when not yet seen, on or after the policy date, and onboardingDate is not set', () => { - const result = getShowPrivacyPolicyToast({ + const result = selectShowPrivacyPolicyToast({ // @ts-expect-error: intentionally passing incomplete input metamask: { newPrivacyPolicyToastClickedOrClosed: false, @@ -180,7 +180,7 @@ describe('#getShowPrivacyPolicyToast', () => { }); it('does not show the privacy policy toast before the policy date', () => { - const result = getShowPrivacyPolicyToast({ + const result = selectShowPrivacyPolicyToast({ // @ts-expect-error: intentionally passing incomplete input metamask: { newPrivacyPolicyToastClickedOrClosed: false, @@ -193,7 +193,7 @@ describe('#getShowPrivacyPolicyToast', () => { }); it('does not show the privacy policy toast before the policy date even if onboardingDate is not set', () => { - const result = getShowPrivacyPolicyToast({ + const result = selectShowPrivacyPolicyToast({ // @ts-expect-error: intentionally passing incomplete input metamask: { newPrivacyPolicyToastClickedOrClosed: false, From 77bbd7f295a544fea8b9323bbcd4bbdd26d11837 Mon Sep 17 00:00:00 2001 From: Howard Braham Date: Sun, 13 Oct 2024 23:03:00 -0700 Subject: [PATCH 26/34] renamed routes-helpers.js to utils.js --- ui/components/app/toast-master/toast-master.js | 2 +- ui/pages/routes/routes.component.js | 2 +- ui/pages/routes/{routes-helpers.js => utils.js} | 0 3 files changed, 2 insertions(+), 2 deletions(-) rename ui/pages/routes/{routes-helpers.js => utils.js} (100%) diff --git a/ui/components/app/toast-master/toast-master.js b/ui/components/app/toast-master/toast-master.js index 99b85560fe05..56bdbd141c54 100644 --- a/ui/components/app/toast-master/toast-master.js +++ b/ui/components/app/toast-master/toast-master.js @@ -18,7 +18,7 @@ import { REVIEW_PERMISSIONS, } from '../../../helpers/constants/routes'; import { getURLHost } from '../../../helpers/utils/util'; -import { getShowAutoNetworkSwitchTest } from '../../../pages/routes/routes-helpers'; +import { getShowAutoNetworkSwitchTest } from '../../../pages/routes/utils'; import { getSelectedAccount, getUseNftDetection } from '../../../selectors'; import { hidePermittedNetworkToast } from '../../../store/actions'; import { diff --git a/ui/pages/routes/routes.component.js b/ui/pages/routes/routes.component.js index 4f365309627a..49d27c58e70e 100644 --- a/ui/pages/routes/routes.component.js +++ b/ui/pages/routes/routes.component.js @@ -121,7 +121,7 @@ import { isConfirmTransactionRoute, setTheme, showOnboardingHeader, -} from './routes-helpers'; +} from './utils'; export default class Routes extends Component { static propTypes = { diff --git a/ui/pages/routes/routes-helpers.js b/ui/pages/routes/utils.js similarity index 100% rename from ui/pages/routes/routes-helpers.js rename to ui/pages/routes/utils.js From dd0d26a353edcc1d63cde2ffe555692b6dd9ae58 Mon Sep 17 00:00:00 2001 From: Howard Braham Date: Sun, 13 Oct 2024 23:22:11 -0700 Subject: [PATCH 27/34] split to selectors.ts and utils.ts --- .../nfts-detection-notice-nfts-tab.js | 2 +- ...toast-master-selectors.ts => selectors.ts} | 47 +------------------ .../app/toast-master/toast-master.js | 2 +- .../app/toast-master/toast-master.test.ts | 2 +- ui/components/app/toast-master/utils.ts | 46 ++++++++++++++++++ 5 files changed, 50 insertions(+), 49 deletions(-) rename ui/components/app/toast-master/{toast-master-selectors.ts => selectors.ts} (71%) create mode 100644 ui/components/app/toast-master/utils.ts diff --git a/ui/components/app/assets/nfts/nfts-detection-notice-nfts-tab/nfts-detection-notice-nfts-tab.js b/ui/components/app/assets/nfts/nfts-detection-notice-nfts-tab/nfts-detection-notice-nfts-tab.js index c86c44fe8257..453a5d0217d9 100644 --- a/ui/components/app/assets/nfts/nfts-detection-notice-nfts-tab/nfts-detection-notice-nfts-tab.js +++ b/ui/components/app/assets/nfts/nfts-detection-notice-nfts-tab/nfts-detection-notice-nfts-tab.js @@ -8,7 +8,7 @@ import { setUseNftDetection, } from '../../../../../store/actions'; import { getOpenSeaEnabled } from '../../../../../selectors'; -import { setShowNftDetectionEnablementToast } from '../../../toast-master/toast-master-selectors'; +import { setShowNftDetectionEnablementToast } from '../../../toast-master/selectors'; export default function NFTsDetectionNoticeNFTsTab() { const t = useI18nContext(); diff --git a/ui/components/app/toast-master/toast-master-selectors.ts b/ui/components/app/toast-master/selectors.ts similarity index 71% rename from ui/components/app/toast-master/toast-master-selectors.ts rename to ui/components/app/toast-master/selectors.ts index 0f47f0c6c146..386b372b6ee7 100644 --- a/ui/components/app/toast-master/toast-master-selectors.ts +++ b/ui/components/app/toast-master/selectors.ts @@ -1,6 +1,4 @@ import { InternalAccount, isEvmAccountType } from '@metamask/keyring-api'; -import { PayloadAction } from '@reduxjs/toolkit'; -import { ReactFragment } from 'react'; import { getAlertEnabledness } from '../../../ducks/metamask/metamask'; import { PRIVACY_POLICY_DATE } from '../../../helpers/constants/privacy-policy'; import { @@ -9,9 +7,8 @@ import { SURVEY_START_TIME, } from '../../../helpers/constants/survey'; import { getPermittedAccountsForCurrentTab } from '../../../selectors'; -import { SHOW_NFT_DETECTION_ENABLEMENT_TOAST } from '../../../store/actionConstants'; -import { submitRequestToBackground } from '../../../store/background-connection'; import { MetaMaskReduxState } from '../../../store/store'; +import { getIsPrivacyToastRecent } from './utils'; // TODO: get this into one of the larger definitions of state type type State = Omit & { @@ -75,52 +72,10 @@ export function selectShowPrivacyPolicyToast(state: State): { return { showPrivacyPolicyToast, newPrivacyPolicyToastShownDate }; } -/** - * Returns true if the privacy policy toast was shown either never, or less than a day ago. - * - * @param newPrivacyPolicyToastShownDate - * @returns true if the privacy policy toast was shown either never, or less than a day ago - */ -function getIsPrivacyToastRecent( - newPrivacyPolicyToastShownDate?: number, -): boolean { - if (!newPrivacyPolicyToastShownDate) { - return true; - } - - const currentDate = new Date(); - const oneDayInMilliseconds = 24 * 60 * 60 * 1000; - const newPrivacyPolicyToastShownDateObj = new Date( - newPrivacyPolicyToastShownDate, - ); - const toastWasShownLessThanADayAgo = - currentDate.valueOf() - newPrivacyPolicyToastShownDateObj.valueOf() < - oneDayInMilliseconds; - - return toastWasShownLessThanADayAgo; -} - -export function setNewPrivacyPolicyToastShownDate(time: number) { - submitRequestToBackground('setNewPrivacyPolicyToastShownDate', [time]); -} - -export function setNewPrivacyPolicyToastClickedOrClosed() { - submitRequestToBackground('setNewPrivacyPolicyToastClickedOrClosed'); -} - export function selectNftDetectionEnablementToast(state: State): boolean { return Boolean(state.appState?.showNftDetectionEnablementToast); } -export function setShowNftDetectionEnablementToast( - value: boolean, -): PayloadAction { - return { - type: SHOW_NFT_DETECTION_ENABLEMENT_TOAST, - payload: value, - }; -} - // If there is more than one connected account to activeTabOrigin, // *BUT* the current account is not one of them, show the banner export function selectShowConnectAccountToast( diff --git a/ui/components/app/toast-master/toast-master.js b/ui/components/app/toast-master/toast-master.js index 56bdbd141c54..1431ac8e732e 100644 --- a/ui/components/app/toast-master/toast-master.js +++ b/ui/components/app/toast-master/toast-master.js @@ -38,7 +38,7 @@ import { setNewPrivacyPolicyToastClickedOrClosed, setNewPrivacyPolicyToastShownDate, setShowNftDetectionEnablementToast, -} from './toast-master-selectors'; +} from './selectors'; // Allow comparison with a previous value, in order to detect changes // (This pattern only works if ToastMaster is a singleton) diff --git a/ui/components/app/toast-master/toast-master.test.ts b/ui/components/app/toast-master/toast-master.test.ts index 490ff6ac61dc..8b29f20a240d 100644 --- a/ui/components/app/toast-master/toast-master.test.ts +++ b/ui/components/app/toast-master/toast-master.test.ts @@ -3,7 +3,7 @@ import { SURVEY_DATE, SURVEY_GMT } from '../../../helpers/constants/survey'; import { selectShowPrivacyPolicyToast, selectShowSurveyToast, -} from './toast-master-selectors'; +} from './selectors'; describe('#getShowSurveyToast', () => { const realDateNow = Date.now; diff --git a/ui/components/app/toast-master/utils.ts b/ui/components/app/toast-master/utils.ts new file mode 100644 index 000000000000..6ac039bbffec --- /dev/null +++ b/ui/components/app/toast-master/utils.ts @@ -0,0 +1,46 @@ +import { PayloadAction } from '@reduxjs/toolkit'; +import { ReactFragment } from 'react'; +import { SHOW_NFT_DETECTION_ENABLEMENT_TOAST } from '../../../store/actionConstants'; +import { submitRequestToBackground } from '../../../store/background-connection'; + +/** + * Returns true if the privacy policy toast was shown either never, or less than a day ago. + * + * @param newPrivacyPolicyToastShownDate + * @returns true if the privacy policy toast was shown either never, or less than a day ago + */ +export function getIsPrivacyToastRecent( + newPrivacyPolicyToastShownDate?: number, +): boolean { + if (!newPrivacyPolicyToastShownDate) { + return true; + } + + const currentDate = new Date(); + const oneDayInMilliseconds = 24 * 60 * 60 * 1000; + const newPrivacyPolicyToastShownDateObj = new Date( + newPrivacyPolicyToastShownDate, + ); + const toastWasShownLessThanADayAgo = + currentDate.valueOf() - newPrivacyPolicyToastShownDateObj.valueOf() < + oneDayInMilliseconds; + + return toastWasShownLessThanADayAgo; +} + +export function setNewPrivacyPolicyToastShownDate(time: number) { + submitRequestToBackground('setNewPrivacyPolicyToastShownDate', [time]); +} + +export function setNewPrivacyPolicyToastClickedOrClosed() { + submitRequestToBackground('setNewPrivacyPolicyToastClickedOrClosed'); +} + +export function setShowNftDetectionEnablementToast( + value: boolean, +): PayloadAction { + return { + type: SHOW_NFT_DETECTION_ENABLEMENT_TOAST, + payload: value, + }; +} From dafb0100001409af542c50a5712bb381b0384356 Mon Sep 17 00:00:00 2001 From: Howard Braham Date: Sun, 13 Oct 2024 23:23:50 -0700 Subject: [PATCH 28/34] usePrevious --- ui/components/app/toast-master/toast-master.js | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/ui/components/app/toast-master/toast-master.js b/ui/components/app/toast-master/toast-master.js index 1431ac8e732e..9f1c850921c5 100644 --- a/ui/components/app/toast-master/toast-master.js +++ b/ui/components/app/toast-master/toast-master.js @@ -18,6 +18,7 @@ import { REVIEW_PERMISSIONS, } from '../../../helpers/constants/routes'; import { getURLHost } from '../../../helpers/utils/util'; +import { usePrevious } from '../../../hooks/usePrevious'; import { getShowAutoNetworkSwitchTest } from '../../../pages/routes/utils'; import { getSelectedAccount, getUseNftDetection } from '../../../selectors'; import { hidePermittedNetworkToast } from '../../../store/actions'; @@ -40,10 +41,6 @@ import { setShowNftDetectionEnablementToast, } from './selectors'; -// Allow comparison with a previous value, in order to detect changes -// (This pattern only works if ToastMaster is a singleton) -let prevAccountAddress; - export function ToastMaster({ props, context }) { const { t } = context; const { @@ -72,8 +69,8 @@ export function ToastMaster({ props, context }) { const account = useSelector(getSelectedAccount); // If the account has changed, allow the connect account toast again - if (account?.address !== prevAccountAddress) { - prevAccountAddress = account.address; + const prevAccountAddress = usePrevious(account?.address); + if (account?.address !== prevAccountAddress && hideConnectAccountToast) { setHideConnectAccountToast(false); } From 8fae36ebdaea1425b1e6f0e9e010da75ab88e519 Mon Sep 17 00:00:00 2001 From: Howard Braham Date: Mon, 14 Oct 2024 01:58:08 -0700 Subject: [PATCH 29/34] Split ToastMaster into several separate local components --- ui/components/app/toast-master/selectors.ts | 11 + .../app/toast-master/toast-master.js | 387 ++++++++++-------- ui/components/app/toast-master/utils.ts | 30 ++ ui/pages/routes/routes.component.js | 13 +- ui/pages/routes/routes.container.js | 12 +- ui/pages/routes/utils.js | 4 - ui/selectors/selectors.js | 10 - ui/store/actions.ts | 12 - 8 files changed, 272 insertions(+), 207 deletions(-) diff --git a/ui/components/app/toast-master/selectors.ts b/ui/components/app/toast-master/selectors.ts index 386b372b6ee7..b88762c3bc19 100644 --- a/ui/components/app/toast-master/selectors.ts +++ b/ui/components/app/toast-master/selectors.ts @@ -21,6 +21,7 @@ type State = Omit & { onboardingDate?: number; showNftDetectionEnablementToast?: boolean; surveyLinkLastClickedOrClosed?: number; + switchedNetworkNeverShowMessage?: boolean; }; }; @@ -95,3 +96,13 @@ export function selectShowConnectAccountToast( !connectedAccounts.some((address) => address === account.address) ); } + +/** + * Retrieves user preference to never see the "Switched Network" toast + * + * @param state - Redux state object. + * @returns Boolean preference value + */ +export function selectSwitchedNetworkNeverShowMessage(state: State): boolean { + return Boolean(state.metamask.switchedNetworkNeverShowMessage); +} diff --git a/ui/components/app/toast-master/toast-master.js b/ui/components/app/toast-master/toast-master.js index 9f1c850921c5..99da8dfe8065 100644 --- a/ui/components/app/toast-master/toast-master.js +++ b/ui/components/app/toast-master/toast-master.js @@ -2,6 +2,7 @@ import React, { useState } from 'react'; import { useDispatch, useSelector } from 'react-redux'; +import { useHistory } from 'react-router-dom'; import { MILLISECOND, SECOND } from '../../../../shared/constants/time'; import { PRIVACY_POLICY_LINK, @@ -13,15 +14,22 @@ import { IconColor, TextVariant, } from '../../../helpers/constants/design-system'; -import { - DEFAULT_ROUTE, - REVIEW_PERMISSIONS, -} from '../../../helpers/constants/routes'; +import { REVIEW_PERMISSIONS } from '../../../helpers/constants/routes'; import { getURLHost } from '../../../helpers/utils/util'; +import { useI18nContext } from '../../../hooks/useI18nContext'; import { usePrevious } from '../../../hooks/usePrevious'; -import { getShowAutoNetworkSwitchTest } from '../../../pages/routes/utils'; -import { getSelectedAccount, getUseNftDetection } from '../../../selectors'; -import { hidePermittedNetworkToast } from '../../../store/actions'; +import { + getCurrentNetwork, + getOriginOfCurrentTab, + getSelectedAccount, + getSwitchedNetworkDetails, + getUseNftDetection, +} from '../../../selectors'; +import { + addPermittedAccount, + clearSwitchedNetworkDetails, + hidePermittedNetworkToast, +} from '../../../store/actions'; import { AvatarAccount, AvatarAccountSize, @@ -36,35 +44,37 @@ import { selectShowConnectAccountToast, selectShowPrivacyPolicyToast, selectShowSurveyToast, - setNewPrivacyPolicyToastClickedOrClosed, + selectSwitchedNetworkNeverShowMessage, +} from './selectors'; +import { + getShowAutoNetworkSwitchTest, + onHomeScreen, setNewPrivacyPolicyToastShownDate, setShowNftDetectionEnablementToast, -} from './selectors'; + setSurveyLinkLastClickedOrClosed, + setSwitchedNetworkNeverShowMessage, +} from './utils'; -export function ToastMaster({ props, context }) { - const { t } = context; - const { - activeTabOrigin, - addPermittedAccount, - clearSwitchedNetworkDetails, - setSurveyLinkLastClickedOrClosed, - setSwitchedNetworkNeverShowMessage, - switchedNetworkDetails, - currentNetwork, - } = props; +export function ToastMaster() { + return ( + onHomeScreen() && ( + + + + + + + + + + ) + ); +} +function ConnectAccountToast() { + const t = useI18nContext(); const dispatch = useDispatch(); - const showAutoNetworkSwitchToast = getShowAutoNetworkSwitchTest(props); - - const { showPrivacyPolicyToast, newPrivacyPolicyToastShownDate } = - useSelector(selectShowPrivacyPolicyToast); - - const autoHideToastDelay = 5 * SECOND; - const safeEncodedHost = encodeURIComponent(activeTabOrigin); - - const showSurveyToast = useSelector(selectShowSurveyToast); - const [hideConnectAccountToast, setHideConnectAccountToast] = useState(false); const account = useSelector(getSelectedAccount); @@ -78,44 +88,28 @@ export function ToastMaster({ props, context }) { selectShowConnectAccountToast(state, account), ); - const showNftEnablementToast = useSelector(selectNftDetectionEnablementToast); - const useNftDetection = useSelector(getUseNftDetection); - - // If the privacy policy toast is shown, and there is no date set, set it - if (showPrivacyPolicyToast && !newPrivacyPolicyToastShownDate) { - setNewPrivacyPolicyToastShownDate(Date.now()); - } - - const isPermittedNetworkToastOpen = useSelector( - (state) => state.appState.showPermittedNetworkToastOpen, - ); - - if (!onHomeScreen(props)) { - return null; - } + const activeTabOrigin = useSelector(getOriginOfCurrentTab); return ( - - - {!hideConnectAccountToast && showConnectAccountToast && ( - - } - text={t('accountIsntConnectedToastText', [ - account?.metadata?.name, - getURLHost(activeTabOrigin), - ])} - actionText={t('connectAccount')} - onActionClick={() => { - // Connect this account - addPermittedAccount(activeTabOrigin, account.address); + Boolean(!hideConnectAccountToast && showConnectAccountToast) && ( + + } + text={t('accountIsntConnectedToastText', [ + account?.metadata?.name, + getURLHost(activeTabOrigin), + ])} + actionText={t('connectAccount')} + onActionClick={() => { + // Connect this account + dispatch(addPermittedAccount(activeTabOrigin, account.address)), // Use setTimeout to prevent React re-render from // hiding the tooltip setTimeout(() => { @@ -127,109 +121,166 @@ export function ToastMaster({ props, context }) { ) ?.dispatchEvent(new CustomEvent('mouseenter', {})); }, 250 * MILLISECOND); - }} - onClose={() => setHideConnectAccountToast(true)} - /> - )} - {showSurveyToast && ( - - } - text={t('surveyTitle')} - actionText={t('surveyConversion')} - onActionClick={() => { - global.platform.openTab({ - url: SURVEY_LINK, - }); - setSurveyLinkLastClickedOrClosed(Date.now()); - }} - onClose={() => { - setSurveyLinkLastClickedOrClosed(Date.now()); - }} - /> - )} - {showPrivacyPolicyToast && ( - - } - text={t('newPrivacyPolicyTitle')} - actionText={t('newPrivacyPolicyActionButton')} - onActionClick={() => { - global.platform.openTab({ - url: PRIVACY_POLICY_LINK, - }); - setNewPrivacyPolicyToastClickedOrClosed(); - }} - onClose={setNewPrivacyPolicyToastClickedOrClosed} - /> - )} - {showAutoNetworkSwitchToast && ( - - } - text={t('switchedNetworkToastMessage', [ - switchedNetworkDetails.nickname, - getURLHost(switchedNetworkDetails.origin), - ])} - actionText={t('switchedNetworkToastDecline')} - onActionClick={() => setSwitchedNetworkNeverShowMessage()} - onClose={() => clearSwitchedNetworkDetails()} - /> - )} - {showNftEnablementToast && useNftDetection && ( - - } - text={t('nftAutoDetectionEnabled')} - borderRadius={BorderRadius.LG} - textVariant={TextVariant.bodyMd} - autoHideTime={autoHideToastDelay} - onAutoHideToast={() => - dispatch(setShowNftDetectionEnablementToast(false)) - } - /> - )} - {isPermittedNetworkToastOpen && ( - - } - text={t('permittedChainToastUpdate', [ - getURLHost(activeTabOrigin), - currentNetwork?.nickname, - ])} - actionText={t('editPermissions')} - onActionClick={() => { - dispatch(hidePermittedNetworkToast()); - props.history.push(`${REVIEW_PERMISSIONS}/${safeEncodedHost}`); - }} - onClose={() => dispatch(hidePermittedNetworkToast())} - /> - )} - + }} + onClose={() => setHideConnectAccountToast(true)} + /> + ) + ); +} + +function SurveyToastMayDelete() { + const t = useI18nContext(); + + const showSurveyToast = useSelector(selectShowSurveyToast); + + return ( + showSurveyToast && ( + + } + text={t('surveyTitle')} + actionText={t('surveyConversion')} + onActionClick={() => { + global.platform.openTab({ + url: SURVEY_LINK, + }); + setSurveyLinkLastClickedOrClosed(Date.now()); + }} + onClose={() => { + setSurveyLinkLastClickedOrClosed(Date.now()); + }} + /> + ) + ); +} + +function PrivacyPolicyToast() { + const t = useI18nContext(); + + const { showPrivacyPolicyToast, newPrivacyPolicyToastShownDate } = + useSelector(selectShowPrivacyPolicyToast); + + // If the privacy policy toast is shown, and there is no date set, set it + if (showPrivacyPolicyToast && !newPrivacyPolicyToastShownDate) { + setNewPrivacyPolicyToastShownDate(Date.now()); + } + + return ( + showPrivacyPolicyToast && ( + + } + text={t('newPrivacyPolicyTitle')} + actionText={t('newPrivacyPolicyActionButton')} + onActionClick={() => { + global.platform.openTab({ + url: PRIVACY_POLICY_LINK, + }); + setNewPrivacyPolicyToastClickedOrClosed(); + }} + onClose={setNewPrivacyPolicyToastClickedOrClosed} + /> + ) + ); +} + +function SwitchedNetworkToast() { + const t = useI18nContext(); + const dispatch = useDispatch(); + + return ( + getShowAutoNetworkSwitchTest() && ( + + } + text={t('switchedNetworkToastMessage', [ + switchedNetworkDetails.nickname, + getURLHost(switchedNetworkDetails.origin), + ])} + actionText={t('switchedNetworkToastDecline')} + onActionClick={setSwitchedNetworkNeverShowMessage} + onClose={() => dispatch(clearSwitchedNetworkDetails())} + /> + ) ); } -function onHomeScreen(props) { - const { location } = props; - return location.pathname === DEFAULT_ROUTE; +function NftEnablementToast() { + const t = useI18nContext(); + const dispatch = useDispatch(); + + const showNftEnablementToast = useSelector(selectNftDetectionEnablementToast); + const useNftDetection = useSelector(getUseNftDetection); + + const autoHideToastDelay = 5 * SECOND; + + return ( + showNftEnablementToast && + useNftDetection && ( + + } + text={t('nftAutoDetectionEnabled')} + borderRadius={BorderRadius.LG} + textVariant={TextVariant.bodyMd} + autoHideTime={autoHideToastDelay} + onAutoHideToast={() => + dispatch(setShowNftDetectionEnablementToast(false)) + } + /> + ) + ); +} + +function PermittedNetworkToast() { + const t = useI18nContext(); + const dispatch = useDispatch(); + + const isPermittedNetworkToastOpen = useSelector( + (state) => state.appState.showPermittedNetworkToastOpen, + ); + + const currentNetwork = useSelector(getCurrentNetwork); + const activeTabOrigin = useSelector(getOriginOfCurrentTab); + const safeEncodedHost = encodeURIComponent(activeTabOrigin); + const history = useHistory(); + + return ( + isPermittedNetworkToastOpen && ( + + } + text={t('permittedChainToastUpdate', [ + getURLHost(activeTabOrigin), + currentNetwork?.nickname, + ])} + actionText={t('editPermissions')} + onActionClick={() => { + dispatch(hidePermittedNetworkToast()); + history.push(`${REVIEW_PERMISSIONS}/${safeEncodedHost}`); + }} + onClose={() => dispatch(hidePermittedNetworkToast())} + /> + ) + ); } diff --git a/ui/components/app/toast-master/utils.ts b/ui/components/app/toast-master/utils.ts index 6ac039bbffec..602aecedfd7c 100644 --- a/ui/components/app/toast-master/utils.ts +++ b/ui/components/app/toast-master/utils.ts @@ -2,6 +2,11 @@ import { PayloadAction } from '@reduxjs/toolkit'; import { ReactFragment } from 'react'; import { SHOW_NFT_DETECTION_ENABLEMENT_TOAST } from '../../../store/actionConstants'; import { submitRequestToBackground } from '../../../store/background-connection'; +import { useLocation } from 'react-router-dom'; +import { DEFAULT_ROUTE } from '../../../helpers/constants/routes'; +import { getSwitchedNetworkDetails } from '../../../selectors'; +import { useSelector } from 'react-redux'; +import { selectSwitchedNetworkNeverShowMessage } from './selectors'; /** * Returns true if the privacy policy toast was shown either never, or less than a day ago. @@ -44,3 +49,28 @@ export function setShowNftDetectionEnablementToast( payload: value, }; } + +export function setSwitchedNetworkNeverShowMessage() { + submitRequestToBackground('setSwitchedNetworkNeverShowMessage', [true]); +} + +export function onHomeScreen() { + const location = useLocation(); + + return location.pathname === DEFAULT_ROUTE; +} + +export function setSurveyLinkLastClickedOrClosed(time: number) { + return async () => { + await submitRequestToBackground('setSurveyLinkLastClickedOrClosed', [time]); + }; +} + +export function getShowAutoNetworkSwitchTest() { + const switchedNetworkDetails = useSelector(getSwitchedNetworkDetails); + const switchedNetworkNeverShowMessage = useSelector( + selectSwitchedNetworkNeverShowMessage, + ); + + return switchedNetworkDetails && !switchedNetworkNeverShowMessage; +} diff --git a/ui/pages/routes/routes.component.js b/ui/pages/routes/routes.component.js index 49d27c58e70e..3b4f40071dee 100644 --- a/ui/pages/routes/routes.component.js +++ b/ui/pages/routes/routes.component.js @@ -116,12 +116,12 @@ import CrossChainSwap from '../bridge'; import { ToastMaster } from '../../components/app/toast-master/toast-master'; import { getConnectingLabel, - getShowAutoNetworkSwitchTest, hideAppHeader, isConfirmTransactionRoute, setTheme, showOnboardingHeader, } from './utils'; +import { getShowAutoNetworkSwitchTest } from '../../components/app/toast-master/utils'; export default class Routes extends Component { static propTypes = { @@ -466,8 +466,6 @@ export default class Routes extends Component { ); ///: END:ONLY_INCLUDE_IF - const showAutoNetworkSwitchToast = getShowAutoNetworkSwitchTest(this.props); - return (
clearSwitchedNetworkDetails() : undefined } @@ -545,8 +543,13 @@ export default class Routes extends Component { {this.renderRoutes()} {isUnlocked ? : null} - +
); } } + +// Will eventually delete this function +function deprecatedGetShowAutoNetworkSwitchTest(props) { + return props.switchedNetworkDetails && !props.switchedNetworkNeverShowMessage; +} diff --git a/ui/pages/routes/routes.container.js b/ui/pages/routes/routes.container.js index 7c2c46b55ad0..6e25d97fddf1 100644 --- a/ui/pages/routes/routes.container.js +++ b/ui/pages/routes/routes.container.js @@ -18,7 +18,6 @@ import { getShowExtensionInFullSizeView, getSelectedAccount, getSwitchedNetworkDetails, - getNeverShowSwitchedNetworkMessage, getNetworkToAutomaticallySwitchTo, getNumberOfAllUnapprovedTransactionsAndMessages, getUseRequestQueue, @@ -35,10 +34,9 @@ import { hideImportTokensModal, hideDeprecatedNetworkModal, addPermittedAccount, - setSurveyLinkLastClickedOrClosed, automaticallySwitchNetwork, clearSwitchedNetworkDetails, - neverShowSwitchedNetworkMessage, + switchedNetworkNeverShowMessage, ///: BEGIN:ONLY_INCLUDE_IF(keyring-snaps) hideKeyringRemovalResultModal, ///: END:ONLY_INCLUDE_IF @@ -53,6 +51,7 @@ import { } from '../../ducks/metamask/metamask'; import { DEFAULT_AUTO_LOCK_TIME_LIMIT } from '../../../shared/constants/preferences'; import Routes from './routes.component'; +import { selectSwitchedNetworkNeverShowMessage } from '../../components/app/toast-master/selectors'; function mapStateToProps(state) { const { activeTab, appState } = state; @@ -111,7 +110,8 @@ function mapStateToProps(state) { currentNetwork, totalUnapprovedConfirmationCount: getNumberOfAllUnapprovedTransactionsAndMessages(state), - neverShowSwitchedNetworkMessage: getNeverShowSwitchedNetworkMessage(state), + switchedNetworkNeverShowMessage: + selectSwitchedNetworkNeverShowMessage(state), currentExtensionPopupId: state.metamask.currentExtensionPopupId, useRequestQueue: getUseRequestQueue(state), ///: BEGIN:ONLY_INCLUDE_IF(keyring-snaps) @@ -138,12 +138,8 @@ function mapDispatchToProps(dispatch) { addPermittedAccount: (activeTabOrigin, address) => dispatch(addPermittedAccount(activeTabOrigin, address)), clearSwitchedNetworkDetails: () => dispatch(clearSwitchedNetworkDetails()), - setSwitchedNetworkNeverShowMessage: () => - dispatch(neverShowSwitchedNetworkMessage()), automaticallySwitchNetwork: (networkId, selectedTabOrigin) => dispatch(automaticallySwitchNetwork(networkId, selectedTabOrigin)), - setSurveyLinkLastClickedOrClosed: (time) => - dispatch(setSurveyLinkLastClickedOrClosed(time)), clearEditedNetwork: () => dispatch(setEditedNetwork()), ///: BEGIN:ONLY_INCLUDE_IF(keyring-snaps) hideShowKeyringSnapRemovalResultModal: () => diff --git a/ui/pages/routes/utils.js b/ui/pages/routes/utils.js index 25e044f0d5b9..31918f91bb07 100644 --- a/ui/pages/routes/utils.js +++ b/ui/pages/routes/utils.js @@ -209,7 +209,3 @@ export function hideAppHeader(props) { isConfirmTransactionRoute(location.pathname) ); } - -export function getShowAutoNetworkSwitchTest(props) { - return props.switchedNetworkDetails && !props.neverShowSwitchedNetworkMessage; -} diff --git a/ui/selectors/selectors.js b/ui/selectors/selectors.js index d00f47bfaa76..2b6a4173159a 100644 --- a/ui/selectors/selectors.js +++ b/ui/selectors/selectors.js @@ -695,16 +695,6 @@ export function getGasIsLoading(state) { return state.appState.gasIsLoading; } -/** - * Retrieves user preference to never see the "Switched Network" toast - * - * @param state - Redux state object. - * @returns Boolean preference value - */ -export function getNeverShowSwitchedNetworkMessage(state) { - return state.metamask.switchedNetworkNeverShowMessage; -} - export const getNetworkConfigurationsByChainId = createDeepEqualSelector( (state) => state.metamask.networkConfigurationsByChainId, /** diff --git a/ui/store/actions.ts b/ui/store/actions.ts index abf047476595..ff473e8a4b56 100644 --- a/ui/store/actions.ts +++ b/ui/store/actions.ts @@ -4242,12 +4242,6 @@ export function setTermsOfUseLastAgreed(lastAgreed: number) { }; } -export function setSurveyLinkLastClickedOrClosed(time: number) { - return async () => { - await submitRequestToBackground('setSurveyLinkLastClickedOrClosed', [time]); - }; -} - export function setLastViewedUserSurvey(id: number) { return async () => { await submitRequestToBackground('setLastViewedUserSurvey', [id]); @@ -4881,12 +4875,6 @@ export function hideNetworkBanner() { return submitRequestToBackground('setShowNetworkBanner', [false]); } -export function neverShowSwitchedNetworkMessage() { - return submitRequestToBackground('setSwitchedNetworkNeverShowMessage', [ - true, - ]); -} - /** * Sends the background state the networkClientId and domain upon network switch * From ae6a5523621597ca964c0e8ef88e081863487f43 Mon Sep 17 00:00:00 2001 From: Howard Braham Date: Mon, 14 Oct 2024 02:19:01 -0700 Subject: [PATCH 30/34] fix lint errors --- .../nfts-detection-notice-nfts-tab.js | 6 ++-- .../app/toast-master/toast-master.js | 28 ++++++++++--------- ui/components/app/toast-master/utils.ts | 6 ++-- ui/pages/routes/routes.component.js | 1 - ui/pages/routes/routes.container.js | 3 +- ui/selectors/selectors.test.js | 5 ++-- 6 files changed, 25 insertions(+), 24 deletions(-) diff --git a/ui/components/app/assets/nfts/nfts-detection-notice-nfts-tab/nfts-detection-notice-nfts-tab.js b/ui/components/app/assets/nfts/nfts-detection-notice-nfts-tab/nfts-detection-notice-nfts-tab.js index 453a5d0217d9..0f8f629cada0 100644 --- a/ui/components/app/assets/nfts/nfts-detection-notice-nfts-tab/nfts-detection-notice-nfts-tab.js +++ b/ui/components/app/assets/nfts/nfts-detection-notice-nfts-tab/nfts-detection-notice-nfts-tab.js @@ -1,14 +1,14 @@ import React from 'react'; import { useDispatch, useSelector } from 'react-redux'; -import { BannerAlert } from '../../../../component-library'; import { useI18nContext } from '../../../../../hooks/useI18nContext'; +import { getOpenSeaEnabled } from '../../../../../selectors'; import { detectNfts, setOpenSeaEnabled, setUseNftDetection, } from '../../../../../store/actions'; -import { getOpenSeaEnabled } from '../../../../../selectors'; -import { setShowNftDetectionEnablementToast } from '../../../toast-master/selectors'; +import { BannerAlert } from '../../../../component-library'; +import { setShowNftDetectionEnablementToast } from '../../../toast-master/utils'; export default function NFTsDetectionNoticeNFTsTab() { const t = useI18nContext(); diff --git a/ui/components/app/toast-master/toast-master.js b/ui/components/app/toast-master/toast-master.js index 99da8dfe8065..ba719dfe37b0 100644 --- a/ui/components/app/toast-master/toast-master.js +++ b/ui/components/app/toast-master/toast-master.js @@ -44,11 +44,11 @@ import { selectShowConnectAccountToast, selectShowPrivacyPolicyToast, selectShowSurveyToast, - selectSwitchedNetworkNeverShowMessage, } from './selectors'; import { getShowAutoNetworkSwitchTest, onHomeScreen, + setNewPrivacyPolicyToastClickedOrClosed, setNewPrivacyPolicyToastShownDate, setShowNftDetectionEnablementToast, setSurveyLinkLastClickedOrClosed, @@ -109,18 +109,18 @@ function ConnectAccountToast() { actionText={t('connectAccount')} onActionClick={() => { // Connect this account - dispatch(addPermittedAccount(activeTabOrigin, account.address)), - // Use setTimeout to prevent React re-render from - // hiding the tooltip - setTimeout(() => { - // Trigger a mouseenter on the header's connection icon - // to display the informative connection tooltip - document - .querySelector( - '[data-testid="connection-menu"] [data-tooltipped]', - ) - ?.dispatchEvent(new CustomEvent('mouseenter', {})); - }, 250 * MILLISECOND); + dispatch(addPermittedAccount(activeTabOrigin, account.address)); + // Use setTimeout to prevent React re-render from + // hiding the tooltip + setTimeout(() => { + // Trigger a mouseenter on the header's connection icon + // to display the informative connection tooltip + document + .querySelector( + '[data-testid="connection-menu"] [data-tooltipped]', + ) + ?.dispatchEvent(new CustomEvent('mouseenter', {})); + }, 250 * MILLISECOND); }} onClose={() => setHideConnectAccountToast(true)} /> @@ -192,6 +192,8 @@ function SwitchedNetworkToast() { const t = useI18nContext(); const dispatch = useDispatch(); + const switchedNetworkDetails = useSelector(getSwitchedNetworkDetails); + return ( getShowAutoNetworkSwitchTest() && ( { }); }); - describe('#getNeverShowSwitchedNetworkMessage', () => { + describe('#selectSwitchedNetworkNeverShowMessage', () => { it('returns the correct value', () => { expect( - selectors.getNeverShowSwitchedNetworkMessage({ + selectSwitchedNetworkNeverShowMessage({ metamask: { switchedNetworkNeverShowMessage: true }, }), ).toStrictEqual(true); From 66bcb758c4d6ae3be30f5eb83d15e61fdd957311 Mon Sep 17 00:00:00 2001 From: Howard Braham Date: Mon, 14 Oct 2024 18:41:50 -0700 Subject: [PATCH 31/34] fixing rules of hooks --- .../app/toast-master/toast-master.js | 23 ++++++++++++++----- ui/components/app/toast-master/utils.ts | 20 ---------------- ui/pages/routes/routes.component.js | 4 ++-- 3 files changed, 19 insertions(+), 28 deletions(-) diff --git a/ui/components/app/toast-master/toast-master.js b/ui/components/app/toast-master/toast-master.js index ba719dfe37b0..863448c906f0 100644 --- a/ui/components/app/toast-master/toast-master.js +++ b/ui/components/app/toast-master/toast-master.js @@ -2,7 +2,7 @@ import React, { useState } from 'react'; import { useDispatch, useSelector } from 'react-redux'; -import { useHistory } from 'react-router-dom'; +import { useHistory, useLocation } from 'react-router-dom'; import { MILLISECOND, SECOND } from '../../../../shared/constants/time'; import { PRIVACY_POLICY_LINK, @@ -14,7 +14,10 @@ import { IconColor, TextVariant, } from '../../../helpers/constants/design-system'; -import { REVIEW_PERMISSIONS } from '../../../helpers/constants/routes'; +import { + DEFAULT_ROUTE, + REVIEW_PERMISSIONS, +} from '../../../helpers/constants/routes'; import { getURLHost } from '../../../helpers/utils/util'; import { useI18nContext } from '../../../hooks/useI18nContext'; import { usePrevious } from '../../../hooks/usePrevious'; @@ -44,10 +47,9 @@ import { selectShowConnectAccountToast, selectShowPrivacyPolicyToast, selectShowSurveyToast, + selectSwitchedNetworkNeverShowMessage, } from './selectors'; import { - getShowAutoNetworkSwitchTest, - onHomeScreen, setNewPrivacyPolicyToastClickedOrClosed, setNewPrivacyPolicyToastShownDate, setShowNftDetectionEnablementToast, @@ -56,8 +58,12 @@ import { } from './utils'; export function ToastMaster() { + const location = useLocation(); + + const onHomeScreen = location.pathname === DEFAULT_ROUTE; + return ( - onHomeScreen() && ( + onHomeScreen && ( @@ -193,9 +199,14 @@ function SwitchedNetworkToast() { const dispatch = useDispatch(); const switchedNetworkDetails = useSelector(getSwitchedNetworkDetails); + const switchedNetworkNeverShowMessage = useSelector( + selectSwitchedNetworkNeverShowMessage, + ); + + const show = switchedNetworkDetails && !switchedNetworkNeverShowMessage; return ( - getShowAutoNetworkSwitchTest() && ( + show && ( { await submitRequestToBackground('setSurveyLinkLastClickedOrClosed', [time]); }; } - -export function getShowAutoNetworkSwitchTest() { - const switchedNetworkDetails = useSelector(getSwitchedNetworkDetails); - const switchedNetworkNeverShowMessage = useSelector( - selectSwitchedNetworkNeverShowMessage, - ); - - return switchedNetworkDetails && !switchedNetworkNeverShowMessage; -} diff --git a/ui/pages/routes/routes.component.js b/ui/pages/routes/routes.component.js index 8161877a7099..e26e17be9e23 100644 --- a/ui/pages/routes/routes.component.js +++ b/ui/pages/routes/routes.component.js @@ -473,7 +473,7 @@ export default class Routes extends Component { })} dir={textDirection} onMouseUp={ - deprecatedGetShowAutoNetworkSwitchTest(this.props) + getShowAutoNetworkSwitchTest(this.props) ? () => clearSwitchedNetworkDetails() : undefined } @@ -549,6 +549,6 @@ export default class Routes extends Component { } // Will eventually delete this function -function deprecatedGetShowAutoNetworkSwitchTest(props) { +function getShowAutoNetworkSwitchTest(props) { return props.switchedNetworkDetails && !props.switchedNetworkNeverShowMessage; } From a43a0770915f10e45f8e46e6e39cb25eba3eb6a5 Mon Sep 17 00:00:00 2001 From: Howard Braham Date: Wed, 16 Oct 2024 09:11:40 -0700 Subject: [PATCH 32/34] submitRequestToBackgroundAndCatch --- ui/components/app/toast-master/utils.ts | 25 ++++++++++++++++----- ui/pages/onboarding-flow/onboarding-flow.js | 4 ++-- 2 files changed, 21 insertions(+), 8 deletions(-) diff --git a/ui/components/app/toast-master/utils.ts b/ui/components/app/toast-master/utils.ts index e55b4f99a09e..025707ba7a50 100644 --- a/ui/components/app/toast-master/utils.ts +++ b/ui/components/app/toast-master/utils.ts @@ -29,11 +29,13 @@ export function getIsPrivacyToastRecent( } export function setNewPrivacyPolicyToastShownDate(time: number) { - submitRequestToBackground('setNewPrivacyPolicyToastShownDate', [time]); + submitRequestToBackgroundAndCatch('setNewPrivacyPolicyToastShownDate', [ + time, + ]); } export function setNewPrivacyPolicyToastClickedOrClosed() { - submitRequestToBackground('setNewPrivacyPolicyToastClickedOrClosed'); + submitRequestToBackgroundAndCatch('setNewPrivacyPolicyToastClickedOrClosed'); } export function setShowNftDetectionEnablementToast( @@ -46,11 +48,22 @@ export function setShowNftDetectionEnablementToast( } export function setSwitchedNetworkNeverShowMessage() { - submitRequestToBackground('setSwitchedNetworkNeverShowMessage', [true]); + submitRequestToBackgroundAndCatch('setSwitchedNetworkNeverShowMessage', [ + true, + ]); } export function setSurveyLinkLastClickedOrClosed(time: number) { - return async () => { - await submitRequestToBackground('setSurveyLinkLastClickedOrClosed', [time]); - }; + submitRequestToBackgroundAndCatch('setSurveyLinkLastClickedOrClosed', [time]); +} + +// May move this to a different file after discussion with team +export function submitRequestToBackgroundAndCatch( + method: string, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + args?: any[], +) { + submitRequestToBackground(method, args).catch((error) => { + console.error('Error caught in submitRequestToBackground', error); + }); } diff --git a/ui/pages/onboarding-flow/onboarding-flow.js b/ui/pages/onboarding-flow/onboarding-flow.js index ecda90c05d53..5b1194229395 100644 --- a/ui/pages/onboarding-flow/onboarding-flow.js +++ b/ui/pages/onboarding-flow/onboarding-flow.js @@ -45,7 +45,7 @@ import ExperimentalArea from '../../components/app/flask/experimental-area'; import OnboardingSuccessful from '../institutional/onboarding-successful/onboarding-successful'; import { RemindSRP } from '../institutional/remind-srp/remind-srp'; ///: END:ONLY_INCLUDE_IF -import { submitRequestToBackground } from '../../store/background-connection'; +import { submitRequestToBackgroundAndCatch } from '../../components/app/toast-master/utils'; import OnboardingFlowSwitch from './onboarding-flow-switch/onboarding-flow-switch'; import CreatePassword from './create-password/create-password'; import ReviewRecoveryPhrase from './recovery-phrase/review-recovery-phrase'; @@ -242,5 +242,5 @@ export default function OnboardingFlow() { } function setOnboardingDate() { - submitRequestToBackground('setOnboardingDate'); + submitRequestToBackgroundAndCatch('setOnboardingDate'); } From 5eb0703ca60dc0a40328a379a900be259935af10 Mon Sep 17 00:00:00 2001 From: Howard Braham Date: Wed, 16 Oct 2024 12:24:33 -0700 Subject: [PATCH 33/34] @MajorLift fix --- ui/components/app/toast-master/utils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/components/app/toast-master/utils.ts b/ui/components/app/toast-master/utils.ts index 025707ba7a50..d6544707f45d 100644 --- a/ui/components/app/toast-master/utils.ts +++ b/ui/components/app/toast-master/utils.ts @@ -63,7 +63,7 @@ export function submitRequestToBackgroundAndCatch( // eslint-disable-next-line @typescript-eslint/no-explicit-any args?: any[], ) { - submitRequestToBackground(method, args).catch((error) => { + submitRequestToBackground(method, args)?.catch((error) => { console.error('Error caught in submitRequestToBackground', error); }); } From bfd7b862101886aad277d599abb56cd0f14cb723 Mon Sep 17 00:00:00 2001 From: Howard Braham Date: Wed, 16 Oct 2024 17:07:01 -0700 Subject: [PATCH 34/34] rename to isShown --- ui/components/app/toast-master/toast-master.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ui/components/app/toast-master/toast-master.js b/ui/components/app/toast-master/toast-master.js index 863448c906f0..584f1cc25983 100644 --- a/ui/components/app/toast-master/toast-master.js +++ b/ui/components/app/toast-master/toast-master.js @@ -203,10 +203,10 @@ function SwitchedNetworkToast() { selectSwitchedNetworkNeverShowMessage, ); - const show = switchedNetworkDetails && !switchedNetworkNeverShowMessage; + const isShown = switchedNetworkDetails && !switchedNetworkNeverShowMessage; return ( - show && ( + isShown && (