Skip to content

Commit

Permalink
Merge pull request #3834 from Emurgo/feat/YOEXT-1689/notifications-se…
Browse files Browse the repository at this point in the history
…ttings

feat(settings): add notifications setting
  • Loading branch information
vsubhuman authored Feb 4, 2025
2 parents bb60607 + 0cdcc6e commit f94932d
Show file tree
Hide file tree
Showing 6 changed files with 185 additions and 4 deletions.
53 changes: 53 additions & 0 deletions packages/yoroi-extension/app/UI/components/Switch/Switch.tsx
Original file line number Diff line number Diff line change
@@ -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) => (
<MuiSwitch focusVisibleClassName=".Mui-focusVisible" disableRipple {...props} />
))(({ 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,
}),
},
}));
Original file line number Diff line number Diff line change
@@ -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;
};
Original file line number Diff line number Diff line change
@@ -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 (
<Box mb="40px">
<Box>
<Typography variant='body1' fontWeight={500} color="ds.text_gray_medium">{strings.notifSettingsTitle}</Typography>
</Box>
<FormControlLabel
label={strings.notifSettingsDesc}
control={
<Box sx={{ alignSelf: 'flex-start' }}>
<Switch
checked={notificationsEnabled}
onChange={handleNotificationsChange}
/>
</Box>
}
labelPlacement="top"
sx={{
mt: '16px',
marginLeft: '0px',
color: 'ds.text_gray_medium',
gap: '16px'
}}
/>
</Box>
);
};

export default NotificationsSettings;
9 changes: 9 additions & 0 deletions packages/yoroi-extension/app/api/localStorage/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down Expand Up @@ -105,6 +106,14 @@ export default class LocalStorageApi {
setSetPortfolioFiatPair: string => Promise<void> = pair => setLocalItem(storageKeys.PORTFOLIO_FIAT_PAIR, pair);

unsetPortfolioFiatPair: void => Promise<void> = () => removeLocalItem(storageKeys.PORTFOLIO_FIAT_PAIR);

// ========== Notifications Setting ========== //

getNotificationsSetting: void => Promise<?string> = () => getLocalItem(storageKeys.NOTIFICATIONS_ENABLED);

setNotificationsSetting: string => Promise<void> = allowed => setLocalItem(storageKeys.NOTIFICATIONS_ENABLED, allowed);

unsetNotificationsSetting: void => Promise<void> = () => removeLocalItem(storageKeys.NOTIFICATIONS_ENABLED);

// ========== Buy/Sell Disclaimer ========== //
getBuySellDisclaimer: void => Promise<?string> = () => getLocalItem(storageKeys.BUY_SELL_DISCLAIMER);
Expand Down
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -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 <StoresProps> {
Expand All @@ -31,6 +34,8 @@ export default class WalletSettingsPage extends Component <StoresProps> {
const { walletSettings } = stores;
const { renameModelRequest, lastUpdatedWalletField, walletFieldBeingEdited } = walletSettings;

const notifFeatFlagEnabled = environment.isDev();

const { selected: selectedWallet, selectedWalletName } = this.props.stores.wallets;
if (selectedWallet == null) {
return (
Expand Down Expand Up @@ -70,8 +75,11 @@ export default class WalletSettingsPage extends Component <StoresProps> {
activeField={walletFieldBeingEdited}
nameValidator={name => isValidWalletName(name)}
/>
{notifFeatFlagEnabled && (
<NotificationsSettings intl={intl}/>
)}
{selectedWallet.type === 'mnemonic' && (
<SpendingPasswordSetting
<SpendingPasswordSetting
openDialog={() =>
stores.uiDialogs.open({
dialog: ChangeWalletPasswordDialogContainer,
Expand Down
2 changes: 2 additions & 0 deletions packages/yoroi-extension/app/i18n/locales/en-US.json
Original file line number Diff line number Diff line change
Expand Up @@ -341,6 +341,8 @@
"settings.unitOfAccount.note": "<strong>Note:</strong> 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",
Expand Down

0 comments on commit f94932d

Please sign in to comment.