diff --git a/packages/yoroi-extension/.flowconfig b/packages/yoroi-extension/.flowconfig index dd79bc1a9d..022d677024 100644 --- a/packages/yoroi-extension/.flowconfig +++ b/packages/yoroi-extension/.flowconfig @@ -31,7 +31,7 @@ deprecated-utility=error [options] types_first=true -include_warnings=true +include_warnings=false esproposal.decorators=ignore module.ignore_non_literal_requires=true module.name_mapper.extension='scss' -> '/flow/mappers/CSSModule.js.flow' diff --git a/packages/yoroi-extension/app/assets/images/revamp/generate-uri.inline.svg b/packages/yoroi-extension/app/assets/images/revamp/generate-uri.inline.svg new file mode 100644 index 0000000000..8f08f9c00d --- /dev/null +++ b/packages/yoroi-extension/app/assets/images/revamp/generate-uri.inline.svg @@ -0,0 +1,4 @@ + + + + diff --git a/packages/yoroi-extension/app/assets/images/revamp/info.inline.svg b/packages/yoroi-extension/app/assets/images/revamp/info.inline.svg new file mode 100644 index 0000000000..890c6157cc --- /dev/null +++ b/packages/yoroi-extension/app/assets/images/revamp/info.inline.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/packages/yoroi-extension/app/assets/images/revamp/verify-icon.inline.svg b/packages/yoroi-extension/app/assets/images/revamp/verify-icon.inline.svg new file mode 100644 index 0000000000..3f7bea4817 --- /dev/null +++ b/packages/yoroi-extension/app/assets/images/revamp/verify-icon.inline.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/yoroi-extension/app/components/wallet/WalletReceiveRevamp.js b/packages/yoroi-extension/app/components/wallet/WalletReceiveRevamp.js new file mode 100644 index 0000000000..20e2d8939e --- /dev/null +++ b/packages/yoroi-extension/app/components/wallet/WalletReceiveRevamp.js @@ -0,0 +1,275 @@ +// @flow +import type { Node } from 'react'; +import type { AddressFilterKind, StandardAddress } from '../../types/AddressFilterTypes'; +import type { Notification } from '../../types/notificationType'; +import type { $npm$ReactIntl$IntlFormat } from 'react-intl'; +import type { UnitOfAccountSettingType } from '../../types/unitOfAccountType'; +import type { TokenEntry, TokenLookupKey } from '../../api/common/lib/MultiToken'; +import type { TokenRow } from '../../api/ada/lib/storage/database/primitives/tables'; +import { Component } from 'react'; +import { observer } from 'mobx-react'; +import { defineMessages, intlShape } from 'react-intl'; +import classnames from 'classnames'; +import { ReactComponent as VerifyIcon } from '../../assets/images/revamp/verify-icon.inline.svg'; +import { ReactComponent as GenerateURIIcon } from '../../assets/images/revamp/generate-uri.inline.svg'; +import styles from './WalletReceiveRevamp.scss'; +import CopyableAddress from '../widgets/CopyableAddress'; +import RawHash from '../widgets/hashWrappers/RawHash'; +import ExplorableHashContainer from '../../containers/widgets/ExplorableHashContainer'; +import { SelectedExplorer } from '../../domain/SelectedExplorer'; +import { truncateAddressShort, splitAmount, truncateToken } from '../../utils/formatters'; +import { ReactComponent as NoTransactionModernSvg } from '../../assets/images/transaction/no-transactions-yet.modern.inline.svg'; +import { hiddenAmount } from '../../utils/strings'; +import { getTokenName } from '../../stores/stateless/tokenHelpers'; +import { CoreAddressTypes } from '../../api/ada/lib/storage/database/primitives/enums'; +import { Box, Typography } from '@mui/material'; + +const messages = defineMessages({ + generatedAddressesSectionTitle: { + id: 'wallet.receive.page.generatedAddressesSectionTitle', + defaultMessage: '!!!Generated addresses', + }, + copyAddressLabel: { + id: 'wallet.receive.page.copyAddressLabel', + defaultMessage: '!!!Copy address', + }, + verifyAddressLabel: { + id: 'wallet.receive.page.verifyAddressLabel', + defaultMessage: '!!!Verify address', + }, + generateURLLabel: { + id: 'wallet.receive.page.generateURLLabel', + defaultMessage: '!!!Generate URL', + }, + outputAmountUTXO: { + id: 'wallet.revamp.receive.page.outputAmountUTXO', + defaultMessage: '!!!Output Amount (UTXO)', + }, + noResultsFoundLabel: { + id: 'wallet.receive.page.noResultsFoundLabel', + defaultMessage: '!!!No results found.', + }, + notFoundAnyAddresses: { + id: 'wallet.receive.page.notFoundAnyAddresses', + defaultMessage: '!!!No wallet addresses have been used yet.', + }, + label: { + id: 'wallet.receive.page.label', + defaultMessage: '!!!Label ', + }, +}); + +type Props = {| + +hierarchy: {| + path: Array, + filter: AddressFilterKind, + |}, + +header: Node, + +selectedExplorer: SelectedExplorer, + +walletAddresses: $ReadOnlyArray<$ReadOnly>, + +onCopyAddressTooltip: (string, string) => void, + +notification: ?Notification, + +onVerifyAddress: ($ReadOnly) => Promise, + +onGeneratePaymentURI: void | (string => void), + +shouldHideBalance: boolean, + +unitOfAccountSetting: UnitOfAccountSettingType, + +getTokenInfo: ($ReadOnly>) => $ReadOnly, + +addressBook: boolean, +|}; + +@observer +export default class WalletReceiveRevamp extends Component { + static contextTypes: {| intl: $npm$ReactIntl$IntlFormat |} = { + intl: intlShape.isRequired, + }; + + getAmount: TokenEntry => ?Node = tokenEntry => { + if (this.props.shouldHideBalance) { + return {hiddenAmount}; + } + const tokenInfo = this.props.getTokenInfo(tokenEntry); + + const shiftedAmount = tokenEntry.amount.shiftedBy(-tokenInfo.Metadata.numberOfDecimals); + + const [beforeDecimalRewards, afterDecimalRewards] = splitAmount( + shiftedAmount, + tokenInfo.Metadata.numberOfDecimals + ); + // recall: can't be negative in this situation + const adjustedBefore = '+' + beforeDecimalRewards; + + return ( + <> + {adjustedBefore} + {afterDecimalRewards}{' '} + {truncateToken(getTokenName(tokenInfo))} + + ); + }; + + getValueBlock: void => {| + header: ?Node, + body: ($ReadOnly) => ?Node, + |} = () => { + if (this.props.addressBook) { + return { header: undefined, body: () => undefined }; + } + const { intl } = this.context; + + const header = ( + + {intl.formatMessage(messages.outputAmountUTXO)} + + ); + const body = address => ( + + {address.values != null ? ( + {this.getAmount(address.values.getDefaultEntry())} + ) : ( + '-' + )} + + ); + return { header, body }; + }; + + render(): Node { + const { + walletAddresses, + onVerifyAddress, + onGeneratePaymentURI, + onCopyAddressTooltip, + notification, + } = this.props; + const { intl } = this.context; + const valueBlock = this.getValueBlock(); + const walletReceiveContent = ( +
+ {/* Header Addresses */} + + + {intl.formatMessage(messages.generatedAddressesSectionTitle)} + + {valueBlock.header} + {onGeneratePaymentURI != null && ( + + {intl.formatMessage(messages.generateURLLabel)} + + )} + + {intl.formatMessage(messages.verifyAddressLabel)} + + + + {/* Content Addresses */} + {walletAddresses.map((address, index) => { + const addressClasses = classnames([ + 'generatedAddress-' + (index + 1), + styles.walletAddress, + styles.generatedAddressesGrid, + address.isUsed === true ? styles.usedWalletAddress : null, + ]); + const notificationElementId = `address-${index}-copyNotification`; + return ( + + {/* Address Id */} + onCopyAddressTooltip(address.address, notificationElementId)} + notification={notification} + placementTooltip="bottom-start" + > + + + + {truncateAddressShort(address.address, 16)} + + + + + {/* Address balance block start */} + {valueBlock.body(address)} + {/* Generate payment URL for Address action */} + {onGeneratePaymentURI != null && ( + + + + )} + {/* Verify Address action */} + + + + {/* Action block end */} + + ); + })} +
+ ); + + if (walletAddresses === undefined || walletAddresses.length === 0) { + return ( +
+ {this.props.header} +
+ +

{intl.formatMessage(messages.noResultsFoundLabel)}

+

{intl.formatMessage(messages.notFoundAnyAddresses)}

+
+
+ ); + } + + return ( + + {this.props.header} + {walletReceiveContent} + + ); + } +} diff --git a/packages/yoroi-extension/app/components/wallet/WalletReceiveRevamp.scss b/packages/yoroi-extension/app/components/wallet/WalletReceiveRevamp.scss new file mode 100644 index 0000000000..055f741563 --- /dev/null +++ b/packages/yoroi-extension/app/components/wallet/WalletReceiveRevamp.scss @@ -0,0 +1,163 @@ +.component { + width: 100%; + overflow-y: overlay; + padding-left: 24px; + + .hierarchy { + margin-bottom: 24px; + font-weight: 400; + font-size: 20px; + padding-bottom: 5px; + border-bottom: 1px solid var(--yoroi-palette-gray-200); + + .filter { + font-size: 14px; + margin-left: 14px; + } + } + + &::-webkit-scrollbar-button { + height: 7px; + display: block; + } + + .notFound { + padding-top: 2rem; + text-align: center; + color: var(--yoroi-palette-gray-600); + svg { + max-width: 140px; + max-height: 140px; + } + h1 { + margin-bottom: 20px; + font-size: 1.2rem; + font-weight: 500; + } + p { + margin: 1rem 0; + font-size: 0.9rem; + } + } + + .generatedAddresses { + color: var(--yoroi-palette-gray-900); + font-weight: 400; + font-size: 14px; + line-height: 22px; + + .generatedAddressesGrid { + display: flex; + text-align: center; + align-items: center; + + & > *:first-child { + text-align: left; + flex: 1.5; + } + & > * { + flex: 1; + } + } + .labelHeader { + text-align: left; + } + .walletAddress { + display: flex; + padding: 8.5px 0; + word-break: break-all; + + .addressActions { + .addressActionItemBlock { + cursor: pointer; + letter-spacing: 0.5px; + text-align: right; + line-height: 20px; + vertical-align: top; + white-space: nowrap; + + * { + vertical-align: middle; + } + + span { + color: var(--yoroi-palette-gray-800); + font-weight: 400; + font-size: 14px; + margin-left: 6px; + } + } + + .verifyActionBlock, + .generateURLActionBlock { + display: flex; + align-items: center; + justify-content: flex-end; + button { + cursor: pointer; + } + } + + .generateURLActionBlock { + margin-left: unset; + } + } + .labelAddress { + font-weight: 400; + text-align: left; + font-size: 12px; + display: flex; + align-items: center; + .labelText { + position: relative; + + background: var(--yoroi-palette-gray-200); + color: var(--yoroi-palette-gray-600); + padding: 2px 6px; + height: 27px; + &:after { + content: ' '; + display: block; + width: 0; + height: 0; + border-top: 13px solid transparent; + border-bottom: 14px solid transparent; + border-left: 14px solid var(--yoroi-palette-gray-200); + position: absolute; + top: 50%; + margin-top: -14px; + right: -14px; + z-index: 2; + } + } + button { + margin: 0; + margin-right: 8px; + } + } + } + } +} + +:global(.YoroiModern), +:global(.YoroiRevamp) { + .component { + .generatedAddresses { + .walletAddress { + padding: 12px 0; + } + } + } +} + +.addressHash { + font-size: 14px; + line-height: 22px; + font-family: 'RobotoMono'; + font-weight: 400; + color: var(--yoroi-palette-gray-900); +} + +.addressHashUsed { + color: var(--yoroi-palette-gray-600); +} diff --git a/packages/yoroi-extension/app/components/wallet/layouts/ReceiveWithNavigation.js b/packages/yoroi-extension/app/components/wallet/layouts/ReceiveWithNavigation.js index d1accd6144..a91f4ed1c9 100644 --- a/packages/yoroi-extension/app/components/wallet/layouts/ReceiveWithNavigation.js +++ b/packages/yoroi-extension/app/components/wallet/layouts/ReceiveWithNavigation.js @@ -6,6 +6,7 @@ import type { LayoutComponentMap } from '../../../styles/context/layout'; import ReceiveNavigation from '../navigation/ReceiveNavigation'; import { Box } from '@mui/material'; import type { AddressTypeName, AddressFilterKind } from '../../../types/AddressFilterTypes'; +import ReceiveNavigationRevamp from '../navigation/ReceiveNavigationRevamp'; export type Props = {| +children?: Node, @@ -32,7 +33,7 @@ function ReceiveWithNavigation({ const { renderLayoutComponent } = useLayout(); const classicReceiveNav = ( - + - + {children} @@ -52,26 +60,19 @@ function ReceiveWithNavigation({ display: 'flex', overflow: 'hidden', height: '100%', - backgroundColor: 'var(--yoroi-palette-common-white)', + bgcolor: 'common.white', borderRadius: '8px', + width: '100%', }} > - div': { backgroundColor: 'var(--yoroi-palette-common-white)' }, - }} - > - + - - {children} - + {children} ); return renderLayoutComponent({ CLASSIC: classicReceiveNav, REVAMP: revampReceiveNav }); diff --git a/packages/yoroi-extension/app/components/wallet/navigation/ReceiveNavButtonRevamp.js b/packages/yoroi-extension/app/components/wallet/navigation/ReceiveNavButtonRevamp.js new file mode 100644 index 0000000000..1bf4bfe4df --- /dev/null +++ b/packages/yoroi-extension/app/components/wallet/navigation/ReceiveNavButtonRevamp.js @@ -0,0 +1,91 @@ +// @flow +import { Component } from 'react'; +import type { Node } from 'react'; +import { observer } from 'mobx-react'; +import { Box, Typography } from '@mui/material'; + +type Props = {| + +label: string, + +isActive: boolean, + +onClick: void => void, + +className?: string, + +icon?: string, + +isToplevel?: boolean, + +noGutters?: boolean, + +sx?: Object, + +tooltip?: Node, +|}; + +@observer +export default class ReceiveNavButtonRevamp extends Component { + static defaultProps: {| + className: void, + icon: void, + isToplevel: void, + noGutters: boolean, + sx: Object, + tooltip: void, + |} = { + className: undefined, + icon: undefined, + isToplevel: undefined, + noGutters: false, + sx: {}, + tooltip: undefined, + }; + + renderButton: void => Node = () => { + const isTopLvl = Boolean(this.props.isToplevel); + const isActive = Boolean(this.props.isActive); + return ( + + + + {this.props.label} + + + {this.props.tooltip} + + + + ); + }; + + render(): Node { + const IconComponent = this.props.icon; + const isTopLvl = Boolean(this.props.isToplevel); + const noGutters = Boolean(this.props.noGutters); + + const paddings = noGutters + ? {} + : { + pr: '24px', + pb: isTopLvl ? '24px' : '16px', + pl: isTopLvl ? '24px' : '52px', + }; + + return ( + + {this.renderButton()} + {IconComponent != null && ( +
+ +
+ )} +
+ ); + } +} diff --git a/packages/yoroi-extension/app/components/wallet/navigation/ReceiveNavigationRevamp.js b/packages/yoroi-extension/app/components/wallet/navigation/ReceiveNavigationRevamp.js new file mode 100644 index 0000000000..ec37167441 --- /dev/null +++ b/packages/yoroi-extension/app/components/wallet/navigation/ReceiveNavigationRevamp.js @@ -0,0 +1,204 @@ +// @flow +import { Component } from 'react'; +import type { Node } from 'react'; +import { observer } from 'mobx-react'; +import { intlShape } from 'react-intl'; +import { ReactComponent as AttentionIcon } from '../../../assets/images/attention-modern.inline.svg'; +import ReceiveNavButtonRevamp from './ReceiveNavButtonRevamp'; +import type { $npm$ReactIntl$IntlFormat } from 'react-intl'; +import { + addressGroupName, + addressSubgroupName, + addressGroupsTooltip, + addressFilter, + AddressGroupTypes, + AddressSubgroup, +} from '../../../types/AddressFilterTypes'; +import Accordion from '../../widgets/Accordion'; +import { ReactComponent as InfoIcon } from '../../../assets/images/revamp/info.inline.svg'; + +import type { AddressTypeName, AddressFilterKind } from '../../../types/AddressFilterTypes'; +import classNames from 'classnames'; +import { Box, Tooltip, Typography } from '@mui/material'; + +type AddressStoreSubset = { + +isActiveStore: boolean, + +setAsActiveStore: void => void, + +name: AddressTypeName, + +validFilters: $ReadOnlyArray, + +wasExecuted: boolean, + ... +}; +export type Props = {| + +setFilter: AddressFilterKind => void, + +activeFilter: AddressFilterKind, + +addressStores: $ReadOnlyArray, +|}; + +@observer +export default class ReceiveNavigationRevamp extends Component { + static contextTypes: {| intl: $npm$ReactIntl$IntlFormat |} = { + intl: intlShape.isRequired, + }; + + genTooltip: AddressStoreSubset => Node = store => { + const { intl } = this.context; + return ( + + {intl.formatMessage(addressGroupsTooltip[store.name.group])} + + } + > +
+ +
+
+ ); + }; + + createAccordionForGroup: ($PropertyType) => Node = stores => { + const { intl } = this.context; + + const store = stores[0]; + if (stores.length === 1 && stores[0].name.subgroup === AddressSubgroup.all) { + return ( +
+ +
+ ); + } + + return ( + !str.wasExecuted) != null} + header={ + address.isActiveStore)} + onClick={store.setAsActiveStore} + tooltip={this.genTooltip(store)} + noGutters + isToplevel + /> + } + headerStyle={{ paddingBottom: '24px' }} + activeHeader={stores.some(address => address.isActiveStore)} + > + {stores.map(type => ( + + ))} + + ); + }; + + createAccordions: void => Node = () => { + // we use an array instead of a map to maintain the order of stores + const groups: Array> = []; + + for (const store of this.props.addressStores) { + const existingGroup = groups.find( + // if any existing group shares the group name + group => group[0].name.group === store.name.group + ); + if (existingGroup == null) { + groups.push([store]); + continue; + } + existingGroup.push(store); + } + + return groups.map(group => ( + {this.createAccordionForGroup(group)} + )); + }; + + generateFilterSection: void => ?Node = () => { + const { intl } = this.context; + + const { activeFilter } = this.props; + + const activeStore = this.props.addressStores.find(store => store.isActiveStore); + if (activeStore == null) return undefined; + + return ( + activeStore.validFilters.length > 0 && ( + + {activeStore.validFilters.map(filter => ( + this.props.setFilter(filter)} + isToplevel + /> + ))} + + ) + ); + }; + + render(): Node { + return ( + + + {this.createAccordions()} + + {/* Section filtered button */} + + {this.generateFilterSection()} + + + ); + } +} diff --git a/packages/yoroi-extension/app/components/wallet/receive/StandardHeader.js b/packages/yoroi-extension/app/components/wallet/receive/StandardHeader.js index 0244c88896..6454904be0 100644 --- a/packages/yoroi-extension/app/components/wallet/receive/StandardHeader.js +++ b/packages/yoroi-extension/app/components/wallet/receive/StandardHeader.js @@ -1,6 +1,6 @@ // @flow import { Component } from 'react'; -import type { Node, ComponentType } from 'react'; +import type { Node } from 'react'; import { observer } from 'mobx-react'; import { defineMessages, intlShape, FormattedHTMLMessage } from 'react-intl'; import classnames from 'classnames'; @@ -15,8 +15,6 @@ import { SelectedExplorer } from '../../../domain/SelectedExplorer'; import type { Notification } from '../../../types/notificationType'; import type { $npm$ReactIntl$IntlFormat } from 'react-intl'; import { truncateAddress } from '../../../utils/formatters'; -import { withLayout } from '../../../styles/context/layout'; -import type { InjectedLayoutProps } from '../../../styles/context/layout'; const messages = defineMessages({ walletAddressLabel: { @@ -47,7 +45,7 @@ type Props = {| |}; @observer -class StandardHeader extends Component { +export default class StandardHeader extends Component { static defaultProps: {| error: void |} = { error: undefined, }; @@ -68,14 +66,13 @@ class StandardHeader extends Component { isWalletAddressUsed, onCopyAddressTooltip, notification, - isRevampLayout, } = this.props; const { intl } = this.context; const mainAddressNotificationId = 'mainAddress-copyNotification'; const generateAddressForm = ( { return walletHeader; } } - -export default (withLayout(StandardHeader): ComponentType); diff --git a/packages/yoroi-extension/app/components/wallet/receive/StandardHeaderRevamp.js b/packages/yoroi-extension/app/components/wallet/receive/StandardHeaderRevamp.js new file mode 100644 index 0000000000..c24041b564 --- /dev/null +++ b/packages/yoroi-extension/app/components/wallet/receive/StandardHeaderRevamp.js @@ -0,0 +1,177 @@ +// @flow +import type { Node } from 'react'; +import type { Notification } from '../../../types/notificationType'; +import type { $npm$ReactIntl$IntlFormat } from 'react-intl'; +import { Component } from 'react'; +import { observer } from 'mobx-react'; +import { defineMessages, intlShape, FormattedHTMLMessage } from 'react-intl'; +import { LoadingButton } from '@mui/lab'; +import { SelectedExplorer } from '../../../domain/SelectedExplorer'; +import { Box, Typography } from '@mui/material'; +import ExplorableHashContainer from '../../../containers/widgets/ExplorableHashContainer'; +import LocalizableError from '../../../i18n/LocalizableError'; +import styles from './StandardHeader.scss'; +import CopyableAddress from '../../widgets/CopyableAddress'; +import QrCodeWrapper from '../../widgets/QrCodeWrapper'; +import RawHash from '../../widgets/hashWrappers/RawHash'; + +const messages = defineMessages({ + walletAddressLabel: { + id: 'wallet.receive.page.walletAddressLabel', + defaultMessage: '!!!Your wallet address', + }, + walletReceiveInstructions: { + id: 'wallet.receive.page.walletReceiveInstructions', + defaultMessage: + '!!!Share this wallet address to receive payments. To protect your privacy, new addresses are generated automatically once you use them.', + }, + generateNewAddressButtonLabel: { + id: 'wallet.receive.page.generateNewAddressButtonLabel', + defaultMessage: '!!!Generate new address', + }, +}); + +type Props = {| + +walletAddress: string, + +selectedExplorer: SelectedExplorer, + +isWalletAddressUsed: boolean, + +onGenerateAddress: void => Promise, + +onCopyAddressTooltip: (string, string) => void, + +notification: ?Notification, + +isSubmitting: boolean, + +error?: ?LocalizableError, + +isFilterActive: boolean, +|}; + +@observer +export default class StandardHeaderRevamp extends Component { + static defaultProps: {| error: void |} = { + error: undefined, + }; + + static contextTypes: {| intl: $npm$ReactIntl$IntlFormat |} = { + intl: intlShape.isRequired, + }; + + submit: void => Promise = async () => { + await this.props.onGenerateAddress(); + }; + + render(): Node { + const { + walletAddress, + isSubmitting, + error, + isWalletAddressUsed, + onCopyAddressTooltip, + notification, + } = this.props; + const { intl } = this.context; + const mainAddressNotificationId = 'mainAddress-copyNotification'; + + const generateAddressForm = ( + + {intl.formatMessage(messages.generateNewAddressButtonLabel)} + + ); + + const walletHeader = ( + + + {intl.formatMessage(messages.walletAddressLabel)} + + + + + theme.palette.gradients['blue-green-bg'], + }} + > + + + + + + + + onCopyAddressTooltip(walletAddress, mainAddressNotificationId)} + notification={notification} + placementTooltip="bottom-start" + > + + + + {walletAddress} + + + + + + + + + + + {generateAddressForm} + {error &&

{intl.formatMessage(error)}

} +
+
+
+ ); + + return walletHeader; + } +} diff --git a/packages/yoroi-extension/app/components/wallet/receive/WarningHeader.scss b/packages/yoroi-extension/app/components/wallet/receive/WarningHeader.scss index d0fc012a5c..e28ce93784 100644 --- a/packages/yoroi-extension/app/components/wallet/receive/WarningHeader.scss +++ b/packages/yoroi-extension/app/components/wallet/receive/WarningHeader.scss @@ -41,6 +41,5 @@ .invalidURIImg { display: block; - width: 25%; text-align: center; } diff --git a/packages/yoroi-extension/app/components/wallet/transactions/TransactionRevamp.js b/packages/yoroi-extension/app/components/wallet/transactions/TransactionRevamp.js index a7797b06c7..f170bcb68a 100644 --- a/packages/yoroi-extension/app/components/wallet/transactions/TransactionRevamp.js +++ b/packages/yoroi-extension/app/components/wallet/transactions/TransactionRevamp.js @@ -214,7 +214,7 @@ export default class TransactionRevamp extends Component { ? beforeDecimalRewards : '+' + beforeDecimalRewards; - if (request.getRawNumber) { + if (request.getRawNumber === true) { return adjustedBefore + afterDecimalRewards; } diff --git a/packages/yoroi-extension/app/components/widgets/Accordion.js b/packages/yoroi-extension/app/components/widgets/Accordion.js index 5b83e5da4a..bc8bd3283e 100644 --- a/packages/yoroi-extension/app/components/widgets/Accordion.js +++ b/packages/yoroi-extension/app/components/widgets/Accordion.js @@ -1,17 +1,21 @@ // @flow import { Component } from 'react'; -import type { Node } from 'react'; +import type { ComponentType, Node } from 'react'; import { observer } from 'mobx-react'; import classnames from 'classnames'; -import { ReactComponent as ArrowDownSVG } from '../../assets/images/expand-arrow-grey.inline.svg'; +import { ReactComponent as ArrowDownSVG } from '../../assets/images/expand-arrow-grey.inline.svg'; import styles from './Accordion.scss'; +import { withLayout } from '../../styles/context/layout'; +import type { InjectedLayoutProps } from '../../styles/context/layout'; type Props = {| +header: Node, +children: Node, +activeHeader: boolean, +showSpinner: boolean, + +style?: Object, + +headerStyle?: Object, |}; type State = {| @@ -19,24 +23,24 @@ type State = {| |}; @observer -export default class Accordion extends Component { - +class Accordion extends Component { state: State = { isToggle: true, }; toggleActive: void => void = () => { this.setState(prevState => ({ isToggle: !prevState.isToggle })); - } + }; render(): Node { - const { header, children } = this.props; + const { header, children, style, isRevampLayout } = this.props; const { isToggle } = this.state; const activeButtonClasses = classnames([ styles.accordionTitle, - isToggle && styles.arrowUp, - this.props.activeHeader && styles.activeHead + isToggle && styles.activeArrow, + isRevampLayout && styles.revamp, + this.props.activeHeader && styles.activeHead, ]); const toggleShowContent = classnames([ @@ -45,19 +49,25 @@ export default class Accordion extends Component { ]); return ( -
-
{children} - {this.props.showSpinner - ?
- : null - } + {this.props.showSpinner ?
: null}
); } } + +export default (withLayout(Accordion): ComponentType); diff --git a/packages/yoroi-extension/app/components/widgets/Accordion.scss b/packages/yoroi-extension/app/components/widgets/Accordion.scss index 3047aa96d6..cdcad742dc 100644 --- a/packages/yoroi-extension/app/components/widgets/Accordion.scss +++ b/packages/yoroi-extension/app/components/widgets/Accordion.scss @@ -25,11 +25,26 @@ transition: transform 300ms; } - &.arrowUp { - .arrowDownIcon svg { + &.revamp { + // Not opened + .arrowIcon svg { transform: rotate(180deg); } + + // Opened + &.activeArrow { + .arrowIcon svg { + transform: rotate(0deg); + } + } } + + &.activeArrow { + .arrowIcon svg { + transform: rotate(180deg); + } + } + &.activeHead { font-weight: 500; color: var(--yoroi-palette-secondary-300); diff --git a/packages/yoroi-extension/app/components/widgets/CopyableAddress.js b/packages/yoroi-extension/app/components/widgets/CopyableAddress.js index 3ab93bd4d2..d7fe686e21 100644 --- a/packages/yoroi-extension/app/components/widgets/CopyableAddress.js +++ b/packages/yoroi-extension/app/components/widgets/CopyableAddress.js @@ -8,7 +8,7 @@ import CopyToClipboard from 'react-copy-to-clipboard'; import { ReactComponent as IconCopy } from '../../assets/images/copy.inline.svg'; import { ReactComponent as IconCopied } from '../../assets/images/copied.inline.svg'; import styles from './CopyableAddress.scss'; -import { Tooltip, Typography } from '@mui/material'; +import { Box, Tooltip, Typography } from '@mui/material'; import type { Notification } from '../../types/notificationType'; import type { $npm$ReactIntl$IntlFormat } from 'react-intl'; @@ -31,6 +31,7 @@ type Props = {| +placementTooltip?: string, +notification: ?Notification, +darkVariant?: boolean, + +sx?: Object, |}; @observer @@ -43,16 +44,18 @@ export default class CopyableAddress extends Component { darkVariant: boolean, elementId: void, onCopyAddress: void, + sx: Object, placementTooltip: string, |} = { onCopyAddress: undefined, placementTooltip: 'bottom', elementId: undefined, + sx: {}, darkVariant: false, }; render(): Node { - const { hash, elementId, onCopyAddress, notification, darkVariant } = this.props; + const { hash, elementId, sx, onCopyAddress, notification, darkVariant } = this.props; const { intl } = this.context; const Icon = notification && notification.id === elementId ? IconCopied : IconCopy; @@ -74,7 +77,7 @@ export default class CopyableAddress extends Component { ); return ( -
+ {this.props.children} { > {tooltipComponent} -
+ ); } } diff --git a/packages/yoroi-extension/app/components/widgets/CopyableAddress.scss b/packages/yoroi-extension/app/components/widgets/CopyableAddress.scss index c266e29f1a..4e0bbe271e 100644 --- a/packages/yoroi-extension/app/components/widgets/CopyableAddress.scss +++ b/packages/yoroi-extension/app/components/widgets/CopyableAddress.scss @@ -8,13 +8,16 @@ object-fit: contain; display: inline-flex; margin-right: 2px; + margin-left: 16px; + width: 24px; + height: 24px; border-radius: 50%; transition: background-color 0.3s; & > svg { display: block; margin: auto; - height: 20px; - width: 20px; + height: 24px; + width: 24px; path { fill: var(--yoroi-palette-gray-900); } diff --git a/packages/yoroi-extension/app/components/widgets/QrCodeWrapper.js b/packages/yoroi-extension/app/components/widgets/QrCodeWrapper.js index f8ef9ac3d6..e90f80f8da 100644 --- a/packages/yoroi-extension/app/components/widgets/QrCodeWrapper.js +++ b/packages/yoroi-extension/app/components/widgets/QrCodeWrapper.js @@ -10,7 +10,8 @@ type Props = {| +size: number, +id?: string, +includeMargin?: boolean, - +addBg?: boolean + +addBg?: boolean, + +fgColor?: string |}; @observer @@ -28,9 +29,9 @@ export default class QrCodeWrapper extends Component { render(): Node { // Get QRCode color value from active theme's CSS variable - const { id, includeMargin, addBg } = this.props; + const { id, includeMargin, addBg, fgColor } = this.props; const qrCodeBackgroundColor = addBg === true ? readCssVar('--yoroi-qr-code-background') : '#ffffff'; - const qrCodeForegroundColor = readCssVar('--yoroi-qr-code-foreground'); + const qrCodeForegroundColor = fgColor ?? readCssVar('--yoroi-qr-code-foreground'); return ( , - +children?: Node + +children?: Node, |}; @observer export default class Receive extends Component { - - static defaultProps: {|children: void|} = { + static defaultProps: {| children: void |} = { children: undefined, }; - static contextTypes: {|intl: $npm$ReactIntl$IntlFormat|} = { + static contextTypes: {| intl: $npm$ReactIntl$IntlFormat |} = { intl: intlShape.isRequired, }; componentDidMount() { const publicDeriver = this.generated.stores.wallets.selected; if (publicDeriver == null) throw new Error(`${nameof(Receive)} no public deriver`); - const rootRoute = buildRoute( - ROUTES.WALLETS.RECEIVE.ROOT - ); + const rootRoute = buildRoute(ROUTES.WALLETS.RECEIVE.ROOT); - const storesForWallet = allAddressSubgroups.filter(store => store.isRelated({ - selected: publicDeriver, - })); + const storesForWallet = allAddressSubgroups.filter(store => + store.isRelated({ + selected: publicDeriver, + }) + ); if (this.generated.stores.app.currentRoute === rootRoute) { // if no store is specified, we just send the user to the first store in the list const firstRoute = routeForStore(storesForWallet[0].name); @@ -57,7 +54,7 @@ export default class Receive extends Component { if (currentSelectedStore == null) { // just send user to the first store supported by this wallet this.generated.actions.router.redirect.trigger({ - route: routeForStore(storesForWallet[0].name) + route: routeForStore(storesForWallet[0].name), }); } } @@ -70,9 +67,8 @@ export default class Receive extends Component { const publicDeriver = this.generated.stores.wallets.selected; if (publicDeriver == null) throw new Error(`${nameof(Receive)} no public deriver`); - const storesForWallet = allAddressSubgroups - .filter(store => store.isRelated({ selected: publicDeriver, })) + .filter(store => store.isRelated({ selected: publicDeriver })) .map(store => { const request = this.generated.stores.addresses.addressSubgroupMap.get(store.class); if (request == null) throw new Error('Should never happen'); @@ -86,16 +82,17 @@ export default class Receive extends Component { isActiveStore: this.generated.stores.app.currentRoute.startsWith( routeForStore(storeInfo.meta.name) ), - setAsActiveStore: () => this.generated.actions.router.goToRoute.trigger({ - route: routeForStore(storeInfo.meta.name), - }), + setAsActiveStore: () => + this.generated.actions.router.goToRoute.trigger({ + route: routeForStore(storeInfo.meta.name), + }), name: storeInfo.meta.name, validFilters: storeInfo.meta.validFilters, wasExecuted: storeInfo.request.wasExecuted, })); return ( - + this.generated.actions.addresses.setFilter.trigger(filter)} @@ -103,7 +100,7 @@ export default class Receive extends Component { > {this.props.children} - + ); } @@ -114,7 +111,7 @@ export default class Receive extends Component { addressFilter: AddressFilterKind, addressSubgroupMap: $ReadOnlyMap, IAddressTypeUiSubset>, |}, - wallets: {|selected: null | PublicDeriver<>|}, + wallets: {| selected: null | PublicDeriver<> |}, |}, actions: {| addresses: {| @@ -125,19 +122,19 @@ export default class Receive extends Component { redirect: {| trigger: (params: {| params?: ?any, - route: string - |}) => void + route: string, + |}) => void, |}, goToRoute: {| trigger: (params: {| publicDeriver?: null | PublicDeriver<>, params?: ?any, - route: string - |}) => void - |} - |} - |} - |} { + route: string, + |}) => void, + |}, + |}, + |}, + |} { if (this.props.generated !== undefined) { return this.props.generated; } @@ -160,14 +157,14 @@ export default class Receive extends Component { }, actions: { addresses: { - setFilter: { trigger: actions.addresses.setFilter.trigger, }, - resetFilter: { trigger: actions.addresses.resetFilter.trigger, }, + setFilter: { trigger: actions.addresses.setFilter.trigger }, + resetFilter: { trigger: actions.addresses.resetFilter.trigger }, }, router: { goToRoute: { trigger: actions.router.goToRoute.trigger }, redirect: { trigger: actions.router.redirect.trigger }, }, - } + }, }); } } diff --git a/packages/yoroi-extension/app/containers/wallet/WalletReceivePage.js b/packages/yoroi-extension/app/containers/wallet/WalletReceivePage.js index fea4c460a3..a5a7e5d035 100644 --- a/packages/yoroi-extension/app/containers/wallet/WalletReceivePage.js +++ b/packages/yoroi-extension/app/containers/wallet/WalletReceivePage.js @@ -1,10 +1,45 @@ // @flow -import type { Node } from 'react'; +import type { Node, ComponentType } from 'react'; +import type { InjectedOrGenerated } from '../../types/injectedPropsType'; +import type { AddressFilterKind, StandardAddress } from '../../types/AddressFilterTypes'; +import type { GeneratedData as UnmangleTxDialogContainerData } from '../transfer/UnmangleTxDialogContainer'; +import type { $npm$ReactIntl$IntlFormat } from 'react-intl'; +import type { Notification } from '../../types/notificationType'; +import type { UnitOfAccountSettingType } from '../../types/unitOfAccountType'; +import type { + AddressSubgroupMeta, + IAddressTypeUiSubset, + IAddressTypeStore, +} from '../../stores/stateless/addressStores'; +import type { ComplexityLevelType } from '../../types/complexityLevelType'; +import type { TokenInfoMap } from '../../stores/toplevel/TokenInfoStore'; import { Component } from 'react'; import { observer } from 'mobx-react'; import { computed, observable, runInAction } from 'mobx'; -import BigNumber from 'bignumber.js'; import { intlShape } from 'react-intl'; +import { PublicDeriver } from '../../api/ada/lib/storage/models/PublicDeriver/index'; +import { WalletTypeOption } from '../../api/ada/lib/storage/models/ConceptualWallet/interfaces'; +import { + addressGroupName, + addressSubgroupName, + AddressFilter, + AddressSubgroup, + AddressGroupTypes, +} from '../../types/AddressFilterTypes'; +import { SelectedExplorer } from '../../domain/SelectedExplorer'; +import { validateAmount } from '../../utils/validations'; +import { Logger } from '../../utils/logging'; +import { + routeForStore, + allAddressSubgroups, + applyAddressFilter, +} from '../../stores/stateless/addressStores'; +import { isCardanoHaskell } from '../../api/ada/lib/storage/database/prepackaged/networks'; +import { handleExternalLinkClick } from '../../utils/routing'; +import { genLookupOrFail, getTokenName } from '../../stores/stateless/tokenHelpers'; +import { truncateToken } from '../../utils/formatters'; +import { withLayout } from '../../styles/context/layout'; +import BigNumber from 'bignumber.js'; import config from '../../config'; import WalletReceive from '../../components/wallet/WalletReceive'; import StandardHeader from '../../components/wallet/receive/StandardHeader'; @@ -15,34 +50,14 @@ import VerticalFlexContainer from '../../components/layout/VerticalFlexContainer import VerifyAddressDialog from '../../components/wallet/receive/VerifyAddressDialog'; import URIGenerateDialog from '../../components/uri/URIGenerateDialog'; import URIDisplayDialog from '../../components/uri/URIDisplayDialog'; -import type { InjectedOrGenerated } from '../../types/injectedPropsType'; import VerticallyCenteredLayout from '../../components/layout/VerticallyCenteredLayout'; import LoadingSpinner from '../../components/widgets/LoadingSpinner'; -import { PublicDeriver } from '../../api/ada/lib/storage/models/PublicDeriver/index'; import Dialog from '../../components/widgets/Dialog'; import globalMessages from '../../i18n/global-messages'; -import { WalletTypeOption } from '../../api/ada/lib/storage/models/ConceptualWallet/interfaces'; -import type { AddressFilterKind, StandardAddress, } from '../../types/AddressFilterTypes'; -import UnmangleTxDialogContainer from '../transfer/UnmangleTxDialogContainer'; -import type { GeneratedData as UnmangleTxDialogContainerData } from '../transfer/UnmangleTxDialogContainer'; -import type { $npm$ReactIntl$IntlFormat } from 'react-intl'; -import { addressGroupName, addressSubgroupName, AddressFilter, AddressSubgroup, AddressGroupTypes } from '../../types/AddressFilterTypes'; +import WalletReceiveRevamp from '../../components/wallet/WalletReceiveRevamp'; import LocalizableError from '../../i18n/LocalizableError'; -import { SelectedExplorer } from '../../domain/SelectedExplorer'; -import type { Notification } from '../../types/notificationType'; -import type { UnitOfAccountSettingType } from '../../types/unitOfAccountType'; -import { validateAmount } from '../../utils/validations'; -import { Logger, } from '../../utils/logging'; -import type { AddressSubgroupMeta, IAddressTypeUiSubset, IAddressTypeStore } from '../../stores/stateless/addressStores'; -import { routeForStore, allAddressSubgroups, applyAddressFilter, } from '../../stores/stateless/addressStores'; -import { - isCardanoHaskell, -} from '../../api/ada/lib/storage/database/prepackaged/networks'; -import type { ComplexityLevelType } from '../../types/complexityLevelType'; -import { handleExternalLinkClick } from '../../utils/routing'; -import type { TokenInfoMap } from '../../stores/toplevel/TokenInfoStore'; -import { genLookupOrFail, getTokenName, } from '../../stores/stateless/tokenHelpers'; -import { truncateToken } from '../../utils/formatters'; +import UnmangleTxDialogContainer from '../transfer/UnmangleTxDialogContainer'; +import StandardHeaderRevamp from '../../components/wallet/receive/StandardHeaderRevamp'; export type GeneratedData = typeof WalletReceivePage.prototype.generated; @@ -50,10 +65,12 @@ type Props = {| ...InjectedOrGenerated, |}; -@observer -export default class WalletReceivePage extends Component { +type InjectedProps = {| +isRevampLayout: boolean |}; +type AllProps = {| ...Props, ...InjectedProps |}; - static contextTypes: {|intl: $npm$ReactIntl$IntlFormat|} = { intl: intlShape.isRequired }; +@observer +class WalletReceivePage extends Component { + static contextTypes: {| intl: $npm$ReactIntl$IntlFormat |} = { intl: intlShape.isRequired }; @observable notificationElementId: string = ''; @@ -90,20 +107,15 @@ export default class WalletReceivePage extends Component { const { intl } = this.context; const actions = this.generated.actions; const { uiNotifications, uiDialogs, profile } = this.generated.stores; - const { - hwVerifyAddress, - } = this.generated.stores.substores.ada; + const { hwVerifyAddress } = this.generated.stores.substores.ada; const publicDeriver = this.generated.stores.wallets.selected; if (!publicDeriver) throw new Error(`Active wallet required for ${nameof(WalletReceivePage)}.`); - this.generated.stores.tokenInfoStore.tokenInfo.get + this.generated.stores.tokenInfoStore.tokenInfo.get; const addressTypeStore = this.getTypeStore(publicDeriver); - if ( - addressTypeStore == null || - !addressTypeStore.request.wasExecuted - ) { + if (addressTypeStore == null || !addressTypeStore.request.wasExecuted) { return ( @@ -111,6 +123,8 @@ export default class WalletReceivePage extends Component { ); } + const { isRevampLayout } = this.props; + // get info about the latest address generated for special rendering const lastAddress = addressTypeStore.request.all[addressTypeStore.request.all.length - 1]; const walletAddress = lastAddress != null ? lastAddress.address : ''; @@ -137,13 +151,15 @@ export default class WalletReceivePage extends Component { } }; - const notification = uiNotifications.getTooltipActiveNotification( - this.notificationElementId - ); + const notification = uiNotifications.getTooltipActiveNotification(this.notificationElementId); - const selectedExplorerForNetwork = this.generated.stores.explorers.selectedExplorer - .get(publicDeriver.getParent().getNetworkInfo().NetworkId) - ?? (() => { throw new Error('No explorer for wallet network'); })(); + const selectedExplorerForNetwork = + this.generated.stores.explorers.selectedExplorer.get( + publicDeriver.getParent().getNetworkInfo().NetworkId + ) ?? + (() => { + throw new Error('No explorer for wallet network'); + })(); const defaultToken = publicDeriver.getParent().getDefaultToken(); const defaultTokenInfo = genLookupOrFail(this.generated.stores.tokenInfoStore.tokenInfo)({ @@ -152,34 +168,38 @@ export default class WalletReceivePage extends Component { }); const header = (() => { + const HeaderComp = isRevampLayout ? StandardHeaderRevamp : StandardHeader; + if (addressTypeStore.meta.name.subgroup === AddressSubgroup.external) { - return (); + return ( + + ); } if (addressTypeStore.meta.name.subgroup === AddressSubgroup.internal) { - return (); + return ; } if (addressTypeStore.meta.name.group === AddressGroupTypes.reward) { - return (); + return ; } if (addressTypeStore.meta.name.subgroup === AddressSubgroup.mangled) { return ( this.generated.actions.dialogs.open.trigger({ - dialog: UnmangleTxDialogContainer, - })} + onClick={() => + this.generated.actions.dialogs.open.trigger({ + dialog: UnmangleTxDialogContainer, + }) + } ticker={truncateToken(getTokenName(defaultTokenInfo))} /> ); @@ -188,37 +208,39 @@ export default class WalletReceivePage extends Component { return null; } if (addressTypeStore.meta.name.subgroup === AddressSubgroup.all) { - return (); + return ( + + ); } throw new Error(`${nameof(WalletReceivePage)} unexpected address tab`); })(); const getSelectedHierarchyPath = () => { if (addressTypeStore.meta.name.subgroup === AddressSubgroup.all) { - return [ - intl.formatMessage(addressGroupName[addressTypeStore.meta.name.group]), - ]; + return [intl.formatMessage(addressGroupName[addressTypeStore.meta.name.group])]; } return [ intl.formatMessage(addressGroupName[addressTypeStore.meta.name.group]), - intl.formatMessage(addressSubgroupName[addressTypeStore.meta.name.subgroup]) + intl.formatMessage(addressSubgroupName[addressTypeStore.meta.name.subgroup]), ]; }; + const WalletReceiveComp = isRevampLayout ? WalletReceiveRevamp : WalletReceive; + return ( - { walletAddresses={applyAddressFilter({ addressFilter: this.generated.stores.addresses.addressFilter, addresses: addressTypeStore.request.all, - }).slice().reverse()} + }) + .slice() + .reverse()} onCopyAddressTooltip={onCopyAddressTooltip} notification={notification} onVerifyAddress={async (request: $ReadOnly) => { @@ -239,15 +263,13 @@ export default class WalletReceivePage extends Component { onGeneratePaymentURI={ !isCardanoHaskell(publicDeriver.getParent().getNetworkInfo()) || addressTypeStore.meta.name.group === AddressGroupTypes.reward || - ( - addressTypeStore.meta.name.subgroup !== AddressSubgroup.external && - addressTypeStore.meta.name.subgroup !== AddressSubgroup.all - ) + (addressTypeStore.meta.name.subgroup !== AddressSubgroup.external && + addressTypeStore.meta.name.subgroup !== AddressSubgroup.all) ? undefined - : (address) => { - this.openURIGenerateDialog(address); - } - } + : address => { + this.openURIGenerateDialog(address); + } + } shouldHideBalance={profile.shouldHideBalance} unitOfAccountSetting={profile.unitOfAccount} addressBook={addressTypeStore.meta.name.group === AddressGroupTypes.addressBook} @@ -272,17 +294,21 @@ export default class WalletReceivePage extends Component { return new BigNumber(val); })()} onClose={() => actions.dialogs.closeActiveDialog.trigger()} - onGenerate={(address, amount) => { this.generateURI(address, amount); }} + onGenerate={(address, amount) => { + this.generateURI(address, amount); + }} classicTheme={profile.isClassicTheme} tokenInfo={defaultTokenInfo} - validateAmount={(amount, tokenRow) => validateAmount( - amount, - tokenRow, - // we don't impose a minimum value for the creation of the QR codes - // since validation happens when the QR code is scanned anyway - new BigNumber(0), - this.context.intl, - )} + validateAmount={(amount, tokenRow) => + validateAmount( + amount, + tokenRow, + // we don't impose a minimum value for the creation of the QR codes + // since validation happens when the QR code is scanned anyway + new BigNumber(0), + this.context.intl + ) + } /> ) : null} @@ -291,11 +317,13 @@ export default class WalletReceivePage extends Component { address={uiDialogs.getParam('address')} amount={new BigNumber(uiDialogs.getParam('amount') ?? '0')} onClose={actions.dialogs.closeActiveDialog.trigger} - onBack={() => this.openURIGenerateDialog( - uiDialogs.getParam('address'), - uiDialogs.getParam('amount') ?? '0', - )} - onCopyAddressTooltip={(elementId) => { + onBack={() => + this.openURIGenerateDialog( + uiDialogs.getParam('address'), + uiDialogs.getParam('amount') ?? '0' + ) + } + onCopyAddressTooltip={elementId => { if (!uiNotifications.isOpen(elementId)) { runInAction(() => { this.notificationElementId = elementId; @@ -307,9 +335,7 @@ export default class WalletReceivePage extends Component { }); } }} - notification={uiNotifications.getTooltipActiveNotification( - this.notificationElementId - )} + notification={uiNotifications.getTooltipActiveNotification(this.notificationElementId)} /> ) : null} @@ -326,7 +352,7 @@ export default class WalletReceivePage extends Component { selectedExplorer={selectedExplorerForNetwork} error={hwVerifyAddress.error} addressInfo={hwVerifyAddress.selectedAddress} - onCopyAddressTooltip={(elementId) => { + onCopyAddressTooltip={elementId => { if (!uiNotifications.isOpen(elementId)) { runInAction(() => { this.notificationElementId = elementId; @@ -338,9 +364,7 @@ export default class WalletReceivePage extends Component { }); } }} - notification={uiNotifications.getTooltipActiveNotification( - this.notificationElementId - )} + notification={uiNotifications.getTooltipActiveNotification(this.notificationElementId)} isHardware={isHwWallet} verify={() => actions.ada.hwVerifyAddress.verifyAddress.trigger(publicDeriver)} cancel={actions.ada.hwVerifyAddress.closeAddressDetailDialog.trigger} @@ -348,22 +372,21 @@ export default class WalletReceivePage extends Component { complexityLevel={profile.selectedComplexityLevel} /> ) : null} - ); } - getTypeStore: PublicDeriver<> => void | {| + getTypeStore: ( + PublicDeriver<> + ) => void | {| +request: IAddressTypeUiSubset, +meta: AddressSubgroupMeta, - |} = (publicDeriver) => { + |} = publicDeriver => { for (const addressStore of allAddressSubgroups) { if (!addressStore.isRelated({ selected: publicDeriver })) { continue; } - if (this.generated.stores.app.currentRoute.startsWith( - routeForStore(addressStore.name) - )) { + if (this.generated.stores.app.currentRoute.startsWith(routeForStore(addressStore.name))) { const request = this.generated.stores.addresses.addressSubgroupMap.get(addressStore.class); if (request == null) throw new Error('Should never happen'); return { @@ -373,28 +396,28 @@ export default class WalletReceivePage extends Component { } } Logger.error(`${nameof(WalletReceivePage)} unexpected address tab`); - } + }; openVerifyAddressDialog: void => void = (): void => { const { actions } = this.generated; actions.dialogs.open.trigger({ dialog: VerifyAddressDialog }); - } + }; - openURIGenerateDialog: ((address: string, amount?: string) => void) = (address, amount) => { + openURIGenerateDialog: (address: string, amount?: string) => void = (address, amount) => { const { actions } = this.generated; actions.dialogs.open.trigger({ dialog: URIGenerateDialog, - params: { address, amount } + params: { address, amount }, }); - } + }; generateURI: (string, BigNumber) => void = (address, amount) => { const { actions } = this.generated; actions.dialogs.open.trigger({ dialog: URIDisplayDialog, - params: { address, amount: amount.toString() } + params: { address, amount: amount.toString() }, }); - } + }; @computed get generated(): {| UnmangleTxDialogContainerProps: InjectedOrGenerated, @@ -402,45 +425,43 @@ export default class WalletReceivePage extends Component { ada: {| hwVerifyAddress: {| closeAddressDetailDialog: {| - trigger: (params: void) => void + trigger: (params: void) => void, |}, selectAddress: {| trigger: (params: $ReadOnly) => Promise, |}, verifyAddress: {| - trigger: ( - params: PublicDeriver<> - ) => Promise - |} - |} + trigger: (params: PublicDeriver<>) => Promise, + |}, + |}, |}, addresses: {| createAddress: {| - trigger: (params: PublicDeriver<>) => Promise + trigger: (params: PublicDeriver<>) => Promise, |}, resetErrors: {| trigger: (params: void) => void |}, resetFilter: {| trigger: (params: void) => void |}, setFilter: {| - trigger: (params: AddressFilterKind) => void - |} + trigger: (params: AddressFilterKind) => void, + |}, |}, dialogs: {| closeActiveDialog: {| - trigger: (params: void) => void + trigger: (params: void) => void, |}, open: {| trigger: (params: {| dialog: any, - params?: any - |}) => void - |} + params?: any, + |}) => void, + |}, |}, notifications: {| closeActiveNotification: {| - trigger: (params: {| id: string |}) => void + trigger: (params: {| id: string |}) => void, |}, - open: {| trigger: (params: Notification) => void |} - |} + open: {| trigger: (params: Notification) => void |}, + |}, |}, canUnmangle: boolean, stores: {| @@ -469,20 +490,20 @@ export default class WalletReceivePage extends Component { error: ?LocalizableError, isActionProcessing: boolean, selectedAddress: ?$ReadOnly, - |} - |} + |}, + |}, |}, uiDialogs: {| getParam: (number | string) => T, - isOpen: any => boolean + isOpen: any => boolean, |}, uiNotifications: {| getTooltipActiveNotification: string => ?Notification, - isOpen: string => boolean + isOpen: string => boolean, |}, - wallets: {| selected: null | PublicDeriver<> |} - |} - |} { + wallets: {| selected: null | PublicDeriver<> |}, + |}, + |} { if (this.props.generated !== undefined) { return this.props.generated; } @@ -551,8 +572,8 @@ export default class WalletReceivePage extends Component { }, actions: { dialogs: { - open: { trigger: actions.dialogs.open.trigger, }, - closeActiveDialog: { trigger: actions.dialogs.closeActiveDialog.trigger, }, + open: { trigger: actions.dialogs.open.trigger }, + closeActiveDialog: { trigger: actions.dialogs.closeActiveDialog.trigger }, }, notifications: { closeActiveNotification: { @@ -563,8 +584,8 @@ export default class WalletReceivePage extends Component { }, }, addresses: { - setFilter: { trigger: actions.addresses.setFilter.trigger, }, - resetFilter: { trigger: actions.addresses.resetFilter.trigger, }, + setFilter: { trigger: actions.addresses.setFilter.trigger }, + resetFilter: { trigger: actions.addresses.resetFilter.trigger }, resetErrors: { trigger: actions.addresses.resetErrors.trigger, }, @@ -574,17 +595,20 @@ export default class WalletReceivePage extends Component { }, ada: { hwVerifyAddress: { - selectAddress: { trigger: actions.ada.hwVerifyAddress.selectAddress.trigger, }, - verifyAddress: { trigger: actions.ada.hwVerifyAddress.verifyAddress.trigger, }, + selectAddress: { trigger: actions.ada.hwVerifyAddress.selectAddress.trigger }, + verifyAddress: { trigger: actions.ada.hwVerifyAddress.verifyAddress.trigger }, closeAddressDetailDialog: { trigger: actions.ada.hwVerifyAddress.closeAddressDetailDialog.trigger, }, }, }, }, - UnmangleTxDialogContainerProps: ( - { stores, actions }: InjectedOrGenerated - ), + UnmangleTxDialogContainerProps: ({ + stores, + actions, + }: InjectedOrGenerated), }); } } + +export default (withLayout(WalletReceivePage): ComponentType); diff --git a/packages/yoroi-extension/app/i18n/locales/en-US.json b/packages/yoroi-extension/app/i18n/locales/en-US.json index b54bfd38cf..03f7551213 100644 --- a/packages/yoroi-extension/app/i18n/locales/en-US.json +++ b/packages/yoroi-extension/app/i18n/locales/en-US.json @@ -748,6 +748,7 @@ "wallet.receive.page.noResultsFoundLabel": "No results found.", "wallet.receive.page.notFoundAnyAddresses": "No wallet addresses have been used yet.", "wallet.receive.page.outputAmountUTXO": "Balance (UTXO sum)", + "wallet.revamp.receive.page.outputAmountUTXO": "Output Amount (UTXO)", "wallet.receive.page.rewardAddressLine1": "Your reward address holds your rewards and is used to validate any changes to your delegation preference", "wallet.receive.page.rewardAddressLine2": "You cannot send {ticker} to reward addresses, but we show it for personal auditing purposes", "wallet.receive.page.unmangeLabel": "Correct delegation preference",