diff --git a/packages/yoroi-extension/app/UI/components/Switch/Switch.tsx b/packages/yoroi-extension/app/UI/components/Switch/Switch.tsx
new file mode 100644
index 0000000000..a23ebcdd11
--- /dev/null
+++ b/packages/yoroi-extension/app/UI/components/Switch/Switch.tsx
@@ -0,0 +1,53 @@
+import * as React from 'react';
+import styled from '@emotion/styled';
+import { Switch as MuiSwitch } from '@mui/material';
+
+const height = 31;
+const width = 51;
+
+export const Switch: any = styled((props: any) => (
+
+))(({ theme }) => ({
+ width,
+ height,
+ padding: 0,
+ '& .MuiSwitch-switchBase': {
+ padding: 0,
+ margin: 3,
+ transitionDuration: '300ms',
+ '&.Mui-checked': {
+ transform: 'translateX(20px)',
+ color: '#fff',
+ '& + .MuiSwitch-track': {
+ backgroundColor: theme.palette.primary[500],
+ opacity: 1,
+ border: 0,
+ },
+ '&.Mui-disabled + .MuiSwitch-track': {
+ opacity: 0.5,
+ },
+ },
+ '&.Mui-focusVisible .MuiSwitch-thumb': {
+ border: '6px solid #fff',
+ },
+ '&.Mui-disabled .MuiSwitch-thumb': {
+ color: theme.palette.grey[100],
+ },
+ '&.Mui-disabled + .MuiSwitch-track': {
+ opacity: 0.7,
+ },
+ },
+ '& .MuiSwitch-thumb': {
+ boxSizing: 'border-box',
+ width: '25px',
+ height: '25px'
+ },
+ '& .MuiSwitch-track': {
+ borderRadius: height / 2,
+ backgroundColor: theme.palette.grayscale[100],
+ opacity: 1,
+ transition: theme.transitions.create(['background-color'], {
+ duration: 500,
+ }),
+ },
+}));
diff --git a/packages/yoroi-extension/app/UI/features/notifications/common/hooks/useStrings.ts b/packages/yoroi-extension/app/UI/features/notifications/common/hooks/useStrings.ts
new file mode 100644
index 0000000000..9da5369c7d
--- /dev/null
+++ b/packages/yoroi-extension/app/UI/features/notifications/common/hooks/useStrings.ts
@@ -0,0 +1,28 @@
+import React from 'react';
+import { defineMessages } from 'react-intl';
+import { useIntl } from '../../../../context/IntlProvider';
+
+export const messages = Object.freeze(
+ defineMessages({
+ notifSettingsTitle: {
+ id: 'notifications.settings.title',
+ defaultMessage: '!!!In-app notifications',
+ },
+ notifSettingsDesc: {
+ id: 'notifications.settings.description',
+ defaultMessage:
+ '!!!Allow display of in-app notifications for key transactions',
+ },
+ })
+);
+
+export const useStrings = (intl = null) => {
+ const { intl: contextIntl } = useIntl();
+
+ const i = intl || contextIntl;
+
+ return React.useRef({
+ notifSettingsTitle: i.formatMessage(messages.notifSettingsTitle),
+ notifSettingsDesc: i.formatMessage(messages.notifSettingsDesc),
+ }).current;
+};
diff --git a/packages/yoroi-extension/app/UI/features/notifications/useCases/NotificationsSettings/NotificationsSettings.tsx b/packages/yoroi-extension/app/UI/features/notifications/useCases/NotificationsSettings/NotificationsSettings.tsx
new file mode 100644
index 0000000000..2e588a24b5
--- /dev/null
+++ b/packages/yoroi-extension/app/UI/features/notifications/useCases/NotificationsSettings/NotificationsSettings.tsx
@@ -0,0 +1,81 @@
+import * as React from 'react';
+import { Box, FormControlLabel, Typography } from '@mui/material';
+import { useStrings } from '../../common/hooks/useStrings';
+import { Switch } from '../../../../components/Switch/Switch';
+import LocalStorageApi from '../../../../../api/localStorage';
+import { ampli } from '../../../../../../ampli';
+
+const NotificationsSettings = ({ intl }) => {
+ const strings = useStrings(intl);
+ const [notificationsEnabled, setNotificationsEnabled] = React.useState(true);
+ const [selectedWalletId, setSelectedWalletId] = React.useState("");
+
+ const lsApi = new LocalStorageApi();
+
+ async function getNotificationsSetting(checkCurrentWallet: boolean = false) {
+ const notifSettingsStr = await lsApi.getNotificationsSetting();
+ const notifSettings = JSON.parse(notifSettingsStr || "{}");
+
+ if (checkCurrentWallet) {
+ const selectedWalletId = await lsApi.getSelectedWalletId();
+ setSelectedWalletId(selectedWalletId);
+
+ return notifSettings[selectedWalletId] !== undefined ? notifSettings[selectedWalletId] : true;
+ }
+
+ return notifSettings;
+ }
+
+ async function setNotificationsSetting(enabled: boolean) {
+ const notifSettings = await getNotificationsSetting();
+ lsApi.setNotificationsSetting(JSON.stringify({ ...notifSettings, [selectedWalletId]: enabled }));
+ }
+
+ // get initial state from localstorage
+ React.useEffect(() => {
+ async function initialNotifStatus() {
+ const notifEnabled = await getNotificationsSetting(true);
+ setNotificationsEnabled(notifEnabled);
+ }
+
+ initialNotifStatus();
+ }, [])
+
+ // handle checkbox change event
+ const handleNotificationsChange = async (event) => {
+ const enabled = event.target.checked;
+ setNotificationsEnabled(enabled);
+ setNotificationsSetting(enabled);
+ ampli.settingsInAppNotificationsStatusUpdated({
+ status: event.target.checked ? "enabled" : "disabled"
+ })
+ }
+
+ return (
+
+
+ {strings.notifSettingsTitle}
+
+
+
+
+ }
+ labelPlacement="top"
+ sx={{
+ mt: '16px',
+ marginLeft: '0px',
+ color: 'ds.text_gray_medium',
+ gap: '16px'
+ }}
+ />
+
+ );
+};
+
+export default NotificationsSettings;
\ No newline at end of file
diff --git a/packages/yoroi-extension/app/api/localStorage/index.js b/packages/yoroi-extension/app/api/localStorage/index.js
index 33a9fe25b8..0335d11ef6 100644
--- a/packages/yoroi-extension/app/api/localStorage/index.js
+++ b/packages/yoroi-extension/app/api/localStorage/index.js
@@ -33,6 +33,7 @@ const storageKeys = {
FLAGS: networkForLocalStorage + '-FLAGS',
USER_THEME: networkForLocalStorage + '-USER-THEME',
PORTFOLIO_FIAT_PAIR: networkForLocalStorage + '-PORTFOLIO_FIAT_PAIR',
+ NOTIFICATIONS_ENABLED: networkForLocalStorage + '-NOTIFICATIONS_ENABLED_PER_WALLET',
BUY_SELL_DISCLAIMER: networkForLocalStorage + '-BUY_SELL_DISCLAIMER',
// ========== CONNECTOR ========== //
DAPP_CONNECTOR_WHITELIST: 'connector_whitelist',
@@ -105,6 +106,14 @@ export default class LocalStorageApi {
setSetPortfolioFiatPair: string => Promise = pair => setLocalItem(storageKeys.PORTFOLIO_FIAT_PAIR, pair);
unsetPortfolioFiatPair: void => Promise = () => removeLocalItem(storageKeys.PORTFOLIO_FIAT_PAIR);
+
+ // ========== Notifications Setting ========== //
+
+ getNotificationsSetting: void => Promise = () => getLocalItem(storageKeys.NOTIFICATIONS_ENABLED);
+
+ setNotificationsSetting: string => Promise = allowed => setLocalItem(storageKeys.NOTIFICATIONS_ENABLED, allowed);
+
+ unsetNotificationsSetting: void => Promise = () => removeLocalItem(storageKeys.NOTIFICATIONS_ENABLED);
// ========== Buy/Sell Disclaimer ========== //
getBuySellDisclaimer: void => Promise = () => getLocalItem(storageKeys.BUY_SELL_DISCLAIMER);
diff --git a/packages/yoroi-extension/app/containers/settings/categories/WalletSettingsPage.js b/packages/yoroi-extension/app/containers/settings/categories/WalletSettingsPage.js
index ac4f60a878..4d615111f8 100644
--- a/packages/yoroi-extension/app/containers/settings/categories/WalletSettingsPage.js
+++ b/packages/yoroi-extension/app/containers/settings/categories/WalletSettingsPage.js
@@ -1,6 +1,8 @@
// @flow
-import { Component } from 'react';
import type { Node } from 'react';
+import type { $npm$ReactIntl$IntlFormat } from 'react-intl';
+import type { StoresProps } from '../../../stores';
+import { Component } from 'react';
import { observer } from 'mobx-react';
import WalletNameSetting from '../../../components/wallet/settings/WalletNameSetting';
import NoWalletMessage from '../../wallet/NoWalletMessage';
@@ -15,9 +17,10 @@ import { isValidWalletName } from '../../../utils/validations';
import ChangeWalletPasswordDialogContainer from '../../wallet/dialogs/ChangeWalletPasswordDialogContainer';
import { Typography } from '@mui/material';
import { intlShape } from 'react-intl';
-import type { $npm$ReactIntl$IntlFormat } from 'react-intl';
import globalMessages from '../../../i18n/global-messages';
-import type { StoresProps } from '../../../stores';
+// $FlowIgnore: suppressing this error
+import NotificationsSettings from '../../../UI/features/notifications/useCases/NotificationsSettings/NotificationsSettings'
+import environment from '../../../environment';
@observer
export default class WalletSettingsPage extends Component {
@@ -31,6 +34,8 @@ export default class WalletSettingsPage extends Component {
const { walletSettings } = stores;
const { renameModelRequest, lastUpdatedWalletField, walletFieldBeingEdited } = walletSettings;
+ const notifFeatFlagEnabled = environment.isDev();
+
const { selected: selectedWallet, selectedWalletName } = this.props.stores.wallets;
if (selectedWallet == null) {
return (
@@ -70,8 +75,11 @@ export default class WalletSettingsPage extends Component {
activeField={walletFieldBeingEdited}
nameValidator={name => isValidWalletName(name)}
/>
+ {notifFeatFlagEnabled && (
+
+ )}
{selectedWallet.type === 'mnemonic' && (
-
stores.uiDialogs.open({
dialog: ChangeWalletPasswordDialogContainer,
diff --git a/packages/yoroi-extension/app/i18n/locales/en-US.json b/packages/yoroi-extension/app/i18n/locales/en-US.json
index 34a1d9d7b6..96f16de246 100644
--- a/packages/yoroi-extension/app/i18n/locales/en-US.json
+++ b/packages/yoroi-extension/app/i18n/locales/en-US.json
@@ -341,6 +341,8 @@
"settings.unitOfAccount.note": "Note: coin price is approximate and may not match the price of any given trading platform. Any transactions based on these price approximates are done at your own risk.",
"settings.unitOfAccount.revamp.label": "Select currency",
"settings.unitOfAccount.title": "Fiat pairing",
+ "notifications.settings.title": "In-app notifications",
+ "notifications.settings.description": "Allow display of in-app notifications for key transactions",
"sidebar.assets": "Assets",
"sidebar.faq": "Faq",
"sidebar.feedback": "Feedback",