diff --git a/src/assets/img/asset_ada.png b/src/assets/img/asset_ada.png new file mode 100644 index 0000000000..5df0efc13d Binary files /dev/null and b/src/assets/img/asset_ada.png differ diff --git a/src/assets/img/asset_ada@2x.png b/src/assets/img/asset_ada@2x.png new file mode 100644 index 0000000000..ade879edf4 Binary files /dev/null and b/src/assets/img/asset_ada@2x.png differ diff --git a/src/assets/img/asset_ada@3x.png b/src/assets/img/asset_ada@3x.png new file mode 100644 index 0000000000..dfb82fd52b Binary files /dev/null and b/src/assets/img/asset_ada@3x.png differ diff --git a/src/assets/img/asset_no_image.png b/src/assets/img/asset_no_image.png new file mode 100644 index 0000000000..9f6b85f340 Binary files /dev/null and b/src/assets/img/asset_no_image.png differ diff --git a/src/assets/img/asset_no_image@2x.png b/src/assets/img/asset_no_image@2x.png new file mode 100644 index 0000000000..67072da553 Binary files /dev/null and b/src/assets/img/asset_no_image@2x.png differ diff --git a/src/assets/img/asset_no_image@3x.png b/src/assets/img/asset_no_image@3x.png new file mode 100644 index 0000000000..b09bd95f70 Binary files /dev/null and b/src/assets/img/asset_no_image@3x.png differ diff --git a/src/components/Common/MultiAsset/AssetList.js b/src/components/Common/MultiAsset/AssetList.js index 75d7311bb1..5f5614c633 100644 --- a/src/components/Common/MultiAsset/AssetList.js +++ b/src/components/Common/MultiAsset/AssetList.js @@ -1,18 +1,13 @@ // @flow import React from 'react' -import {type IntlShape, injectIntl} from 'react-intl' +import {defineMessages, useIntl} from 'react-intl' import {FlatList, Text, TouchableOpacity, View} from 'react-native' import type {TokenEntry} from '../../../crypto/MultiToken' import globalMessages, {txLabels} from '../../../i18n/global-messages' import type {Token} from '../../../types/HistoryTransaction' -import { - ASSET_DENOMINATION, - formatTokenAmount, - getAssetDenomination, - getAssetDenominationOrUnknown, -} from '../../../utils/format' +import {formatTokenAmount, getName, getTicker, getTokenFingerprint} from '../../../utils/format' import assetListSendStyle from './styles/AssetListSend.style' import assetListTransactionStyle from './styles/AssetListTransaction.style' import baseStyle from './styles/Base.style' @@ -25,21 +20,23 @@ type AssetRowProps = {| assetMetadata: Token, backColor: {|backgroundColor: string|}, onSelect?: (TokenEntry) => any, - intl: IntlShape, |} -const AssetRow = ({styles, asset, assetMetadata, backColor, onSelect, intl}: AssetRowProps) => { +const AssetRow = ({styles, asset, assetMetadata, backColor, onSelect}: AssetRowProps) => { + const intl = useIntl() const item = ( <> {assetMetadata.isDefault - ? getAssetDenominationOrUnknown(assetMetadata, ASSET_DENOMINATION.TICKER, intl) - : getAssetDenominationOrUnknown(assetMetadata, ASSET_DENOMINATION.NAME, intl)} + ? getTicker(assetMetadata) || intl.formatMessage(messages.unknownAssetName) + : getName(assetMetadata) || intl.formatMessage(messages.unknownAssetName)} + - {assetMetadata.isDefault ? '' : getAssetDenomination(assetMetadata, ASSET_DENOMINATION.FINGERPRINT)} + {assetMetadata.isDefault ? '' : getTokenFingerprint(assetMetadata)} + {formatTokenAmount(asset.amount, assetMetadata, 15)} @@ -62,10 +59,9 @@ type AssetListProps = { assetsMetadata: Dict, styles: NodeStyle, onSelect?: (TokenEntry) => any, - intl: IntlShape, } - -const AssetList = ({assets, assetsMetadata, styles, onSelect, intl}: AssetListProps) => { +const AssetList = ({assets, assetsMetadata, styles, onSelect}: AssetListProps) => { + const intl = useIntl() const colors = [styles.rowColor1, styles.rowColor2] return ( @@ -74,10 +70,11 @@ const AssetList = ({assets, assetsMetadata, styles, onSelect, intl}: AssetListPr {intl.formatMessage(globalMessages.assetsLabel)} {intl.formatMessage(txLabels.amount)} + index.toString()} + data={assets.sort((a) => (assetsMetadata[a.identifier].isDefault ? -1 : 1))} + keyExtractor={(item) => item.identifier} renderItem={({item, index}) => ( )} /> @@ -93,4 +89,11 @@ const AssetList = ({assets, assetsMetadata, styles, onSelect, intl}: AssetListPr ) } -export default injectIntl(AssetList) +export default AssetList + +const messages = defineMessages({ + unknownAssetName: { + id: 'utils.format.unknownAssetName', + defaultMessage: '!!![Unknown asset name]', + }, +}) diff --git a/src/components/Common/MultiAsset/AssetList.stories.js b/src/components/Common/MultiAsset/AssetList.stories.js new file mode 100644 index 0000000000..86931a265e --- /dev/null +++ b/src/components/Common/MultiAsset/AssetList.stories.js @@ -0,0 +1,177 @@ +// @flow + +import {action} from '@storybook/addon-actions' +import {storiesOf} from '@storybook/react-native' +import {BigNumber} from 'bignumber.js' +import React from 'react' + +import type {TokenEntry} from '../../../crypto/MultiToken' +import {type Token} from '../../../types/HistoryTransaction' +import AssetList from './AssetList' +import sendStyle from './styles/AssetListSend.style' +import baseStyle from './styles/Base.style' + +storiesOf('AssetList', module) + .add('baseStyle', () => ( + + )) + .add('sendStyle', () => ( + + )) + +const assetTokens: Array = [ + { + networkId: 123, + identifier: 'policyId123assetName123', + amount: new BigNumber(12344.00234523), + }, + { + networkId: 456, + identifier: 'policyId456assetName456', + amount: new BigNumber(10), + }, + { + networkId: 789, + identifier: 'policyId789assetName789', + amount: new BigNumber(0.00001), + }, + { + networkId: 111, + identifier: 'policyId111assetName111', + amount: new BigNumber(0.00001), + }, + { + networkId: 222, + identifier: 'policyId222assetName222', + amount: new BigNumber(0.00001), + }, + { + networkId: 333, + identifier: 'policyId333assetName333', + amount: new BigNumber(0.00001), + }, + { + networkId: 444, + identifier: 'policyId444assetName444', + amount: new BigNumber(0.00001), + }, + { + networkId: 555, + identifier: 'policyId555assetName555', + amount: new BigNumber(0.00001), + }, +] +const assetTokenInfos: Dict = { + policyId123assetName123: { + networkId: 123, + isDefault: false, + identifier: 'policyId123assetName123', + metadata: { + assetName: 'assetName123', + longName: 'longName123', + maxSupply: 'maxSupply123', + numberOfDecimals: 10, + policyId: 'policyId1233', + ticker: 'ticker123', + type: 'Cardano', + }, + }, + policyId456assetName456: { + networkId: 456, + isDefault: true, + identifier: 'policyId456assetName456', + metadata: { + assetName: 'assetName456', + longName: 'longName456', + maxSupply: 'maxSupply456', + numberOfDecimals: 10, + policyId: 'policyId4566', + ticker: 'ticker456', + type: 'Cardano', + }, + }, + policyId789assetName789: { + networkId: 789, + isDefault: false, + identifier: 'policyId789assetName789', + metadata: { + assetName: 'assetName789', + longName: 'longName789', + maxSupply: 'maxSupply789', + numberOfDecimals: 10, + policyId: 'policyId7899', + ticker: 'ticker789', + type: 'Cardano', + }, + }, + policyId111assetName111: { + networkId: 111, + isDefault: false, + identifier: 'policyId111assetName111', + metadata: { + assetName: 'assetName111', + longName: 'longName111', + maxSupply: 'maxSupply111', + numberOfDecimals: 10, + policyId: 'policyId1119', + ticker: 'ticker111', + type: 'Cardano', + }, + }, + policyId222assetName222: { + networkId: 222, + isDefault: false, + identifier: 'policyId222assetName222', + metadata: { + assetName: 'assetName222', + longName: 'longName222', + maxSupply: 'maxSupply222', + numberOfDecimals: 10, + policyId: 'policyId2229', + ticker: 'ticker222', + type: 'Cardano', + }, + }, + policyId333assetName333: { + networkId: 333, + isDefault: false, + identifier: 'policyId333assetName333', + metadata: { + assetName: 'assetName333', + longName: 'longName333', + maxSupply: 'maxSupply333', + numberOfDecimals: 10, + policyId: 'policyId3339', + ticker: 'ticker333', + type: 'Cardano', + }, + }, + policyId444assetName444: { + networkId: 444, + isDefault: false, + identifier: 'policyId444assetName444', + metadata: { + assetName: 'assetName444', + longName: 'longName444', + maxSupply: 'maxSupply444', + numberOfDecimals: 10, + policyId: 'policyId4449', + ticker: 'ticker444', + type: 'Cardano', + }, + }, + policyId555assetName555: { + networkId: 555, + isDefault: false, + identifier: 'policyId555assetName555', + metadata: { + assetName: 'assetName555', + longName: 'longName555', + maxSupply: 'maxSupply555', + numberOfDecimals: 10, + policyId: 'policyId5559', + ticker: 'ticker555', + type: 'Cardano', + }, + }, +} diff --git a/src/components/Common/MultiAsset/AssetSelector.js b/src/components/Common/MultiAsset/AssetSelector.js deleted file mode 100644 index 16817b38b0..0000000000 --- a/src/components/Common/MultiAsset/AssetSelector.js +++ /dev/null @@ -1,86 +0,0 @@ -// @flow - -import React, {useState} from 'react' -import {type IntlShape, defineMessages, injectIntl} from 'react-intl' -import {Image, LayoutAnimation, TouchableOpacity, View} from 'react-native' - -import arrowDown from '../../../assets/img/arrow_down_fill.png' -import arrowUp from '../../../assets/img/arrow_up_fill.png' -import closeIcon from '../../../assets/img/cross_fill.png' -import type {TokenEntry} from '../../../crypto/MultiToken' -import type {Token} from '../../../types/HistoryTransaction' -import {getAssetDenominationOrId} from '../../../utils/format' -import {Text} from '../../UiKit' -import AssetList from './AssetList' -import assetListStyle from './styles/AssetListSend.style' -import styles from './styles/AssetSelector.style' - -const messages = defineMessages({ - placeHolder: { - id: 'components.ma.assetSelector.placeHolder', - defaultMessage: '!!!Select an asset', - }, -}) - -type Props = { - label?: string, - assets: Array, - assetsMetadata: Dict, - onSelect: (TokenEntry | void) => any, - selectedAsset: TokenEntry | null, - unselectEnabled: boolean, - intl: IntlShape, -} - -const AssetSelector = ({label, assets, assetsMetadata, onSelect, selectedAsset, unselectEnabled, intl}: Props) => { - const [expanded, setExpanded] = useState(false) - - const toggleExpand = () => { - LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut) - setExpanded(!expanded) - } - return ( - - toggleExpand()}> - {selectedAsset == null ? ( - {intl.formatMessage(messages.placeHolder)} - ) : ( - - {' '} - {getAssetDenominationOrId(assetsMetadata[selectedAsset.identifier])}{' '} - - )} - - {unselectEnabled && ( - onSelect()}> - - - )} - - - - - - {label != null && ( - - {label} - - )} - {expanded && ( - - { - onSelect(item) - toggleExpand() - }} - styles={assetListStyle} - assets={assets} - assetsMetadata={assetsMetadata} - /> - - )} - - ) -} - -export default injectIntl(AssetSelector) diff --git a/src/components/Send/AmountField.js b/src/components/Send/AmountField.js index 7b8589e546..4e45ec037c 100644 --- a/src/components/Send/AmountField.js +++ b/src/components/Send/AmountField.js @@ -3,7 +3,7 @@ import React from 'react' import {defineMessages, useIntl} from 'react-intl' -import {ValidatedTextInput} from '../UiKit' +import {TextInput} from '../UiKit' import {editedFormatter, pastedFormatter} from './amountUtils' export const messages = defineMessages({ @@ -34,13 +34,13 @@ const AmountField = ({amount, error, editable, setAmount}: Props) => { } return ( - ) diff --git a/src/components/Send/AssetSelectorScreen/AssetSelectorScreen.js b/src/components/Send/AssetSelectorScreen/AssetSelectorScreen.js new file mode 100644 index 0000000000..6f3dc5e01e --- /dev/null +++ b/src/components/Send/AssetSelectorScreen/AssetSelectorScreen.js @@ -0,0 +1,150 @@ +/* eslint-disable react-native/no-inline-styles */ +// @flow + +import React from 'react' +import {defineMessages} from 'react-intl' +import {useIntl} from 'react-intl' +import {FlatList, TouchableOpacity, View} from 'react-native' +import {Avatar} from 'react-native-paper' +import {SafeAreaView} from 'react-native-safe-area-context' + +import AdaImage from '../../../assets/img/asset_ada.png' +import NoImage from '../../../assets/img/asset_no_image.png' +import type {TokenEntry} from '../../../crypto/MultiToken' +import globalMessages, {txLabels} from '../../../i18n/global-messages' +import {COLORS} from '../../../styles/config' +import {type Token} from '../../../types/HistoryTransaction' +import {decodeHexAscii, getAssetDenominationOrId} from '../../../utils/format' +import {Button, Spacer, Text, TextInput} from '../../UiKit' + +type Props = { + assetTokens: Array, + assetTokenInfos: Dict, + onSelect: (TokenEntry) => mixed, + onSelectAll: () => mixed, +} +export const AssetSelectorScreen = ({assetTokens, assetTokenInfos, onSelect, onSelectAll}: Props) => { + const intl = useIntl() + + const [filter, setFilter] = React.useState('') + const visibleAssetTokens = matches(assetTokenInfos, assetTokens, filter) + .sort((a: TokenEntry, b: TokenEntry) => (a.amount.isGreaterThan(b.amount) ? -1 : 1)) + .sort((a) => (getTokenInfo(assetTokenInfos, a).isDefault ? -1 : 1)) + + return ( + + + setFilter(normalize(text))} /> + + + {intl.formatMessage(globalMessages.assetsLabel)} + {intl.formatMessage(txLabels.amount)} + + + + +
+
+ + ( + + )} + ItemSeparatorComponent={() => } + bounces={false} + contentContainerStyle={{paddingTop: 16, paddingHorizontal: 16}} + keyExtractor={(item) => item.identifier} + /> + + +