diff options
author | Francesco Agosti <francesco.agosti93@gmail.com> | 2018-11-15 07:20:49 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-11-15 07:20:49 +0800 |
commit | 8afcba7ba16382fd656518e8de3b55267e0f7722 (patch) | |
tree | 80456455c6e2a72956c481fb5e02898f556337a8 /packages | |
parent | 2eef766aff1d0d1b13af9a28f28d38bb722996d1 (diff) | |
parent | fe23bf9e51dab87ee8c8995a9d53438daff19ea0 (diff) | |
download | dexon-sol-tools-8afcba7ba16382fd656518e8de3b55267e0f7722.tar dexon-sol-tools-8afcba7ba16382fd656518e8de3b55267e0f7722.tar.gz dexon-sol-tools-8afcba7ba16382fd656518e8de3b55267e0f7722.tar.bz2 dexon-sol-tools-8afcba7ba16382fd656518e8de3b55267e0f7722.tar.lz dexon-sol-tools-8afcba7ba16382fd656518e8de3b55267e0f7722.tar.xz dexon-sol-tools-8afcba7ba16382fd656518e8de3b55267e0f7722.tar.zst dexon-sol-tools-8afcba7ba16382fd656518e8de3b55267e0f7722.zip |
Merge pull request #1257 from 0xProject/feature/instant/different-install-wallet-copy-for-mobile
[instant] Adapt content to operating system, browser and provider type
Diffstat (limited to 'packages')
-rw-r--r-- | packages/instant/package.json | 1 | ||||
-rw-r--r-- | packages/instant/src/components/coinbase_wallet_logo.tsx | 48 | ||||
-rw-r--r-- | packages/instant/src/components/install_wallet_panel_content.tsx | 125 | ||||
-rw-r--r-- | packages/instant/src/components/meta_mask_logo.tsx | 2 | ||||
-rw-r--r-- | packages/instant/src/components/payment_method.tsx | 48 | ||||
-rw-r--r-- | packages/instant/src/components/payment_method_dropdown.tsx | 4 | ||||
-rw-r--r-- | packages/instant/src/components/standard_panel_content.tsx | 14 | ||||
-rw-r--r-- | packages/instant/src/components/wallet_prompt.tsx | 34 | ||||
-rw-r--r-- | packages/instant/src/constants.ts | 14 | ||||
-rw-r--r-- | packages/instant/src/containers/connected_account_payment_method.ts | 1 | ||||
-rw-r--r-- | packages/instant/src/containers/connected_buy_order_progress_or_payment_method.tsx | 3 | ||||
-rw-r--r-- | packages/instant/src/style/theme.ts | 2 | ||||
-rw-r--r-- | packages/instant/src/types.ts | 28 | ||||
-rw-r--r-- | packages/instant/src/util/env.ts | 65 | ||||
-rw-r--r-- | packages/instant/src/util/provider_state_factory.ts | 4 |
15 files changed, 327 insertions, 66 deletions
diff --git a/packages/instant/package.json b/packages/instant/package.json index 6116d67ae..3ad043c2b 100644 --- a/packages/instant/package.json +++ b/packages/instant/package.json @@ -52,6 +52,7 @@ "@0x/subproviders": "^2.1.4", "@0x/types": "^1.2.1", "@0x/typescript-typings": "^3.0.4", + "bowser": "^2.0.0-beta.3", "@0x/utils": "^2.0.5", "@0x/web3-wrapper": "^3.1.4", "copy-to-clipboard": "^3.0.8", diff --git a/packages/instant/src/components/coinbase_wallet_logo.tsx b/packages/instant/src/components/coinbase_wallet_logo.tsx new file mode 100644 index 000000000..f3c9ccdc2 --- /dev/null +++ b/packages/instant/src/components/coinbase_wallet_logo.tsx @@ -0,0 +1,48 @@ +import * as React from 'react'; + +export interface CoinbaseWalletLogoProps { + width?: number; +} + +export const CoinbaseWalletLogo: React.StatelessComponent<CoinbaseWalletLogoProps> = ({ width }) => ( + <svg width={width} viewBox="0 0 164 28" fill="none" xmlns="http://www.w3.org/2000/svg"> + <path + fillRule="evenodd" + clipRule="evenodd" + d="M49.576172.545441h-2.9453v19.595c1.3398.4742 3.0058.8594 4.9101.8594 4.8809 0 7.7071-2.8458 7.7071-7.0846 0-4.20983-2.8555-6.64028-6.666-6.64028-1.1602 0-2.3809.32621-3.0059.65241V.545441zm-21.0176 1.18585c-.9824 0-1.8457.80039-1.8457 1.80802 0 1.03784.8633 1.8684 1.8457 1.8684.9824 0 1.8457-.83056 1.8457-1.8684 0-1.00763-.8633-1.80802-1.8457-1.80802zm-18.2324 16.30675c-.64258.251-1.32031.3832-2.05273.3832-2.56055 0-4.34766-1.6602-4.34766-4.2689 0-2.7568 1.8457-4.26886 4.22656-4.26886.70117 0 1.38086.11985 2.04102.38318.31836-.77942.74023-1.48746 1.25001-2.11145-1.09376-.58255-2.1797-.88037-3.55665-.88037-3.8711 0-6.93555 2.6377-6.93555 6.8775 0 4.2099 2.91602 6.8476 6.93555 6.8476 1.35156 0 2.58984-.2827 3.77345-.9166-.5371-.6008-.98634-1.2863-1.334-2.0453zm8.084-10.76317c3.957 0 6.8144 2.78684 6.8144 6.87757 0 4.0616-2.8574 6.8475-6.8144 6.8475-3.9278 0-6.7852-2.7859-6.7852-6.8475 0-4.09073 2.8574-6.87757 6.7852-6.87757zm-3.8125 6.87757c0-2.6677 1.6074-4.35792 3.8086-4.35792 2.2637 0 3.8398 1.69022 3.8398 4.35792 0 2.6388-1.5761 4.3279-3.8398 4.3279-2.2012 0-3.8086-1.6891-3.8086-4.3279zm12.5 6.5512h2.9453V7.571011h-2.9453v13.13263zm5.5176-12.302c1.8457-.71139 4.1074-1.1268 6.041-1.1268 3.3633 0 5.5058 1.27481 5.5058 4.9803v8.4486h-2.916v-8.1814c0-1.8973-1.1894-2.57984-2.8281-2.57984-1.041 0-2.082.14823-2.8555.38544v10.3758h-2.9472v-12.3021zm16.9609 9.8125c.5664.1771 1.3105.2661 2.0527.2661 2.709 0 4.6446-1.4819 4.6446-4.5059 0-2.5498-1.8164-4.06165-4.0489-4.06165-1.1601 0-2.0527.29712-2.6484.62332v7.67813zm15.6523-8.38946c1.459 0 2.5293.65127 2.5293 2.07426v1.0978h-1.457c-3.8105 0-6.1016 1.3039-6.1016 3.9715 0 2.965 2.5 4.0026 5.9239 4.0026 1.4863 0 3.2715-.2081 4.5215-.5343v-8.6858c0-3.29008-2.0528-4.4759-4.9395-4.4759-1.6367 0-3.125.41541-4.2851 1.06665v2.54985c1.1015-.62242 2.291-1.06666 3.8085-1.06666zm2.5313 5.12836h-1.25c-2.0527 0-3.3926.5922-3.3926 1.9263 0 1.3638 1.25 1.8972 3.0352 1.8972.4472 0 1.0722-.059 1.6074-.1482v-3.6753zm9.0449 3.6754c-1.3379 0-2.7949-.5634-4.0156-1.482v2.7868c.9824.6524 2.5293 1.0667 4.0762 1.0667 2.8574 0 4.9707-1.2449 4.9707-4.0015 0-2.3717-1.3086-3.4985-4.3164-4.1509-1.5176-.4143-2.0821-.8596-2.0821-1.719 0-.85965.6836-1.48209 1.9649-1.48209 1.3965 0 2.5293.5044 3.6894 1.33379v-2.69759c-1.1015-.68142-2.2929-1.00763-3.7793-1.00763-2.6797 0-4.7031 1.30365-4.7031 3.97242 0 2.2827 1.1621 3.3793 3.8984 4.0017 1.6973.4443 2.4707.8295 2.4707 1.8973 0 1.0376-.8632 1.482-2.1738 1.482zm9.0625-3.8236v.0591c.1778 2.3414 2.2012 3.6163 4.2559 3.6163 1.8144 0 3.125-.4152 4.4336-1.2749v2.5798c-1.1914.8295-2.9473 1.2147-4.6426 1.2147-4.1074 0-6.9043-2.6086-6.9043-6.7584 0-4.17964 2.7383-6.96648 6.3691-6.96648 3.8379 0 5.6543 2.46064 5.6543 6.04698v1.4829h-9.166zm3.4238-5.06921c1.9043 0 2.9161 1.12571 2.9747 3.17201h-6.3086c.3867-1.9862 1.6367-3.17201 3.3339-3.17201z" + fill="#1452F5" + /> + <path + fillRule="evenodd" + clipRule="evenodd" + d="M105.543.479151c0-.264628.214523-.479151.479151-.479151s.479151.214523.479151.479151V27.0663c0 .2646-.214523.4792-.479151.4792s-.479151-.2146-.479151-.4792V.479151z" + fill="#AAB4C0" + /> + <path + d="M123.76075 7.60523l-2.94965 11.86278-3.08272-11.86278H116.309l3.57063 13.12198h1.81859l2.83876-11.55353 2.83872 11.55353h1.8186l3.5707-13.12198h-1.3529l-3.1049 11.86278-2.92746-11.86278h-1.61899zM138.3712 15.16032h-1.0867c-2.9496 0-4.7017.99409-4.7017 2.89389 0 2.0324 1.7521 2.8497 4.3691 2.8497.8205 0 1.8629-.1104 2.75-.3534v-6.64937c0-2.16491-1.2641-3.02646-3.2158-3.02646-1.131 0-2.1734.30928-2.994.75109v1.25919c.8871-.46391 1.7299-.77319 2.8388-.77319 1.2419 0 2.0403.64064 2.0403 1.87773v1.17082zm0 4.50659c-.4657.0883-.9536.1325-1.3528.1325-1.7742 0-3.1049-.4639-3.1049-1.7452s1.4637-1.8114 3.4597-1.8114h.998v3.4241zM143.1294 5.70541h-1.3307v15.0218h1.3307V5.70541zM146.7177 5.70541h-1.3307v15.0218h1.3307V5.70541zM156.8928 15.97771v-.90575c0-2.73928-1.619-4.19728-4.0586-4.19728-2.5948 0-4.5908 2.14282-4.5908 5.03673 0 3.1369 2.1291 5.0367 4.9235 5.0367 1.1976 0 2.4396-.2872 3.3045-.9278v-1.3254c-.9758.729-1.9295 1.0824-3.3045 1.0824-1.8851 0-3.5928-1.3917-3.5928-3.7554v-.0442h7.3187zm-7.23-1.10457c.377-1.81146 1.5968-2.82764 3.1049-2.82764 1.619 0 2.7057.81737 2.7944 2.82764h-5.8993zM157.1456 12.22223h1.4859v6.00868c0 1.9882 1.0423 2.7172 2.4395 2.7172.8428 0 1.5081-.1988 2.0848-.486v-1.1708c-.6654.3314-1.1755.5302-1.7965.5302-.9092 0-1.3972-.486-1.3972-1.7231v-5.87618h2.7279v-1.12664h-2.7279V8.75395h-1.3306v2.34164h-1.4859v1.12664z" + fill="#202A36" + /> + </svg> +); + +CoinbaseWalletLogo.displayName = 'CoinbaseWalletLogo'; + +CoinbaseWalletLogo.defaultProps = { + width: 164, +}; + +export interface CoinbaseWalletAppLogoProps { + width?: number; +} + +export const CoinbaseWalletAppLogo: React.StatelessComponent<CoinbaseWalletAppLogoProps> = ({ width }) => ( + <svg width={width} viewBox="0 0 51 51" fill="none" xmlns="http://www.w3.org/2000/svg"> + <circle cx="25.5" cy="25.5" r="25.5" fill="#3263E9" /> + <path + fillRule="evenodd" + clipRule="evenodd" + d="M25.5 41C34.0604 41 41 34.0604 41 25.5C41 16.9396 34.0604 10 25.5 10C16.9396 10 10 16.9396 10 25.5C10 34.0604 16.9396 41 25.5 41ZM21.5108 20.5107C20.9586 20.5107 20.5108 20.9584 20.5108 21.5107V29.6223C20.5108 30.1746 20.9586 30.6223 21.5108 30.6223H29.6224C30.1747 30.6223 30.6224 30.1746 30.6224 29.6223V21.5107C30.6224 20.9584 30.1747 20.5107 29.6224 20.5107H21.5108Z" + fill="white" + /> + </svg> +); diff --git a/packages/instant/src/components/install_wallet_panel_content.tsx b/packages/instant/src/components/install_wallet_panel_content.tsx index 546874212..5cba8d996 100644 --- a/packages/instant/src/components/install_wallet_panel_content.tsx +++ b/packages/instant/src/components/install_wallet_panel_content.tsx @@ -1,32 +1,113 @@ import * as React from 'react'; -import { META_MASK_CHROME_STORE_URL, META_MASK_SITE_URL } from '../constants'; +import { + COINBASE_WALLET_ANDROID_APP_STORE_URL, + COINBASE_WALLET_IOS_APP_STORE_URL, + COINBASE_WALLET_SITE_URL, + META_MASK_CHROME_STORE_URL, + META_MASK_FIREFOX_STORE_URL, + META_MASK_OPERA_STORE_URL, + META_MASK_SITE_URL, +} from '../constants'; import { ColorOption } from '../style/theme'; +import { Browser, OperatingSystem } from '../types'; +import { envUtil } from '../util/env'; +import { CoinbaseWalletLogo } from './coinbase_wallet_logo'; import { MetaMaskLogo } from './meta_mask_logo'; -import { StandardPanelContent } from './standard_panel_content'; +import { StandardPanelContent, StandardPanelContentProps } from './standard_panel_content'; import { Button } from './ui/button'; export interface InstallWalletPanelContentProps {} -export const InstallWalletPanelContent: React.StatelessComponent<InstallWalletPanelContentProps> = () => ( - <StandardPanelContent - image={<MetaMaskLogo width={85} height={80} />} - title="Install MetaMask" - description="Please install the MetaMask wallet extension from the Chrome Store." - moreInfoSettings={{ - href: META_MASK_SITE_URL, - text: 'What is MetaMask?', - }} - action={ - <Button - href={META_MASK_CHROME_STORE_URL} - width="100%" - fontColor={ColorOption.white} - backgroundColor={ColorOption.darkOrange} - > - Get Chrome Extension - </Button> +export class InstallWalletPanelContent extends React.Component<InstallWalletPanelContentProps> { + public render(): React.ReactNode { + const panelProps = this._getStandardPanelContentProps(); + return <StandardPanelContent {...panelProps} />; + } + private readonly _getStandardPanelContentProps = (): StandardPanelContentProps => { + const isMobileOS = envUtil.isMobileOperatingSystem(); + if (isMobileOS) { + return this._getMobilePanelContentProps(); + } else { + return this._getDesktopPanelContentProps(); } - /> -); + }; + private readonly _getDesktopPanelContentProps = (): StandardPanelContentProps => { + const browser = envUtil.getBrowser(); + let description = 'Please install the MetaMask wallet browser extension.'; + let actionText = 'Learn More'; + let actionUrl = META_MASK_SITE_URL; + switch (browser) { + case Browser.Chrome: + description = 'Please install the MetaMask wallet browser extension from the Chrome Store.'; + actionText = 'Get Chrome Extension'; + actionUrl = META_MASK_CHROME_STORE_URL; + break; + case Browser.Firefox: + description = 'Please install the MetaMask wallet browser extension from the Firefox Store.'; + actionText = 'Get Firefox Extension'; + actionUrl = META_MASK_FIREFOX_STORE_URL; + break; + case Browser.Opera: + description = 'Please install the MetaMask wallet browser extension from the Opera Store.'; + actionText = 'Get Opera Add-on'; + actionUrl = META_MASK_OPERA_STORE_URL; + break; + default: + break; + } + return { + image: <MetaMaskLogo width={85} height={80} />, + title: 'Install MetaMask', + description, + moreInfoSettings: { + href: META_MASK_SITE_URL, + text: 'What is MetaMask?', + }, + action: ( + <Button + href={actionUrl} + width="100%" + fontColor={ColorOption.white} + backgroundColor={ColorOption.darkOrange} + > + {actionText} + </Button> + ), + }; + }; + private readonly _getMobilePanelContentProps = (): StandardPanelContentProps => { + const operatingSystem = envUtil.getOperatingSystem(); + let description = 'Please install the Coinbase Wallet app.'; + let actionText = 'Learn More'; + let actionUrl = COINBASE_WALLET_SITE_URL; + switch (operatingSystem) { + case OperatingSystem.Android: + description = 'Please install the Coinbase Wallet app from the Google Play Store.'; + actionText = 'Get Coinbase Wallet'; + actionUrl = COINBASE_WALLET_ANDROID_APP_STORE_URL; + break; + case OperatingSystem.iOS: + description = 'Please install the Coinbase Wallet app from the iOS App Store.'; + actionText = 'Get Coinbase Wallet'; + actionUrl = COINBASE_WALLET_IOS_APP_STORE_URL; + break; + default: + break; + } + return { + image: <CoinbaseWalletLogo width={246} />, + description, + moreInfoSettings: { + href: COINBASE_WALLET_SITE_URL, + text: 'What is Coinbase Wallet?', + }, + action: ( + <Button href={actionUrl} width="100%" fontColor={ColorOption.white} backgroundColor={ColorOption.blue}> + {actionText} + </Button> + ), + }; + }; +} diff --git a/packages/instant/src/components/meta_mask_logo.tsx b/packages/instant/src/components/meta_mask_logo.tsx index d1ad10c23..bfbc67270 100644 --- a/packages/instant/src/components/meta_mask_logo.tsx +++ b/packages/instant/src/components/meta_mask_logo.tsx @@ -72,6 +72,8 @@ export const MetaMaskLogo: React.StatelessComponent<MetaMaskLogoProps> = ({ widt </svg> ); +MetaMaskLogo.displayName = 'MetaMaskLogo'; + MetaMaskLogo.defaultProps = { width: 85, height: 80, diff --git a/packages/instant/src/components/payment_method.tsx b/packages/instant/src/components/payment_method.tsx index 49ec22164..f9d02ec5b 100644 --- a/packages/instant/src/components/payment_method.tsx +++ b/packages/instant/src/components/payment_method.tsx @@ -3,7 +3,9 @@ import * as React from 'react'; import { ColorOption } from '../style/theme'; import { Account, AccountState, Network } from '../types'; +import { envUtil } from '../util/env'; +import { CoinbaseWalletAppLogo } from './coinbase_wallet_logo'; import { MetaMaskLogo } from './meta_mask_logo'; import { PaymentMethodDropdown } from './payment_method_dropdown'; import { Circle } from './ui/circle'; @@ -11,10 +13,12 @@ import { Container } from './ui/container'; import { Flex } from './ui/flex'; import { Icon } from './ui/icon'; import { Text } from './ui/text'; +import { WalletPrompt } from './wallet_prompt'; export interface PaymentMethodProps { account: Account; network: Network; + walletName: string; onInstallWalletClick: () => void; onUnlockWalletClick: () => void; } @@ -34,7 +38,7 @@ export class PaymentMethod extends React.Component<PaymentMethodProps> { > {this._renderTitleText()} </Text> - <Flex>{this._renderTitleLabel()}</Flex> + {this._renderTitleLabel()} </Flex> </Container> {this._renderMainContent()} @@ -58,20 +62,22 @@ export class PaymentMethod extends React.Component<PaymentMethodProps> { if (account.state === AccountState.Ready || account.state === AccountState.Locked) { const circleColor: ColorOption = account.state === AccountState.Ready ? ColorOption.green : ColorOption.red; return ( - <React.Fragment> + <Flex> <Circle diameter={8} color={circleColor} /> <Container marginLeft="3px"> <Text fontColor={ColorOption.darkGrey} fontSize="12px"> - MetaMask + {this.props.walletName} </Text> </Container> - </React.Fragment> + </Flex> ); } return null; }; private readonly _renderMainContent = (): React.ReactNode => { const { account, network } = this.props; + const isMobile = envUtil.isMobileOperatingSystem(); + const logo = isMobile ? <CoinbaseWalletAppLogo width={22} /> : <MetaMaskLogo width={19} height={18} />; switch (account.state) { case AccountState.Loading: // Just take up the same amount of space as the other states. @@ -82,16 +88,13 @@ export class PaymentMethod extends React.Component<PaymentMethodProps> { onClick={this.props.onUnlockWalletClick} image={<Icon width={13} icon="lock" color={ColorOption.black} />} > - Please Unlock MetaMask + Please Unlock {this.props.walletName} </WalletPrompt> ); case AccountState.None: return ( - <WalletPrompt - onClick={this.props.onInstallWalletClick} - image={<MetaMaskLogo width={19} height={18} />} - > - Install MetaMask + <WalletPrompt onClick={this.props.onInstallWalletClick} image={logo}> + {isMobile ? 'Install Coinbase Wallet' : 'Install MetaMask'} </WalletPrompt> ); case AccountState.Ready: @@ -105,28 +108,3 @@ export class PaymentMethod extends React.Component<PaymentMethodProps> { } }; } - -interface WalletPromptProps { - image: React.ReactNode; - onClick?: () => void; -} - -const WalletPrompt: React.StatelessComponent<WalletPromptProps> = ({ onClick, image, children }) => ( - <Container - padding="14.5px" - border={`1px solid ${ColorOption.darkOrange}`} - backgroundColor={ColorOption.lightOrange} - width="100%" - borderRadius="4px" - onClick={onClick} - cursor={onClick ? 'pointer' : undefined} - boxShadowOnHover={!!onClick} - > - <Flex> - <Container marginRight="10px">{image}</Container> - <Text fontSize="16px" fontColor={ColorOption.darkOrange}> - {children} - </Text> - </Flex> - </Container> -); diff --git a/packages/instant/src/components/payment_method_dropdown.tsx b/packages/instant/src/components/payment_method_dropdown.tsx index 58f1cc044..b330dbcd6 100644 --- a/packages/instant/src/components/payment_method_dropdown.tsx +++ b/packages/instant/src/components/payment_method_dropdown.tsx @@ -3,6 +3,7 @@ import copy from 'copy-to-clipboard'; import * as React from 'react'; import { Network } from '../types'; +import { envUtil } from '../util/env'; import { etherscanUtil } from '../util/etherscan'; import { format } from '../util/format'; @@ -22,6 +23,9 @@ export class PaymentMethodDropdown extends React.Component<PaymentMethodDropdown return <Dropdown value={value} label={label} items={this._getDropdownItemConfigs()} />; } private readonly _getDropdownItemConfigs = (): DropdownItemConfig[] => { + if (envUtil.isMobileOperatingSystem()) { + return []; + } const viewOnEtherscan = { text: 'View on Etherscan', onClick: this._handleEtherscanClick, diff --git a/packages/instant/src/components/standard_panel_content.tsx b/packages/instant/src/components/standard_panel_content.tsx index 89e4da70c..582b3318e 100644 --- a/packages/instant/src/components/standard_panel_content.tsx +++ b/packages/instant/src/components/standard_panel_content.tsx @@ -13,7 +13,7 @@ export interface MoreInfoSettings { export interface StandardPanelContentProps { image: React.ReactNode; - title: string; + title?: string; description: string; moreInfoSettings?: MoreInfoSettings; action: React.ReactNode; @@ -31,11 +31,13 @@ export const StandardPanelContent: React.StatelessComponent<StandardPanelContent <Container height="100%"> <Flex direction="column" height="calc(100% - 58px)"> <Container marginBottom={SPACING_BETWEEN_PX}>{image}</Container> - <Container marginBottom={SPACING_BETWEEN_PX}> - <Text fontSize="20px" fontWeight={700} fontColor={ColorOption.black}> - {title} - </Text> - </Container> + {title && ( + <Container marginBottom={SPACING_BETWEEN_PX}> + <Text fontSize="20px" fontWeight={700} fontColor={ColorOption.black}> + {title} + </Text> + </Container> + )} <Container marginBottom={SPACING_BETWEEN_PX}> <Text fontSize="14px" fontColor={ColorOption.grey} center={true}> {description} diff --git a/packages/instant/src/components/wallet_prompt.tsx b/packages/instant/src/components/wallet_prompt.tsx new file mode 100644 index 000000000..bcf66ee81 --- /dev/null +++ b/packages/instant/src/components/wallet_prompt.tsx @@ -0,0 +1,34 @@ +import * as React from 'react'; + +import { ColorOption } from '../style/theme'; + +import { Container } from './ui/container'; +import { Flex } from './ui/flex'; +import { Text } from './ui/text'; + +export interface WalletPromptProps { + image: React.ReactNode; + onClick?: () => void; +} + +export const WalletPrompt: React.StatelessComponent<WalletPromptProps> = ({ onClick, image, children }) => ( + <Container + padding="14.5px" + border={`1px solid ${ColorOption.darkOrange}`} + backgroundColor={ColorOption.lightOrange} + width="100%" + borderRadius="4px" + onClick={onClick} + cursor={onClick ? 'pointer' : undefined} + boxShadowOnHover={!!onClick} + > + <Flex> + {image} + <Container marginLeft="10px"> + <Text fontSize="16px" fontColor={ColorOption.darkOrange}> + {children} + </Text> + </Container> + </Flex> + </Container> +); diff --git a/packages/instant/src/constants.ts b/packages/instant/src/constants.ts index 2bf7849ec..5bd2349b3 100644 --- a/packages/instant/src/constants.ts +++ b/packages/instant/src/constants.ts @@ -1,6 +1,6 @@ import { BigNumber } from '@0x/utils'; -import { AccountNotReady, AccountState, Network } from './types'; +import { AccountNotReady, AccountState, Network, ProviderType } from './types'; export const BIG_NUMBER_ZERO = new BigNumber(0); export const ETH_DECIMALS = 18; @@ -19,8 +19,13 @@ export const ETH_GAS_STATION_API_BASE_URL = 'https://ethgasstation.info'; export const COINBASE_API_BASE_URL = 'https://api.coinbase.com/v2'; export const PROGRESS_STALL_AT_WIDTH = '95%'; export const PROGRESS_FINISH_ANIMATION_TIME_MS = 200; +export const COINBASE_WALLET_IOS_APP_STORE_URL = 'https://itunes.apple.com/us/app/coinbase-wallet/id1278383455?mt=8'; +export const COINBASE_WALLET_ANDROID_APP_STORE_URL = 'https://play.google.com/store/apps/details?id=org.toshi&hl=en'; +export const COINBASE_WALLET_SITE_URL = 'https://wallet.coinbase.com/'; +export const META_MASK_FIREFOX_STORE_URL = 'https://addons.mozilla.org/en-US/firefox/addon/ether-metamask/'; export const META_MASK_CHROME_STORE_URL = 'https://chrome.google.com/webstore/detail/metamask/nkbihfbeogaeaoehlefnkodbefgpgknn?hl=en'; +export const META_MASK_OPERA_STORE_URL = 'https://addons.opera.com/en/extensions/details/metamask/'; export const META_MASK_SITE_URL = 'https://metamask.io/'; export const ETHEREUM_NODE_URL_BY_NETWORK = { [Network.Mainnet]: 'https://mainnet.infura.io/', @@ -36,3 +41,10 @@ export const LOADING_ACCOUNT: AccountNotReady = { export const LOCKED_ACCOUNT: AccountNotReady = { state: AccountState.Locked, }; +export const PROVIDER_TYPE_TO_NAME: { [key in ProviderType]: string } = { + [ProviderType.Cipher]: 'Cipher', + [ProviderType.MetaMask]: 'MetaMask', + [ProviderType.Mist]: 'Mist', + [ProviderType.CoinbaseWallet]: 'Coinbase Wallet', + [ProviderType.Parity]: 'Parity', +}; diff --git a/packages/instant/src/containers/connected_account_payment_method.ts b/packages/instant/src/containers/connected_account_payment_method.ts index 65b3710a6..69c2ddf19 100644 --- a/packages/instant/src/containers/connected_account_payment_method.ts +++ b/packages/instant/src/containers/connected_account_payment_method.ts @@ -47,6 +47,7 @@ const mergeProps = ( network: connectedState.network, account: connectedState.providerState.account, onInstallWalletClick: connectedDispatch.onInstallWalletClick, + walletName: connectedState.providerState.name, onUnlockWalletClick: () => { connectedDispatch.unlockWalletAndDispatchToStore(connectedState.providerState); }, diff --git a/packages/instant/src/containers/connected_buy_order_progress_or_payment_method.tsx b/packages/instant/src/containers/connected_buy_order_progress_or_payment_method.tsx index 05071c8c3..cace18e7e 100644 --- a/packages/instant/src/containers/connected_buy_order_progress_or_payment_method.tsx +++ b/packages/instant/src/containers/connected_buy_order_progress_or_payment_method.tsx @@ -18,8 +18,7 @@ export const BuyOrderProgressOrPaymentMethod = (props: BuyOrderProgressOrPayment orderProcessState === OrderProcessState.Failure ) { return <SelectedAssetBuyOrderProgress />; - } - if (orderProcessState === OrderProcessState.None) { + } else { return <ConnectedAccountPaymentMethod />; } return null; diff --git a/packages/instant/src/style/theme.ts b/packages/instant/src/style/theme.ts index 1e9f55e00..489f11dc3 100644 --- a/packages/instant/src/style/theme.ts +++ b/packages/instant/src/style/theme.ts @@ -17,6 +17,7 @@ export enum ColorOption { darkOrange = 'darkOrange', green = 'green', red = 'red', + blue = 'blue', } export const theme: Theme = { @@ -32,6 +33,7 @@ export const theme: Theme = { darkOrange: '#F2994C', green: '#3CB34F', red: '#D00000', + blue: '#135df6', }; export const transparentWhite = 'rgba(255,255,255,0.3)'; diff --git a/packages/instant/src/types.ts b/packages/instant/src/types.ts index b6f449f38..67f21a396 100644 --- a/packages/instant/src/types.ts +++ b/packages/instant/src/types.ts @@ -95,6 +95,7 @@ export interface AffiliateInfo { } export interface ProviderState { + name: string; provider: Provider; assetBuyer: AssetBuyer; web3Wrapper: Web3Wrapper; @@ -137,3 +138,30 @@ export interface StandardSlidingPanelSettings { animationState: SlideAnimationState; content: StandardSlidingPanelContent; } + +export enum Browser { + Chrome = 'CHROME', + Firefox = 'FIREFOX', + Opera = 'OPERA', + Safari = 'SAFARI', + Edge = 'EDGE', + Other = 'OTHER', +} + +export enum OperatingSystem { + Android = 'ANDROID', + iOS = 'IOS', + Mac = 'MAC', + Windows = 'WINDOWS', + WindowsPhone = 'WINDOWS_PHONE', + Linux = 'LINUX', + Other = 'OTHER', +} + +export enum ProviderType { + Parity = 'PARITY', + MetaMask = 'META_MASK', + Mist = 'MIST', + CoinbaseWallet = 'COINBASE_WALLET', + Cipher = 'CIPHER', +} diff --git a/packages/instant/src/util/env.ts b/packages/instant/src/util/env.ts new file mode 100644 index 000000000..4a32f9cb1 --- /dev/null +++ b/packages/instant/src/util/env.ts @@ -0,0 +1,65 @@ +import * as bowser from 'bowser'; +import { Provider } from 'ethereum-types'; +import * as _ from 'lodash'; + +import { PROVIDER_TYPE_TO_NAME } from '../constants'; +import { Browser, OperatingSystem, ProviderType } from '../types'; + +export const envUtil = { + getBrowser(): Browser { + if (bowser.chrome) { + return Browser.Chrome; + } else if (bowser.firefox) { + return Browser.Firefox; + } else if (bowser.opera) { + return Browser.Opera; + } else if (bowser.msedge) { + return Browser.Edge; + } else if (bowser.safari) { + return Browser.Safari; + } else { + return Browser.Other; + } + }, + isMobileOperatingSystem(): boolean { + return bowser.mobile; + }, + getOperatingSystem(): OperatingSystem { + if (bowser.android) { + return OperatingSystem.Android; + } else if (bowser.ios) { + return OperatingSystem.iOS; + } else if (bowser.mac) { + return OperatingSystem.Mac; + } else if (bowser.windows) { + return OperatingSystem.Windows; + } else if (bowser.windowsphone) { + return OperatingSystem.WindowsPhone; + } else if (bowser.linux) { + return OperatingSystem.Linux; + } else { + return OperatingSystem.Other; + } + }, + getProviderType(provider: Provider): ProviderType | undefined { + if (provider.constructor.name === 'EthereumProvider') { + return ProviderType.Mist; + } else if ((provider as any).isParity) { + return ProviderType.Parity; + } else if ((provider as any).isMetaMask) { + return ProviderType.MetaMask; + } else if (!_.isUndefined(_.get(window, 'SOFA'))) { + return ProviderType.CoinbaseWallet; + } else if (!_.isUndefined(_.get(window, '__CIPHER__'))) { + return ProviderType.Cipher; + } + return; + }, + getProviderName(provider: Provider): string { + const providerTypeIfExists = envUtil.getProviderType(provider); + if (_.isUndefined(providerTypeIfExists)) { + return provider.constructor.name; + } + return PROVIDER_TYPE_TO_NAME[providerTypeIfExists]; + }, +}; diff --git a/packages/instant/src/util/provider_state_factory.ts b/packages/instant/src/util/provider_state_factory.ts index 3281f6bfb..452a71460 100644 --- a/packages/instant/src/util/provider_state_factory.ts +++ b/packages/instant/src/util/provider_state_factory.ts @@ -4,6 +4,7 @@ import * as _ from 'lodash'; import { LOADING_ACCOUNT, NO_ACCOUNT } from '../constants'; import { Maybe, Network, OrderSource, ProviderState } from '../types'; +import { envUtil } from '../util/env'; import { assetBuyerFactory } from './asset_buyer_factory'; import { providerFactory } from './provider_factory'; @@ -29,6 +30,7 @@ export const providerStateFactory = { provider: Provider, ): ProviderState => { const providerState: ProviderState = { + name: envUtil.getProviderName(provider), provider, web3Wrapper: new Web3Wrapper(provider), assetBuyer: assetBuyerFactory.getAssetBuyer(provider, orderSource, network), @@ -40,6 +42,7 @@ export const providerStateFactory = { const injectedProviderIfExists = providerFactory.getInjectedProviderIfExists(); if (!_.isUndefined(injectedProviderIfExists)) { const providerState: ProviderState = { + name: envUtil.getProviderName(injectedProviderIfExists), provider: injectedProviderIfExists, web3Wrapper: new Web3Wrapper(injectedProviderIfExists), assetBuyer: assetBuyerFactory.getAssetBuyer(injectedProviderIfExists, orderSource, network), @@ -53,6 +56,7 @@ export const providerStateFactory = { getInitialProviderStateFallback: (orderSource: OrderSource, network: Network): ProviderState => { const provider = providerFactory.getFallbackNoSigningProvider(network); const providerState: ProviderState = { + name: envUtil.getProviderName(provider), provider, web3Wrapper: new Web3Wrapper(provider), assetBuyer: assetBuyerFactory.getAssetBuyer(provider, orderSource, network), |