Skip to content

Commit

Permalink
feat: add notifications actions selectors - 4/7 (#10338)
Browse files Browse the repository at this point in the history
<!--
Please submit this PR as a draft initially.
Do not mark it as "Ready for review" until the template has been
completely filled out, and PR status checks have passed at least once.
-->

## **Description**

This PR implements Actions, Selections, and utils for the upcoming
Notifications feature. No UI change is presented on this PR.

## **Related issues**

Fixes:

## **Manual testing steps**

1. Go to this page...
2.
3.

## **Screenshots/Recordings**

<!-- If applicable, add screenshots and/or recordings to visualize the
before and after of your change. -->

### **Before**

<!-- [screenshots/recordings] -->

### **After**

<!-- [screenshots/recordings] -->

## **Pre-merge author checklist**

- [x] I’ve followed [MetaMask Contributor
Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile
Coding
Standards](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/CODING_GUIDELINES.md).
- [x] I've completed the PR template to the best of my ability
- [x] I’ve included tests if applicable
- [x] I’ve documented my code using [JSDoc](https://jsdoc.app/) format
if applicable
- [x] I’ve applied the right labels on the PR (see [labeling
guidelines](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/LABELING_GUIDELINES.md)).
Not required for external contributors.

## **Pre-merge reviewer checklist**

- [x] I've manually tested the PR (e.g. pull and build branch, run the
app, test code being changed).
- [x] I confirm that this PR addresses all acceptance criteria described
in the ticket it closes and includes the necessary testing evidence such
as recordings and or screenshots.
  • Loading branch information
Jonathansoufer authored Jul 19, 2024
1 parent 3cdd6de commit 2f69e5f
Show file tree
Hide file tree
Showing 13 changed files with 726 additions and 1,202 deletions.
23 changes: 23 additions & 0 deletions app/actions/notification/constants/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
export enum notificationsErrors {
PERFORM_SIGN_IN = 'Error while trying to sign in',
PERFORM_SIGN_OUT = 'Error while trying to sign out',
ENABLE_PROFILE_SYNCING = 'Error while trying to enable profile syncing',
DISABLE_PROFILE_SYNCING = 'Error while trying to disable profile syncing',
ENABLE_PUSH_NOTIFICATIONS = 'Error while trying to enable push notifications',
DISABLE_PUSH_NOTIFICATIONS = 'Error while trying to disable push notifications',
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',
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',
FETCH_AND_UPDATE_METAMASK_NOTIFICATIONS = 'Error while trying to fetch and update metamask notifications',
MARK_METAMASK_NOTIFICATIONS_AS_READ = 'Error while trying to mark metamask notifications as read',
DELETE_NOTIFICATION_STATUS = 'Error while trying to delete notification',
SET_PARTICIPATE_IN_META_METRICS = 'Error while trying to set participate in meta metrics',
UPDATE_TRIGGER_PUSH_NOTIFICATIONS = 'Error while trying to update trigger push notifications',
ENABLE_NOTIFICATIONS_SERVICES = 'Error while trying to enable notifications services',
DISABLE_NOTIFICATIONS_SERVICES = 'Error while trying to disable notifications services',
}

export default notificationsErrors;
155 changes: 155 additions & 0 deletions app/actions/notification/helpers/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
import { getErrorMessage } from '@metamask/utils';

import { notificationsErrors } from '../constants';
import Engine from '../../../core/Engine';
import { Notification } from '../../../util/notifications';

const {
AuthenticationController,
UserStorageController,
NotificationServicesController,
} = Engine.context;

type MarkAsReadNotificationsParam = Pick<
Notification,
'id' | 'type' | 'isRead'
>[];

export const signIn = async () => {
try {
const accessToken = await AuthenticationController.performSignIn();
if (!accessToken) {
return getErrorMessage(notificationsErrors.PERFORM_SIGN_IN);
}

const profile = await AuthenticationController.getSessionProfile();
if (!profile) {
return getErrorMessage(notificationsErrors.PERFORM_SIGN_IN);
}
} catch (error) {
return getErrorMessage(error);
}
};

export const signOut = async () => {
try {
await AuthenticationController.performSignOut();
} catch (error) {
return getErrorMessage(error);
}
};

export const enableProfileSyncing = async () => {
try {
await UserStorageController.enableProfileSyncing();
} catch (error) {
return getErrorMessage(error);
}
};

export const disableProfileSyncing = async () => {
try {
await NotificationServicesController.disableNotificationServices();
await UserStorageController.disableProfileSyncing();
} catch (error) {
return getErrorMessage(error);
}
};

export const enableNotificationServices = async () => {
try {
await NotificationServicesController.enableMetamaskNotifications();
} catch (error) {
return getErrorMessage(error);
}
};

export const disableNotificationServices = async () => {
try {
await NotificationServicesController.disableNotificationServices();
} catch (error) {
return getErrorMessage(error);
}
};

export const checkAccountsPresence = async (accounts: string[]) => {
try {
const { presence } =
await NotificationServicesController.checkAccountsPresence(accounts);
if (!presence) {
return getErrorMessage(notificationsErrors.CHECK_ACCOUNTS_PRESENCE);
}
} catch (error) {
return getErrorMessage(error);
}
};

export const deleteOnChainTriggersByAccount = async (accounts: string[]) => {
try {
const { userStorage } =
await NotificationServicesController.deleteOnChainTriggersByAccount(
accounts,
);
if (!userStorage) {
return getErrorMessage(
notificationsErrors.DELETE_ON_CHAIN_TRIGGERS_BY_ACCOUNT,
);
}
} catch (error) {
return getErrorMessage(error);
}
};

export const updateOnChainTriggersByAccount = async (accounts: string[]) => {
try {
const { userStorage } =
await NotificationServicesController.updateOnChainTriggersByAccount(
accounts,
);
if (!userStorage) {
return getErrorMessage(
notificationsErrors.UPDATE_ON_CHAIN_TRIGGERS_BY_ACCOUNT,
);
}
} catch (error) {
return getErrorMessage(error);
}
};

export const setFeatureAnnouncementsEnabled = async (
featureAnnouncementsEnabled: boolean,
) => {
try {
await NotificationServicesController.setFeatureAnnouncementsEnabled(
featureAnnouncementsEnabled,
);
} catch (error) {
return getErrorMessage(error);
}
};

export const fetchAndUpdateMetamaskNotifications = async () => {
try {
const metamaskNotifications =
await NotificationServicesController.fetchAndUpdateMetamaskNotifications();
if (!metamaskNotifications) {
return getErrorMessage(
notificationsErrors.FETCH_AND_UPDATE_METAMASK_NOTIFICATIONS,
);
}
} catch (error) {
return getErrorMessage(error);
}
};

export const markMetamaskNotificationsAsRead = async (
notifications: MarkAsReadNotificationsParam,
) => {
try {
await NotificationServicesController.markMetamaskNotificationsAsRead(
notifications,
);
} catch (error) {
return getErrorMessage(error);
}
};
32 changes: 15 additions & 17 deletions app/actions/notification/index.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
/**
* This file contains all the actions related to the in app (old/v1) notification system.
*/
import { ACTIONS } from '../../reducers/notification';

export function hideCurrentNotification() {
return {
type: 'HIDE_CURRENT_NOTIFICATION',
type: ACTIONS.HIDE_CURRENT_NOTIFICATION,
};
}

export function hideNotificationById(id) {
return {
type: 'HIDE_NOTIFICATION_BY_ID',
type: ACTIONS.HIDE_NOTIFICATION_BY_ID,
id,
};
}
Expand All @@ -17,7 +22,7 @@ export function modifyOrShowTransactionNotificationById({
status,
}) {
return {
type: 'MODIFY_OR_SHOW_TRANSACTION_NOTIFICATION',
type: ACTIONS.MODIFY_OR_SHOW_TRANSACTION_NOTIFICATION,
autodismiss,
transaction,
status,
Expand All @@ -31,7 +36,7 @@ export function modifyOrShowSimpleNotificationById({
status,
}) {
return {
type: 'MODIFY_OR_SHOW_SIMPLE_NOTIFICATION',
type: ACTIONS.MODIFY_OR_SHOW_SIMPLE_NOTIFICATION,
autodismiss,
title,
description,
Expand All @@ -41,22 +46,22 @@ export function modifyOrShowSimpleNotificationById({

export function replaceNotificationById(notification) {
return {
type: 'REPLACE_NOTIFICATION_BY_ID',
type: ACTIONS.REPLACE_NOTIFICATION_BY_ID,
notification,
id: notification.id,
};
}

export function removeNotificationById(id) {
return {
type: 'REMOVE_NOTIFICATION_BY_ID',
type: ACTIONS.REMOVE_NOTIFICATION_BY_ID,
id,
};
}

export function removeCurrentNotification() {
return {
type: 'REMOVE_CURRENT_NOTIFICATION',
type: ACTIONS.REMOVE_CURRENT_NOTIFICATION,
};
}

Expand All @@ -69,7 +74,7 @@ export function showSimpleNotification({
}) {
return {
id,
type: 'SHOW_SIMPLE_NOTIFICATION',
type: ACTIONS.SHOW_SIMPLE_NOTIFICATION,
autodismiss,
title,
description,
Expand All @@ -83,7 +88,7 @@ export function showTransactionNotification({
status,
}) {
return {
type: 'SHOW_TRANSACTION_NOTIFICATION',
type: ACTIONS.SHOW_TRANSACTION_NOTIFICATION,
autodismiss,
transaction,
status,
Expand All @@ -92,13 +97,6 @@ export function showTransactionNotification({

export function removeNotVisibleNotifications() {
return {
type: 'REMOVE_NOT_VISIBLE_NOTIFICATIONS',
};
}

export function updateNotificationStatus(notificationsSettings) {
return {
type: 'UPDATE_NOTIFICATION_STATUS',
notificationsSettings,
type: ACTIONS.REMOVE_NOT_VISIBLE_NOTIFICATIONS,
};
}
48 changes: 2 additions & 46 deletions app/components/Views/Settings/NotificationsSettings/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
/* eslint-disable react/display-name */
import React, { FC, useEffect } from 'react';
import { Pressable, ScrollView, Switch, View } from 'react-native';
import { useSelector, useDispatch } from 'react-redux';
import { useSelector } from 'react-redux';
import { camelCase } from 'lodash';

import { strings } from '../../../../../locales/i18n';
Expand All @@ -25,11 +25,9 @@ import {
} from './NotificationsSettings.constants';

import {
notificationSettings as defaultDisabledNotificationSettings,
mmStorage,
requestPushNotificationsPermission,
} from '../../../../util/notifications';
import { updateNotificationStatus } from '../../../../actions/notification';
import { STORAGE_IDS } from '../../../../util/notifications/settings/storage/constants';
import Routes from '../../../../constants/navigation/Routes';
import { IconName } from '../../../../component-library/components/Icons/Icon';
Expand Down Expand Up @@ -72,7 +70,6 @@ const NotificationsSettings = ({ navigation, route }: Props) => {

const isNotificationEnabled = notificationsSettingsState?.isEnabled;

const dispatch = useDispatch();
const { accounts } = useAccounts();

// TODO: Replace "any" with type
Expand All @@ -84,15 +81,7 @@ const NotificationsSettings = ({ navigation, route }: Props) => {
);

const toggleNotificationsEnabled = () => {
!isNotificationEnabled
? requestPushNotificationsPermission()
: dispatch(
updateNotificationStatus({
isEnabled: false,
notificationsOpts: defaultDisabledNotificationSettings,
accounts: [],
}),
);
!isNotificationEnabled && requestPushNotificationsPermission();
};

const isFullScreenModal = route?.params?.isFullScreenModal;
Expand Down Expand Up @@ -120,17 +109,6 @@ const NotificationsSettings = ({ navigation, route }: Props) => {
null,
),
);
dispatch(
updateNotificationStatus({
...notificationsSettingsState,
accounts:
notificationsSettingsState?.accounts ??
accounts.reduce((acc: { [key: string]: boolean }, account) => {
acc[account.address] = true;
return acc;
}, {}),
}),
);
},
// eslint-disable-next-line react-hooks/exhaustive-deps
[colors],
Expand Down Expand Up @@ -192,17 +170,6 @@ const NotificationsSettings = ({ navigation, route }: Props) => {
camelCase(opt.title)
]
}
onOptionUpdated={(value) => {
dispatch(
updateNotificationStatus({
...notificationsSettingsState,
notificationsOpts: {
...notificationsSettingsState.notificationsOpts,
[camelCase(opt.title)]: value,
},
}),
);
}}
testId={NotificationsViewSelectorsIDs[opt.title]}
disabled={opt.disabled}
/>
Expand All @@ -226,17 +193,6 @@ const NotificationsSettings = ({ navigation, route }: Props) => {
value={
notificationsSettingsState?.accounts[account.address] ?? true
}
onOptionUpdated={(value) => {
dispatch(
updateNotificationStatus({
...notificationsSettingsState,
accounts: {
...notificationsSettingsState.accounts,
[account.address]: value,
},
}),
);
}}
/>
))}
</>
Expand Down
9 changes: 0 additions & 9 deletions app/reducers/notification/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,6 @@ const { TRANSACTION, SIMPLE } = NotificationTypes;

export const initialState = {
notifications: [],
notification: {
notificationsSettings: {},
},
};

export const ACTIONS = {
Expand Down Expand Up @@ -193,12 +190,6 @@ const notificationReducer = (state = initialState, action) => {
notifications: visibleNotifications,
};
}
case ACTIONS.UPDATE_NOTIFICATION_STATUS: {
return {
...state,
notificationsSettings: action.notificationsSettings,
};
}
default:
return state;
}
Expand Down
Loading

0 comments on commit 2f69e5f

Please sign in to comment.