From 1d6acf3f6b59a34ca42060f635487cd9296bab20 Mon Sep 17 00:00:00 2001 From: Marek Polak Date: Mon, 29 Nov 2021 11:29:52 +0100 Subject: [PATCH] feat(suite): #4512 add AOPP autofill notification --- packages/suite-desktop/package.json | 3 +- .../components/withCoinProtocolScheme.tsx | 115 +++++++----------- .../components/withConditionalAction.tsx | 94 ++++++++++++++ .../suite/hocNotification/index.tsx | 11 +- .../src/reducers/suite/notificationReducer.ts | 5 + packages/suite/src/support/messages.ts | 8 ++ packages/suite/src/support/suite/Protocol.tsx | 21 +++- 7 files changed, 175 insertions(+), 82 deletions(-) create mode 100644 packages/suite/src/components/suite/hocNotification/components/withConditionalAction.tsx diff --git a/packages/suite-desktop/package.json b/packages/suite-desktop/package.json index b6e087baff57..be6b513cfcb2 100644 --- a/packages/suite-desktop/package.json +++ b/packages/suite-desktop/package.json @@ -58,7 +58,8 @@ "protocols": { "name": "Trezor Suite", "schemes": [ - "bitcoin" + "bitcoin", + "aopp" ] }, "publish": { diff --git a/packages/suite/src/components/suite/hocNotification/components/withCoinProtocolScheme.tsx b/packages/suite/src/components/suite/hocNotification/components/withCoinProtocolScheme.tsx index 29a27b0c9c72..90b2287a5e9f 100644 --- a/packages/suite/src/components/suite/hocNotification/components/withCoinProtocolScheme.tsx +++ b/packages/suite/src/components/suite/hocNotification/components/withCoinProtocolScheme.tsx @@ -1,89 +1,60 @@ import React from 'react'; -import { connect } from 'react-redux'; import styled from 'styled-components'; -import { useRouteMatch } from 'react-router-dom'; import * as protocolActions from '@suite-actions/protocolActions'; - -import { useSelector } from '@suite-hooks'; import { Translation } from '@suite-components'; -import { CoinLogo, variables } from '@trezor/components'; +import { CoinLogo } from '@trezor/components'; import { capitalizeFirstLetter } from '@suite-utils/string'; import { PROTOCOL_TO_SYMBOL } from '@suite-reducers/protocolReducer'; +import withConditionalAction from './withConditionalAction'; import type { NotificationEntry } from '@suite-reducers/notificationReducer'; -import type { Dispatch } from '@suite-types'; import type { ViewProps } from '../definitions'; -type StrictViewProps = ViewProps & { - notification: Extract; -}; - -const Header = styled.div` - font-weight: ${variables.FONT_WEIGHT.MEDIUM}; - color: ${props => props.theme.TYPE_LIGHT_GREY}; - margin-top: 1px; -`; - -const Body = styled.div` - font-weight: ${variables.FONT_WEIGHT.MEDIUM}; - margin-top: 1px; -`; - const Row = styled.span` display: flex; `; -const withCoinProtocolScheme = (View: React.ComponentType, props: StrictViewProps) => { - const WrappedView = connect()(({ dispatch }: { dispatch: Dispatch }) => { - const { message, notification } = props; - - const { selectedAccount } = useSelector(state => ({ - selectedAccount: state.wallet.selectedAccount, - })); - - if (typeof message !== 'string') { - message.values = { - header: ( -
- -
- ), - body: ( - - - {notification.amount && `${notification.amount} `} - {capitalizeFirstLetter(notification.scheme)} - - {notification.address} - - ), - }; - } - - const isCorrectCoinSendForm = - useRouteMatch(`${process.env.ASSET_PREFIX || ''}/accounts/send`) && - selectedAccount?.network?.symbol === PROTOCOL_TO_SYMBOL[notification.scheme]; - - return ( - dispatch(protocolActions.fillSendForm(true)), - label: 'TOAST_COIN_SCHEME_PROTOCOL_ACTION', - position: 'bottom', - variant: 'primary', - } - : undefined - } - icon={} - onCancel={() => dispatch(protocolActions.resetProtocol())} - /> - ); +export const withAoppProtocol = ( + View: React.ComponentType, + notification: Extract, +) => + withConditionalAction(View, { + notification, + header: , + body: notification.message, + icon: , + actionLabel: 'TOAST_AOPP_FILL_ACTION', + actionCondition: { + path: '/accounts/sign-verify', + network: notification.asset, + }, + onAction: protocolActions.fillAopp(true), + onCancel: protocolActions.resetProtocol(), }); - return ; -}; -export default withCoinProtocolScheme; +export const withCoinProtocol = ( + View: React.ComponentType, + notification: Extract, +) => + withConditionalAction(View, { + notification, + header: , + body: ( + <> + + {notification.amount && `${notification.amount} `} + {capitalizeFirstLetter(notification.scheme)} + + {notification.address} + + ), + actionLabel: 'TOAST_COIN_SCHEME_PROTOCOL_ACTION', + actionCondition: { + path: '/accounts/send', + network: PROTOCOL_TO_SYMBOL[notification.scheme], + }, + onAction: protocolActions.fillSendForm(true), + onCancel: protocolActions.resetProtocol(), + icon: , + }); diff --git a/packages/suite/src/components/suite/hocNotification/components/withConditionalAction.tsx b/packages/suite/src/components/suite/hocNotification/components/withConditionalAction.tsx new file mode 100644 index 000000000000..a3b4238f79d3 --- /dev/null +++ b/packages/suite/src/components/suite/hocNotification/components/withConditionalAction.tsx @@ -0,0 +1,94 @@ +import React from 'react'; +import styled from 'styled-components'; +import { connect } from 'react-redux'; +import { useRouteMatch } from 'react-router-dom'; + +import { useSelector } from '@suite-hooks'; +import { variables } from '@trezor/components'; + +import type { NotificationEntry } from '@suite-reducers/notificationReducer'; +import type { Dispatch, Action, ExtendedMessageDescriptor } from '@suite-types'; +import type { Network } from '@wallet-types'; +import type { ViewProps } from '../definitions'; + +const Header = styled.div` + font-weight: ${variables.FONT_WEIGHT.MEDIUM}; + color: ${props => props.theme.TYPE_LIGHT_GREY}; + margin-top: 1px; +`; + +const Body = styled.div` + font-weight: ${variables.FONT_WEIGHT.MEDIUM}; + margin-top: 1px; +`; + +type WithConditionalActionProps = { + notification: NotificationEntry; + header: React.ReactNode; + body: React.ReactNode; + icon?: JSX.Element; + actionLabel: ExtendedMessageDescriptor['id']; + actionCondition: { + path?: string; + network?: Network['symbol']; + }; + onAction: Action; + onCancel: Action; +}; + +const withConditionalAction = ( + View: React.ComponentType, + props: WithConditionalActionProps, +) => { + const WrappedView = connect()(({ dispatch }: { dispatch: Dispatch }) => { + const { + notification, + header, + body, + icon, + actionCondition: { path, network }, + actionLabel, + onAction, + onCancel, + } = props; + + const { selectedAccount } = useSelector(state => ({ + selectedAccount: state.wallet.selectedAccount, + })); + + const actionAllowed = + (!path || useRouteMatch(`${process.env.ASSET_PREFIX || ''}${path}`)) && + (!network || selectedAccount?.network?.symbol === network); + + const action = actionAllowed + ? ({ + onClick: () => dispatch(onAction), + label: actionLabel, + position: 'bottom', + variant: 'primary', + } as const) + : undefined; + + const message = { + id: 'TOAST_COIN_SCHEME_PROTOCOL', + values: { + header:
{header}
, + body: {body}, + }, + } as const; + + return ( + dispatch(onCancel)} + /> + ); + }); + return ; +}; + +export default withConditionalAction; diff --git a/packages/suite/src/components/suite/hocNotification/index.tsx b/packages/suite/src/components/suite/hocNotification/index.tsx index f4bfac9dae24..4c1537167884 100644 --- a/packages/suite/src/components/suite/hocNotification/index.tsx +++ b/packages/suite/src/components/suite/hocNotification/index.tsx @@ -4,7 +4,7 @@ import { SUITE } from '@suite-actions/constants'; import { NotificationEntry } from '@suite-reducers/notificationReducer'; import withAction from './components/withAction'; import withTransaction from './components/withTransaction'; -import withCoinProtocolScheme from './components/withCoinProtocolScheme'; +import { withAoppProtocol, withCoinProtocol } from './components/withCoinProtocolScheme'; import { ViewProps } from './definitions'; const simple = (View: React.ComponentType, props: ViewProps) => ( @@ -21,12 +21,9 @@ const simple = (View: React.ComponentType, props: ViewProps) => ( const hocNotification = (notification: NotificationEntry, View: React.ComponentType) => { switch (notification.type) { case 'coin-scheme-protocol': - return withCoinProtocolScheme(View, { - notification, - variant: 'transparent', - // values get filled in `withCoinProtocolScheme` - message: { id: 'TOAST_COIN_SCHEME_PROTOCOL', values: {} }, - }); + return withCoinProtocol(View, notification); + case 'aopp-protocol': + return withAoppProtocol(View, notification); case 'acquire-error': return simple(View, { notification, diff --git a/packages/suite/src/reducers/suite/notificationReducer.ts b/packages/suite/src/reducers/suite/notificationReducer.ts index d47ebb81c393..e4fc8fa5e5f2 100644 --- a/packages/suite/src/reducers/suite/notificationReducer.ts +++ b/packages/suite/src/reducers/suite/notificationReducer.ts @@ -91,6 +91,11 @@ export type ToastPayload = ( address: string; amount?: number; } + | { + type: 'aopp-protocol'; + message: string; + asset: Network['symbol']; + } ) & Options; diff --git a/packages/suite/src/support/messages.ts b/packages/suite/src/support/messages.ts index 823ffb879122..62409002e75a 100644 --- a/packages/suite/src/support/messages.ts +++ b/packages/suite/src/support/messages.ts @@ -6882,4 +6882,12 @@ export default defineMessages({ id: 'TR_DO_YOU_REALLY_WANT_TO_SKIP', defaultMessage: 'Do you really want to skip this step?', }, + TOAST_AOPP_FILL_HEADER: { + id: 'TOAST_AOPP_FILL_HEADER', + defaultMessage: 'Go to Sign & Verify form', + }, + TOAST_AOPP_FILL_ACTION: { + id: 'TOAST_AOPP_FILL_ACTION', + defaultMessage: 'Fill form', + }, }); diff --git a/packages/suite/src/support/suite/Protocol.tsx b/packages/suite/src/support/suite/Protocol.tsx index 1cda3c7e32c8..15b513310879 100644 --- a/packages/suite/src/support/suite/Protocol.tsx +++ b/packages/suite/src/support/suite/Protocol.tsx @@ -9,9 +9,10 @@ import * as protocolActions from '@suite-actions/protocolActions'; import { PROTOCOL_SCHEME } from '@suite-reducers/protocolReducer'; const Protocol = () => { - const { addToast, saveCoinProtocol } = useActions({ + const { addToast, saveCoinProtocol, saveAoppProtocol } = useActions({ addToast: notificationActions.addToast, saveCoinProtocol: protocolActions.saveCoinProtocol, + saveAoppProtocol: protocolActions.saveAoppProtocol, }); const handleProtocolRequest = useCallback( @@ -30,11 +31,27 @@ const Protocol = () => { }); break; } + case PROTOCOL_SCHEME.AOPP: { + const { asset, msg, callback, format } = protocolInfo; + saveAoppProtocol({ + asset, + message: msg, + callback, + format, + }); + addToast({ + type: 'aopp-protocol', + message: msg, + asset, + autoClose: false, + }); + break; + } default: break; } }, - [addToast, saveCoinProtocol], + [addToast, saveCoinProtocol, saveAoppProtocol], ); const { search } = useLocation();