diff options
author | Fabio Berger <me@fabioberger.com> | 2018-06-25 17:49:14 +0800 |
---|---|---|
committer | Fabio Berger <me@fabioberger.com> | 2018-06-25 17:49:14 +0800 |
commit | 9b196ba68c9f958cd82ef99ae87fdd87c70441a9 (patch) | |
tree | 26f265a0293703627fffd1993336a57ee694d670 /packages/website | |
parent | 5bfdffda1155e306b17eee0a4e60320c3433d5c4 (diff) | |
parent | f8bde5ab9b8e5d4ec8b9532dfbf18d1202dbfb29 (diff) | |
download | dexon-sol-tools-9b196ba68c9f958cd82ef99ae87fdd87c70441a9.tar dexon-sol-tools-9b196ba68c9f958cd82ef99ae87fdd87c70441a9.tar.gz dexon-sol-tools-9b196ba68c9f958cd82ef99ae87fdd87c70441a9.tar.bz2 dexon-sol-tools-9b196ba68c9f958cd82ef99ae87fdd87c70441a9.tar.lz dexon-sol-tools-9b196ba68c9f958cd82ef99ae87fdd87c70441a9.tar.xz dexon-sol-tools-9b196ba68c9f958cd82ef99ae87fdd87c70441a9.tar.zst dexon-sol-tools-9b196ba68c9f958cd82ef99ae87fdd87c70441a9.zip |
Merge branch 'v2-prototype' into feature/combinatorial-testing
* v2-prototype: (97 commits)
Fix typos in comments
Add modifier and tests for removeAuthorizedAddressAtIndex
Update and add tests
Change removeAuthorizedAddress => removeAuthorizedAddressAtIndex
Move isFunctionRemoveAuthorizedAddress to test
Fix usage of `popLastByte`
Fix LibBytes is a library
Remove `areBytesEqual`
Fix usage of `contentAddress()`
Clean low bits in bytes4
Clean high bits in address
Refactor LibBytes.readBytes4 for consistency
Fix LibBytes.equals
Add trailing garbage testcase for LibBytes.equals
Rename bytes.equals
Add slice and sliceDestructive
Rename bytes.rawAddress and add bytes.contentAddress
Rename read/writeBytesWithLength
Using LibBytes for bytes
Make LibBytes a library
...
# Conflicts:
# packages/contracts/src/utils/constants.ts
# packages/contracts/test/exchange/core.ts
Diffstat (limited to 'packages/website')
30 files changed, 441 insertions, 309 deletions
diff --git a/packages/website/public/images/team/chris.png b/packages/website/public/images/team/chris.png Binary files differnew file mode 100644 index 000000000..242a2813f --- /dev/null +++ b/packages/website/public/images/team/chris.png diff --git a/packages/website/public/images/team/mel.png b/packages/website/public/images/team/mel.png Binary files differnew file mode 100644 index 000000000..52d779ad2 --- /dev/null +++ b/packages/website/public/images/team/mel.png diff --git a/packages/website/ts/components/generate_order/asset_picker.tsx b/packages/website/ts/components/generate_order/asset_picker.tsx index d7cc554c4..b0dcf5678 100644 --- a/packages/website/ts/components/generate_order/asset_picker.tsx +++ b/packages/website/ts/components/generate_order/asset_picker.tsx @@ -9,6 +9,7 @@ import { TokenIcon } from 'ts/components/ui/token_icon'; import { trackedTokenStorage } from 'ts/local_storage/tracked_token_storage'; import { Dispatcher } from 'ts/redux/dispatcher'; import { DialogConfigs, Token, TokenByAddress, TokenVisibility } from 'ts/types'; +import { constants } from 'ts/utils/constants'; const TOKEN_ICON_DIMENSION = 100; const TILE_DIMENSION = 146; @@ -134,7 +135,9 @@ export class AssetPicker extends React.Component<AssetPickerProps, AssetPickerSt const gridTiles = _.map(this.props.tokenByAddress, (token: Token, address: string) => { if ( (this.props.tokenVisibility === TokenVisibility.TRACKED && !token.isTracked) || - (this.props.tokenVisibility === TokenVisibility.UNTRACKED && token.isTracked) + (this.props.tokenVisibility === TokenVisibility.UNTRACKED && token.isTracked) || + token.symbol === constants.ZRX_TOKEN_SYMBOL || + token.symbol === constants.ETHER_TOKEN_SYMBOL ) { return null; // Skip } @@ -151,12 +154,12 @@ export class AssetPicker extends React.Component<AssetPickerProps, AssetPickerSt height: TILE_DIMENSION, ...tileStyles, }} - className="p2 mx-auto" + className="p2 flex flex-column items-center" onClick={this._onChooseToken.bind(this, address)} onMouseEnter={this._onToggleHover.bind(this, address, true)} onMouseLeave={this._onToggleHover.bind(this, address, false)} > - <div className="p1 center"> + <div className="p1"> <TokenIcon token={token} diameter={TOKEN_ICON_DIMENSION} /> </div> <div className="center">{token.name}</div> @@ -232,12 +235,14 @@ export class AssetPicker extends React.Component<AssetPickerProps, AssetPickerSt this.props.onTokenChosen(newToken.address); } private async _onTrackConfirmationRespondedAsync(didUserAcceptTracking: boolean): Promise<void> { + const resetState: AssetPickerState = { + ...this.state, + isAddingTokenToTracked: false, + assetView: AssetViews.ASSET_PICKER, + chosenTrackTokenAddress: undefined, + }; if (!didUserAcceptTracking) { - this.setState({ - isAddingTokenToTracked: false, - assetView: AssetViews.ASSET_PICKER, - chosenTrackTokenAddress: undefined, - }); + this.setState(resetState); this._onCloseDialog(); return; } @@ -246,6 +251,10 @@ export class AssetPicker extends React.Component<AssetPickerProps, AssetPickerSt }); const tokenAddress = this.state.chosenTrackTokenAddress; const token = this.props.tokenByAddress[tokenAddress]; + if (_.isUndefined(tokenAddress)) { + this.setState(resetState); + return; + } const newTokenEntry = { ...token, }; @@ -254,11 +263,7 @@ export class AssetPicker extends React.Component<AssetPickerProps, AssetPickerSt trackedTokenStorage.addTrackedTokenToUser(this.props.userAddress, this.props.networkId, newTokenEntry); this.props.dispatcher.updateTokenByAddress([newTokenEntry]); - this.setState({ - isAddingTokenToTracked: false, - assetView: AssetViews.ASSET_PICKER, - chosenTrackTokenAddress: undefined, - }); + this.setState(resetState); this.props.onTokenChosen(tokenAddress); } } diff --git a/packages/website/ts/components/onboarding/onboarding_card.tsx b/packages/website/ts/components/onboarding/onboarding_card.tsx new file mode 100644 index 000000000..bc83b8034 --- /dev/null +++ b/packages/website/ts/components/onboarding/onboarding_card.tsx @@ -0,0 +1,84 @@ +import { colors } from '@0xproject/react-shared'; +import * as React from 'react'; + +import { Button } from 'ts/components/ui/button'; +import { Container } from 'ts/components/ui/container'; +import { IconButton } from 'ts/components/ui/icon_button'; +import { Island } from 'ts/components/ui/island'; +import { Text, Title } from 'ts/components/ui/text'; + +export type ContinueButtonDisplay = 'enabled' | 'disabled'; + +export interface OnboardingCardProps { + title?: string; + content: React.ReactNode; + isLastStep: boolean; + onClose: () => void; + onClickNext: () => void; + onClickBack: () => void; + continueButtonDisplay?: ContinueButtonDisplay; + shouldHideBackButton?: boolean; + shouldHideNextButton?: boolean; + continueButtonText?: string; + borderRadius?: string; +} + +export const OnboardingCard: React.StatelessComponent<OnboardingCardProps> = ({ + title, + content, + continueButtonDisplay, + continueButtonText, + onClickNext, + onClickBack, + onClose, + shouldHideBackButton, + shouldHideNextButton, + borderRadius, +}) => ( + <Island borderRadius={borderRadius}> + <Container paddingRight="30px" paddingLeft="30px" maxWidth={350} paddingTop="15px" paddingBottom="15px"> + <div className="flex flex-column"> + <div className="flex justify-between"> + <Title>{title}</Title> + <Container position="relative" bottom="20px" left="15px"> + <IconButton color={colors.grey} iconName="zmdi-close" onClick={onClose}> + Close + </IconButton> + </Container> + </div> + <Container marginBottom="15px"> + <Text>{content}</Text> + </Container> + {continueButtonDisplay && ( + <Button + isDisabled={continueButtonDisplay === 'disabled'} + onClick={onClickNext} + fontColor={colors.white} + fontSize="15px" + backgroundColor={colors.mediumBlue} + > + {continueButtonText} + </Button> + )} + <Container className="flex justify-between" marginTop="15px"> + {!shouldHideBackButton && ( + <Text fontColor={colors.grey} onClick={onClickBack}> + Back + </Text> + )} + {!shouldHideNextButton && ( + <Text fontColor={colors.grey} onClick={onClickNext}> + Skip + </Text> + )} + </Container> + </div> + </Container> + </Island> +); + +OnboardingCard.defaultProps = { + continueButtonText: 'Continue', +}; + +OnboardingCard.displayName = 'OnboardingCard'; diff --git a/packages/website/ts/components/onboarding/onboarding_flow.tsx b/packages/website/ts/components/onboarding/onboarding_flow.tsx index 34aeace82..ec8d96191 100644 --- a/packages/website/ts/components/onboarding/onboarding_flow.tsx +++ b/packages/website/ts/components/onboarding/onboarding_flow.tsx @@ -1,7 +1,9 @@ import * as React from 'react'; import { Placement, Popper, PopperChildrenProps } from 'react-popper'; +import { OnboardingCard } from 'ts/components/onboarding/onboarding_card'; import { ContinueButtonDisplay, OnboardingTooltip } from 'ts/components/onboarding/onboarding_tooltip'; +import { Animation } from 'ts/components/ui/animation'; import { Container } from 'ts/components/ui/container'; import { Overlay } from 'ts/components/ui/overlay'; @@ -22,26 +24,41 @@ export interface OnboardingFlowProps { isRunning: boolean; onClose: () => void; updateOnboardingStep: (stepIndex: number) => void; + disableOverlay?: boolean; + isMobile: boolean; } export class OnboardingFlow extends React.Component<OnboardingFlowProps> { + public static defaultProps = { + disableOverlay: false, + isMobile: false, + }; public render(): React.ReactNode { if (!this.props.isRunning) { return null; } - return ( - <Overlay> - <Popper referenceElement={this._getElementForStep()} placement={this._getCurrentStep().placement}> + let onboardingElement = null; + if (this.props.isMobile) { + onboardingElement = <Animation type="easeUpFromBottom">{this._renderOnboardignCard()}</Animation>; + } else { + onboardingElement = ( + <Popper + referenceElement={this._getElementForStep()} + placement={this._getCurrentStep().placement} + positionFixed={true} + > {this._renderPopperChildren.bind(this)} </Popper> - </Overlay> - ); + ); + } + if (this.props.disableOverlay) { + return onboardingElement; + } + return <Overlay>{onboardingElement}</Overlay>; } - private _getElementForStep(): Element { return document.querySelector(this._getCurrentStep().target); } - private _renderPopperChildren(props: PopperChildrenProps): React.ReactNode { return ( <div ref={props.ref} style={props.style} data-placement={props.placement}> @@ -49,13 +66,12 @@ export class OnboardingFlow extends React.Component<OnboardingFlowProps> { </div> ); } - private _renderToolTip(): React.ReactNode { const { steps, stepIndex } = this.props; const step = steps[stepIndex]; const isLastStep = steps.length - 1 === stepIndex; return ( - <Container marginLeft="30px"> + <Container marginLeft="30px" maxWidth={350}> <OnboardingTooltip title={step.title} content={step.content} @@ -72,10 +88,31 @@ export class OnboardingFlow extends React.Component<OnboardingFlowProps> { ); } + private _renderOnboardignCard(): React.ReactNode { + const { steps, stepIndex } = this.props; + const step = steps[stepIndex]; + const isLastStep = steps.length - 1 === stepIndex; + return ( + <Container position="relative" zIndex={1} maxWidth="100vw"> + <OnboardingCard + title={step.title} + content={step.content} + isLastStep={isLastStep} + shouldHideBackButton={step.shouldHideBackButton} + shouldHideNextButton={step.shouldHideNextButton} + onClose={this.props.onClose} + onClickNext={this._goToNextStep.bind(this)} + onClickBack={this._goToPrevStep.bind(this)} + continueButtonDisplay={step.continueButtonDisplay} + continueButtonText={step.continueButtonText} + borderRadius="10px 10px 0px 0px" + /> + </Container> + ); + } private _getCurrentStep(): Step { return this.props.steps[this.props.stepIndex]; } - private _goToNextStep(): void { const nextStep = this.props.stepIndex + 1; if (nextStep < this.props.steps.length) { @@ -84,7 +121,6 @@ export class OnboardingFlow extends React.Component<OnboardingFlowProps> { this.props.onClose(); } } - private _goToPrevStep(): void { const nextStep = this.props.stepIndex - 1; if (nextStep >= 0) { diff --git a/packages/website/ts/components/onboarding/onboarding_tooltip.tsx b/packages/website/ts/components/onboarding/onboarding_tooltip.tsx index 45851b4de..d8065625d 100644 --- a/packages/website/ts/components/onboarding/onboarding_tooltip.tsx +++ b/packages/website/ts/components/onboarding/onboarding_tooltip.tsx @@ -1,90 +1,25 @@ -import { colors } from '@0xproject/react-shared'; import * as React from 'react'; -import { Button } from 'ts/components/ui/button'; -import { Container } from 'ts/components/ui/container'; -import { IconButton } from 'ts/components/ui/icon_button'; -import { Island } from 'ts/components/ui/island'; +import { OnboardingCard, OnboardingCardProps } from 'ts/components/onboarding/onboarding_card'; import { Pointer, PointerDirection } from 'ts/components/ui/pointer'; -import { Text, Title } from 'ts/components/ui/text'; export type ContinueButtonDisplay = 'enabled' | 'disabled'; -export interface OnboardingTooltipProps { - title?: string; - content: React.ReactNode; - isLastStep: boolean; - onClose: () => void; - onClickNext: () => void; - onClickBack: () => void; - continueButtonDisplay?: ContinueButtonDisplay; - shouldHideBackButton?: boolean; - shouldHideNextButton?: boolean; - pointerDirection?: PointerDirection; - continueButtonText?: string; +export interface OnboardingTooltipProps extends OnboardingCardProps { className?: string; + pointerDirection?: PointerDirection; } -export const OnboardingTooltip: React.StatelessComponent<OnboardingTooltipProps> = ({ - title, - content, - continueButtonDisplay, - continueButtonText, - onClickNext, - onClickBack, - onClose, - shouldHideBackButton, - shouldHideNextButton, - pointerDirection, - className, -}) => ( - <Pointer className={className} direction={pointerDirection}> - <Island> - <Container paddingRight="30px" paddingLeft="30px" maxWidth={350} paddingTop="15px" paddingBottom="15px"> - <div className="flex flex-column"> - <div className="flex justify-between"> - <Title>{title}</Title> - <Container position="relative" bottom="20px" left="15px"> - <IconButton color={colors.grey} iconName="zmdi-close" onClick={onClose}> - Close - </IconButton> - </Container> - </div> - <Container marginBottom="15px"> - <Text>{content}</Text> - </Container> - {continueButtonDisplay && ( - <Button - isDisabled={continueButtonDisplay === 'disabled'} - onClick={onClickNext} - fontColor={colors.white} - fontSize="15px" - backgroundColor={colors.mediumBlue} - > - {continueButtonText} - </Button> - )} - <Container className="flex justify-between" marginTop="15px"> - {!shouldHideBackButton && ( - <Text fontColor={colors.grey} onClick={onClickBack}> - Back - </Text> - )} - {!shouldHideNextButton && ( - <Text fontColor={colors.grey} onClick={onClickNext}> - Skip - </Text> - )} - </Container> - </div> - </Container> - </Island> - </Pointer> -); - +export const OnboardingTooltip: React.StatelessComponent<OnboardingTooltipProps> = props => { + const { pointerDirection, className, ...cardProps } = props; + return ( + <Pointer className={className} direction={pointerDirection}> + <OnboardingCard {...cardProps} /> + </Pointer> + ); +}; OnboardingTooltip.defaultProps = { pointerDirection: 'left', - continueButtonText: 'Continue', }; OnboardingTooltip.displayName = 'OnboardingTooltip'; diff --git a/packages/website/ts/components/onboarding/portal_onboarding_flow.tsx b/packages/website/ts/components/onboarding/portal_onboarding_flow.tsx index 7e40192f6..10d4af30e 100644 --- a/packages/website/ts/components/onboarding/portal_onboarding_flow.tsx +++ b/packages/website/ts/components/onboarding/portal_onboarding_flow.tsx @@ -14,7 +14,7 @@ import { SetAllowancesOnboardingStep } from 'ts/components/onboarding/set_allowa import { UnlockWalletOnboardingStep } from 'ts/components/onboarding/unlock_wallet_onboarding_step'; import { WrapEthOnboardingStep } from 'ts/components/onboarding/wrap_eth_onboarding_step'; import { AllowanceToggle } from 'ts/containers/inputs/allowance_toggle'; -import { ProviderType, Token, TokenByAddress, TokenStateByAddress } from 'ts/types'; +import { ProviderType, ScreenWidths, Token, TokenByAddress, TokenStateByAddress } from 'ts/types'; import { analytics } from 'ts/utils/analytics'; import { utils } from 'ts/utils/utils'; @@ -34,6 +34,7 @@ export interface PortalOnboardingFlowProps extends RouteComponentProps<any> { updateIsRunning: (isRunning: boolean) => void; updateOnboardingStep: (stepIndex: number) => void; refetchTokenStateAsync: (tokenAddress: string) => Promise<void>; + screenWidth: ScreenWidths; } class PlainPortalOnboardingFlow extends React.Component<PortalOnboardingFlowProps> { @@ -57,6 +58,8 @@ class PlainPortalOnboardingFlow extends React.Component<PortalOnboardingFlowProp isRunning={this.props.isRunning} onClose={this._closeOnboarding.bind(this)} updateOnboardingStep={this._updateOnboardingStep.bind(this)} + disableOverlay={this.props.screenWidth === ScreenWidths.Sm} + isMobile={this.props.screenWidth === ScreenWidths.Sm} /> ); } diff --git a/packages/website/ts/components/onboarding/unlock_wallet_onboarding_step.tsx b/packages/website/ts/components/onboarding/unlock_wallet_onboarding_step.tsx index 6e6a74a06..0039aa545 100644 --- a/packages/website/ts/components/onboarding/unlock_wallet_onboarding_step.tsx +++ b/packages/website/ts/components/onboarding/unlock_wallet_onboarding_step.tsx @@ -10,7 +10,7 @@ export const UnlockWalletOnboardingStep: React.StatelessComponent<UnlockWalletOn <Container marginTop="15px" marginBottom="15px"> <img src="/images/metamask_icon.png" height="50px" width="50px" /> </Container> - <Text>Unlock your metamask extension to begin.</Text> + <Text center={true}>Unlock your metamask extension to get started.</Text> </div> </div> ); diff --git a/packages/website/ts/components/portal/back_button.tsx b/packages/website/ts/components/portal/back_button.tsx index 2d0bbefc3..ca35abc2b 100644 --- a/packages/website/ts/components/portal/back_button.tsx +++ b/packages/website/ts/components/portal/back_button.tsx @@ -2,6 +2,7 @@ import { Styles } from '@0xproject/react-shared'; import * as React from 'react'; import { Link } from 'react-router-dom'; +import { Island } from 'ts/components/ui/island'; import { colors } from 'ts/style/colors'; export interface BackButtonProps { @@ -15,9 +16,7 @@ const styles: Styles = { backButton: { height: BACK_BUTTON_HEIGHT, paddingTop: 10, - backgroundColor: colors.white, borderRadius: BACK_BUTTON_HEIGHT, - boxShadow: `0px 4px 6px ${colors.walletBoxShadow}`, }, backButtonIcon: { color: colors.mediumBlue, @@ -29,14 +28,14 @@ export const BackButton = (props: BackButtonProps) => { return ( <div style={{ height: 65, paddingTop: 25 }}> <Link to={props.to} style={{ textDecoration: 'none' }}> - <div className="flex right" style={styles.backButton}> + <Island className="flex right" style={styles.backButton}> <div style={{ marginLeft: 12 }}> <i style={styles.backButtonIcon} className={`zmdi zmdi-arrow-left`} /> </div> <div style={{ marginLeft: 12, marginRight: 12 }}> <div style={{ fontSize: 16, color: colors.lightGrey }}>{props.labelText}</div> </div> - </div> + </Island> </Link> </div> ); diff --git a/packages/website/ts/components/portal/drawer_menu.tsx b/packages/website/ts/components/portal/drawer_menu.tsx index 4bd07769f..205a60afc 100644 --- a/packages/website/ts/components/portal/drawer_menu.tsx +++ b/packages/website/ts/components/portal/drawer_menu.tsx @@ -65,7 +65,7 @@ interface HeaderProps { const Header = (props: HeaderProps) => { return ( <div className="flex flex-center py4"> - <div className="flex flex-column mx-auto"> + <div className="flex flex-column mx-auto items-center"> <Identicon address={props.userAddress} diameter={IDENTICON_DIAMETER} style={styles.identicon} /> <Text className="pt2" fontColor={colors.white}> {props.displayMessage} diff --git a/packages/website/ts/components/portal/portal.tsx b/packages/website/ts/components/portal/portal.tsx index 67314678b..4166fde53 100644 --- a/packages/website/ts/components/portal/portal.tsx +++ b/packages/website/ts/components/portal/portal.tsx @@ -1,4 +1,4 @@ -import { colors, constants as sharedConstants, Styles } from '@0xproject/react-shared'; +import { colors, constants as sharedConstants } from '@0xproject/react-shared'; import { BigNumber } from '@0xproject/utils'; import * as _ from 'lodash'; import ActionAccountBalanceWallet from 'material-ui/svg-icons/action/account-balance-wallet'; @@ -107,26 +107,7 @@ const TOP_BAR_HEIGHT = TopBar.heightForDisplayType(TopBarDisplayType.Expanded); const LEFT_COLUMN_WIDTH = 346; const MENU_PADDING_LEFT = 185; const LARGE_LAYOUT_MAX_WIDTH = 1200; - -const styles: Styles = { - root: { - width: '100%', - height: '100%', - backgroundColor: colors.lightestGrey, - }, - body: { - height: `calc(100vh - ${TOP_BAR_HEIGHT}px)`, - }, - leftColumn: { - width: LEFT_COLUMN_WIDTH, - height: '100%', - }, - scrollContainer: { - height: `calc(100vh - ${TOP_BAR_HEIGHT}px)`, - WebkitOverflowScrolling: 'touch', - overflow: 'auto', - }, -}; +const LARGE_LAYOUT_MARGIN = 30; export class Portal extends React.Component<PortalProps, PortalState> { private _blockchain: Blockchain; @@ -245,12 +226,7 @@ export class Portal extends React.Component<PortalProps, PortalState> { ? TokenVisibility.UNTRACKED : TokenVisibility.TRACKED; return ( - <div style={styles.root}> - <PortalOnboardingFlow - blockchain={this._blockchain} - trackedTokenStateByAddress={this.state.trackedTokenStateByAddress} - refetchTokenStateAsync={this._refetchTokenStateAsync.bind(this)} - /> + <Container> <DocumentTitle title="0x Portal DApp" /> <TopBar userAddress={this.props.userAddress} @@ -264,10 +240,14 @@ export class Portal extends React.Component<PortalProps, PortalState> { blockchain={this._blockchain} translate={this.props.translate} displayType={TopBarDisplayType.Expanded} - style={{ backgroundColor: colors.lightestGrey }} + style={{ + backgroundColor: colors.lightestGrey, + position: 'fixed', + zIndex: zIndex.topBar, + }} maxWidth={LARGE_LAYOUT_MAX_WIDTH} /> - <div id="portal" style={styles.body}> + <Container marginTop={TOP_BAR_HEIGHT} minHeight="100vh" backgroundColor={colors.lightestGrey}> <Switch> <Route path={`${WebsitePaths.Portal}/:route`} render={this._renderOtherRoutes.bind(this)} /> <Route @@ -306,8 +286,8 @@ export class Portal extends React.Component<PortalProps, PortalState> { tokenByAddress={this.props.tokenByAddress} tokenVisibility={tokenVisibility} /> - </div> - </div> + </Container> + </Container> ); } private _renderMainRoute(): React.ReactNode { @@ -340,62 +320,84 @@ export class Portal extends React.Component<PortalProps, PortalState> { ); } private _renderWallet(): React.ReactNode { + const startOnboarding = this._renderStartOnboarding(); + const isMobile = utils.isMobile(this.props.screenWidth); + // We need room to scroll down for mobile onboarding + const marginBottom = isMobile ? '200px' : '15px'; return ( <div> - <Wallet - style={this.props.isPortalOnboardingShowing ? { zIndex: zIndex.aboveOverlay } : undefined} - userAddress={this.props.userAddress} - networkId={this.props.networkId} + <Container> + {isMobile && <Container marginBottom="15px">{startOnboarding}</Container>} + <Container marginBottom={marginBottom}> + <Wallet + style={ + !isMobile && this.props.isPortalOnboardingShowing + ? { zIndex: zIndex.aboveOverlay, position: 'relative' } + : undefined + } + userAddress={this.props.userAddress} + networkId={this.props.networkId} + blockchain={this._blockchain} + blockchainIsLoaded={this.props.blockchainIsLoaded} + blockchainErr={this.props.blockchainErr} + dispatcher={this.props.dispatcher} + tokenByAddress={this.props.tokenByAddress} + trackedTokens={this._getCurrentTrackedTokens()} + userEtherBalanceInWei={this.props.userEtherBalanceInWei} + lastForceTokenStateRefetch={this.props.lastForceTokenStateRefetch} + injectedProviderName={this.props.injectedProviderName} + providerType={this.props.providerType} + screenWidth={this.props.screenWidth} + location={this.props.location} + trackedTokenStateByAddress={this.state.trackedTokenStateByAddress} + onToggleLedgerDialog={this._onToggleLedgerDialog.bind(this)} + onAddToken={this._onAddToken.bind(this)} + onRemoveToken={this._onRemoveToken.bind(this)} + refetchTokenStateAsync={this._refetchTokenStateAsync.bind(this)} + /> + </Container> + {!isMobile && <Container marginTop="15px">{startOnboarding}</Container>} + </Container> + <PortalOnboardingFlow blockchain={this._blockchain} - blockchainIsLoaded={this.props.blockchainIsLoaded} - blockchainErr={this.props.blockchainErr} - dispatcher={this.props.dispatcher} - tokenByAddress={this.props.tokenByAddress} - trackedTokens={this._getCurrentTrackedTokens()} - userEtherBalanceInWei={this.props.userEtherBalanceInWei} - lastForceTokenStateRefetch={this.props.lastForceTokenStateRefetch} - injectedProviderName={this.props.injectedProviderName} - providerType={this.props.providerType} - screenWidth={this.props.screenWidth} - location={this.props.location} trackedTokenStateByAddress={this.state.trackedTokenStateByAddress} - onToggleLedgerDialog={this._onToggleLedgerDialog.bind(this)} - onAddToken={this._onAddToken.bind(this)} - onRemoveToken={this._onRemoveToken.bind(this)} refetchTokenStateAsync={this._refetchTokenStateAsync.bind(this)} /> - <Container marginTop="15px"> - <Island> - <Container - marginTop="30px" - marginBottom="30px" - marginLeft="30px" - marginRight="30px" - className="flex justify-around items-center" - > - <ActionAccountBalanceWallet - style={{ width: '30px', height: '30px' }} - color={colors.orange} - /> - <Text - fontColor={colors.grey} - fontSize="16px" - center={true} - onClick={this._startOnboarding.bind(this)} - > - Learn how to set up your account - </Text> - </Container> - </Island> - </Container> </div> ); } + private _renderStartOnboarding(): React.ReactNode { + return ( + <Island> + <Container + marginTop="30px" + marginBottom="30px" + marginLeft="30px" + marginRight="30px" + className="flex justify-around items-center" + > + <ActionAccountBalanceWallet style={{ width: '30px', height: '30px' }} color={colors.orange} /> + <Text + fontColor={colors.grey} + fontSize="16px" + center={true} + onClick={this._startOnboarding.bind(this)} + > + Learn how to set up your account + </Text> + </Container> + </Island> + ); + } private _startOnboarding(): void { const networkName = sharedConstants.NETWORK_NAME_BY_ID[this.props.networkId]; analytics.logEvent('Portal', 'Onboarding Started - Manual', networkName, this.props.portalOnboardingStep); this.props.dispatcher.updatePortalOnboardingShowing(true); + // On mobile, make sure the wallet is completely visible. + if (this.props.screenWidth === ScreenWidths.Sm) { + document.querySelector('.wallet').scrollIntoView(); + } } private _renderWalletSection(): React.ReactNode { return <Section header={<TextHeader labelText="Your Account" />} body={this._renderWallet()} />; @@ -536,7 +538,7 @@ export class Portal extends React.Component<PortalProps, PortalState> { private _renderRelayerIndexSection(): React.ReactNode { return ( <Section - header={<TextHeader labelText="Explore 0x Relayers" />} + header={<TextHeader labelText="0x Relayers" />} body={<RelayerIndex networkId={this.props.networkId} screenWidth={this.props.screenWidth} />} /> ); @@ -693,14 +695,23 @@ interface LargeLayoutProps { } const LargeLayout = (props: LargeLayoutProps) => { return ( - <div className="mx-auto flex flex-center" style={{ maxWidth: LARGE_LAYOUT_MAX_WIDTH }}> - <div className="flex-last px2"> - <div style={styles.leftColumn}>{props.left}</div> - </div> - <div className="flex-auto px2" style={styles.scrollContainer}> - {props.right} + <Container className="mx-auto flex flex-center" maxWidth={LARGE_LAYOUT_MAX_WIDTH}> + <div className="flex-last"> + <Container + width={LEFT_COLUMN_WIDTH} + position="fixed" + zIndex={zIndex.aboveTopBar} + marginLeft={LARGE_LAYOUT_MARGIN} + > + {props.left} + </Container> </div> - </div> + <Container className="flex-auto" marginLeft={LEFT_COLUMN_WIDTH + LARGE_LAYOUT_MARGIN}> + <Container className="flex-auto" marginLeft={LARGE_LAYOUT_MARGIN} marginRight={LARGE_LAYOUT_MARGIN}> + {props.right} + </Container> + </Container> + </Container> ); }; @@ -710,9 +721,7 @@ interface SmallLayoutProps { const SmallLayout = (props: SmallLayoutProps) => { return ( <div className="flex flex-center"> - <div className="flex-auto px3" style={styles.scrollContainer}> - {props.content} - </div> + <div className="flex-auto px3">{props.content}</div> </div> ); }; // tslint:disable:max-file-line-count diff --git a/packages/website/ts/components/portal/text_header.tsx b/packages/website/ts/components/portal/text_header.tsx index 4aabd47d0..853da3a29 100644 --- a/packages/website/ts/components/portal/text_header.tsx +++ b/packages/website/ts/components/portal/text_header.tsx @@ -1,21 +1,16 @@ -import { Styles } from '@0xproject/react-shared'; +import { colors } from '@0xproject/react-shared'; import * as React from 'react'; +import { Text } from 'ts/components/ui/text'; + export interface TextHeaderProps { labelText: string; } -const styles: Styles = { - title: { - fontWeight: 'bold', - fontSize: 20, - }, -}; - export const TextHeader = (props: TextHeaderProps) => { return ( - <div className="py3" style={styles.title}> + <Text className="pt3 pb2" fontWeight="bold" fontSize="16px" fontColor={colors.darkestGrey}> {props.labelText} - </div> + </Text> ); }; diff --git a/packages/website/ts/components/relayer_index/relayer_grid_tile.tsx b/packages/website/ts/components/relayer_index/relayer_grid_tile.tsx index 23860856b..18b069ae2 100644 --- a/packages/website/ts/components/relayer_index/relayer_grid_tile.tsx +++ b/packages/website/ts/components/relayer_index/relayer_grid_tile.tsx @@ -1,6 +1,6 @@ import { constants as sharedConstants, Styles } from '@0xproject/react-shared'; import * as _ from 'lodash'; -import { GridTile } from 'material-ui/GridList'; +import { GridTile as PlainGridTile } from 'material-ui/GridList'; import * as React from 'react'; import { analytics } from 'ts/utils/analytics'; @@ -9,7 +9,9 @@ import { Container } from 'ts/components/ui/container'; import { Image } from 'ts/components/ui/image'; import { Island } from 'ts/components/ui/island'; import { colors } from 'ts/style/colors'; +import { styled } from 'ts/style/theme'; import { WebsiteBackendRelayerInfo } from 'ts/types'; +import { utils } from 'ts/utils/utils'; export interface RelayerGridTileProps { relayerInfo: WebsiteBackendRelayerInfo; @@ -19,29 +21,23 @@ export interface RelayerGridTileProps { const styles: Styles = { root: { boxSizing: 'border-box', + // All material UI components have position: relative + // which creates a new stacking context and makes z-index stuff impossible. So reset. + position: 'static', }, innerDiv: { - padding: 6, height: '100%', boxSizing: 'border-box', }, header: { height: '50%', width: '100%', - borderBottomRightRadius: 4, - borderBottomLeftRadius: 4, - borderTopRightRadius: 4, - borderTopLeftRadius: 4, - borderWidth: 1, - borderStyle: 'solid', - borderColor: colors.walletBorder, }, body: { - paddingLeft: 6, - paddingRight: 6, height: '50%', width: '100%', boxSizing: 'border-box', + padding: 12, }, weeklyTradeVolumeLabel: { fontSize: 14, @@ -69,7 +65,10 @@ export const RelayerGridTile: React.StatelessComponent<RelayerGridTileProps> = ( const weeklyTxnVolume = props.relayerInfo.weeklyTxnVolume; const networkName = sharedConstants.NETWORK_NAME_BY_ID[props.networkId]; const eventLabel = `${props.relayerInfo.name}-${networkName}`; - const trackRelayerClick = () => analytics.logEvent('Portal', 'Relayer Click', eventLabel); + const onClick = () => { + analytics.logEvent('Portal', 'Relayer Click', eventLabel); + utils.openUrl(link); + }; const headerImageUrl = props.relayerInfo.logoImgUrl; const headerBackgroundColor = !_.isUndefined(headerImageUrl) && !_.isUndefined(props.relayerInfo.primaryColor) @@ -77,22 +76,17 @@ export const RelayerGridTile: React.StatelessComponent<RelayerGridTileProps> = ( : FALLBACK_PRIMARY_COLOR; return ( <Island style={styles.root} Component={GridTile}> - <div style={styles.innerDiv}> - <a href={link} target="_blank" style={{ textDecoration: 'none' }} onClick={trackRelayerClick}> - <div - className="flex items-center" - style={{ ...styles.header, backgroundColor: headerBackgroundColor }} - > - <Image - className="mx-auto" - src={props.relayerInfo.logoImgUrl} - fallbackSrc={FALLBACK_IMG_SRC} - height={RELAYER_ICON_HEIGHT} - /> - </div> - </a> + <div style={styles.innerDiv} onClick={onClick}> + <div className="flex items-center" style={{ ...styles.header, backgroundColor: headerBackgroundColor }}> + <Image + className="mx-auto" + src={props.relayerInfo.logoImgUrl} + fallbackSrc={FALLBACK_IMG_SRC} + height={RELAYER_ICON_HEIGHT} + /> + </div> <div style={styles.body}> - <div className="py1" style={styles.relayerNameLabel}> + <div className="pb1" style={styles.relayerNameLabel}> {props.relayerInfo.name} </div> <Section titleText="Weekly Trade Volume"> @@ -111,6 +105,14 @@ export const RelayerGridTile: React.StatelessComponent<RelayerGridTileProps> = ( ); }; +const GridTile = styled(PlainGridTile)` + cursor: pointer; + transition: transform 0.2s ease; + &:hover { + transform: translate(0px, -3px); + } +`; + interface SectionProps { titleText: string; children?: React.ReactNode; diff --git a/packages/website/ts/components/relayer_index/relayer_index.tsx b/packages/website/ts/components/relayer_index/relayer_index.tsx index d565eb608..4aea1bbbb 100644 --- a/packages/website/ts/components/relayer_index/relayer_index.tsx +++ b/packages/website/ts/components/relayer_index/relayer_index.tsx @@ -1,4 +1,3 @@ -import { Styles } from '@0xproject/react-shared'; import * as _ from 'lodash'; import CircularProgress from 'material-ui/CircularProgress'; import { GridList } from 'material-ui/GridList'; @@ -6,7 +5,6 @@ import * as React from 'react'; import { RelayerGridTile } from 'ts/components/relayer_index/relayer_grid_tile'; import { Retry } from 'ts/components/ui/retry'; -import { colors } from 'ts/style/colors'; import { ScreenWidths, WebsiteBackendRelayerInfo } from 'ts/types'; import { backendClient } from 'ts/utils/backend_client'; @@ -20,22 +18,6 @@ interface RelayerIndexState { error?: Error; } -const styles: Styles = { - root: { - width: '100%', - }, - item: { - backgroundColor: colors.white, - borderBottomRightRadius: 10, - borderBottomLeftRadius: 10, - borderTopRightRadius: 10, - borderTopLeftRadius: 10, - boxShadow: `0px 4px 6px ${colors.walletBoxShadow}`, - overflow: 'hidden', - padding: 4, - }, -}; - const CELL_HEIGHT = 290; const NUMBER_OF_COLUMNS_LARGE = 3; const NUMBER_OF_COLUMNS_MEDIUM = 2; @@ -76,18 +58,16 @@ export class RelayerIndex extends React.Component<RelayerIndexProps, RelayerInde } else { const numberOfColumns = this._numberOfColumnsForScreenWidth(this.props.screenWidth); return ( - <div style={styles.root}> - <GridList - cellHeight={CELL_HEIGHT} - cols={numberOfColumns} - padding={GRID_PADDING} - style={styles.gridList} - > - {this.state.relayerInfos.map((relayerInfo: WebsiteBackendRelayerInfo, index) => ( - <RelayerGridTile key={index} relayerInfo={relayerInfo} networkId={this.props.networkId} /> - ))} - </GridList> - </div> + <GridList + cellHeight={CELL_HEIGHT} + cols={numberOfColumns} + padding={GRID_PADDING} + style={{ marginTop: -10, marginBottom: 0 }} + > + {this.state.relayerInfos.map((relayerInfo: WebsiteBackendRelayerInfo, index) => ( + <RelayerGridTile key={index} relayerInfo={relayerInfo} networkId={this.props.networkId} /> + ))} + </GridList> ); } } diff --git a/packages/website/ts/components/relayer_index/relayer_top_tokens.tsx b/packages/website/ts/components/relayer_index/relayer_top_tokens.tsx index b599e7123..f544fc924 100644 --- a/packages/website/ts/components/relayer_index/relayer_top_tokens.tsx +++ b/packages/website/ts/components/relayer_index/relayer_top_tokens.tsx @@ -70,7 +70,10 @@ class TokenLink extends React.Component<TokenLinkProps, TokenLinkState> { }; const networkName = sharedConstants.NETWORK_NAME_BY_ID[this.props.networkId]; const eventLabel = `${this.props.tokenInfo.symbol}-${networkName}`; - const trackTokenClick = () => analytics.logEvent('Portal', 'Token Click', eventLabel); + const onClick = (event: React.MouseEvent<HTMLElement>) => { + event.stopPropagation(); + analytics.logEvent('Portal', 'Token Click', eventLabel); + }; return ( <a href={tokenLinkFromToken(this.props.tokenInfo, this.props.networkId)} @@ -78,7 +81,7 @@ class TokenLink extends React.Component<TokenLinkProps, TokenLinkState> { style={style} onMouseEnter={this._onToggleHover.bind(this, true)} onMouseLeave={this._onToggleHover.bind(this, false)} - onClick={trackTokenClick} + onClick={onClick} > {this.props.tokenInfo.symbol} </a> diff --git a/packages/website/ts/components/top_bar/provider_display.tsx b/packages/website/ts/components/top_bar/provider_display.tsx index 1e8855c14..496e5cae0 100644 --- a/packages/website/ts/components/top_bar/provider_display.tsx +++ b/packages/website/ts/components/top_bar/provider_display.tsx @@ -10,6 +10,7 @@ import { Container } from 'ts/components/ui/container'; import { DropDown } from 'ts/components/ui/drop_down'; import { Identicon } from 'ts/components/ui/identicon'; import { Image } from 'ts/components/ui/image'; +import { Island } from 'ts/components/ui/island'; import { Text } from 'ts/components/ui/text'; import { Dispatcher } from 'ts/redux/dispatcher'; import { colors } from 'ts/style/colors'; @@ -35,9 +36,7 @@ interface ProviderDisplayState {} const styles: Styles = { root: { height: ROOT_HEIGHT, - backgroundColor: colors.white, borderRadius: ROOT_HEIGHT, - boxShadow: `0px 4px 6px ${colors.walletBoxShadow}`, }, }; @@ -62,7 +61,7 @@ export class ProviderDisplay extends React.Component<ProviderDisplayProps, Provi this.props.providerType === ProviderType.Injected ? injectedProviderName : 'Ledger Nano S'; const isProviderMetamask = providerTitle === constants.PROVIDER_NAME_METAMASK; const hoverActiveNode = ( - <div className="flex items-center p1" style={styles.root}> + <Island className="flex items-center p1" style={styles.root}> <div> {this._isBlockchainReady() ? ( <Identicon address={this.props.userAddress} diameter={ROOT_HEIGHT} /> @@ -78,7 +77,7 @@ export class ProviderDisplay extends React.Component<ProviderDisplayProps, Provi {isProviderMetamask && ( <Image src="/images/metamask_icon.png" height={ROOT_HEIGHT} width={ROOT_HEIGHT} /> )} - </div> + </Island> ); const hasLedgerProvider = this.props.providerType === ProviderType.Ledger; const horizontalPosition = isExternallyInjectedProvider || hasLedgerProvider ? 'left' : 'middle'; diff --git a/packages/website/ts/components/ui/animation.tsx b/packages/website/ts/components/ui/animation.tsx new file mode 100644 index 000000000..136f3d005 --- /dev/null +++ b/packages/website/ts/components/ui/animation.tsx @@ -0,0 +1,34 @@ +import * as React from 'react'; +import { keyframes, styled } from 'ts/style/theme'; + +export type AnimationType = 'easeUpFromBottom'; + +export interface AnimationProps { + type: AnimationType; +} + +const PlainAnimation: React.StatelessComponent<AnimationProps> = props => <div {...props} />; + +const appearFromBottomFrames = keyframes` + from { + position: fixed; + bottom: -500px; + left: 0px; + } + + to { + position: fixed; + bottom: 0px; + left: 0px; + } +`; + +const animations: { [K in AnimationType]: string } = { + easeUpFromBottom: `${appearFromBottomFrames} 1s ease 0s 1 forwards`, +}; + +export const Animation = styled(PlainAnimation)` + animation: ${props => animations[props.type]}; +`; + +Animation.displayName = 'Animation'; diff --git a/packages/website/ts/components/ui/button.tsx b/packages/website/ts/components/ui/button.tsx index cb542a386..02fa47480 100644 --- a/packages/website/ts/components/ui/button.tsx +++ b/packages/website/ts/components/ui/button.tsx @@ -1,5 +1,5 @@ import { colors } from '@0xproject/react-shared'; -import { darken, grayscale } from 'polished'; +import { darken, saturate } from 'polished'; import * as React from 'react'; import { styled } from 'ts/style/theme'; @@ -17,7 +17,7 @@ export interface ButtonProps { } const PlainButton: React.StatelessComponent<ButtonProps> = ({ children, isDisabled, onClick, type, className }) => ( - <button type={type} className={className} onClick={isDisabled ? undefined : onClick}> + <button type={type} className={className} onClick={isDisabled ? undefined : onClick} disabled={isDisabled}> {children} </button> ); @@ -26,14 +26,15 @@ export const Button = styled(PlainButton)` cursor: ${props => (props.isDisabled ? 'default' : 'pointer')}; font-size: ${props => props.fontSize}; color: ${props => props.fontColor}; - transition: background-color 0.5s ease; + transition: background-color, opacity 0.5s ease; padding: 0.8em 2.2em; border-radius: 6px; box-shadow: 0px 0px 4px rgba(0, 0, 0, 0.25); font-weight: 500; + outline: none; font-family: ${props => props.fontFamily}; width: ${props => props.width}; - background-color: ${props => (props.isDisabled ? grayscale(props.backgroundColor) : props.backgroundColor)}; + background-color: ${props => props.backgroundColor}; border: ${props => (props.borderColor ? `1px solid ${props.borderColor}` : 'none')}; &:hover { background-color: ${props => (!props.isDisabled ? darken(0.1, props.backgroundColor) : '')}; @@ -41,6 +42,13 @@ export const Button = styled(PlainButton)` &:active { background-color: ${props => (!props.isDisabled ? darken(0.2, props.backgroundColor) : '')}; } + &:disabled { + opacity: 0.5; + box-shadow: none; + } + &:focus { + background-color: ${props => saturate(0.2, props.backgroundColor)}; + } `; Button.defaultProps = { diff --git a/packages/website/ts/components/ui/container.tsx b/packages/website/ts/components/ui/container.tsx index 1776345da..a747ef01f 100644 --- a/packages/website/ts/components/ui/container.tsx +++ b/packages/website/ts/components/ui/container.tsx @@ -15,6 +15,7 @@ export interface ContainerProps { borderRadius?: StringOrNum; maxWidth?: StringOrNum; width?: StringOrNum; + minHeight?: StringOrNum; isHidden?: boolean; className?: string; position?: 'absolute' | 'fixed' | 'relative' | 'unset'; @@ -23,6 +24,7 @@ export interface ContainerProps { left?: string; right?: string; bottom?: string; + zIndex?: number; } export const Container: React.StatelessComponent<ContainerProps> = ({ children, className, isHidden, ...style }) => { diff --git a/packages/website/ts/components/ui/identicon.tsx b/packages/website/ts/components/ui/identicon.tsx index 30df995c8..cc1655962 100644 --- a/packages/website/ts/components/ui/identicon.tsx +++ b/packages/website/ts/components/ui/identicon.tsx @@ -23,7 +23,7 @@ export class Identicon extends React.Component<IdenticonProps, IdenticonState> { const radius = diameter / 2; return ( <div - className="circle mx-auto relative transitionFix" + className="circle relative transitionFix" style={{ width: diameter, height: diameter, diff --git a/packages/website/ts/components/ui/island.tsx b/packages/website/ts/components/ui/island.tsx index de90b664f..c8abfb7e0 100644 --- a/packages/website/ts/components/ui/island.tsx +++ b/packages/website/ts/components/ui/island.tsx @@ -1,31 +1,28 @@ import * as React from 'react'; import { colors } from 'ts/style/colors'; +import { styled } from 'ts/style/theme'; export interface IslandProps { style?: React.CSSProperties; - children?: React.ReactNode; className?: string; Component?: string | React.ComponentClass<any> | React.StatelessComponent<any>; + borderRadius?: string; } -const defaultStyle: React.CSSProperties = { - backgroundColor: colors.white, - borderBottomRightRadius: 10, - borderBottomLeftRadius: 10, - borderTopRightRadius: 10, - borderTopLeftRadius: 10, - boxShadow: `0px 4px 6px ${colors.walletBoxShadow}`, - overflow: 'hidden', -}; - -export const Island: React.StatelessComponent<IslandProps> = (props: IslandProps) => ( - <props.Component style={{ ...defaultStyle, ...props.style }} className={props.className}> - {props.children} - </props.Component> +const PlainIsland: React.StatelessComponent<IslandProps> = ({ Component, style, className, children }) => ( + <Component style={style} className={className} children={children} /> ); +export const Island = styled(PlainIsland)` + background-color: ${colors.white}; + border-radius: ${props => props.borderRadius}; + box-shadow: 0px 4px 6px ${colors.walletBoxShadow}; + overflow: hidden; +`; + Island.defaultProps = { Component: 'div', + borderRadius: '10px', style: {}, }; diff --git a/packages/website/ts/components/wallet/wallet.tsx b/packages/website/ts/components/wallet/wallet.tsx index ac2fe0d31..dc48d6619 100644 --- a/packages/website/ts/components/wallet/wallet.tsx +++ b/packages/website/ts/components/wallet/wallet.tsx @@ -29,6 +29,7 @@ import { WrapEtherItem } from 'ts/components/wallet/wrap_ether_item'; import { AllowanceToggle } from 'ts/containers/inputs/allowance_toggle'; import { Dispatcher } from 'ts/redux/dispatcher'; import { colors } from 'ts/style/colors'; +import { styled } from 'ts/style/theme'; import { BlockchainErrs, ProviderType, @@ -86,7 +87,6 @@ interface AccessoryItemConfig { const styles: Styles = { root: { width: '100%', - position: 'relative', }, footerItemInnerDiv: { paddingLeft: 24, @@ -139,6 +139,12 @@ const USD_DECIMAL_PLACES = 2; const NO_ALLOWANCE_TOGGLE_SPACE_WIDTH = 56; const ACCOUNT_PATH = `${WebsitePaths.Portal}/account`; +const ActionButton = styled(FloatingActionButton)` + button { + position: static !important; + } +`; + export class Wallet extends React.Component<WalletProps, WalletState> { public static defaultProps = { style: {}, @@ -245,17 +251,12 @@ export class Wallet extends React.Component<WalletProps, WalletState> { <ListItem primaryText={ <div className="flex"> - <FloatingActionButton mini={true} zDepth={0} onClick={this.props.onAddToken}> + <ActionButton mini={true} zDepth={0} onClick={this.props.onAddToken}> <ContentAdd /> - </FloatingActionButton> - <FloatingActionButton - mini={true} - zDepth={0} - className="px1" - onClick={this.props.onRemoveToken} - > + </ActionButton> + <ActionButton mini={true} zDepth={0} className="px1" onClick={this.props.onRemoveToken}> <ContentRemove /> - </FloatingActionButton> + </ActionButton> <div style={{ paddingLeft: 10, @@ -310,7 +311,7 @@ export class Wallet extends React.Component<WalletProps, WalletState> { wrappedEtherDirection: Side.Deposit, }; const key = ETHER_ITEM_KEY; - return this._renderBalanceRow(key, icon, primaryText, secondaryText, accessoryItemConfig, 'eth-row'); + return this._renderBalanceRow(key, icon, primaryText, secondaryText, accessoryItemConfig, false, 'eth-row'); } private _renderTokenRows(): React.ReactNode { const trackedTokens = this.props.trackedTokens; @@ -321,8 +322,11 @@ export class Wallet extends React.Component<WalletProps, WalletState> { ); return _.map(trackedTokensStartingWithEtherToken, this._renderTokenRow.bind(this)); } - private _renderTokenRow(token: Token, _index: number): React.ReactNode { + private _renderTokenRow(token: Token, index: number): React.ReactNode { const tokenState = this.props.trackedTokenStateByAddress[token.address]; + if (_.isUndefined(tokenState)) { + return null; + } const tokenLink = sharedUtils.getEtherScanLinkIfExists( token.address, this.props.networkId, @@ -346,12 +350,14 @@ export class Wallet extends React.Component<WalletProps, WalletState> { }, }; const key = token.address; + const isLastRow = index === this.props.trackedTokens.length - 1; return this._renderBalanceRow( key, icon, primaryText, secondaryText, accessoryItemConfig, + isLastRow, isWeth ? 'weth-row' : undefined, ); } @@ -361,13 +367,19 @@ export class Wallet extends React.Component<WalletProps, WalletState> { primaryText: React.ReactNode, secondaryText: React.ReactNode, accessoryItemConfig: AccessoryItemConfig, + isLastRow: boolean, className?: string, ): React.ReactNode { const shouldShowWrapEtherItem = !_.isUndefined(this.state.wrappedEtherDirection) && this.state.wrappedEtherDirection === accessoryItemConfig.wrappedEtherDirection && !_.isUndefined(this.props.userEtherBalanceInWei); - const additionalStyle = shouldShowWrapEtherItem ? walletItemStyles.focusedItem : styles.borderedItem; + let additionalStyle; + if (shouldShowWrapEtherItem) { + additionalStyle = walletItemStyles.focusedItem; + } else if (!isLastRow) { + additionalStyle = styles.borderedItem; + } const style = { ...styles.tokenItem, ...additionalStyle }; const etherToken = this._getEthToken(); return ( diff --git a/packages/website/ts/containers/portal_onboarding_flow.ts b/packages/website/ts/containers/portal_onboarding_flow.ts index ba2b8f512..12daad021 100644 --- a/packages/website/ts/containers/portal_onboarding_flow.ts +++ b/packages/website/ts/containers/portal_onboarding_flow.ts @@ -3,7 +3,7 @@ import * as React from 'react'; import { connect } from 'react-redux'; import { Dispatch } from 'redux'; import { Blockchain } from 'ts/blockchain'; -import { ActionTypes, ProviderType, TokenByAddress, TokenStateByAddress } from 'ts/types'; +import { ActionTypes, ProviderType, ScreenWidths, TokenByAddress, TokenStateByAddress } from 'ts/types'; import { PortalOnboardingFlow as PortalOnboardingFlowComponent } from 'ts/components/onboarding/portal_onboarding_flow'; import { State } from 'ts/redux/reducer'; @@ -25,6 +25,7 @@ interface ConnectedState { blockchainIsLoaded: boolean; userEtherBalanceInWei?: BigNumber; tokenByAddress: TokenByAddress; + screenWidth: ScreenWidths; } interface ConnectedDispatch { @@ -43,6 +44,7 @@ const mapStateToProps = (state: State, _ownProps: PortalOnboardingFlowProps): Co userEtherBalanceInWei: state.userEtherBalanceInWei, tokenByAddress: state.tokenByAddress, hasBeenSeen: state.hasPortalOnboardingBeenSeen, + screenWidth: state.screenWidth, }); const mapDispatchToProps = (dispatch: Dispatch<State>): ConnectedDispatch => ({ diff --git a/packages/website/ts/pages/about/about.tsx b/packages/website/ts/pages/about/about.tsx index 3136dbca3..0259af36f 100644 --- a/packages/website/ts/pages/about/about.tsx +++ b/packages/website/ts/pages/about/about.tsx @@ -150,14 +150,32 @@ const teamRow5: ProfileInfo[] = [ }, { name: 'Francesco Agosti', - title: 'Senior Frontend Engineer', + title: 'Engineer', description: `Full-stack engineer. Previously senior software engineer at Yelp. Computer Science at Duke.`, image: 'images/team/fragosti.png', linkedIn: 'https://www.linkedin.com/in/fragosti/', github: 'http://github.com/fragosti', }, + { + name: 'Mel Oberto', + title: 'Office Ops / Executive Assistant', + description: `Daily Operations. Previously People Operations Associate at Heap. Marketing and MBA at Sacred Heart University.`, + image: 'images/team/mel.png', + linkedIn: 'https://www.linkedin.com/in/melanieoberto', + }, ]; +// const teamRow6: ProfileInfo[] = [ +// { +// name: 'Chris Kalani', +// title: 'Director of Design', +// description: `Previously founded Wake (acquired by InVision). Early Facebook product designer.`, +// image: 'images/team/chris.png', +// linkedIn: 'https://www.linkedin.com/in/chriskalani/', +// github: 'https://github.com/chriskalani', +// }, +// ]; + const advisors: ProfileInfo[] = [ { name: 'Fred Ehrsam', @@ -241,9 +259,9 @@ export class About extends React.Component<AboutProps, AboutState> { lineHeight: 1.5, }} > - Our team is a diverse and globally distributed group with backgrounds in engineering, - research, business and design. We are passionate about decentralized technology and its - potential to act as an equalizing force in the world. + Our team is a globally distributed group with backgrounds in engineering, research, business + and design. We are passionate about decentralized technology and its potential to act as an + equalizing force in the world. </div> </div> <div className="pt3 md-px4 lg-px0"> diff --git a/packages/website/ts/pages/about/profile.tsx b/packages/website/ts/pages/about/profile.tsx index dd046a8cb..bcbeb0272 100644 --- a/packages/website/ts/pages/about/profile.tsx +++ b/packages/website/ts/pages/about/profile.tsx @@ -39,6 +39,7 @@ export const Profile = (props: ProfileProps) => { fontSize: 14, fontFamily: 'Roboto Mono', color: colors.darkGrey, + whiteSpace: 'nowrap', }} > {props.profileInfo.title.toUpperCase()} @@ -47,7 +48,7 @@ export const Profile = (props: ProfileProps) => { <div style={{ minHeight: 60, lineHeight: 1.4 }} className="pt1 pb2 mx-auto lg-h6 md-h6 sm-h5 sm-center"> {props.profileInfo.description} </div> - <div className="flex pb3 mx-auto sm-hide xs-hide" style={{ width: 280, opacity: 0.5 }}> + <div className="flex pb3 sm-hide xs-hide" style={{ width: 280, opacity: 0.5 }}> {renderSocialMediaIcons(props.profileInfo)} </div> </div> diff --git a/packages/website/ts/pages/jobs/open_positions.tsx b/packages/website/ts/pages/jobs/open_positions.tsx index f3d980315..e789795c1 100644 --- a/packages/website/ts/pages/jobs/open_positions.tsx +++ b/packages/website/ts/pages/jobs/open_positions.tsx @@ -11,6 +11,7 @@ import { colors } from 'ts/style/colors'; import { styled } from 'ts/style/theme'; import { ScreenWidths, WebsiteBackendJobInfo } from 'ts/types'; import { backendClient } from 'ts/utils/backend_client'; +import { utils } from 'ts/utils/utils'; const labelStyle = { fontFamily: 'Roboto Mono', fontSize: 18 }; const HEADER_TEXT = 'Open Positions'; @@ -161,7 +162,7 @@ export class OpenPositions extends React.Component<OpenPositionsProps, OpenPosit private _openJobInfoUrl(jobInfo: WebsiteBackendJobInfo): void { const url = jobInfo.url; - window.open(url, '_blank'); + utils.openUrl(url); } } diff --git a/packages/website/ts/redux/reducer.ts b/packages/website/ts/redux/reducer.ts index 9d3d8f7d9..ed6a4868e 100644 --- a/packages/website/ts/redux/reducer.ts +++ b/packages/website/ts/redux/reducer.ts @@ -156,7 +156,7 @@ export function reducer(state: State = INITIAL_STATE, action: Action): State { } case ActionTypes.AddTokenToTokenByAddress: { - const newTokenByAddress = state.tokenByAddress; + const newTokenByAddress = { ...state.tokenByAddress }; newTokenByAddress[action.data.address] = action.data; return { ...state, @@ -165,7 +165,7 @@ export function reducer(state: State = INITIAL_STATE, action: Action): State { } case ActionTypes.RemoveTokenFromTokenByAddress: { - const newTokenByAddress = state.tokenByAddress; + const newTokenByAddress = { ...state.tokenByAddress }; delete newTokenByAddress[action.data.address]; return { ...state, @@ -174,7 +174,7 @@ export function reducer(state: State = INITIAL_STATE, action: Action): State { } case ActionTypes.UpdateTokenByAddress: { - const tokenByAddress = state.tokenByAddress; + const tokenByAddress = { ...state.tokenByAddress }; const tokens = action.data; _.each(tokens, token => { const updatedToken = { @@ -253,7 +253,7 @@ export function reducer(state: State = INITIAL_STATE, action: Action): State { } case ActionTypes.UpdateChosenAssetTokenAddress: { - const newAssetToken = state.sideToAssetToken[action.data.side]; + const newAssetToken = { ...state.sideToAssetToken[action.data.side] }; newAssetToken.address = action.data.address; const newSideToAssetToken = { ...state.sideToAssetToken, diff --git a/packages/website/ts/style/colors.ts b/packages/website/ts/style/colors.ts index b15000d7a..45be4fe7f 100644 --- a/packages/website/ts/style/colors.ts +++ b/packages/website/ts/style/colors.ts @@ -1,7 +1,7 @@ import { colors as sharedColors } from '@0xproject/react-shared'; const appColors = { - walletBoxShadow: 'rgba(56, 59, 137, 0.2)', + walletBoxShadow: 'rgba(0, 0, 0, 0.05)', walletBorder: '#ededee', walletDefaultItemBackground: '#fbfbfc', walletFocusedItemBackground: '#f0f1f4', diff --git a/packages/website/ts/style/z_index.ts b/packages/website/ts/style/z_index.ts index 0411cdd91..a3998f59b 100644 --- a/packages/website/ts/style/z_index.ts +++ b/packages/website/ts/style/z_index.ts @@ -1,5 +1,6 @@ export const zIndex = { topBar: 1100, + aboveTopBar: 1101, overlay: 1105, aboveOverlay: 1106, }; diff --git a/packages/website/ts/utils/utils.ts b/packages/website/ts/utils/utils.ts index 0bd3dbcfa..0cb965f05 100644 --- a/packages/website/ts/utils/utils.ts +++ b/packages/website/ts/utils/utils.ts @@ -362,4 +362,10 @@ export const utils = { const formattedAmount = unitAmount.toFixed(precision); return `${formattedAmount} ${symbol}`; }, + openUrl(url: string): void { + window.open(url, '_blank'); + }, + isMobile(screenWidth: ScreenWidths): boolean { + return screenWidth === ScreenWidths.Sm; + }, }; |