Consolidate account state messaging logic

This commit is contained in:
Brandon Millman
2018-06-18 16:55:52 -07:00
parent 49f5495c45
commit f97e605bf6
5 changed files with 80 additions and 47 deletions

View File

@@ -2,10 +2,12 @@ import { Styles } from '@0xproject/react-shared';
import * as _ from 'lodash'; import * as _ from 'lodash';
import * as React from 'react'; import * as React from 'react';
import { Blockchain } from 'ts/blockchain';
import { defaultMenuItemEntries, Menu } from 'ts/components/portal/menu'; import { defaultMenuItemEntries, Menu } from 'ts/components/portal/menu';
import { Identicon } from 'ts/components/ui/identicon'; import { Identicon } from 'ts/components/ui/identicon';
import { Text } from 'ts/components/ui/text';
import { colors } from 'ts/style/colors'; import { colors } from 'ts/style/colors';
import { WebsitePaths } from 'ts/types'; import { ProviderType, WebsitePaths } from 'ts/types';
import { utils } from 'ts/utils/utils'; import { utils } from 'ts/utils/utils';
const IDENTICON_DIAMETER = 45; const IDENTICON_DIAMETER = 45;
@@ -25,14 +27,15 @@ const styles: Styles = {
MozBorderRadius: BORDER_RADIUS, MozBorderRadius: BORDER_RADIUS,
WebkitBorderRadius: BORDER_RADIUS, WebkitBorderRadius: BORDER_RADIUS,
}, },
userAddress: {
color: colors.white,
},
}; };
export interface DrawerMenuProps { export interface DrawerMenuProps {
selectedPath?: string; selectedPath?: string;
userAddress?: string; userAddress?: string;
injectedProviderName: string;
providerType: ProviderType;
blockchain?: Blockchain;
blockchainIsLoaded: boolean;
} }
export const DrawerMenu = (props: DrawerMenuProps) => { export const DrawerMenu = (props: DrawerMenuProps) => {
const relayerItemEntry = { const relayerItemEntry = {
@@ -41,9 +44,15 @@ export const DrawerMenu = (props: DrawerMenuProps) => {
iconName: 'zmdi-portable-wifi', iconName: 'zmdi-portable-wifi',
}; };
const menuItemEntries = _.concat(relayerItemEntry, defaultMenuItemEntries); const menuItemEntries = _.concat(relayerItemEntry, defaultMenuItemEntries);
const displayMessage = utils.getReadableAccountState(
props.blockchainIsLoaded && !_.isUndefined(props.blockchain),
props.providerType,
props.injectedProviderName,
props.userAddress,
);
return ( return (
<div style={styles.root}> <div style={styles.root}>
<Header userAddress={props.userAddress} /> <Header userAddress={props.userAddress} displayMessage={displayMessage} />
<Menu selectedPath={props.selectedPath} menuItemEntries={menuItemEntries} /> <Menu selectedPath={props.selectedPath} menuItemEntries={menuItemEntries} />
</div> </div>
); );
@@ -51,17 +60,16 @@ export const DrawerMenu = (props: DrawerMenuProps) => {
interface HeaderProps { interface HeaderProps {
userAddress?: string; userAddress?: string;
displayMessage: string;
} }
const Header = (props: HeaderProps) => { const Header = (props: HeaderProps) => {
return ( return (
<div className="flex flex-center py4"> <div className="flex flex-center py4">
<div className="flex flex-column mx-auto"> <div className="flex flex-column mx-auto">
<Identicon address={props.userAddress} diameter={IDENTICON_DIAMETER} style={styles.identicon} /> <Identicon address={props.userAddress} diameter={IDENTICON_DIAMETER} style={styles.identicon} />
{!_.isUndefined(props.userAddress) && ( <Text className="pt2" fontColor={colors.white}>
<div className="pt2" style={styles.userAddress}> {props.displayMessage}
{utils.getAddressBeginAndEnd(props.userAddress)} </Text>
</div>
)}
</div> </div>
</div> </div>
); );

View File

@@ -6,8 +6,10 @@ import * as React from 'react';
import { Blockchain } from 'ts/blockchain'; import { Blockchain } from 'ts/blockchain';
import { ProviderPicker } from 'ts/components/top_bar/provider_picker'; import { ProviderPicker } from 'ts/components/top_bar/provider_picker';
import { Container } from 'ts/components/ui/container';
import { DropDown } from 'ts/components/ui/drop_down'; import { DropDown } from 'ts/components/ui/drop_down';
import { Identicon } from 'ts/components/ui/identicon'; import { Identicon } from 'ts/components/ui/identicon';
import { Text } from 'ts/components/ui/text';
import { Dispatcher } from 'ts/redux/dispatcher'; import { Dispatcher } from 'ts/redux/dispatcher';
import { colors } from 'ts/style/colors'; import { colors } from 'ts/style/colors';
import { ProviderType } from 'ts/types'; import { ProviderType } from 'ts/types';
@@ -40,23 +42,16 @@ const styles: Styles = {
export class ProviderDisplay extends React.Component<ProviderDisplayProps, ProviderDisplayState> { export class ProviderDisplay extends React.Component<ProviderDisplayProps, ProviderDisplayState> {
public render(): React.ReactNode { public render(): React.ReactNode {
const isAddressAvailable = !_.isEmpty(this.props.userAddress);
const isExternallyInjectedProvider = utils.isExternallyInjected( const isExternallyInjectedProvider = utils.isExternallyInjected(
this.props.providerType, this.props.providerType,
this.props.injectedProviderName, this.props.injectedProviderName,
); );
let displayMessage; const displayMessage = utils.getReadableAccountState(
if (!this._isBlockchainReady()) { this._isBlockchainReady(),
displayMessage = 'loading account'; this.props.providerType,
} else if (isAddressAvailable) { this.props.injectedProviderName,
displayMessage = utils.getAddressBeginAndEnd(this.props.userAddress); this.props.userAddress,
// tslint:disable-next-line: prefer-conditional-expression );
} else if (isExternallyInjectedProvider) {
displayMessage = 'Account locked';
} else {
displayMessage = '0x0000...0000';
}
// If the "injected" provider is our fallback public node, then we want to // If the "injected" provider is our fallback public node, then we want to
// show the "connect a wallet" message instead of the providerName // show the "connect a wallet" message instead of the providerName
const injectedProviderName = isExternallyInjectedProvider const injectedProviderName = isExternallyInjectedProvider
@@ -66,7 +61,7 @@ export class ProviderDisplay extends React.Component<ProviderDisplayProps, Provi
this.props.providerType === ProviderType.Injected ? injectedProviderName : 'Ledger Nano S'; this.props.providerType === ProviderType.Injected ? injectedProviderName : 'Ledger Nano S';
const isProviderMetamask = providerTitle === constants.PROVIDER_NAME_METAMASK; const isProviderMetamask = providerTitle === constants.PROVIDER_NAME_METAMASK;
const hoverActiveNode = ( const hoverActiveNode = (
<div className="flex right lg-pr0 md-pr2 sm-pr2 p1" style={styles.root}> <div className="flex items-center p1" style={styles.root}>
<div> <div>
{this._isBlockchainReady() ? ( {this._isBlockchainReady() ? (
<Identicon address={this.props.userAddress} diameter={ROOT_HEIGHT} /> <Identicon address={this.props.userAddress} diameter={ROOT_HEIGHT} />
@@ -74,13 +69,13 @@ export class ProviderDisplay extends React.Component<ProviderDisplayProps, Provi
<CircularProgress size={ROOT_HEIGHT} thickness={2} /> <CircularProgress size={ROOT_HEIGHT} thickness={2} />
)} )}
</div> </div>
<div style={{ marginLeft: 12, paddingTop: 3 }}> <Container marginLeft="12px" marginRight="12px">
<div style={{ fontSize: 16, color: colors.darkGrey }}>{displayMessage}</div> <Text fontSize="14px" fontColor={colors.darkGrey}>
</div> {displayMessage}
</Text>
</Container>
{isProviderMetamask && ( {isProviderMetamask && (
<div style={{ marginLeft: 16 }}> <img src="/images/metamask_icon.png" style={{ width: ROOT_HEIGHT, height: ROOT_HEIGHT }} />
<img src="/images/metamask_icon.png" style={{ width: ROOT_HEIGHT, height: ROOT_HEIGHT }} />
</div>
)} )}
</div> </div>
); );

View File

@@ -297,7 +297,14 @@ export class TopBar extends React.Component<TopBarProps, TopBarState> {
openSecondary={true} openSecondary={true}
onRequestChange={this._onMenuButtonClick.bind(this)} onRequestChange={this._onMenuButtonClick.bind(this)}
> >
<DrawerMenu selectedPath={this.props.location.pathname} userAddress={this.props.userAddress} /> <DrawerMenu
selectedPath={this.props.location.pathname}
userAddress={this.props.userAddress}
injectedProviderName={this.props.injectedProviderName}
providerType={this.props.providerType}
blockchainIsLoaded={this.props.blockchainIsLoaded}
blockchain={this.props.blockchain}
/>
</Drawer> </Drawer>
); );
} }

View File

@@ -1,7 +1,8 @@
import blockies = require('blockies'); import blockies = require('blockies');
import * as _ from 'lodash'; import * as _ from 'lodash';
import * as React from 'react'; import * as React from 'react';
import { constants } from 'ts/utils/constants';
import { colors } from 'ts/style/colors';
interface IdenticonProps { interface IdenticonProps {
address: string; address: string;
@@ -16,14 +17,9 @@ export class Identicon extends React.Component<IdenticonProps, IdenticonState> {
style: {}, style: {},
}; };
public render(): React.ReactNode { public render(): React.ReactNode {
let address = this.props.address; const address = this.props.address;
if (_.isEmpty(address)) {
address = constants.NULL_ADDRESS;
}
const diameter = this.props.diameter; const diameter = this.props.diameter;
const icon = blockies({ const radius = diameter / 2;
seed: address.toLowerCase(),
});
return ( return (
<div <div
className="circle mx-auto relative transitionFix" className="circle mx-auto relative transitionFix"
@@ -34,14 +30,22 @@ export class Identicon extends React.Component<IdenticonProps, IdenticonState> {
...this.props.style, ...this.props.style,
}} }}
> >
<img {!_.isEmpty(address) ? (
src={icon.toDataURL()} <img
style={{ src={blockies({
width: diameter, seed: address.toLowerCase(),
height: diameter, }).toDataURL()}
imageRendering: 'pixelated', style={{
}} width: diameter,
/> height: diameter,
imageRendering: 'pixelated',
}}
/>
) : (
<svg height={diameter} width={diameter}>
<circle cx={radius} cy={radius} r={radius} fill={colors.grey200} />
</svg>
)}
</div> </div>
); );
} }

View File

@@ -190,6 +190,25 @@ export const utils = {
const truncatedAddress = `${address.substring(0, 6)}...${address.substr(-4)}`; // 0x3d5a...b287 const truncatedAddress = `${address.substring(0, 6)}...${address.substr(-4)}`; // 0x3d5a...b287
return truncatedAddress; return truncatedAddress;
}, },
getReadableAccountState(
isBlockchainReady: boolean,
providerType: ProviderType,
injectedProviderName: string,
userAddress?: string,
): string {
const isAddressAvailable = !_.isUndefined(userAddress) && !_.isEmpty(userAddress);
const isExternallyInjectedProvider = utils.isExternallyInjected(providerType, injectedProviderName);
if (!isBlockchainReady) {
return 'Loading account';
} else if (isAddressAvailable) {
return utils.getAddressBeginAndEnd(userAddress);
// tslint:disable-next-line: prefer-conditional-expression
} else if (isExternallyInjectedProvider) {
return 'Account locked';
} else {
return 'No wallet detected';
}
},
hasUniqueNameAndSymbol(tokens: Token[], token: Token): boolean { hasUniqueNameAndSymbol(tokens: Token[], token: Token): boolean {
if (token.isRegistered) { if (token.isRegistered) {
return true; // Since it's registered, it is the canonical token return true; // Since it's registered, it is the canonical token