Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

feat: bootstrap a reset notifications feat #11455

Merged
merged 23 commits into from
Oct 2, 2024
Merged
Show file tree
Hide file tree
Changes from 21 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
063fc36
bootstrap a reset notifications feat
Jonathansoufer Sep 26, 2024
897f2a2
add unit test & clean unnecessary imports
Jonathansoufer Sep 26, 2024
3df0353
add hook call for resetting notifications
Jonathansoufer Sep 26, 2024
834f881
Merge branch 'main' into feat/add-notifications-reset
Jonathansoufer Sep 26, 2024
6be4672
updates snapshot
Jonathansoufer Sep 26, 2024
603a7ad
Merge branch 'main' into feat/add-notifications-reset
Jonathansoufer Sep 27, 2024
94b8ddd
reduce duplicate code
Jonathansoufer Sep 27, 2024
30eacec
remove unnecessary top style
Jonathansoufer Sep 27, 2024
7c5e9da
add optional loading state on baseButton
Jonathansoufer Sep 27, 2024
18c8dde
refactor to handle loading
Jonathansoufer Sep 27, 2024
f96f599
update snapshot
Jonathansoufer Sep 27, 2024
3e5c5f2
Merge branch 'main' into feat/add-notifications-reset
Jonathansoufer Sep 29, 2024
81e7d58
bump controllers version
Jonathansoufer Oct 1, 2024
79fe911
Merge branch 'main' into feat/add-notifications-reset
Jonathansoufer Oct 1, 2024
40e2677
dedup
Jonathansoufer Oct 1, 2024
9e8edcb
Merge branch 'feat/add-notifications-reset' of github.com:MetaMask/me…
Jonathansoufer Oct 1, 2024
d3d92f6
reduce duplicate styles
Jonathansoufer Oct 1, 2024
bfa1261
Merge branch 'main' into feat/add-notifications-reset
Jonathansoufer Oct 1, 2024
a0dbe45
bump versions
Jonathansoufer Oct 2, 2024
b31f2b4
Merge branch 'feat/add-notifications-reset' of github.com:MetaMask/me…
Jonathansoufer Oct 2, 2024
cf4a036
Merge branch 'main' into feat/add-notifications-reset
Jonathansoufer Oct 2, 2024
3b13228
fix test
Jonathansoufer Oct 2, 2024
335a00e
Merge branch 'main' into feat/add-notifications-reset
Jonathansoufer Oct 2, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions app/actions/notification/constants/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export enum notificationsErrors {
CHECK_ACCOUNTS_PRESENCE = 'Error while trying to check accounts presence',
DELETE_ON_CHAIN_TRIGGERS_BY_ACCOUNT = 'Error while trying to delete on chain triggers by account',
UPDATE_ON_CHAIN_TRIGGERS_BY_ACCOUNT = 'Error while trying to update on chain triggers by account',
CREATE_ON_CHAIN_TRIGGERS_BY_ACCOUNT = 'Error while trying to create on chain triggers by account',
SET_FEATURE_ANNOUNCEMENTS_ENABLED = 'Error while trying to set feature announcements enabled',
SET_SNAP_NOTIFICATIONS_ENABLED = 'Error while trying to set snap notifications enabled',
SET_METAMASK_NOTIFICATIONS_FEATURE_SEEN = 'Error while trying to set metamask notifications feature seen',
Expand Down
21 changes: 21 additions & 0 deletions app/actions/notification/helpers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,27 @@ export const updateOnChainTriggersByAccount = async (accounts: string[]) => {
}
};

export const createOnChainTriggersByAccount = async (
resetNotifications: boolean,
) => {
try {
const { userStorage } =
await Engine.context.NotificationServicesController.createOnChainTriggers(
{
resetNotifications,
},
);

if (!userStorage) {
return getErrorMessage(
notificationsErrors.CREATE_ON_CHAIN_TRIGGERS_BY_ACCOUNT,
);
}
} catch (error) {
return getErrorMessage(error);
}
};

