diff --git a/app/components/Nav/Main/index.js b/app/components/Nav/Main/index.js index d800e78ce30..5ee817d1970 100644 --- a/app/components/Nav/Main/index.js +++ b/app/components/Nav/Main/index.js @@ -113,9 +113,6 @@ const Main = (props) => { useEnableAutomaticSecurityChecks(); useMinimumVersions(); - - - useEffect(() => { if (DEPRECATED_NETWORKS.includes(props.chainId)) { setShowDeprecatedAlert(true); @@ -268,7 +265,6 @@ const Main = (props) => { initForceReload(); return; } - }); // Remove all notifications that aren't visible diff --git a/app/components/hooks/MinimumVersions/useMinimumVersions.test.ts b/app/components/hooks/MinimumVersions/useMinimumVersions.test.ts new file mode 100644 index 00000000000..529f2d520af --- /dev/null +++ b/app/components/hooks/MinimumVersions/useMinimumVersions.test.ts @@ -0,0 +1,68 @@ +import { renderHook } from '@testing-library/react-hooks'; +import { useSelector } from 'react-redux'; +import { useNavigation } from '@react-navigation/native'; +import { getBuildNumber } from 'react-native-device-info'; +import useMinimumVersions from './useMinimumVersions'; + +jest.mock('react-redux', () => ({ + useSelector: jest.fn(), +})); + +jest.mock('@react-navigation/native', () => ({ + useNavigation: jest.fn(), +})); + +jest.mock('react-native-device-info', () => ({ + getBuildNumber: jest.fn(), +})); + +jest.mock('react-native', () => ({ + InteractionManager: { + runAfterInteractions: jest.fn((callback) => callback()), + }, +})); + +jest.mock('../../UI/UpdateNeeded/UpdateNeeded', () => ({ + createUpdateNeededNavDetails: jest.fn(), +})); + +describe('useMinimumVersions', () => { + const mockNavigation = { + navigate: jest.fn(), + }; + + beforeEach(() => { + jest.clearAllMocks(); + (useNavigation as jest.Mock).mockReturnValue(mockNavigation); + }); + + it('requires update only if automaticSecurityChecksEnabled', () => { + (useSelector as jest.Mock).mockImplementation(() => ({ + security: { automaticSecurityChecksEnabled: false }, + featureFlags: { + featureFlags: { mobileMinimumVersions: { appMinimumBuild: 100 } }, + }, + })); + + (getBuildNumber as jest.Mock).mockReturnValue('101'); + + renderHook(() => useMinimumVersions()); + + expect(mockNavigation.navigate).not.toHaveBeenCalled(); + }); + + it('requires update only if currentBuildNumber is lower than appMinimumBuild', () => { + (useSelector as jest.Mock).mockImplementation(() => ({ + security: { automaticSecurityChecksEnabled: true }, + featureFlags: { + featureFlags: { mobileMinimumVersions: { appMinimumBuild: 100 } }, + }, + })); + + (getBuildNumber as jest.Mock).mockReturnValue('101'); + + renderHook(() => useMinimumVersions()); + + expect(mockNavigation.navigate).not.toHaveBeenCalled(); + }); +}); diff --git a/app/components/hooks/MinimumVersions/useMinimumVersions.tsx b/app/components/hooks/MinimumVersions/useMinimumVersions.tsx index 8cb6b59bf96..88831f75112 100644 --- a/app/components/hooks/MinimumVersions/useMinimumVersions.tsx +++ b/app/components/hooks/MinimumVersions/useMinimumVersions.tsx @@ -1,30 +1,25 @@ -import { useEffect, useMemo } from 'react'; +import { useEffect } from 'react'; import { getBuildNumber } from 'react-native-device-info'; -import { useAppConfig } from '../AppConfig'; import { createUpdateNeededNavDetails } from '../../UI/UpdateNeeded/UpdateNeeded'; import { useSelector } from 'react-redux'; import { useNavigation } from '@react-navigation/native'; import { InteractionManager } from 'react-native'; +import { FeatureFlagsState } from '../../../core/redux/slices/featureFlags'; +import { SecurityState } from '../../../../app/reducers/security'; +import { RootState } from '../../../../app/reducers'; const useMinimumVersions = () => { - const allowAutomaticSecurityChecks = useSelector( - // TODO: Replace "any" with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any - (state: any) => state.security.automaticSecurityChecksEnabled, + const { automaticSecurityChecksEnabled }: SecurityState = useSelector( + (state: RootState) => state.security, + ); + const { featureFlags }: FeatureFlagsState = useSelector( + (state: RootState) => state.featureFlags, ); - const minimumValues = useAppConfig(allowAutomaticSecurityChecks); const currentBuildNumber = Number(getBuildNumber()); const navigation = useNavigation(); - const shouldTriggerUpdateFlow = useMemo( - () => - !!( - allowAutomaticSecurityChecks && - minimumValues.data && - minimumValues.data.security.minimumVersions.appMinimumBuild > - currentBuildNumber - ), - [allowAutomaticSecurityChecks, currentBuildNumber, minimumValues.data], - ); + const shouldTriggerUpdateFlow = + automaticSecurityChecksEnabled && + featureFlags?.mobileMinimumVersions?.appMinimumBuild > currentBuildNumber; useEffect(() => { if (shouldTriggerUpdateFlow) { diff --git a/app/core/AppConstants.ts b/app/core/AppConstants.ts index 080f41edd90..4999b670957 100644 --- a/app/core/AppConstants.ts +++ b/app/core/AppConstants.ts @@ -212,7 +212,7 @@ export default { 'config-api.metamask.io/featureFlags', ], FEATURE_FLAGS_API: { - BASE_URL: 'https://client-config.api.cx.metamask.io/', + BASE_URL: 'https://client-config.api.cx.metamask.io', VERSION: 'v1', }, } as const; diff --git a/app/reducers/index.ts b/app/reducers/index.ts index 51f397e1128..cc8f3b7cd26 100644 --- a/app/reducers/index.ts +++ b/app/reducers/index.ts @@ -21,7 +21,7 @@ import infuraAvailabilityReducer from './infuraAvailability'; import collectiblesReducer from './collectibles'; import navigationReducer from './navigation'; import networkOnboardReducer from './networkSelector'; -import securityReducer from './security'; +import securityReducer, { SecurityState } from './security'; import { combineReducers, Reducer } from 'redux'; import experimentalSettingsReducer from './experimentalSettings'; import { EngineState } from '../core/Engine'; @@ -106,7 +106,7 @@ export interface RootState { // TODO: Replace "any" with type // eslint-disable-next-line @typescript-eslint/no-explicit-any networkOnboarded: any; - security: StateFromReducer; + security: SecurityState; sdk: StateFromReducer; // The experimentalSettings reducer is TypeScript but not yet a valid reducer // TODO: Replace "any" with type diff --git a/app/reducers/security/index.ts b/app/reducers/security/index.ts index f089fadfb25..caba093843a 100644 --- a/app/reducers/security/index.ts +++ b/app/reducers/security/index.ts @@ -2,6 +2,15 @@ import { ActionType, Action } from '../../actions/security'; import { SecuritySettingsState } from '../../actions/security/state'; +export interface SecurityState { + allowLoginWithRememberMe: boolean; + automaticSecurityChecksEnabled: boolean; + hasUserSelectedAutomaticSecurityCheckOption: boolean; + isAutomaticSecurityChecksModalOpen: boolean; + dataCollectionForMarketing: boolean | null; + isNFTAutoDetectionModalViewed: boolean; +} + export const initialState: Readonly = { allowLoginWithRememberMe: false, automaticSecurityChecksEnabled: false,