export const setFeatureAnnouncementsEnabled = async (
featureAnnouncementsEnabled: boolean,
) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,10 @@ export interface ButtonBaseProps extends TouchableOpacityProps {
* Optional param to disable the button.
*/
isDisabled?: boolean;
/**
* An optional loading state of Button.
*/
loading?: boolean;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

// Third party dependencies.
import React, { useCallback, useState } from 'react';
import { GestureResponderEvent } from 'react-native';
import { ActivityIndicator, GestureResponderEvent } from 'react-native';

// External dependencies.
import { useStyles } from '../../../../../hooks';
Expand Down Expand Up @@ -60,10 +60,14 @@ const ButtonPrimary = ({
label
);

const renderLoading = () => (
<ActivityIndicator size="small" color={DEFAULT_BUTTONPRIMARY_LABEL_COLOR} />
);

return (
<Button
style={styles.base}
label={renderLabel()}
label={!props.loading ? renderLabel() : renderLoading()}
labelColor={DEFAULT_BUTTONPRIMARY_LABEL_COLOR}
onPressIn={triggerOnPressedIn}
onPressOut={triggerOnPressedOut}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

// Third party dependencies.
import React, { useCallback, useState } from 'react';
import { GestureResponderEvent } from 'react-native';
import { ActivityIndicator, GestureResponderEvent } from 'react-native';

// External dependencies.
import { useStyles } from '../../../../../hooks';
Expand Down Expand Up @@ -72,10 +72,14 @@ const ButtonSecondary = ({
label
);

const renderLoading = () => (
<ActivityIndicator size="small" color={DEFAULT_BUTTONSECONDARY_LABEL_TEXTVARIANT} />
);

return (
<Button
style={styles.base}
label={renderLabel()}
label={!props.loading ? renderLabel() : renderLoading()}
labelColor={getLabelColor()}
onPressIn={triggerOnPressedIn}
onPressOut={triggerOnPressedOut}
Expand Down
5 changes: 5 additions & 0 deletions app/components/Nav/App/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@
import BasicFunctionalityModal from '../../UI/BasicFunctionality/BasicFunctionalityModal/BasicFunctionalityModal';
import SmartTransactionsOptInModal from '../../Views/SmartTransactionsOptInModal/SmartTranactionsOptInModal';
import ProfileSyncingModal from '../../UI/ProfileSyncing/ProfileSyncingModal/ProfileSyncingModal';
import ResetNotificationsModal from '../../UI/Notification/ResetNotificationsModal';
import NFTAutoDetectionModal from '../../../../app/components/Views/NFTAutoDetectionModal/NFTAutoDetectionModal';
import NftOptions from '../../../components/Views/NftOptions';
import ShowTokenIdSheet from '../../../components/Views/ShowTokenIdSheet';
Expand Down Expand Up @@ -545,7 +546,7 @@
}
};

const DetectedTokensFlow = () => (

Check warning on line 549 in app/components/Nav/App/index.js

View workflow job for this annotation

GitHub Actions / scripts (lint)

Do not define components during render. React will see a new component type on every render and destroy the entire subtree’s DOM nodes and state (https://reactjs.org/docs/reconciliation.html#elements-of-different-types). Instead, move this component definition out of the parent component “App” and pass data as props
<Stack.Navigator
mode={'modal'}
screenOptions={clearStackNavigatorOptions}
Expand All @@ -559,7 +560,7 @@
</Stack.Navigator>
);

const RootModalFlow = () => (

Check warning on line 563 in app/components/Nav/App/index.js

View workflow job for this annotation

GitHub Actions / scripts (lint)

Do not define components during render. React will see a new component type on every render and destroy the entire subtree’s DOM nodes and state (https://reactjs.org/docs/reconciliation.html#elements-of-different-types). Instead, move this component definition out of the parent component “App” and pass data as props
<Stack.Navigator mode={'modal'} screenOptions={clearStackNavigatorOptions}>
<Stack.Screen
name={Routes.MODAL.WALLET_ACTIONS}
Expand Down Expand Up @@ -634,6 +635,10 @@
name={Routes.SHEET.PROFILE_SYNCING}
component={ProfileSyncingModal}
/>
<Stack.Screen
name={Routes.SHEET.RESET_NOTIFICATIONS}
component={ResetNotificationsModal}
/>
<Stack.Screen
name={Routes.SHEET.RETURN_TO_DAPP_MODAL}
component={ReturnToAppModal}
Expand Down Expand Up @@ -698,7 +703,7 @@
</Stack.Navigator>
);

const ImportPrivateKeyView = () => (

Check warning on line 706 in app/components/Nav/App/index.js

View workflow job for this annotation

GitHub Actions / scripts (lint)

Do not define components during render. React will see a new component type on every render and destroy the entire subtree’s DOM nodes and state (https://reactjs.org/docs/reconciliation.html#elements-of-different-types). Instead, move this component definition out of the parent component “App” and pass data as props
<Stack.Navigator
screenOptions={{
headerShown: false,
Expand All @@ -719,7 +724,7 @@
</Stack.Navigator>
);

const ConnectQRHardwareFlow = () => (

Check warning on line 727 in app/components/Nav/App/index.js

View workflow job for this annotation

GitHub Actions / scripts (lint)

Do not define components during render. React will see a new component type on every render and destroy the entire subtree’s DOM nodes and state (https://reactjs.org/docs/reconciliation.html#elements-of-different-types). Instead, move this component definition out of the parent component “App” and pass data as props
<Stack.Navigator
screenOptions={{
headerShown: false,
Expand All @@ -729,7 +734,7 @@
</Stack.Navigator>
);

const LedgerConnectFlow = () => (

Check warning on line 737 in app/components/Nav/App/index.js

View workflow job for this annotation

GitHub Actions / scripts (lint)

Do not define components during render. React will see a new component type on every render and destroy the entire subtree’s DOM nodes and state (https://reactjs.org/docs/reconciliation.html#elements-of-different-types). Instead, move this component definition out of the parent component “App” and pass data as props
<Stack.Navigator
screenOptions={{
headerShown: false,
Expand All @@ -743,7 +748,7 @@
</Stack.Navigator>
);

const ConnectHardwareWalletFlow = () => (

Check warning on line 751 in app/components/Nav/App/index.js

View workflow job for this annotation

GitHub Actions / scripts (lint)

Do not define components during render. React will see a new component type on every render and destroy the entire subtree’s DOM nodes and state (https://reactjs.org/docs/reconciliation.html#elements-of-different-types). Instead, move this component definition out of the parent component “App” and pass data as props
<Stack.Navigator name="ConnectHardwareWallet">
<Stack.Screen
name={Routes.HW.SELECT_DEVICE}
Expand All @@ -753,14 +758,14 @@
</Stack.Navigator>
);

const EditAccountNameFlow = () => (

Check warning on line 761 in app/components/Nav/App/index.js

View workflow job for this annotation

GitHub Actions / scripts (lint)

Do not define components during render. React will see a new component type on every render and destroy the entire subtree’s DOM nodes and state (https://reactjs.org/docs/reconciliation.html#elements-of-different-types). Instead, move this component definition out of the parent component “App” and pass data as props
<Stack.Navigator>
<Stack.Screen name="EditAccountName" component={EditAccountName} />
</Stack.Navigator>
);

// eslint-disable-next-line react/prop-types
const AddNetworkFlow = ({ route }) => (

Check warning on line 768 in app/components/Nav/App/index.js

View workflow job for this annotation

GitHub Actions / scripts (lint)

Do not define components during render. React will see a new component type on every render and destroy the entire subtree’s DOM nodes and state (https://reactjs.org/docs/reconciliation.html#elements-of-different-types). Instead, move this component definition out of the parent component “App” and pass data as props
<Stack.Navigator>
<Stack.Screen
name="AddNetwork"
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import Button, {
import Checkbox from '../../../../component-library/components/Checkbox/Checkbox';
import { useDispatch, useSelector } from 'react-redux';
import { toggleBasicFunctionality } from '../../../../actions/settings';
import createStyles from './BasicFunctionalityModal.styles';
import createStyles from '../../Notification/Modal/styles';
import { RootState } from 'app/reducers';
import Icon, {
IconColor,
Expand Down
90 changes: 90 additions & 0 deletions app/components/UI/Notification/Modal/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import React from 'react';
import { View } from 'react-native';
import Checkbox from '../../../../component-library/components/Checkbox/Checkbox';
import Icon, {
IconColor,
IconName,
IconSize,
} from '../../../../component-library/components/Icons/Icon';
import Text, {
TextVariant,
} from '../../../../component-library/components/Texts/Text';
import Button, {
ButtonSize,
ButtonVariants,
} from '../../../../component-library/components/Buttons/Button';
import createStyles from './styles';
import { useTheme } from '../../../../util/theme';
interface ModalContentProps {
title: string;
message: string;
iconName: IconName;
iconColor: IconColor;
iconSize: IconSize;
checkBoxLabel: string;
btnLabelCancel: string;
btnLabelCta: string;
isChecked: boolean;
setIsChecked: (isChecked: boolean) => void;
hascheckBox?: boolean | null
handleCta: () => void;
handleCancel: () => void;
loading?: boolean;
}

const ModalContent = ({ title, message, iconName, iconColor, iconSize, checkBoxLabel, btnLabelCancel, btnLabelCta, isChecked, setIsChecked, hascheckBox, handleCancel, handleCta, loading } :ModalContentProps) => {
const { colors } = useTheme();
const styles = createStyles(colors);

return (
<View style={styles.container}>
<Icon
name={iconName}
color={iconColor}
size={iconSize}
style={styles.icon}
/>
<Text variant={TextVariant.HeadingMD} style={styles.title}>
{title}
</Text>
<Text variant={TextVariant.BodyMD} style={styles.description}>
{message}
</Text>
<View style={styles.bottom}>
{hascheckBox && (
<Checkbox
label={checkBoxLabel}
isChecked={isChecked}
onPress={() => setIsChecked(!isChecked)}
/>
)}
<View style={styles.buttonsContainer}>
<Button
variant={ButtonVariants.Secondary}
size={ButtonSize.Lg}
style={styles.button}
accessibilityRole={'button'}
accessible
label={btnLabelCancel}
onPress={handleCancel}
/>
<View style={styles.spacer} />
<Button
variant={ButtonVariants.Primary}
isDisabled={hascheckBox ? !isChecked : false}
isDanger={hascheckBox ?? false}
size={ButtonSize.Lg}
style={styles.button}
accessibilityRole={'button'}
accessible
label={btnLabelCta}
onPress={handleCta}
loading={loading}
/>
</View>
</View>
</View>
);};


export default ModalContent;
Original file line number Diff line number Diff line change
Expand Up @@ -49,4 +49,14 @@ export default (colors: ThemeColors) =>
bottom: { paddingTop: 20 },
spacer: { width: 20 },
icon: { alignSelf: 'center', marginBottom: 10 },
loaderWrapper: {
flex: 1,
alignItems: 'center',
paddingVertical: 30,
},
loader: {
marginTop: 180,
justifyContent: 'center',
textAlign: 'center',
},
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// Third party dependencies.
import React from 'react';

// Internal dependencies.
import ResetNotificationsModal from '.';
import renderWithProvider from '../../../../util/test/renderWithProvider';
import { useNavigation } from '@react-navigation/native';

jest.mock('react-native-safe-area-context', () => {
const inset = { top: 0, right: 0, bottom: 0, left: 0 };
const frame = { width: 0, height: 0, x: 0, y: 0 };
return {
SafeAreaProvider: jest.fn().mockImplementation(({ children }) => children),
SafeAreaConsumer: jest
.fn()
.mockImplementation(({ children }) => children(inset)),
useSafeAreaInsets: jest.fn().mockImplementation(() => inset),
useSafeAreaFrame: jest.fn().mockImplementation(() => frame),
};
});

jest.mock('@react-navigation/native', () => {
const actualReactNavigation = jest.requireActual('@react-navigation/native');
return {
...actualReactNavigation,
useNavigation: () => ({
navigate: jest.fn(),
setOptions: jest.fn(),
goBack: jest.fn(),
reset: jest.fn(),
dangerouslyGetParent: () => ({
pop: jest.fn(),
}),
}),
};
});

describe('ProfileSyncingModal', () => {
it('should render correctly', () => {
const { toJSON } = renderWithProvider(
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
//@ts-ignore
Jonathansoufer marked this conversation as resolved.
Show resolved Hide resolved
<ResetNotificationsModal navigation={useNavigation()} />,
);
expect(toJSON()).toMatchSnapshot();
});
});
Loading
Loading