Commit 71376cf7 authored by Noah Zinsmeister's avatar Noah Zinsmeister

set up eslint and prettier; run prettier

parent da47f33b
{
"semi": false,
"singleQuote": true,
"printWidth": 120
}
...@@ -42,9 +42,18 @@ ...@@ -42,9 +42,18 @@
"build": "react-scripts build", "build": "react-scripts build",
"build:rinkeby": "REACT_APP_NETWORK_ID=4 REACT_APP_NETWORK='Rinkeby Test Network' yarn build", "build:rinkeby": "REACT_APP_NETWORK_ID=4 REACT_APP_NETWORK='Rinkeby Test Network' yarn build",
"test": "react-scripts test --env=jsdom", "test": "react-scripts test --env=jsdom",
"eject": "react-scripts eject" "eject": "react-scripts eject",
"lint:base": "yarn eslint './src/**/*.{js,jsx}'",
"format:base": "yarn prettier './src/**/*.{js,jsx,scss}'",
"lint": "yarn lint:base --fix",
"format": "yarn format:base --write",
"check:lint": "yarn lint:base",
"check:format": "yarn format:base --check",
"check:all": "yarn check:lint && yarn check:format"
},
"devDependencies": {
"prettier": "^1.17.0"
}, },
"devDependencies": {},
"browserslist": [ "browserslist": [
">0.2%", ">0.2%",
"not dead", "not dead",
......
import React, { Component } from 'react'; import React, { Component } from 'react'
import PropTypes from 'prop-types'; import PropTypes from 'prop-types'
import c from 'classnames'; import c from 'classnames'
import QrCode from '../QrCode'; import QrCode from '../QrCode'
import './address-input-panel.scss'; import './address-input-panel.scss'
class AddressInputPanel extends Component { class AddressInputPanel extends Component {
static propTypes = { static propTypes = {
title: PropTypes.string, title: PropTypes.string,
onChange: PropTypes.func, onChange: PropTypes.func,
value: PropTypes.string, value: PropTypes.string,
errorMessage: PropTypes.string, errorMessage: PropTypes.string
}; }
static defaultProps = { static defaultProps = {
onChange() {}, onChange() {},
value: '', value: ''
}; }
render() { render() {
const { const { t, title, onChange, value, errorMessage } = this.props
t,
title,
onChange,
value,
errorMessage,
} = this.props;
return ( return (
<div className="currency-input-panel"> <div className="currency-input-panel">
<div className={c('currency-input-panel__container address-input-panel__recipient-row', { <div
'currency-input-panel__container--error': errorMessage, className={c('currency-input-panel__container address-input-panel__recipient-row', {
})}> 'currency-input-panel__container--error': errorMessage
})}
>
<div className="address-input-panel__input-container"> <div className="address-input-panel__input-container">
<div className="currency-input-panel__label-row"> <div className="currency-input-panel__label-row">
<div className="currency-input-panel__label-container"> <div className="currency-input-panel__label-container">
<span className="currency-input-panel__label">{title || t("recipientAddress")}</span> <span className="currency-input-panel__label">{title || t('recipientAddress')}</span>
</div> </div>
</div> </div>
<div className="currency-input-panel__input-row"> <div className="currency-input-panel__input-row">
<input <input
type="text" type="text"
className={c('address-input-panel__input',{ className={c('address-input-panel__input', {
'address-input-panel__input--error': errorMessage, 'address-input-panel__input--error': errorMessage
})} })}
placeholder="0x1234..." placeholder="0x1234..."
onChange={e => onChange(e.target.value)} onChange={e => onChange(e.target.value)}
...@@ -59,4 +55,4 @@ class AddressInputPanel extends Component { ...@@ -59,4 +55,4 @@ class AddressInputPanel extends Component {
} }
} }
export default AddressInputPanel; export default AddressInputPanel
@import "../../variables.scss"; @import '../../variables.scss';
.contextual-info { .contextual-info {
&__summary-wrapper { &__summary-wrapper {
......
import React, { Component } from 'react'; import React, { Component } from 'react'
import PropTypes from 'prop-types'; import PropTypes from 'prop-types'
import c from 'classnames'; import c from 'classnames'
import DropdownBlue from "../../assets/images/dropdown-blue.svg"; import DropdownBlue from '../../assets/images/dropdown-blue.svg'
import DropupBlue from "../../assets/images/dropup-blue.svg"; import DropupBlue from '../../assets/images/dropup-blue.svg'
import './contextual-info.scss'; import './contextual-info.scss'
class ContextualInfo extends Component { class ContextualInfo extends Component {
static propTypes = { static propTypes = {
openDetailsText: PropTypes.string, openDetailsText: PropTypes.string,
renderTransactionDetails: PropTypes.func, renderTransactionDetails: PropTypes.func,
contextualInfo: PropTypes.string, contextualInfo: PropTypes.string,
isError: PropTypes.bool, isError: PropTypes.bool
}; }
static defaultProps = { static defaultProps = {
openDetailsText: 'Transaction Details', openDetailsText: 'Transaction Details',
closeDetailsText: 'Hide Details', closeDetailsText: 'Hide Details',
renderTransactionDetails() {}, renderTransactionDetails() {},
contextualInfo: '', contextualInfo: '',
isError: false, isError: false
}; }
state = { state = {
showDetails: false, showDetails: false
}; }
renderDetails() { renderDetails() {
if (!this.state.showDetails) { if (!this.state.showDetails) {
return null; return null
} }
return ( return <div className="contextual-info__details">{this.props.renderTransactionDetails()}</div>
<div className="contextual-info__details">
{this.props.renderTransactionDetails()}
</div>
);
} }
render() { render() {
const { const { openDetailsText, closeDetailsText, contextualInfo, isError } = this.props
openDetailsText,
closeDetailsText,
contextualInfo,
isError,
} = this.props;
if (contextualInfo) { if (contextualInfo) {
return ( return (
<div className={c({ 'contextual-info--error': isError }, 'contextual-info__summary-wrapper')}> <div className={c({ 'contextual-info--error': isError }, 'contextual-info__summary-wrapper')}>
<div>{contextualInfo}</div> <div>{contextualInfo}</div>
</div> </div>
); )
} }
return [ return [
<div <div
key="open-details" key="open-details"
className="contextual-info__summary-wrapper contextual-info__open-details-container" className="contextual-info__summary-wrapper contextual-info__open-details-container"
onClick={() => this.setState((prevState) => { onClick={() =>
return { showDetails: !prevState.showDetails } this.setState(prevState => {
})} return { showDetails: !prevState.showDetails }
})
}
> >
{!this.state.showDetails ? ( {!this.state.showDetails ? (
<> <>
<span>{openDetailsText}</span> <span>{openDetailsText}</span>
<img src={DropdownBlue} alt='dropdown' /> <img src={DropdownBlue} alt="dropdown" />
</> </>
) : ( ) : (
<> <>
<span>{closeDetailsText}</span> <span>{closeDetailsText}</span>
<img src={DropupBlue} alt='dropup' /> <img src={DropupBlue} alt="dropup" />
</> </>
)} )}
</div>, </div>,
...@@ -79,4 +72,4 @@ class ContextualInfo extends Component { ...@@ -79,4 +72,4 @@ class ContextualInfo extends Component {
} }
} }
export default ContextualInfo; export default ContextualInfo
...@@ -9,20 +9,20 @@ ...@@ -9,20 +9,20 @@
&__container { &__container {
border-radius: 1.25rem; border-radius: 1.25rem;
box-shadow: 0 0 0 .5px $mercury-gray; box-shadow: 0 0 0 0.5px $mercury-gray;
background-color: $white; background-color: $white;
transition: box-shadow 200ms ease-in-out; transition: box-shadow 200ms ease-in-out;
&--error { &--error {
box-shadow: 0 0 0 .5px $salmon-red; box-shadow: 0 0 0 0.5px $salmon-red;
} }
&:focus-within { &:focus-within {
box-shadow: 0 0 .5px .5px $malibu-blue; box-shadow: 0 0 0.5px 0.5px $malibu-blue;
} }
&--error:focus-within { &--error:focus-within {
box-shadow: 0 0 .5px .5px $salmon-red; box-shadow: 0 0 0.5px 0.5px $salmon-red;
} }
} }
...@@ -30,9 +30,9 @@ ...@@ -30,9 +30,9 @@
@extend %row-nowrap; @extend %row-nowrap;
align-items: center; align-items: center;
color: $dove-gray; color: $dove-gray;
font-size: .75rem; font-size: 0.75rem;
line-height: 1rem; line-height: 1rem;
padding: .75rem 1rem; padding: 0.75rem 1rem;
} }
&__label-container { &__label-container {
...@@ -44,14 +44,14 @@ ...@@ -44,14 +44,14 @@
} }
&__label-description { &__label-description {
opacity: .75; opacity: 0.75;
margin-left: .25rem; margin-left: 0.25rem;
} }
&__input-row { &__input-row {
@extend %row-nowrap; @extend %row-nowrap;
align-items: center; align-items: center;
padding: .25rem .85rem .75rem; padding: 0.25rem 0.85rem 0.75rem;
} }
&__input { &__input {
...@@ -62,12 +62,12 @@ ...@@ -62,12 +62,12 @@
} }
&[type='number'] { &[type='number'] {
-moz-appearance:textfield; -moz-appearance: textfield;
} }
&::-webkit-outer-spin-button, &::-webkit-outer-spin-button,
&::-webkit-inner-spin-button { &::-webkit-inner-spin-button {
-webkit-appearance: none; -webkit-appearance: none;
} }
} }
...@@ -92,14 +92,14 @@ ...@@ -92,14 +92,14 @@
user-select: none; user-select: none;
&:active { &:active {
background-color: rgba($zumthor-blue, .8); background-color: rgba($zumthor-blue, 0.8);
} }
&--selected { &--selected {
background-color: $concrete-gray; background-color: $concrete-gray;
border-color: $mercury-gray; border-color: $mercury-gray;
color: $black; color: $black;
padding: 0 .5rem; padding: 0 0.5rem;
.currency-input-panel__dropdown-icon { .currency-input-panel__dropdown-icon {
background-image: url(../../assets/images/dropdown.svg); background-image: url(../../assets/images/dropdown.svg);
...@@ -128,18 +128,18 @@ ...@@ -128,18 +128,18 @@
user-select: none; user-select: none;
&--pending { &--pending {
line-height: .9; line-height: 0.9;
.loader { .loader {
height: .5rem; height: 0.5rem;
width: .5rem; width: 0.5rem;
} }
} }
} }
&__dropdown-icon { &__dropdown-icon {
height: 1rem; height: 1rem;
width: .75rem; width: 0.75rem;
margin-left: .7rem; margin-left: 0.7rem;
background-image: url(../../assets/images/dropdown-blue.svg); background-image: url(../../assets/images/dropdown-blue.svg);
background-repeat: no-repeat; background-repeat: no-repeat;
background-size: contain; background-size: contain;
...@@ -147,13 +147,12 @@ ...@@ -147,13 +147,12 @@
} }
&__selected-token-logo { &__selected-token-logo {
margin-right: .4rem; margin-right: 0.4rem;
border-radius: 1rem; border-radius: 1rem;
object-fit: contain; object-fit: contain;
} }
} }
.token-modal { .token-modal {
background-color: $white; background-color: $white;
position: relative; position: relative;
...@@ -176,7 +175,7 @@ ...@@ -176,7 +175,7 @@
} }
&__search-icon { &__search-icon {
margin-right: .2rem; margin-right: 0.2rem;
} }
&__token-list { &__token-list {
...@@ -189,7 +188,7 @@ ...@@ -189,7 +188,7 @@
@extend %row-nowrap; @extend %row-nowrap;
align-items: center; align-items: center;
padding: 1rem 1.5rem; padding: 1rem 1.5rem;
margin: .25rem .5rem; margin: 0.25rem 0.5rem;
justify-content: space-between; justify-content: space-between;
cursor: pointer; cursor: pointer;
user-select: none; user-select: none;
...@@ -235,7 +234,7 @@ ...@@ -235,7 +234,7 @@
justify-content: center; justify-content: center;
background-color: $malibu-blue; background-color: $malibu-blue;
&:hover { &:hover {
background-color: lighten($malibu-blue, 1) background-color: lighten($malibu-blue, 1);
} }
&:active { &:active {
...@@ -253,7 +252,7 @@ ...@@ -253,7 +252,7 @@
color: $dove-gray; color: $dove-gray;
} }
@media only screen and (min-width : 768px) { @media only screen and (min-width: 768px) {
max-width: 560px; max-width: 560px;
max-height: 768px; max-height: 768px;
position: absolute; position: absolute;
...@@ -269,7 +268,6 @@ ...@@ -269,7 +268,6 @@
} }
} }
.token-modal-appear { .token-modal-appear {
bottom: 0; bottom: 0;
} }
......
This diff is collapsed.
@import "../../variables.scss"; @import '../../variables.scss';
.header { .header {
@extend %col-nowrap; @extend %col-nowrap;
&__top { &__top {
@extend %row-nowrap; @extend %row-nowrap;
padding: 1.25rem .75rem; padding: 1.25rem 0.75rem;
align-items: center; align-items: center;
border-bottom: 1px solid $concrete-gray; border-bottom: 1px solid $concrete-gray;
} }
...@@ -28,22 +28,22 @@ ...@@ -28,22 +28,22 @@
} }
&--inactive { &--inactive {
opacity: .5; opacity: 0.5;
} }
&__dialog { &__dialog {
@extend %col-nowrap; @extend %col-nowrap;
border-radius: .875rem; border-radius: 0.875rem;
border: 1px solid $mercury-gray; border: 1px solid $mercury-gray;
margin: 1rem .75rem 0 .75rem; margin: 1rem 0.75rem 0 0.75rem;
padding: 1.5rem 1rem; padding: 1.5rem 1rem;
text-align: center; text-align: center;
display: none; display: none;
&__description { &__description {
font-size: .75rem; font-size: 0.75rem;
color: $dove-gray; color: $dove-gray;
margin-top: .4rem; margin-top: 0.4rem;
} }
&--disconnected { &--disconnected {
...@@ -68,7 +68,7 @@ ...@@ -68,7 +68,7 @@
} }
} }
@media only screen and (min-width : 768px) { @media only screen and (min-width: 768px) {
//position: fixed; //position: fixed;
top: 0px; top: 0px;
left: 0px; left: 0px;
......
import React, { Component } from 'react'; import React, { Component } from 'react'
import PropTypes from 'prop-types'; import PropTypes from 'prop-types'
import { connect } from 'react-redux'; import { connect } from 'react-redux'
import classnames from 'classnames'; import classnames from 'classnames'
import UAParser from 'ua-parser-js'; import UAParser from 'ua-parser-js'
import { withNamespaces } from 'react-i18next'; import { withNamespaces } from 'react-i18next'
import Logo from '../Logo'; import Logo from '../Logo'
import CoinbaseWalletLogo from '../../assets/images/coinbase-wallet-logo.png'; import CoinbaseWalletLogo from '../../assets/images/coinbase-wallet-logo.png'
import TrustLogo from '../../assets/images/trust-wallet-logo.svg'; import TrustLogo from '../../assets/images/trust-wallet-logo.svg'
import BraveLogo from '../../assets/images/brave-logo.svg'; import BraveLogo from '../../assets/images/brave-logo.svg'
import MetamaskLogo from '../../assets/images/metamask-logo.svg'; import MetamaskLogo from '../../assets/images/metamask-logo.svg'
import Web3Status from '../Web3Status'; import Web3Status from '../Web3Status'
import "./header.scss"; import './header.scss'
const links = { const links = {
coinbaseWallet: { coinbaseWallet: {
...@@ -19,135 +19,132 @@ const links = { ...@@ -19,135 +19,132 @@ const links = {
ios: 'https://itunes.apple.com/us/app/coinbase-wallet/id1278383455' ios: 'https://itunes.apple.com/us/app/coinbase-wallet/id1278383455'
}, },
trust: { trust: {
android: 'https://links.trustwalletapp.com/a/key_live_lfvIpVeI9TFWxPCqwU8rZnogFqhnzs4D?&event=openURL&url=https://uniswap.exchange/swap', android:
ios: 'https://links.trustwalletapp.com/a/key_live_lfvIpVeI9TFWxPCqwU8rZnogFqhnzs4D?&event=openURL&url=https://uniswap.exchange/swap', 'https://links.trustwalletapp.com/a/key_live_lfvIpVeI9TFWxPCqwU8rZnogFqhnzs4D?&event=openURL&url=https://uniswap.exchange/swap',
ios:
'https://links.trustwalletapp.com/a/key_live_lfvIpVeI9TFWxPCqwU8rZnogFqhnzs4D?&event=openURL&url=https://uniswap.exchange/swap'
}, },
metamask: { metamask: {
chrome: 'https://chrome.google.com/webstore/detail/metamask/nkbihfbeogaeaoehlefnkodbefgpgknn', chrome: 'https://chrome.google.com/webstore/detail/metamask/nkbihfbeogaeaoehlefnkodbefgpgknn'
}, },
brave: { brave: {
android: 'https://play.google.com/store/apps/details?id=com.brave.browser', android: 'https://play.google.com/store/apps/details?id=com.brave.browser',
ios: 'https://itunes.apple.com/us/app/brave-browser-fast-adblocker/id1052879175', ios: 'https://itunes.apple.com/us/app/brave-browser-fast-adblocker/id1052879175'
}, }
}; }
const ua = new UAParser(window.navigator.userAgent); const ua = new UAParser(window.navigator.userAgent)
function getTrustLink() { function getTrustLink() {
const os = ua.getOS(); const os = ua.getOS()
if (os.name === 'Android') { if (os.name === 'Android') {
return links.trust.android; return links.trust.android
} }
if (os.name === 'iOS') { if (os.name === 'iOS') {
return links.trust.ios; return links.trust.ios
} }
} }
function getCoinbaseWalletLink() { function getCoinbaseWalletLink() {
const os = ua.getOS(); const os = ua.getOS()
if (os.name === 'Android') { if (os.name === 'Android') {
return links.coinbaseWallet.android; return links.coinbaseWallet.android
} }
if (os.name === 'iOS') { if (os.name === 'iOS') {
return links.coinbaseWallet.ios; return links.coinbaseWallet.ios
} }
} }
function getBraveLink() { function getBraveLink() {
const os = ua.getOS(); const os = ua.getOS()
if (os.name === 'Mac OS') { if (os.name === 'Mac OS') {
return links.brave.ios; return links.brave.ios
} }
return links.brave.android; return links.brave.android
} }
function getMetamaskLink() { function getMetamaskLink() {
return links.metamask.chrome; return links.metamask.chrome
} }
function isMobile() { function isMobile() {
return ua.getDevice().type === 'mobile'; return ua.getDevice().type === 'mobile'
} }
class BlockingWarning extends Component { class BlockingWarning extends Component {
render () { render() {
const { const { t, isConnected, initialized, networkId } = this.props
t, let content = []
isConnected,
initialized,
networkId,
} = this.props;
let content = [];
const correctNetworkId = process.env.REACT_APP_NETWORK_ID || 1; const correctNetworkId = process.env.REACT_APP_NETWORK_ID || 1
const correctNetwork = process.env.REACT_APP_NETWORK || 'Main Ethereum Network'; const correctNetwork = process.env.REACT_APP_NETWORK || 'Main Ethereum Network'
const wrongNetwork = networkId !== correctNetworkId; const wrongNetwork = networkId !== correctNetworkId
if (wrongNetwork && initialized) { if (wrongNetwork && initialized) {
content = [ content = [
<div key="warning-title">{t("wrongNetwork")}</div>, <div key="warning-title">{t('wrongNetwork')}</div>,
<div key="warning-desc" className="header__dialog__description"> <div key="warning-desc" className="header__dialog__description">
{t("switchNetwork", {correctNetwork})} {t('switchNetwork', { correctNetwork })}
</div>, </div>
]; ]
} }
if (!isConnected && initialized) { if (!isConnected && initialized) {
content = [ content = [
<div key="warning-title">{t("noWallet")}</div>, <div key="warning-title">{t('noWallet')}</div>,
<div key="warning-desc" className="header__dialog__description"> <div key="warning-desc" className="header__dialog__description">
{ {isMobile() ? t('installWeb3MobileBrowser') : t('installMetamask')}
isMobile()
? t("installWeb3MobileBrowser")
: t("installMetamask")
}
</div>, </div>,
<div key="warning-logos" className="header__download"> <div key="warning-logos" className="header__download">
{ {isMobile()
isMobile() ? [
? ( <img
[ alt="coinbase"
<img alt='coinbase' src={CoinbaseWalletLogo} key="coinbase-wallet" onClick={() => window.open(getCoinbaseWalletLink(), '_blank')} />, src={CoinbaseWalletLogo}
<img alt='trust' src={TrustLogo} key="trust" onClick={() => window.open(getTrustLink(), '_blank')} /> key="coinbase-wallet"
] onClick={() => window.open(getCoinbaseWalletLink(), '_blank')}
) />,
: ( <img alt="trust" src={TrustLogo} key="trust" onClick={() => window.open(getTrustLink(), '_blank')} />
[ ]
<img alt='metamask' src={MetamaskLogo} key="metamask" onClick={() => window.open(getMetamaskLink(), '_blank')} />, : [
<img alt='brave' src={BraveLogo} key="brave" onClick={() => window.open(getBraveLink(), '_blank')} /> <img
] alt="metamask"
) src={MetamaskLogo}
} key="metamask"
</div>, onClick={() => window.open(getMetamaskLink(), '_blank')}
]; />,
<img alt="brave" src={BraveLogo} key="brave" onClick={() => window.open(getBraveLink(), '_blank')} />
]}
</div>
]
} }
return ( return (
<div <div
className={classnames('header__dialog', { className={classnames('header__dialog', {
'header__dialog--disconnected': (!isConnected || wrongNetwork) && initialized, 'header__dialog--disconnected': (!isConnected || wrongNetwork) && initialized
})} })}
> >
{content} {content}
</div> </div>
); )
} }
} }
function Header (props) { function Header(props) {
return ( return (
<div className="header"> <div className="header">
<BlockingWarning {...props} /> <BlockingWarning {...props} />
<div <div
className={classnames('header__top', { className={classnames('header__top', {
'header--inactive': !props.isConnected, 'header--inactive': !props.isConnected
})} })}
> >
<Logo /> <Logo />
...@@ -162,15 +159,13 @@ function Header (props) { ...@@ -162,15 +159,13 @@ function Header (props) {
Header.propTypes = { Header.propTypes = {
currentAddress: PropTypes.string, currentAddress: PropTypes.string,
isConnected: PropTypes.bool.isRequired, isConnected: PropTypes.bool.isRequired
}; }
export default connect( export default connect(state => ({
state => ({ currentAddress: state.web3connect.account,
currentAddress: state.web3connect.account, initialized: state.web3connect.initialized,
initialized: state.web3connect.initialized, isConnected: !!state.web3connect.account,
isConnected: !!state.web3connect.account, web3: state.web3connect.web3,
web3: state.web3connect.web3, networkId: state.web3connect.networkId
networkId: state.web3connect.networkId, }))(withNamespaces()(Header))
}),
)(withNamespaces()(Header));
import React from 'react'; import React from 'react'
import "./logo.scss"; import './logo.scss'
export default function Logo(props) { export default function Logo(props) {
return ( return (
<div className="logo"> <div className="logo">
<span role="img" aria-label="logo">🦄</span> <span role="img" aria-label="logo">
🦄
</span>
</div> </div>
); )
} }
.logo { .logo {
font-size: 1.5rem; font-size: 1.5rem;
line-height: 1.75rem; line-height: 1.75rem;
} }
\ No newline at end of file
import React, { Component } from 'react'; import React, { Component } from 'react'
import ReactDOM from 'react-dom'; import ReactDOM from 'react-dom'
import PropTypes from 'prop-types'; import PropTypes from 'prop-types'
import { CSSTransitionGroup } from 'react-transition-group'; import { CSSTransitionGroup } from 'react-transition-group'
import './modal.scss'; import './modal.scss'
const modalRoot = document.querySelector('#modal-root'); const modalRoot = document.querySelector('#modal-root')
export default class Modal extends Component { export default class Modal extends Component {
static propTypes = { static propTypes = {
onClose: PropTypes.func.isRequired, onClose: PropTypes.func.isRequired
}; }
componentDidMount() { componentDidMount() {
// The portal element is inserted in the DOM tree after // The portal element is inserted in the DOM tree after
...@@ -28,7 +28,7 @@ export default class Modal extends Component { ...@@ -28,7 +28,7 @@ export default class Modal extends Component {
setTimeout(() => { setTimeout(() => {
// modalRoot.style.display = 'none'; // modalRoot.style.display = 'none';
// modalRoot.removeChild(this.el); // modalRoot.removeChild(this.el);
}, 500); }, 500)
} }
render() { render() {
...@@ -46,7 +46,7 @@ export default class Modal extends Component { ...@@ -46,7 +46,7 @@ export default class Modal extends Component {
</CSSTransitionGroup> </CSSTransitionGroup>
{this.props.children} {this.props.children}
</div>, </div>,
modalRoot, modalRoot
); )
} }
} }
...@@ -4,9 +4,8 @@ ...@@ -4,9 +4,8 @@
position: relative; position: relative;
height: 100vh; height: 100vh;
width: 100vw; width: 100vw;
background-color: rgba($black, .6); background-color: rgba($black, 0.6);
z-index: 1000; z-index: 1000;
} }
.modal-container-appear { .modal-container-appear {
......
@import "../../variables"; @import '../../variables';
.beta-message { .beta-message {
@extend %row-nowrap; @extend %row-nowrap;
flex: 1 0 auto; flex: 1 0 auto;
align-items: center; align-items: center;
position: relative; position: relative;
padding: .5rem 1rem; padding: 0.5rem 1rem;
margin-bottom: 1rem; margin-bottom: 1rem;
border: 1px solid rgba($wisteria-purple, .4); border: 1px solid rgba($wisteria-purple, 0.4);
background-color: rgba($wisteria-purple, .1); background-color: rgba($wisteria-purple, 0.1);
border-radius: 2rem; border-radius: 2rem;
font-size: .75rem; font-size: 0.75rem;
line-height: 1rem; line-height: 1rem;
text-align: center; text-align: center;
color: $wisteria-purple; color: $wisteria-purple;
&:after { &:after {
content: '✕'; content: '✕';
top: .5rem; top: 0.5rem;
right: 1rem; right: 1rem;
position: absolute; position: absolute;
color: $wisteria-purple; color: $wisteria-purple;
......
import React, { Component } from 'react'; import React, { Component } from 'react'
import { withRouter } from 'react-router-dom'; import { withRouter } from 'react-router-dom'
import { connect } from 'react-redux'; import { connect } from 'react-redux'
import PropTypes from 'prop-types'; import PropTypes from 'prop-types'
import { withNamespaces } from 'react-i18next'; import { withNamespaces } from 'react-i18next'
import { dismissBetaMessage } from '../../ducks/app'; import { dismissBetaMessage } from '../../ducks/app'
import {Tab, Tabs} from "../Tab"; import { Tab, Tabs } from '../Tab'
import './beta-message.scss'; import './beta-message.scss'
class NavigationTabs extends Component { class NavigationTabs extends Component {
static propTypes = { static propTypes = {
history: PropTypes.shape({ history: PropTypes.shape({
push: PropTypes.func.isRequired, push: PropTypes.func.isRequired
}), }),
className: PropTypes.string, className: PropTypes.string,
dismissBetaMessage: PropTypes.func.isRequired, dismissBetaMessage: PropTypes.func.isRequired,
showBetaMessage: PropTypes.bool.isRequired, showBetaMessage: PropTypes.bool.isRequired
}; }
constructor(props) { constructor(props) {
super(props); super(props)
this.state = { this.state = {
selectedPath: this.props.location.pathname, selectedPath: this.props.location.pathname,
className: '', className: '',
showWarning: true, showWarning: true
}; }
} }
renderTab(name, path, regex) { renderTab(name, path, regex) {
const { push } = this.props.history; const { push } = this.props.history
return ( return <Tab text={name} onClick={() => push(path)} isSelected={regex.test(this.props.location.pathname)} />
<Tab
text={name}
onClick={() => push(path)}
isSelected={regex.test(this.props.location.pathname)}
/>
)
} }
render() { render() {
const { t, showBetaMessage, className, dismissBetaMessage } = this.props; const { t, showBetaMessage, className, dismissBetaMessage } = this.props
return ( return (
<div> <div>
<Tabs className={className}> <Tabs className={className}>
{ this.renderTab(t("swap"), '/swap', /swap/) } {this.renderTab(t('swap'), '/swap', /swap/)}
{ this.renderTab(t("send"), '/send', /send/) } {this.renderTab(t('send'), '/send', /send/)}
{ this.renderTab(t("pool"), '/add-liquidity', /add-liquidity|remove-liquidity|create-exchange/) } {this.renderTab(t('pool'), '/add-liquidity', /add-liquidity|remove-liquidity|create-exchange/)}
</Tabs> </Tabs>
{ {showBetaMessage && (
showBetaMessage && ( <div className="beta-message" onClick={dismissBetaMessage}>
<div className="beta-message" onClick={dismissBetaMessage}> <span role="img" aria-label="warning">
<span role='img' aria-label='warning'>💀</span> {t("betaWarning")} 💀
</div> </span>{' '}
) {t('betaWarning')}
} </div>
)}
</div> </div>
); )
} }
} }
export default withRouter( export default withRouter(
connect( connect(
state => ({ state => ({
showBetaMessage: state.app.showBetaMessage, showBetaMessage: state.app.showBetaMessage
}), }),
dispatch => ({ dispatch => ({
dismissBetaMessage: () => dispatch(dismissBetaMessage()), dismissBetaMessage: () => dispatch(dismissBetaMessage())
}), })
)(withNamespaces()(NavigationTabs)) )(withNamespaces()(NavigationTabs))
); )
import React from 'react'; import React from 'react'
import './oversized-panel.scss'; import './oversized-panel.scss'
export default function OversizedPanel(props) { export default function OversizedPanel(props) {
return ( return (
<div className="oversized-panel"> <div className="oversized-panel">
{ props.hideTop || <div className="oversized-panel__top" /> } {props.hideTop || <div className="oversized-panel__top" />}
{props.children} {props.children}
{ props.hideBottom || <div className="oversized-panel__bottom" /> } {props.hideBottom || <div className="oversized-panel__bottom" />}
</div> </div>
); )
} }
...@@ -5,12 +5,12 @@ ...@@ -5,12 +5,12 @@
background-color: $concrete-gray; background-color: $concrete-gray;
width: calc(100% - 1rem); width: calc(100% - 1rem);
margin: 0 auto; margin: 0 auto;
border-radius: .625rem; border-radius: 0.625rem;
&__top { &__top {
content: ""; content: '';
position: absolute; position: absolute;
top: -.5rem; top: -0.5rem;
left: 0; left: 0;
height: 1rem; height: 1rem;
width: 100%; width: 100%;
...@@ -27,4 +27,4 @@ ...@@ -27,4 +27,4 @@
background-color: $concrete-gray; background-color: $concrete-gray;
z-index: 100; z-index: 100;
} }
} }
\ No newline at end of file
import React, { Component } from 'react'; import React, { Component } from 'react'
import PropTypes from 'prop-types'; import PropTypes from 'prop-types'
import { CSSTransitionGroup } from "react-transition-group"; import { CSSTransitionGroup } from 'react-transition-group'
import Modal from '../Modal'; import Modal from '../Modal'
import QrCodeSVG from '../../assets/images/qr-code.svg'; import QrCodeSVG from '../../assets/images/qr-code.svg'
import QrScanner from '../../libraries/qr-scanner'; import QrScanner from '../../libraries/qr-scanner'
import './qr-code.scss'; import './qr-code.scss'
class QrCode extends Component { class QrCode extends Component {
static propTypes = { static propTypes = {
onValueReceived: PropTypes.func, onValueReceived: PropTypes.func
}; }
static defaultProps = { static defaultProps = {
onValueReceived() {}, onValueReceived() {}
}; }
state = { state = {
videoOpen: false, videoOpen: false,
stream: null, stream: null
}; }
componentDidUpdate() { componentDidUpdate() {
const { videoOpen, stream } = this.state; const { videoOpen, stream } = this.state
if (videoOpen && !stream && this.videoRef) { if (videoOpen && !stream && this.videoRef) {
this.startStreamingVideo(this.videoRef) this.startStreamingVideo(this.videoRef)
} else if (!videoOpen && stream) { } else if (!videoOpen && stream) {
this.setState({stream: null}); this.setState({ stream: null })
} }
} }
startStreamingVideo(videoRef) { startStreamingVideo(videoRef) {
if (navigator && navigator.mediaDevices && navigator.mediaDevices.getUserMedia) { if (navigator && navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
navigator.mediaDevices.getUserMedia({video: { facingMode: 'user'}, audio: false}) navigator.mediaDevices
.then((stream) => { .getUserMedia({ video: { facingMode: 'user' }, audio: false })
videoRef.srcObject = stream; .then(stream => {
new QrScanner(videoRef, (val) => { videoRef.srcObject = stream
this.closeVideo(); new QrScanner(videoRef, val => {
this.props.onValueReceived(val); this.closeVideo()
this.props.onValueReceived(val)
}) })
this.setState({ this.setState({
stream: stream.getTracks()[0] stream: stream.getTracks()[0]
}); })
})
.catch(error => {
this.closeVideo()
console.error(error)
}) })
.catch((error) => {
this.closeVideo();
console.error(error);
});
} }
} }
openVideo = () => { openVideo = () => {
this.setState({videoOpen: true}); this.setState({ videoOpen: true })
} }
closeVideo = () => { closeVideo = () => {
if (this.state.stream) { if (this.state.stream) {
this.state.stream.stop(); this.state.stream.stop()
} }
this.setState({videoOpen: false, stream: null}); this.setState({ videoOpen: false, stream: null })
this.videoRef = null; this.videoRef = null
}; }
setVideoRef = (element) => { setVideoRef = element => {
this.videoRef = element; this.videoRef = element
} }
renderQrReader() { renderQrReader() {
...@@ -80,31 +81,29 @@ class QrCode extends Component { ...@@ -80,31 +81,29 @@ class QrCode extends Component {
transitionEnterTimeout={200} transitionEnterTimeout={200}
> >
<div className="qr-code__modal"> <div className="qr-code__modal">
<video <video playsInline muted autoPlay ref={this.setVideoRef} className="qr-code__video" />
playsInline
muted
autoPlay
ref={this.setVideoRef}
className="qr-code__video"
>
</video>
</div> </div>
</CSSTransitionGroup> </CSSTransitionGroup>
</Modal> </Modal>
); )
} }
return null; return null
} }
render() { render() {
return [ return [
<img key="icon" src={QrCodeSVG} alt='code' onClick={() => { <img
this.state.videoOpen ? this.closeVideo() : this.openVideo(); key="icon"
}} />, src={QrCodeSVG}
alt="code"
onClick={() => {
this.state.videoOpen ? this.closeVideo() : this.openVideo()
}}
/>,
this.renderQrReader() this.renderQrReader()
] ]
} }
} }
export default QrCode; export default QrCode
.qr-code { .qr-code {
&__video { &__video {
height: 100%; height: 100%;
......
import React from 'react'; import React from 'react'
import PropTypes from 'prop-types'; import PropTypes from 'prop-types'
import classnames from 'classnames'; import classnames from 'classnames'
import './tab.scss'; import './tab.scss'
export const Tabs = props => { export const Tabs = props => {
return ( return <div className={classnames('tabs', props.className)}>{props.children}</div>
<div className={classnames("tabs", props.className)}> }
{ props.children }
</div>
);
};
export const Tab = props => { export const Tab = props => {
return ( return (
<div <div
className={classnames("tab", { className={classnames('tab', {
'tab--selected': props.isSelected, 'tab--selected': props.isSelected
})} })}
onClick={props.onClick} onClick={props.onClick}
> >
{ props.text ? <span>{props.text}</span> : null } {props.text ? <span>{props.text}</span> : null}
</div> </div>
); )
}; }
Tab.propTypes = { Tab.propTypes = {
className: PropTypes.string, className: PropTypes.string,
text: PropTypes.string, text: PropTypes.string,
isSelected: PropTypes.bool, isSelected: PropTypes.bool,
onClick: PropTypes.func, onClick: PropTypes.func
}; }
Tab.defaultProps = { Tab.defaultProps = {
className: '', className: ''
}; }
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
height: 2.5rem; height: 2.5rem;
background-color: $concrete-gray; background-color: $concrete-gray;
border-radius: 3rem; border-radius: 3rem;
box-shadow: 0 0 0 .5px darken($concrete-gray, 5); box-shadow: 0 0 0 0.5px darken($concrete-gray, 5);
.tab:first-child { .tab:first-child {
//margin-left: -1px; //margin-left: -1px;
...@@ -35,7 +35,7 @@ ...@@ -35,7 +35,7 @@
&--selected { &--selected {
background-color: $white; background-color: $white;
border-radius: 3rem; border-radius: 3rem;
box-shadow: 0 0 .5px .5px $mercury-gray; box-shadow: 0 0 0.5px 0.5px $mercury-gray;
font-weight: 500; font-weight: 500;
span { span {
......
import React, { Component } from 'react'; import React, { Component } from 'react'
import PropTypes from 'prop-types'; import PropTypes from 'prop-types'
import EthereumLogo from '../../assets/images/ethereum-logo.svg'; import EthereumLogo from '../../assets/images/ethereum-logo.svg'
const RINKEBY_TOKEN_MAP = { const RINKEBY_TOKEN_MAP = {
'0xDA5B056Cfb861282B4b59d29c9B395bcC238D29B': '0x0d8775f648430679a709e98d2b0cb6250d2887ef', '0xDA5B056Cfb861282B4b59d29c9B395bcC238D29B': '0x0d8775f648430679a709e98d2b0cb6250d2887ef',
'0x2448eE2641d78CC42D7AD76498917359D961A783': '0x89d24a6b4ccb1b6faa2625fe562bdd9a23260359', '0x2448eE2641d78CC42D7AD76498917359D961A783': '0x89d24a6b4ccb1b6faa2625fe562bdd9a23260359',
'0xF9bA5210F91D0474bd1e1DcDAeC4C58E359AaD85': '0x9f8f72aa9304c8b593d555f12ef6589cc3a579a2', '0xF9bA5210F91D0474bd1e1DcDAeC4C58E359AaD85': '0x9f8f72aa9304c8b593d555f12ef6589cc3a579a2',
'0x879884c3C46A24f56089f3bBbe4d5e38dB5788C0': '0xd26114cd6ee289accf82350c8d8487fedb8a0c07', '0x879884c3C46A24f56089f3bBbe4d5e38dB5788C0': '0xd26114cd6ee289accf82350c8d8487fedb8a0c07',
'0xF22e3F33768354c9805d046af3C0926f27741B43': '0xe41d2489571d322189246dafa5ebde1f4699f498', '0xF22e3F33768354c9805d046af3C0926f27741B43': '0xe41d2489571d322189246dafa5ebde1f4699f498'
}; }
const TOKEN_ICON_API = 'https://raw.githubusercontent.com/TrustWallet/tokens/master/tokens'; const TOKEN_ICON_API = 'https://raw.githubusercontent.com/TrustWallet/tokens/master/tokens'
const BAD_IMAGES = {}; const BAD_IMAGES = {}
export default class TokenLogo extends Component { export default class TokenLogo extends Component {
static propTypes = { static propTypes = {
address: PropTypes.string, address: PropTypes.string,
size: PropTypes.string, size: PropTypes.string,
className: PropTypes.string, className: PropTypes.string
}; }
static defaultProps = { static defaultProps = {
address: '', address: '',
size: '1rem', size: '1rem',
className: '', className: ''
}; }
state = { state = {
error: false, error: false
}; }
render() { render() {
const { address, size, className } = this.props; const { address, size, className } = this.props
// let path = GenericTokenLogo; // let path = GenericTokenLogo;
let path = ''; let path = ''
const mainAddress = RINKEBY_TOKEN_MAP[address] ? RINKEBY_TOKEN_MAP[address] : address; const mainAddress = RINKEBY_TOKEN_MAP[address] ? RINKEBY_TOKEN_MAP[address] : address
if (mainAddress === 'ETH') { if (mainAddress === 'ETH') {
path = EthereumLogo; path = EthereumLogo
} }
if (!this.state.error && !BAD_IMAGES[mainAddress] && mainAddress !== 'ETH') { if (!this.state.error && !BAD_IMAGES[mainAddress] && mainAddress !== 'ETH') {
path = `${TOKEN_ICON_API}/${mainAddress.toLowerCase()}.png`; path = `${TOKEN_ICON_API}/${mainAddress.toLowerCase()}.png`
} }
if (!path) { if (!path) {
return <div className={className} style={{ width: size, fontSize: size }}><span role='img' aria-label='thinking'>🤔</span></div> return (
<div className={className} style={{ width: size, fontSize: size }}>
<span role="img" aria-label="thinking">
🤔
</span>
</div>
)
} }
return ( return (
<img <img
alt='images' alt="images"
src={path} src={path}
className={className} className={className}
style={{ style={{
width: size, width: size,
height: size, height: size
}} }}
onError={() => { onError={() => {
this.setState({ error: true }); this.setState({ error: true })
BAD_IMAGES[mainAddress] = true; BAD_IMAGES[mainAddress] = true
}} }}
/> />
); )
} }
} }
import React, { Component } from 'react'; import React, { Component } from 'react'
import PropTypes from 'prop-types'; import PropTypes from 'prop-types'
import { connect } from 'react-redux'; import { connect } from 'react-redux'
import classnames from 'classnames'; import classnames from 'classnames'
import Jazzicon from 'jazzicon'; import Jazzicon from 'jazzicon'
import { CSSTransitionGroup } from "react-transition-group"; import { CSSTransitionGroup } from 'react-transition-group'
import { withNamespaces } from 'react-i18next'; import { withNamespaces } from 'react-i18next'
import { ethers } from 'ethers' import { ethers } from 'ethers'
import './web3-status.scss'; import './web3-status.scss'
import Modal from '../Modal'; import Modal from '../Modal'
function getEtherscanLink(tx) { function getEtherscanLink(tx) {
return `https://etherscan.io/tx/${tx}`; return `https://etherscan.io/tx/${tx}`
} }
console.log(ethers) console.log(ethers)
class Web3Status extends Component { class Web3Status extends Component {
state = { state = {
isShowingModal: false, isShowingModal: false
}; }
handleClick = () => { handleClick = () => {
if (this.props.pending.length && !this.state.isShowingModal) { if (this.props.pending.length && !this.state.isShowingModal) {
this.setState({isShowingModal: true}); this.setState({ isShowingModal: true })
} }
}; }
renderPendingTransactions() { renderPendingTransactions() {
return this.props.pending.map((transaction) => { return this.props.pending.map(transaction => {
return ( return (
<div <div
key={transaction} key={transaction}
className={classnames('pending-modal__transaction-row')} className={classnames('pending-modal__transaction-row')}
onClick={() => window.open(getEtherscanLink(transaction), '_blank')} onClick={() => window.open(getEtherscanLink(transaction), '_blank')}
> >
<div className="pending-modal__transaction-label"> <div className="pending-modal__transaction-label">{transaction}</div>
{transaction}
</div>
<div className="pending-modal__pending-indicator"> <div className="pending-modal__pending-indicator">
<div className="loader" /> {this.props.t("pending")} <div className="loader" /> {this.props.t('pending')}
</div> </div>
</div> </div>
); )
}); })
} }
renderModal() { renderModal() {
if (!this.state.isShowingModal) { if (!this.state.isShowingModal) {
return null; return null
} }
return ( return (
...@@ -68,84 +66,82 @@ class Web3Status extends Component { ...@@ -68,84 +66,82 @@ class Web3Status extends Component {
</div> </div>
</CSSTransitionGroup> </CSSTransitionGroup>
</Modal> </Modal>
); )
} }
render() { render() {
const { t, address, pending, confirmed } = this.props; const { t, address, pending, confirmed } = this.props
const hasPendingTransactions = !!pending.length; const hasPendingTransactions = !!pending.length
const hasConfirmedTransactions = !!confirmed.length; const hasConfirmedTransactions = !!confirmed.length
return ( return (
<div <div
className={classnames("web3-status", { className={classnames('web3-status', {
'web3-status__connected': this.props.isConnected, 'web3-status__connected': this.props.isConnected,
'web3-status--pending': hasPendingTransactions, 'web3-status--pending': hasPendingTransactions,
'web3-status--confirmed': hasConfirmedTransactions, 'web3-status--confirmed': hasConfirmedTransactions
})} })}
onClick={this.handleClick} onClick={this.handleClick}
> >
<div className="web3-status__text"> <div className="web3-status__text">
{hasPendingTransactions ? getPendingText(pending, t("pending")) : getText(address, t("disconnected")) } {hasPendingTransactions ? getPendingText(pending, t('pending')) : getText(address, t('disconnected'))}
</div> </div>
<div <div
className="web3-status__identicon" className="web3-status__identicon"
ref={el => { ref={el => {
if (!el) { if (!el) {
return; return
} }
if (!address || address.length < 42 || !ethers.utils.isHexString(address)) { if (!address || address.length < 42 || !ethers.utils.isHexString(address)) {
return; return
} }
el.innerHTML = ''; el.innerHTML = ''
el.appendChild(Jazzicon(16, parseInt(address.slice(2), 16))); el.appendChild(Jazzicon(16, parseInt(address.slice(2), 16)))
}} }}
/> />
{this.renderModal()} {this.renderModal()}
</div> </div>
); )
} }
} }
function getPendingText(pendingTransactions, pendingLabel) { function getPendingText(pendingTransactions, pendingLabel) {
return ( return (
<div className="web3-status__pending-container"> <div className="web3-status__pending-container">
<div className="loader" /> <div className="loader" />
<span key="text">{pendingTransactions.length} {pendingLabel}</span> <span key="text">
{pendingTransactions.length} {pendingLabel}
</span>
</div> </div>
); )
} }
function getText(text, disconnectedText) { function getText(text, disconnectedText) {
if (!text || text.length < 42 || !ethers.utils.isHexString(text)) { if (!text || text.length < 42 || !ethers.utils.isHexString(text)) {
return disconnectedText; return disconnectedText
} }
const address = ethers.utils.getAddress(text); const address = ethers.utils.getAddress(text)
return `${address.substring(0, 6)}...${address.substring(38)}`; return `${address.substring(0, 6)}...${address.substring(38)}`
} }
Web3Status.propTypes = { Web3Status.propTypes = {
isConnected: PropTypes.bool, isConnected: PropTypes.bool,
address: PropTypes.string, address: PropTypes.string
}; }
Web3Status.defaultProps = { Web3Status.defaultProps = {
isConnected: false, isConnected: false,
address: 'Disconnected', address: 'Disconnected'
}; }
export default connect( export default connect(state => {
state => { return {
return { address: state.web3connect.account,
address: state.web3connect.account, isConnected: !!(state.web3connect.web3 && state.web3connect.account),
isConnected: !!(state.web3connect.web3 && state.web3connect.account), pending: state.web3connect.transactions.pending,
pending: state.web3connect.transactions.pending, confirmed: state.web3connect.transactions.confirmed
confirmed: state.web3connect.transactions.confirmed,
};
} }
)(withNamespaces()(Web3Status)); })(withNamespaces()(Web3Status))
@import "../../variables.scss"; @import '../../variables.scss';
.web3-status { .web3-status {
@extend %row-nowrap; @extend %row-nowrap;
height: 2rem; height: 2rem;
font-size: .9rem; font-size: 0.9rem;
align-items: center; align-items: center;
border: 1px solid $mercury-gray; border: 1px solid $mercury-gray;
padding: .5rem; padding: 0.5rem;
border-radius: 2rem; border-radius: 2rem;
color: $dove-gray; color: $dove-gray;
font-weight: 400; font-weight: 400;
...@@ -22,9 +22,9 @@ ...@@ -22,9 +22,9 @@
&__text { &__text {
flex: 1 1 auto; flex: 1 1 auto;
overflow: hidden; overflow: hidden;
margin-right: .75rem; margin-right: 0.75rem;
margin-left: .25rem; margin-left: 0.25rem;
font-size: .75rem; font-size: 0.75rem;
} }
&__pending-container { &__pending-container {
...@@ -39,7 +39,6 @@ ...@@ -39,7 +39,6 @@
} }
} }
.pending-modal { .pending-modal {
background-color: $white; background-color: $white;
position: relative; position: relative;
...@@ -80,12 +79,12 @@ ...@@ -80,12 +79,12 @@
color: $royal-blue; color: $royal-blue;
border: 1px solid $royal-blue; border: 1px solid $royal-blue;
background-color: $zumthor-blue; background-color: $zumthor-blue;
padding: .5rem .75rem;; padding: 0.5rem 0.75rem;
border-radius: 100px; border-radius: 100px;
font-size: .75rem; font-size: 0.75rem;
& > .loading { & > .loading {
margin-right: .5rem; margin-right: 0.5rem;
} }
} }
...@@ -96,7 +95,6 @@ ...@@ -96,7 +95,6 @@
} }
} }
.pending-modal-appear { .pending-modal-appear {
bottom: 0; bottom: 0;
} }
......
// string literals for actions // string literals for actions
// set global web3 object // set global web3 object
export const INITIALIZE_GLOBAL_WEB3 = 'INITIALIZE_GLOBAL_WEB3'; export const INITIALIZE_GLOBAL_WEB3 = 'INITIALIZE_GLOBAL_WEB3'
// web3 actions, all set from action creator to reducer to app // web3 actions, all set from action creator to reducer to app
export const SET_WEB3_CONNECTION_STATUS = 'WEB3_CONNECTION_STATUS'; export const SET_WEB3_CONNECTION_STATUS = 'WEB3_CONNECTION_STATUS'
export const CHECK_WEB3_CONNECTION = 'CHECK_WEB3_CONNECTION'; export const CHECK_WEB3_CONNECTION = 'CHECK_WEB3_CONNECTION'
export const SET_CURRENT_MASK_ADDRESS = 'SET_CURRENT_MASK_ADDRESS'; export const SET_CURRENT_MASK_ADDRESS = 'SET_CURRENT_MASK_ADDRESS'
export const METAMASK_LOCKED = 'METAMASK_LOCKED'; export const METAMASK_LOCKED = 'METAMASK_LOCKED'
export const METAMASK_UNLOCKED = 'METAMASK_UNLOCKED'; export const METAMASK_UNLOCKED = 'METAMASK_UNLOCKED'
export const SET_INTERACTION_STATE = 'SET_INTERACTION_STATE'; export const SET_INTERACTION_STATE = 'SET_INTERACTION_STATE'
export const SET_NETWORK_MESSAGE = 'SET_NETWORK_MESSAGE'; export const SET_NETWORK_MESSAGE = 'SET_NETWORK_MESSAGE'
export const SET_BLOCK_TIMESTAMP = 'SET_BLOCK_TIMESTAMP'; export const SET_BLOCK_TIMESTAMP = 'SET_BLOCK_TIMESTAMP'
export const SET_EXCHANGE_TYPE = 'SET_EXCHANGE_TYPE'; export const SET_EXCHANGE_TYPE = 'SET_EXCHANGE_TYPE'
// actions to toggle divs // actions to toggle divs
export const TOGGLE_ABOUT = 'TOGGLE_ABOUT'; export const TOGGLE_ABOUT = 'TOGGLE_ABOUT'
export const TOGGLE_INVEST = 'TOGGLE_INVEST'; export const TOGGLE_INVEST = 'TOGGLE_INVEST'
// CONTRACT actions in actions, action creator, reducer // CONTRACT actions in actions, action creator, reducer
export const FACTORY_CONTRACT_READY = 'FACTORY_CONTRACT_READY'; export const FACTORY_CONTRACT_READY = 'FACTORY_CONTRACT_READY'
export const EXCHANGE_CONTRACT_READY = 'EXCHANGE_CONTRACT_READY'; export const EXCHANGE_CONTRACT_READY = 'EXCHANGE_CONTRACT_READY'
export const TOKEN_CONTRACT_READY = 'TOKEN_CONTRACT_READY'; export const TOKEN_CONTRACT_READY = 'TOKEN_CONTRACT_READY'
// actions for the exchange // actions for the exchange
export const SET_INPUT_BALANCE = 'SET_INPUT_BALANCE'; export const SET_INPUT_BALANCE = 'SET_INPUT_BALANCE'
export const SET_OUTPUT_BALANCE = 'SET_OUTPUT_BALANCE'; export const SET_OUTPUT_BALANCE = 'SET_OUTPUT_BALANCE'
export const SET_INPUT_TOKEN = 'SET_INPUT_TOKEN'; export const SET_INPUT_TOKEN = 'SET_INPUT_TOKEN'
export const SET_OUTPUT_TOKEN = 'SET_OUTPUT_TOKEN'; export const SET_OUTPUT_TOKEN = 'SET_OUTPUT_TOKEN'
export const SET_ETH_POOL_1 = 'SET_ETH_POOL_1'; export const SET_ETH_POOL_1 = 'SET_ETH_POOL_1'
export const SET_ETH_POOL_2 = 'SET_ETH_POOL_2'; export const SET_ETH_POOL_2 = 'SET_ETH_POOL_2'
export const SET_TOKEN_POOL_1 = 'SET_TOKEN_POOL_1'; export const SET_TOKEN_POOL_1 = 'SET_TOKEN_POOL_1'
export const SET_TOKEN_POOL_2 = 'SET_TOKEN_POOL_2'; export const SET_TOKEN_POOL_2 = 'SET_TOKEN_POOL_2'
export const SET_ALLOWANCE_APPROVAL_STATE = 'SET_ALLOWANCE_APPROVAL_STATE'; export const SET_ALLOWANCE_APPROVAL_STATE = 'SET_ALLOWANCE_APPROVAL_STATE'
export const SET_EXCHANGE_INPUT_VALUE = 'SET_EXCHANGE_INPUT_VALUE'; export const SET_EXCHANGE_INPUT_VALUE = 'SET_EXCHANGE_INPUT_VALUE'
export const SET_EXCHANGE_OUTPUT_VALUE = 'SET_EXCHANGE_OUTPUT_VALUE'; export const SET_EXCHANGE_OUTPUT_VALUE = 'SET_EXCHANGE_OUTPUT_VALUE'
export const SET_EXCHANGE_RATE = 'SET_EXCHANGE_RATE'; export const SET_EXCHANGE_RATE = 'SET_EXCHANGE_RATE'
export const SET_EXCHANGE_FEE = 'SET_EXCHANGE_FEE'; export const SET_EXCHANGE_FEE = 'SET_EXCHANGE_FEE'
export const SET_INVEST_TOKEN = 'SET_INVEST_TOKEN'; export const SET_INVEST_TOKEN = 'SET_INVEST_TOKEN'
export const SET_INVEST_ETH_POOL = 'SET_INVEST_ETH'; export const SET_INVEST_ETH_POOL = 'SET_INVEST_ETH'
export const SET_INVEST_TOKEN_POOL = 'SET_INVEST_TOKENS'; export const SET_INVEST_TOKEN_POOL = 'SET_INVEST_TOKENS'
export const SET_INVEST_TOKEN_ALLOWANCE = 'SET_INVEST_TOKEN_ALLOWANCE'; export const SET_INVEST_TOKEN_ALLOWANCE = 'SET_INVEST_TOKEN_ALLOWANCE'
export const SET_INVEST_SHARES = 'SET_INVEST_SHARES'; export const SET_INVEST_SHARES = 'SET_INVEST_SHARES'
export const SET_USER_SHARES = 'SET_USER_SHARES'; export const SET_USER_SHARES = 'SET_USER_SHARES'
export const SET_INVEST_TOKEN_BALANCE = 'SET_INVEST_TOKEN_BALANCE'; export const SET_INVEST_TOKEN_BALANCE = 'SET_INVEST_TOKEN_BALANCE'
export const SET_INVEST_ETH_BALANCE = 'SET_INVEST_ETH_BALANCE'; export const SET_INVEST_ETH_BALANCE = 'SET_INVEST_ETH_BALANCE'
export const SET_INVEST_SHARES_INPUT = 'SET_INVEST_SHARES_INPUT'; export const SET_INVEST_SHARES_INPUT = 'SET_INVEST_SHARES_INPUT'
export const SET_INVEST_ETH_REQUIRED = 'SET_INVEST_ETH_REQUIRED'; export const SET_INVEST_ETH_REQUIRED = 'SET_INVEST_ETH_REQUIRED'
export const SET_INVEST_TOKENS_REQUIRED = 'SET_INVEST_TOKENS_REQUIRED'; export const SET_INVEST_TOKENS_REQUIRED = 'SET_INVEST_TOKENS_REQUIRED'
export const SET_INVEST_CHECKED = 'SET_INVEST_CHECKED'; export const SET_INVEST_CHECKED = 'SET_INVEST_CHECKED'
export const INSUFFICIENT_BALANCE = 'Insufficient balance'; export const INSUFFICIENT_BALANCE = 'Insufficient balance'
...@@ -2,30 +2,30 @@ const RINKEBY = { ...@@ -2,30 +2,30 @@ const RINKEBY = {
factoryAddress: '0xf5D915570BC477f9B8D6C0E980aA81757A3AaC36', factoryAddress: '0xf5D915570BC477f9B8D6C0E980aA81757A3AaC36',
exchangeAddresses: { exchangeAddresses: {
addresses: [ addresses: [
['BAT','0x9B913956036a3462330B0642B20D3879ce68b450'], ['BAT', '0x9B913956036a3462330B0642B20D3879ce68b450'],
['DAI','0x77dB9C915809e7BE439D2AB21032B1b8B58F6891'], ['DAI', '0x77dB9C915809e7BE439D2AB21032B1b8B58F6891'],
['MKR','0x93bB63aFe1E0180d0eF100D774B473034fd60C36'], ['MKR', '0x93bB63aFe1E0180d0eF100D774B473034fd60C36'],
['OMG','0x26C226EBb6104676E593F8A070aD6f25cDa60F8D'], ['OMG', '0x26C226EBb6104676E593F8A070aD6f25cDa60F8D']
// ['ZRX','0xaBD44a1D1b9Fb0F39fE1D1ee6b1e2a14916D067D'], // ['ZRX','0xaBD44a1D1b9Fb0F39fE1D1ee6b1e2a14916D067D'],
], ],
fromToken: { fromToken: {
'0xDA5B056Cfb861282B4b59d29c9B395bcC238D29B': '0x9B913956036a3462330B0642B20D3879ce68b450', '0xDA5B056Cfb861282B4b59d29c9B395bcC238D29B': '0x9B913956036a3462330B0642B20D3879ce68b450',
'0x2448eE2641d78CC42D7AD76498917359D961A783': '0x77dB9C915809e7BE439D2AB21032B1b8B58F6891', '0x2448eE2641d78CC42D7AD76498917359D961A783': '0x77dB9C915809e7BE439D2AB21032B1b8B58F6891',
'0xF9bA5210F91D0474bd1e1DcDAeC4C58E359AaD85': '0x93bB63aFe1E0180d0eF100D774B473034fd60C36', '0xF9bA5210F91D0474bd1e1DcDAeC4C58E359AaD85': '0x93bB63aFe1E0180d0eF100D774B473034fd60C36',
'0x879884c3C46A24f56089f3bBbe4d5e38dB5788C0': '0x26C226EBb6104676E593F8A070aD6f25cDa60F8D', '0x879884c3C46A24f56089f3bBbe4d5e38dB5788C0': '0x26C226EBb6104676E593F8A070aD6f25cDa60F8D'
// '0xF22e3F33768354c9805d046af3C0926f27741B43': '0xaBD44a1D1b9Fb0F39fE1D1ee6b1e2a14916D067D', // '0xF22e3F33768354c9805d046af3C0926f27741B43': '0xaBD44a1D1b9Fb0F39fE1D1ee6b1e2a14916D067D',
}, }
}, },
tokenAddresses: { tokenAddresses: {
addresses: [ addresses: [
['BAT','0xDA5B056Cfb861282B4b59d29c9B395bcC238D29B'], ['BAT', '0xDA5B056Cfb861282B4b59d29c9B395bcC238D29B'],
['DAI','0x2448eE2641d78CC42D7AD76498917359D961A783'], ['DAI', '0x2448eE2641d78CC42D7AD76498917359D961A783'],
['MKR','0xF9bA5210F91D0474bd1e1DcDAeC4C58E359AaD85'], ['MKR', '0xF9bA5210F91D0474bd1e1DcDAeC4C58E359AaD85'],
['OMG','0x879884c3C46A24f56089f3bBbe4d5e38dB5788C0'], ['OMG', '0x879884c3C46A24f56089f3bBbe4d5e38dB5788C0']
// ['ZRX','0xF22e3F33768354c9805d046af3C0926f27741B43'], // ['ZRX','0xF22e3F33768354c9805d046af3C0926f27741B43'],
], ]
}, }
}; }
const MAIN = { const MAIN = {
factoryAddress: '0xc0a47dFe034B400B47bDaD5FecDa2621de6c4d95', factoryAddress: '0xc0a47dFe034B400B47bDaD5FecDa2621de6c4d95',
...@@ -73,7 +73,7 @@ const MAIN = { ...@@ -73,7 +73,7 @@ const MAIN = {
['WETH', '0xA2881A90Bf33F03E7a3f803765Cd2ED5c8928dFb'], ['WETH', '0xA2881A90Bf33F03E7a3f803765Cd2ED5c8928dFb'],
['XCHF', '0x8dE0d002DC83478f479dC31F76cB0a8aa7CcEa17'], ['XCHF', '0x8dE0d002DC83478f479dC31F76cB0a8aa7CcEa17'],
['ZIL', '0x7dc095A5CF7D6208CC680fA9866F80a53911041a'], ['ZIL', '0x7dc095A5CF7D6208CC680fA9866F80a53911041a'],
['ZRX', '0xaE76c84C9262Cdb9abc0C2c8888e62Db8E22A0bF'], ['ZRX', '0xaE76c84C9262Cdb9abc0C2c8888e62Db8E22A0bF']
], ],
fromToken: { fromToken: {
'0x960b236A07cf122663c4303350609A66A7B288C0': '0x077d52B047735976dfdA76feF74d4d988AC25196', '0x960b236A07cf122663c4303350609A66A7B288C0': '0x077d52B047735976dfdA76feF74d4d988AC25196',
...@@ -118,8 +118,8 @@ const MAIN = { ...@@ -118,8 +118,8 @@ const MAIN = {
'0x05f4a42e251f2d52b8ed15E9FEdAacFcEF1FAD27': '0x7dc095A5CF7D6208CC680fA9866F80a53911041a', '0x05f4a42e251f2d52b8ed15E9FEdAacFcEF1FAD27': '0x7dc095A5CF7D6208CC680fA9866F80a53911041a',
'0xE41d2489571d322189246DaFA5ebDe1F4699F498': '0xaE76c84C9262Cdb9abc0C2c8888e62Db8E22A0bF', '0xE41d2489571d322189246DaFA5ebDe1F4699F498': '0xaE76c84C9262Cdb9abc0C2c8888e62Db8E22A0bF',
'0x3772f9716Cf6D7a09edE3587738AA2af5577483a': '0x5d8888a212d033cff5f2e0ac24ad91a5495bad62', '0x3772f9716Cf6D7a09edE3587738AA2af5577483a': '0x5d8888a212d033cff5f2e0ac24ad91a5495bad62',
'0x0cbe2df57ca9191b64a7af3baa3f946fa7df2f25': '0xa1ecdcca26150cf69090280ee2ee32347c238c7b', '0x0cbe2df57ca9191b64a7af3baa3f946fa7df2f25': '0xa1ecdcca26150cf69090280ee2ee32347c238c7b'
}, }
}, },
tokenAddresses: { tokenAddresses: {
addresses: [ addresses: [
...@@ -165,94 +165,90 @@ const MAIN = { ...@@ -165,94 +165,90 @@ const MAIN = {
['WETH', '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2'], ['WETH', '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2'],
['XCHF', '0xB4272071eCAdd69d933AdcD19cA99fe80664fc08'], ['XCHF', '0xB4272071eCAdd69d933AdcD19cA99fe80664fc08'],
['ZIL', '0x05f4a42e251f2d52b8ed15E9FEdAacFcEF1FAD27'], ['ZIL', '0x05f4a42e251f2d52b8ed15E9FEdAacFcEF1FAD27'],
['ZRX', '0xE41d2489571d322189246DaFA5ebDe1F4699F498'], ['ZRX', '0xE41d2489571d322189246DaFA5ebDe1F4699F498']
], ]
}, }
}; }
const SET_ADDRESSES = 'app/addresses/setAddresses'; const SET_ADDRESSES = 'app/addresses/setAddresses'
const ADD_EXCHANGE = 'app/addresses/addExchange'; const ADD_EXCHANGE = 'app/addresses/addExchange'
const initialState = RINKEBY; const initialState = RINKEBY
export const addExchange = ({label, exchangeAddress, tokenAddress}) => (dispatch, getState) => { export const addExchange = ({ label, exchangeAddress, tokenAddress }) => (dispatch, getState) => {
const { addresses: { tokenAddresses, exchangeAddresses } } = getState(); const {
addresses: { tokenAddresses, exchangeAddresses }
} = getState()
if (tokenAddresses.addresses.filter(([ symbol ]) => symbol === label).length) { if (tokenAddresses.addresses.filter(([symbol]) => symbol === label).length) {
return; return
} }
if (exchangeAddresses.fromToken[tokenAddresses]) { if (exchangeAddresses.fromToken[tokenAddresses]) {
return; return
} }
dispatch({ dispatch({
type: ADD_EXCHANGE, type: ADD_EXCHANGE,
payload: { payload: {
label, label,
exchangeAddress, exchangeAddress,
tokenAddress, tokenAddress
}, }
}); })
}; }
export const setAddresses = networkId => { export const setAddresses = networkId => {
switch(networkId) { switch (networkId) {
// Main Net // Main Net
case 1: case 1:
case '1': case '1':
return { return {
type: SET_ADDRESSES, type: SET_ADDRESSES,
payload: MAIN, payload: MAIN
}; }
// Rinkeby // Rinkeby
case 4: case 4:
case '4': case '4':
default: default:
return { return {
type: SET_ADDRESSES, type: SET_ADDRESSES,
payload: RINKEBY, payload: RINKEBY
}; }
} }
}; }
export default (state = initialState, { type, payload }) => { export default (state = initialState, { type, payload }) => {
switch (type) { switch (type) {
case SET_ADDRESSES: case SET_ADDRESSES:
return payload; return payload
case ADD_EXCHANGE: case ADD_EXCHANGE:
return handleAddExchange(state, { payload }); return handleAddExchange(state, { payload })
default: default:
return state; return state
} }
} }
function handleAddExchange(state, { payload }) { function handleAddExchange(state, { payload }) {
const { label, tokenAddress, exchangeAddress } = payload; const { label, tokenAddress, exchangeAddress } = payload
if (!label || !tokenAddress || !exchangeAddress) { if (!label || !tokenAddress || !exchangeAddress) {
return state; return state
} }
return { return {
...state, ...state,
exchangeAddresses: { exchangeAddresses: {
...state.exchangeAddresses, ...state.exchangeAddresses,
addresses: [ addresses: [...state.exchangeAddresses.addresses, [label, exchangeAddress]],
...state.exchangeAddresses.addresses,
[label, exchangeAddress]
],
fromToken: { fromToken: {
...state.exchangeAddresses.fromToken, ...state.exchangeAddresses.fromToken,
[tokenAddress]: exchangeAddress, [tokenAddress]: exchangeAddress
}, }
}, },
tokenAddresses: { tokenAddresses: {
...state.tokenAddresses, ...state.tokenAddresses,
addresses: [ addresses: [...state.tokenAddresses.addresses, [label, tokenAddress]]
...state.tokenAddresses.addresses, }
[label, tokenAddress] }
],
},
};
} }
const DISMISS_BETA_MESSAGE = 'app/app/dismissBetaMessage'; const DISMISS_BETA_MESSAGE = 'app/app/dismissBetaMessage'
const initialState = { const initialState = {
showBetaMessage: true, showBetaMessage: true
}; }
export const dismissBetaMessage = () => ({ type: DISMISS_BETA_MESSAGE }); export const dismissBetaMessage = () => ({ type: DISMISS_BETA_MESSAGE })
export default function appReducer(state = initialState, { type, payload }) { export default function appReducer(state = initialState, { type, payload }) {
switch (type) { switch (type) {
case DISMISS_BETA_MESSAGE: case DISMISS_BETA_MESSAGE:
return { ...state, showBetaMessage: false }; return { ...state, showBetaMessage: false }
default: default:
return state; return state
} }
} }
import { combineReducers } from 'redux'; import { combineReducers } from 'redux'
import addresses from './addresses'; import addresses from './addresses'
import app from './app'; import app from './app'
import pending from './pending'; import pending from './pending'
import web3connect from './web3connect'; import web3connect from './web3connect'
export default combineReducers({ export default combineReducers({
app, app,
addresses, addresses,
pending, pending,
web3connect, web3connect
}); })
const ADD_APPROVAL_TX = 'app/send/addApprovalTx'; const ADD_APPROVAL_TX = 'app/send/addApprovalTx'
const getInitialState = () => { const getInitialState = () => {
return { return {
approvals: {}, approvals: {}
}; }
}; }
export const addApprovalTx = ({ tokenAddress, txId }) => ({ export const addApprovalTx = ({ tokenAddress, txId }) => ({
type: ADD_APPROVAL_TX, type: ADD_APPROVAL_TX,
payload: { tokenAddress, txId }, payload: { tokenAddress, txId }
}); })
export default function sendReducer(state = getInitialState(), { type, payload }) { export default function sendReducer(state = getInitialState(), { type, payload }) {
switch (type) { switch (type) {
...@@ -17,10 +17,10 @@ export default function sendReducer(state = getInitialState(), { type, payload } ...@@ -17,10 +17,10 @@ export default function sendReducer(state = getInitialState(), { type, payload }
return { return {
approvals: { approvals: {
...state.approvals, ...state.approvals,
[payload.tokenAddress]: payload.txId, [payload.tokenAddress]: payload.txId
} }
}; }
default: default:
return state; return state
} }
} }
This diff is collapsed.
export default function (matchmask = [], minMatchCharLength = 1) { export default function(matchmask = [], minMatchCharLength = 1) {
let matchedIndices = [] let matchedIndices = []
let start = -1 let start = -1
let end = -1 let end = -1
...@@ -10,7 +10,7 @@ export default function (matchmask = [], minMatchCharLength = 1) { ...@@ -10,7 +10,7 @@ export default function (matchmask = [], minMatchCharLength = 1) {
start = i start = i
} else if (!match && start !== -1) { } else if (!match && start !== -1) {
end = i - 1 end = i - 1
if ((end - start) + 1 >= minMatchCharLength) { if (end - start + 1 >= minMatchCharLength) {
matchedIndices.push([start, end]) matchedIndices.push([start, end])
} }
start = -1 start = -1
...@@ -18,7 +18,7 @@ export default function (matchmask = [], minMatchCharLength = 1) { ...@@ -18,7 +18,7 @@ export default function (matchmask = [], minMatchCharLength = 1) {
} }
// (i-1 - start) + 1 => i - start // (i-1 - start) + 1 => i - start
if (matchmask[i - 1] && (i - start) >= minMatchCharLength) { if (matchmask[i - 1] && i - start >= minMatchCharLength) {
matchedIndices.push([start, i - 1]) matchedIndices.push([start, i - 1])
} }
......
export default function (pattern) { export default function(pattern) {
let mask = {} let mask = {}
let len = pattern.length let len = pattern.length
......
// eslint-disable-next-line no-useless-escape // eslint-disable-next-line no-useless-escape
const SPECIAL_CHARS_REGEX = /[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g const SPECIAL_CHARS_REGEX = /[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g
export default function (text, pattern, tokenSeparator = / +/g) { export default function(text, pattern, tokenSeparator = / +/g) {
let regex = new RegExp(pattern.replace(SPECIAL_CHARS_REGEX, '\\$&').replace(tokenSeparator, '|')) let regex = new RegExp(pattern.replace(SPECIAL_CHARS_REGEX, '\\$&').replace(tokenSeparator, '|'))
let matches = text.match(regex) let matches = text.match(regex)
let isMatch = !!matches let isMatch = !!matches
......
export default function (pattern, { errors = 0, currentLocation = 0, expectedLocation = 0, distance = 100 }) { export default function(pattern, { errors = 0, currentLocation = 0, expectedLocation = 0, distance = 100 }) {
const accuracy = errors / pattern.length const accuracy = errors / pattern.length
const proximity = Math.abs(expectedLocation - currentLocation) const proximity = Math.abs(expectedLocation - currentLocation)
...@@ -7,5 +7,5 @@ export default function (pattern, { errors = 0, currentLocation = 0, expectedLoc ...@@ -7,5 +7,5 @@ export default function (pattern, { errors = 0, currentLocation = 0, expectedLoc
return proximity ? 1.0 : accuracy return proximity ? 1.0 : accuracy
} }
return accuracy + (proximity / distance) return accuracy + proximity / distance
} }
import bitapScore from './bitap_score'; import bitapScore from './bitap_score'
import matchedIndices from './bitap_matched_indices'; import matchedIndices from './bitap_matched_indices'
export default function (text, pattern, patternAlphabet, { location = 0, distance = 100, threshold = 0.6, findAllMatches = false, minMatchCharLength = 1 }) { export default function(
text,
pattern,
patternAlphabet,
{ location = 0, distance = 100, threshold = 0.6, findAllMatches = false, minMatchCharLength = 1 }
) {
const expectedLocation = location const expectedLocation = location
// Set starting location at beginning text and initialize the alphabet. // Set starting location at beginning text and initialize the alphabet.
const textLen = text.length const textLen = text.length
...@@ -63,7 +68,7 @@ export default function (text, pattern, patternAlphabet, { location = 0, distanc ...@@ -63,7 +68,7 @@ export default function (text, pattern, patternAlphabet, { location = 0, distanc
currentLocation: expectedLocation + binMid, currentLocation: expectedLocation + binMid,
expectedLocation, expectedLocation,
distance distance
}); })
if (score <= currentThreshold) { if (score <= currentThreshold) {
binMin = binMid binMin = binMid
...@@ -98,7 +103,7 @@ export default function (text, pattern, patternAlphabet, { location = 0, distanc ...@@ -98,7 +103,7 @@ export default function (text, pattern, patternAlphabet, { location = 0, distanc
// Subsequent passes: fuzzy match // Subsequent passes: fuzzy match
if (i !== 0) { if (i !== 0) {
bitArr[j] |= (((lastBitArr[j + 1] | lastBitArr[j]) << 1) | 1) | lastBitArr[j + 1] bitArr[j] |= ((lastBitArr[j + 1] | lastBitArr[j]) << 1) | 1 | lastBitArr[j + 1]
} }
if (bitArr[j] & mask) { if (bitArr[j] & mask) {
......
import bitapRegexSearch from './bitap_regex_search'; import bitapRegexSearch from './bitap_regex_search'
import bitapSearch from './bitap_search'; import bitapSearch from './bitap_search'
import patternAlphabet from './bitap_pattern_alphabet'; import patternAlphabet from './bitap_pattern_alphabet'
class Bitap { class Bitap {
constructor (pattern, { constructor(
// Approximately where in the text is the pattern expected to be found? pattern,
location = 0, {
// Determines how close the match must be to the fuzzy location (specified above). // Approximately where in the text is the pattern expected to be found?
// An exact letter match which is 'distance' characters away from the fuzzy location location = 0,
// would score as a complete mismatch. A distance of '0' requires the match be at // Determines how close the match must be to the fuzzy location (specified above).
// the exact location specified, a threshold of '1000' would require a perfect match // An exact letter match which is 'distance' characters away from the fuzzy location
// to be within 800 characters of the fuzzy location to be found using a 0.8 threshold. // would score as a complete mismatch. A distance of '0' requires the match be at
distance = 100, // the exact location specified, a threshold of '1000' would require a perfect match
// At what point does the match algorithm give up. A threshold of '0.0' requires a perfect match // to be within 800 characters of the fuzzy location to be found using a 0.8 threshold.
// (of both letters and location), a threshold of '1.0' would match anything. distance = 100,
threshold = 0.6, // At what point does the match algorithm give up. A threshold of '0.0' requires a perfect match
// Machine word size // (of both letters and location), a threshold of '1.0' would match anything.
maxPatternLength = 32, threshold = 0.6,
// Indicates whether comparisons should be case sensitive. // Machine word size
isCaseSensitive = false, maxPatternLength = 32,
// Regex used to separate words when searching. Only applicable when `tokenize` is `true`. // Indicates whether comparisons should be case sensitive.
tokenSeparator = / +/g, isCaseSensitive = false,
// When true, the algorithm continues searching to the end of the input even if a perfect // Regex used to separate words when searching. Only applicable when `tokenize` is `true`.
// match is found before the end of the same input. tokenSeparator = / +/g,
findAllMatches = false, // When true, the algorithm continues searching to the end of the input even if a perfect
// Minimum number of characters that must be matched before a result is considered a match // match is found before the end of the same input.
minMatchCharLength = 1 findAllMatches = false,
}) { // Minimum number of characters that must be matched before a result is considered a match
minMatchCharLength = 1
}
) {
this.options = { this.options = {
location, location,
distance, distance,
...@@ -45,7 +48,7 @@ class Bitap { ...@@ -45,7 +48,7 @@ class Bitap {
} }
} }
search (text) { search(text) {
if (!this.options.isCaseSensitive) { if (!this.options.isCaseSensitive) {
text = text.toLowerCase() text = text.toLowerCase()
} }
......
module.exports = obj => !Array.isArray ? Object.prototype.toString.call(obj) === '[object Array]' : Array.isArray(obj) module.exports = obj => (!Array.isArray ? Object.prototype.toString.call(obj) === '[object Array]' : Array.isArray(obj))
This diff is collapsed.
export function retry(func, retryCount=5) { export function retry(func, retryCount = 5) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
func().then((...args) => { func().then(
resolve(...args); (...args) => {
}, () => { resolve(...args)
if (retryCount === 0) { },
return reject(); () => {
if (retryCount === 0) {
return reject()
}
setTimeout(() => retry(func, retryCount - 1).then(resolve, reject), 50)
} }
setTimeout(() => retry(func, retryCount - 1).then(resolve, reject), 50); )
}); })
});
} }
export default function promisify(web3, methodName, ...args) { export default function promisify(web3, methodName, ...args) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
if (!web3) { if (!web3) {
reject(new Error('No Web3 object')); reject(new Error('No Web3 object'))
return; return
} }
const method = web3.eth[methodName]; const method = web3.eth[methodName]
if (!method) { if (!method) {
reject(new Error(`Cannot find web3.eth.${methodName}`)); reject(new Error(`Cannot find web3.eth.${methodName}`))
return; return
} }
method(...args, (error, data) => { method(...args, (error, data) => {
if (error) { if (error) {
reject(error); reject(error)
return; return
} }
resolve(data); resolve(data)
}) })
}); })
} }
import promisify from "./web3-promisfy"; import promisify from './web3-promisfy'
export function getBlockDeadline(web3, deadline) { export function getBlockDeadline(web3, deadline) {
return new Promise(async (resolve, reject) => { return new Promise(async (resolve, reject) => {
const blockNumber = await promisify(web3, 'getBlockNumber'); const blockNumber = await promisify(web3, 'getBlockNumber')
if (!blockNumber && blockNumber !== 0) { if (!blockNumber && blockNumber !== 0) {
return reject(); return reject()
} }
const block = await promisify(web3, 'getBlock', blockNumber); const block = await promisify(web3, 'getBlock', blockNumber)
if (!block) { if (!block) {
return reject(); return reject()
} }
resolve(block.timestamp + deadline); resolve(block.timestamp + deadline)
}); })
} }
import i18n from "i18next"; import i18n from 'i18next'
import Backend from 'i18next-xhr-backend'; import Backend from 'i18next-xhr-backend'
import LanguageDetector from 'i18next-browser-languagedetector'; import LanguageDetector from 'i18next-browser-languagedetector'
import { reactI18nextModule } from "react-i18next"; import { reactI18nextModule } from 'react-i18next'
const resources = { const resources = {
loadPath: `./locales/{{lng}}.json` loadPath: `./locales/{{lng}}.json`
...@@ -20,13 +20,13 @@ i18n ...@@ -20,13 +20,13 @@ i18n
// for all options read: https://www.i18next.com/overview/configuration-options // for all options read: https://www.i18next.com/overview/configuration-options
.init({ .init({
backend: resources, backend: resources,
fallbackLng: "en", fallbackLng: 'en',
keySeparator: false, keySeparator: false,
interpolation: { interpolation: {
escapeValue: false escapeValue: false
} }
}); })
export default i18n; export default i18n
import React from 'react'; import React from 'react'
import ReactDOM from 'react-dom'; import ReactDOM from 'react-dom'
import { Provider } from 'react-redux'; import { Provider } from 'react-redux'
import ReactGA from 'react-ga'; import ReactGA from 'react-ga'
import './i18n'; import './i18n'
import App from './pages/App'; import App from './pages/App'
import store from './store'; import store from './store'
import './index.scss'; import './index.scss'
if (process.env.NODE_ENV === 'development') { if (process.env.NODE_ENV === 'development') {
// ReactGA.initialize('UA-128182339-02'); // ReactGA.initialize('UA-128182339-02');
} else { } else {
ReactGA.initialize('UA-128182339-1'); ReactGA.initialize('UA-128182339-1')
} }
ReactGA.pageview(window.location.pathname + window.location.search); ReactGA.pageview(window.location.pathname + window.location.search)
window.addEventListener('load', function() { window.addEventListener('load', function() {
ReactDOM.render( ReactDOM.render(
<Provider store={store}> <Provider store={store}>
<App /> <App />
</Provider> </Provider>,
, document.getElementById('root') document.getElementById('root')
); )
}); })
@import url('https://rsms.me/inter/inter-ui.css'); @import url('https://rsms.me/inter/inter-ui.css');
@import "./variables.scss"; @import './variables.scss';
html, body { html,
body {
margin: 0; margin: 0;
padding: 0; padding: 0;
font-family: "Inter UI", sans-serif; font-family: 'Inter UI', sans-serif;
font-size: 16px; font-size: 16px;
-webkit-font-smoothing: antialiased; -webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale; -moz-osx-font-smoothing: grayscale;
...@@ -21,8 +22,8 @@ html, body { ...@@ -21,8 +22,8 @@ html, body {
overflow-y: auto; overflow-y: auto;
background-color: $white; background-color: $white;
z-index: 100; z-index: 100;
-webkit-tap-highlight-color: rgba(255, 255, 255, 0); -webkit-tap-highlight-color: rgba(255, 255, 255, 0);
@media only screen and (min-width : 768px) { @media only screen and (min-width: 768px) {
justify-content: center; justify-content: center;
align-items: center; align-items: center;
} }
...@@ -40,13 +41,17 @@ html, body { ...@@ -40,13 +41,17 @@ html, body {
border: 1px solid transparent; /* Light grey */ border: 1px solid transparent; /* Light grey */
border-top: 1px solid $royal-blue; /* Blue */ border-top: 1px solid $royal-blue; /* Blue */
border-radius: 50%; border-radius: 50%;
width: .75rem; width: 0.75rem;
height: .75rem; height: 0.75rem;
margin-right: .25rem; margin-right: 0.25rem;
animation: spin 1s cubic-bezier(0.25, 0.46, 0.45, 0.94) infinite; animation: spin 1s cubic-bezier(0.25, 0.46, 0.45, 0.94) infinite;
} }
@keyframes spin { @keyframes spin {
0% { transform: rotate(0deg); } 0% {
100% { transform: rotate(360deg); } transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
} }
This diff is collapsed.
import React, { Component } from 'react'; import React, { Component } from 'react'
import { connect } from 'react-redux'; import { connect } from 'react-redux'
import { BrowserRouter, Redirect, Route, Switch } from 'react-router-dom'; import { BrowserRouter, Redirect, Route, Switch } from 'react-router-dom'
import MediaQuery from 'react-responsive'; import MediaQuery from 'react-responsive'
import { Web3Connect, startWatching, initialize } from '../ducks/web3connect'; import { Web3Connect, startWatching, initialize } from '../ducks/web3connect'
import { setAddresses } from '../ducks/addresses'; import { setAddresses } from '../ducks/addresses'
import Header from '../components/Header'; import Header from '../components/Header'
import Swap from './Swap'; import Swap from './Swap'
import Send from './Send'; import Send from './Send'
import Pool from './Pool'; import Pool from './Pool'
import './App.scss'; import './App.scss'
class App extends Component { class App extends Component {
componentWillMount() { componentWillMount() {
const { initialize, startWatching} = this.props; const { initialize, startWatching } = this.props
initialize().then(startWatching); initialize().then(startWatching)
}; }
componentWillUpdate() { componentWillUpdate() {
const { web3, setAddresses } = this.props; const { web3, setAddresses } = this.props
if (this.hasSetNetworkId || !web3 || !web3.eth || !web3.eth.net || !web3.eth.net.getId) { if (this.hasSetNetworkId || !web3 || !web3.eth || !web3.eth.net || !web3.eth.net.getId) {
return; return
} }
web3.eth.net.getId((err, networkId) => { web3.eth.net.getId((err, networkId) => {
if (!err && !this.hasSetNetworkId) { if (!err && !this.hasSetNetworkId) {
setAddresses(networkId); setAddresses(networkId)
this.hasSetNetworkId = true; this.hasSetNetworkId = true
} }
}); })
} }
render() { render() {
if (!this.props.initialized) { if (!this.props.initialized) {
return <noscript />; return <noscript />
} }
return ( return (
...@@ -56,7 +56,7 @@ class App extends Component { ...@@ -56,7 +56,7 @@ class App extends Component {
</Switch> </Switch>
</BrowserRouter> </BrowserRouter>
</div> </div>
); )
} }
} }
...@@ -64,11 +64,11 @@ export default connect( ...@@ -64,11 +64,11 @@ export default connect(
state => ({ state => ({
account: state.web3connect.account, account: state.web3connect.account,
initialized: state.web3connect.initialized, initialized: state.web3connect.initialized,
web3: state.web3connect.web3, web3: state.web3connect.web3
}), }),
dispatch => ({ dispatch => ({
setAddresses: networkId => dispatch(setAddresses(networkId)), setAddresses: networkId => dispatch(setAddresses(networkId)),
initialize: () => dispatch(initialize()), initialize: () => dispatch(initialize()),
startWatching: () => dispatch(startWatching()), startWatching: () => dispatch(startWatching())
}), })
)(App); )(App)
@import "../variables.scss"; @import '../variables.scss';
.app { .app {
&__wrapper { &__wrapper {
...@@ -23,4 +23,4 @@ ...@@ -23,4 +23,4 @@
height: 100vh; height: 100vh;
@extend %col-nowrap; @extend %col-nowrap;
} }
\ No newline at end of file
import React from 'react'; import React from 'react'
import ReactDOM from 'react-dom'; import ReactDOM from 'react-dom'
import App from './App'; import App from './App'
it('renders without crashing', () => { it('renders without crashing', () => {
const div = document.createElement('div'); const div = document.createElement('div')
ReactDOM.render(<App />, div); ReactDOM.render(<App />, div)
ReactDOM.unmountComponentAtNode(div); ReactDOM.unmountComponentAtNode(div)
}); })
This diff is collapsed.
import React, { Component } from 'react'; import React, { Component } from 'react'
import { connect } from 'react-redux'; import { connect } from 'react-redux'
import PropTypes from 'prop-types'; import PropTypes from 'prop-types'
import { withRouter } from 'react-router-dom'; import { withRouter } from 'react-router-dom'
import { withNamespaces } from 'react-i18next'; import { withNamespaces } from 'react-i18next'
import {selectors, addPendingTx} from "../../ducks/web3connect"; import { selectors, addPendingTx } from '../../ducks/web3connect'
import classnames from "classnames"; import classnames from 'classnames'
import NavigationTabs from "../../components/NavigationTabs"; import NavigationTabs from '../../components/NavigationTabs'
import ModeSelector from "./ModeSelector"; import ModeSelector from './ModeSelector'
import AddressInputPanel from "../../components/AddressInputPanel"; import AddressInputPanel from '../../components/AddressInputPanel'
import OversizedPanel from "../../components/OversizedPanel"; import OversizedPanel from '../../components/OversizedPanel'
import FACTORY_ABI from "../../abi/factory"; import FACTORY_ABI from '../../abi/factory'
import {addExchange} from "../../ducks/addresses"; import { addExchange } from '../../ducks/addresses'
import ReactGA from "react-ga"; import ReactGA from 'react-ga'
class CreateExchange extends Component { class CreateExchange extends Component {
static propTypes = { static propTypes = {
...@@ -22,23 +22,27 @@ class CreateExchange extends Component { ...@@ -22,23 +22,27 @@ class CreateExchange extends Component {
isConnected: PropTypes.bool.isRequired, isConnected: PropTypes.bool.isRequired,
factoryAddress: PropTypes.string.isRequired, factoryAddress: PropTypes.string.isRequired,
exchangeAddresses: PropTypes.shape({ exchangeAddresses: PropTypes.shape({
fromToken: PropTypes.object.isRequired, fromToken: PropTypes.object.isRequired
}).isRequired, }).isRequired
}; }
constructor(props) { constructor(props) {
super(props); super(props)
const { match: { params: { tokenAddress } } } = this.props; const {
match: {
params: { tokenAddress }
}
} = this.props
this.state = { this.state = {
tokenAddress, tokenAddress,
label: '', label: '',
decimals: 0, decimals: 0
}; }
} }
validate() { validate() {
const { tokenAddress } = this.state; const { tokenAddress } = this.state
const { const {
t, t,
web3, web3,
...@@ -46,104 +50,104 @@ class CreateExchange extends Component { ...@@ -46,104 +50,104 @@ class CreateExchange extends Component {
selectors, selectors,
factoryAddress, factoryAddress,
exchangeAddresses: { fromToken }, exchangeAddresses: { fromToken },
addExchange, addExchange
} = this.props; } = this.props
let isValid = true; let isValid = true
let errorMessage = ''; let errorMessage = ''
if (!tokenAddress) { if (!tokenAddress) {
return { return {
isValid: false, isValid: false
}; }
} }
if (web3 && web3.utils && !web3.utils.isAddress(tokenAddress)) { if (web3 && web3.utils && !web3.utils.isAddress(tokenAddress)) {
return { return {
isValid: false, isValid: false,
errorMessage: t("invalidTokenAddress"), errorMessage: t('invalidTokenAddress')
}; }
} }
const { label, decimals } = selectors().getBalance(account, tokenAddress); const { label, decimals } = selectors().getBalance(account, tokenAddress)
const factory = new web3.eth.Contract(FACTORY_ABI, factoryAddress); const factory = new web3.eth.Contract(FACTORY_ABI, factoryAddress)
const exchangeAddress = fromToken[tokenAddress]; const exchangeAddress = fromToken[tokenAddress]
if (!exchangeAddress) { if (!exchangeAddress) {
factory.methods.getExchange(tokenAddress).call((err, data) => { factory.methods.getExchange(tokenAddress).call((err, data) => {
if (!err && data !== '0x0000000000000000000000000000000000000000') { if (!err && data !== '0x0000000000000000000000000000000000000000') {
addExchange({ label, tokenAddress, exchangeAddress: data }); addExchange({ label, tokenAddress, exchangeAddress: data })
} }
}); })
} else { } else {
errorMessage = t("exchangeExists", { label }); errorMessage = t('exchangeExists', { label })
} }
if (!label) { if (!label) {
errorMessage = t("invalidSymbol"); errorMessage = t('invalidSymbol')
} }
if (!decimals) { if (!decimals) {
errorMessage = t("invalidDecimals"); errorMessage = t('invalidDecimals')
} }
return { return {
isValid: isValid && !errorMessage, isValid: isValid && !errorMessage,
errorMessage, errorMessage
}; }
} }
onChange = tokenAddress => { onChange = tokenAddress => {
const { selectors, account, web3 } = this.props; const { selectors, account, web3 } = this.props
if (web3 && web3.utils && web3.utils.isAddress(tokenAddress)) { if (web3 && web3.utils && web3.utils.isAddress(tokenAddress)) {
const { label, decimals } = selectors().getBalance(account, tokenAddress); const { label, decimals } = selectors().getBalance(account, tokenAddress)
this.setState({ this.setState({
label, label,
decimals, decimals,
tokenAddress, tokenAddress
}); })
} else { } else {
this.setState({ this.setState({
label: '', label: '',
decimals: 0, decimals: 0,
tokenAddress, tokenAddress
}); })
} }
}; }
onCreateExchange = () => { onCreateExchange = () => {
const { tokenAddress } = this.state; const { tokenAddress } = this.state
const { account, web3, factoryAddress } = this.props; const { account, web3, factoryAddress } = this.props
if (web3 && web3.utils && !web3.utils.isAddress(tokenAddress)) { if (web3 && web3.utils && !web3.utils.isAddress(tokenAddress)) {
return; return
} }
const factory = new web3.eth.Contract(FACTORY_ABI, factoryAddress); const factory = new web3.eth.Contract(FACTORY_ABI, factoryAddress)
factory.methods.createExchange(tokenAddress).send({ from: account }, (err, data) => { factory.methods.createExchange(tokenAddress).send({ from: account }, (err, data) => {
if (!err) { if (!err) {
this.setState({ this.setState({
label: '', label: '',
decimals: 0, decimals: 0,
tokenAddress: '', tokenAddress: ''
}); })
this.props.addPendingTx(data); this.props.addPendingTx(data)
ReactGA.event({ ReactGA.event({
category: 'Pool', category: 'Pool',
action: 'CreateExchange', action: 'CreateExchange'
}); })
} }
}) })
}; }
renderSummary() { renderSummary() {
const { tokenAddress } = this.state; const { tokenAddress } = this.state
const { errorMessage } = this.validate(); const { errorMessage } = this.validate()
if (!tokenAddress) { if (!tokenAddress) {
return ( return (
<div className="create-exchange__summary-panel"> <div className="create-exchange__summary-panel">
<div className="create-exchange__summary-text">{this.props.t("enterTokenCont")}</div> <div className="create-exchange__summary-text">{this.props.t('enterTokenCont')}</div>
</div> </div>
) )
} }
...@@ -156,36 +160,36 @@ class CreateExchange extends Component { ...@@ -156,36 +160,36 @@ class CreateExchange extends Component {
) )
} }
return null; return null
} }
render() { render() {
const { tokenAddress } = this.state; const { tokenAddress } = this.state
const { t, isConnected, account, selectors, web3 } = this.props; const { t, isConnected, account, selectors, web3 } = this.props
const { isValid, errorMessage } = this.validate(); const { isValid, errorMessage } = this.validate()
let label, decimals; let label, decimals
if (web3 && web3.utils && web3.utils.isAddress(tokenAddress)) { if (web3 && web3.utils && web3.utils.isAddress(tokenAddress)) {
const { label: _label, decimals: _decimals } = selectors().getBalance(account, tokenAddress); const { label: _label, decimals: _decimals } = selectors().getBalance(account, tokenAddress)
label = _label; label = _label
decimals = _decimals; decimals = _decimals
} }
return ( return (
<div <div
key="content" key="content"
className={classnames('swap__content', { className={classnames('swap__content', {
'swap--inactive': !isConnected, 'swap--inactive': !isConnected
})} })}
> >
<NavigationTabs <NavigationTabs
className={classnames('header__navigation', { className={classnames('header__navigation', {
'header--inactive': !isConnected, 'header--inactive': !isConnected
})} })}
/> />
<ModeSelector title={t("createExchange")} /> <ModeSelector title={t('createExchange')} />
<AddressInputPanel <AddressInputPanel
title={t("tokenAddress")} title={t('tokenAddress')}
value={tokenAddress} value={tokenAddress}
onChange={this.onChange} onChange={this.onChange}
errorMessage={errorMessage} errorMessage={errorMessage}
...@@ -193,46 +197,47 @@ class CreateExchange extends Component { ...@@ -193,46 +197,47 @@ class CreateExchange extends Component {
<OversizedPanel hideBottom> <OversizedPanel hideBottom>
<div className="pool__summary-panel"> <div className="pool__summary-panel">
<div className="pool__exchange-rate-wrapper"> <div className="pool__exchange-rate-wrapper">
<span className="pool__exchange-rate">{t("label")}</span> <span className="pool__exchange-rate">{t('label')}</span>
<span>{label || ' - '}</span> <span>{label || ' - '}</span>
</div> </div>
<div className="pool__exchange-rate-wrapper"> <div className="pool__exchange-rate-wrapper">
<span className="swap__exchange-rate">{t("decimals")}</span> <span className="swap__exchange-rate">{t('decimals')}</span>
<span>{decimals || ' - '}</span> <span>{decimals || ' - '}</span>
</div> </div>
</div> </div>
</OversizedPanel> </OversizedPanel>
{ this.renderSummary() } {this.renderSummary()}
<div className="pool__cta-container"> <div className="pool__cta-container">
<button <button
className={classnames('pool__cta-btn', { className={classnames('pool__cta-btn', {
'swap--inactive': !isConnected, 'swap--inactive': !isConnected
})} })}
disabled={!isValid} disabled={!isValid}
onClick={this.onCreateExchange} onClick={this.onCreateExchange}
> >
{t("createExchange")} {t('createExchange')}
</button> </button>
</div> </div>
</div> </div>
); )
} }
} }
export default withRouter( export default withRouter(
connect( connect(
state => ({ state => ({
isConnected: Boolean(state.web3connect.account) && state.web3connect.networkId === (process.env.REACT_APP_NETWORK_ID||1), isConnected:
Boolean(state.web3connect.account) && state.web3connect.networkId === (process.env.REACT_APP_NETWORK_ID || 1),
account: state.web3connect.account, account: state.web3connect.account,
balances: state.web3connect.balances, balances: state.web3connect.balances,
web3: state.web3connect.web3, web3: state.web3connect.web3,
exchangeAddresses: state.addresses.exchangeAddresses, exchangeAddresses: state.addresses.exchangeAddresses,
factoryAddress: state.addresses.factoryAddress, factoryAddress: state.addresses.factoryAddress
}), }),
dispatch => ({ dispatch => ({
selectors: () => dispatch(selectors()), selectors: () => dispatch(selectors()),
addExchange: opts => dispatch(addExchange(opts)), addExchange: opts => dispatch(addExchange(opts)),
addPendingTx: id => dispatch(addPendingTx(id)), addPendingTx: id => dispatch(addPendingTx(id))
}) })
)(withNamespaces()(CreateExchange)) )(withNamespaces()(CreateExchange))
); )
import React, { Component } from 'react'; import React, { Component } from 'react'
import { withRouter } from 'react-router-dom'; import { withRouter } from 'react-router-dom'
import { withNamespaces } from 'react-i18next'; import { withNamespaces } from 'react-i18next'
import OversizedPanel from "../../components/OversizedPanel"; import OversizedPanel from '../../components/OversizedPanel'
import Dropdown from "../../assets/images/dropdown-blue.svg"; import Dropdown from '../../assets/images/dropdown-blue.svg'
import Modal from "../../components/Modal"; import Modal from '../../components/Modal'
import {CSSTransitionGroup} from "react-transition-group"; import { CSSTransitionGroup } from 'react-transition-group'
const ADD = 'Add Liquidity'; const ADD = 'Add Liquidity'
const REMOVE = 'Remove Liquidity'; const REMOVE = 'Remove Liquidity'
const CREATE = 'Create Exchange'; const CREATE = 'Create Exchange'
class ModeSelector extends Component { class ModeSelector extends Component {
state = { state = {
isShowingModal: false, isShowingModal: false,
selected: ADD, selected: ADD
}; }
changeView(view) { changeView(view) {
const { history } = this.props; const { history } = this.props
this.setState({ this.setState({
isShowingModal: false, isShowingModal: false,
selected: view, selected: view
}); })
switch (view) { switch (view) {
case ADD: case ADD:
return history.push('/add-liquidity'); return history.push('/add-liquidity')
case REMOVE: case REMOVE:
return history.push('/remove-liquidity'); return history.push('/remove-liquidity')
case CREATE: case CREATE:
return history.push('/create-exchange'); return history.push('/create-exchange')
default: default:
return; return
} }
} }
renderModal() { renderModal() {
if (!this.state.isShowingModal) { if (!this.state.isShowingModal) {
return; return
} }
return ( return (
...@@ -52,41 +52,27 @@ class ModeSelector extends Component { ...@@ -52,41 +52,27 @@ class ModeSelector extends Component {
transitionEnterTimeout={200} transitionEnterTimeout={200}
> >
<div className="pool-modal"> <div className="pool-modal">
<div <div className="pool-modal__item" onClick={() => this.changeView(ADD)}>
className="pool-modal__item" {this.props.t('addLiquidity')}
onClick={() => this.changeView(ADD)}
>
{this.props.t("addLiquidity")}
</div> </div>
<div <div className="pool-modal__item" onClick={() => this.changeView(REMOVE)}>
className="pool-modal__item" {this.props.t('removeLiquidity')}
onClick={() => this.changeView(REMOVE)}
>
{this.props.t("removeLiquidity")}
</div> </div>
<div <div className="pool-modal__item" onClick={() => this.changeView(CREATE)}>
className="pool-modal__item" {this.props.t('createExchange')}
onClick={() => this.changeView(CREATE)}
>
{this.props.t("createExchange")}
</div> </div>
</div> </div>
</CSSTransitionGroup> </CSSTransitionGroup>
</Modal> </Modal>
); )
} }
render() { render() {
return ( return (
<OversizedPanel hideTop> <OversizedPanel hideTop>
<div <div className="pool__liquidity-container" onClick={() => this.setState({ isShowingModal: true })}>
className="pool__liquidity-container" <span className="pool__liquidity-label">{this.props.title}</span>
onClick={() => this.setState({ isShowingModal: true })} <img src={Dropdown} alt="dropdown" />
>
<span className="pool__liquidity-label">
{this.props.title}
</span>
<img src={Dropdown} alt='dropdown' />
</div> </div>
{this.renderModal()} {this.renderModal()}
</OversizedPanel> </OversizedPanel>
...@@ -94,4 +80,4 @@ class ModeSelector extends Component { ...@@ -94,4 +80,4 @@ class ModeSelector extends Component {
} }
} }
export default withRouter(withNamespaces()(ModeSelector)); export default withRouter(withNamespaces()(ModeSelector))
This diff is collapsed.
import React, { Component } from 'react'; import React, { Component } from 'react'
import Header from '../../components/Header'; import Header from '../../components/Header'
import AddLiquidity from './AddLiquidity'; import AddLiquidity from './AddLiquidity'
import CreateExchange from './CreateExchange'; import CreateExchange from './CreateExchange'
import RemoveLiquidity from './RemoveLiquidity'; import RemoveLiquidity from './RemoveLiquidity'
import { Switch, Route } from 'react-router-dom'; import { Switch, Route } from 'react-router-dom'
import "./pool.scss"; import './pool.scss'
import MediaQuery from "react-responsive"; import MediaQuery from 'react-responsive'
import ReactGA from "react-ga"; import ReactGA from 'react-ga'
class Pool extends Component { class Pool extends Component {
componentWillMount() { componentWillMount() {
ReactGA.pageview(window.location.pathname + window.location.search); ReactGA.pageview(window.location.pathname + window.location.search)
} }
render() { render() {
return ( return (
...@@ -25,8 +24,8 @@ class Pool extends Component { ...@@ -25,8 +24,8 @@ class Pool extends Component {
<Route exact path="/create-exchange/:tokenAddress?" component={CreateExchange} /> <Route exact path="/create-exchange/:tokenAddress?" component={CreateExchange} />
</Switch> </Switch>
</div> </div>
); )
} }
} }
export default Pool; export default Pool
@import "../../variables.scss"; @import '../../variables.scss';
.pool { .pool {
@extend %col-nowrap; @extend %col-nowrap;
...@@ -8,16 +8,16 @@ ...@@ -8,16 +8,16 @@
&__liquidity-container { &__liquidity-container {
@extend %row-nowrap; @extend %row-nowrap;
align-items: center; align-items: center;
font-size: .75rem; font-size: 0.75rem;
padding: .625rem 1rem; padding: 0.625rem 1rem;
font-size: .75rem; font-size: 0.75rem;
color: $royal-blue; color: $royal-blue;
font-weight: 500; font-weight: 500;
cursor: pointer; cursor: pointer;
img { img {
height: .75rem; height: 0.75rem;
width: .75rem; width: 0.75rem;
} }
} }
...@@ -38,8 +38,8 @@ ...@@ -38,8 +38,8 @@
@extend %row-nowrap; @extend %row-nowrap;
align-items: center; align-items: center;
color: $dove-gray; color: $dove-gray;
font-size: .75rem; font-size: 0.75rem;
padding: .25rem 1rem 0; padding: 0.25rem 1rem 0;
} }
&__exchange-rate { &__exchange-rate {
...@@ -64,25 +64,25 @@ ...@@ -64,25 +64,25 @@
&__new-exchange-warning { &__new-exchange-warning {
padding: 1rem; padding: 1rem;
margin-bottom: 2rem; margin-bottom: 2rem;
border: 1px solid rgba($pizazz-orange, .4); border: 1px solid rgba($pizazz-orange, 0.4);
background-color: rgba($pizazz-orange, .1); background-color: rgba($pizazz-orange, 0.1);
border-radius: 1rem; border-radius: 1rem;
} }
&__new-exchange-warning-text { &__new-exchange-warning-text {
font-size: .75rem; font-size: 0.75rem;
line-height: 1rem; line-height: 1rem;
text-align: center; text-align: center;
&:first-child { &:first-child {
padding-bottom: .3rem; padding-bottom: 0.3rem;
font-weight: 500; font-weight: 500;
} }
} }
&__summary-item { &__summary-item {
&:not(:last-child) { &:not(:last-child) {
margin-bottom: .5rem; margin-bottom: 0.5rem;
} }
} }
} }
...@@ -97,9 +97,9 @@ ...@@ -97,9 +97,9 @@
border-top-left-radius: 1rem; border-top-left-radius: 1rem;
border-top-right-radius: 1rem; border-top-right-radius: 1rem;
transition: 250ms ease-in-out; transition: 250ms ease-in-out;
padding: 1rem 0 .5rem; padding: 1rem 0 0.5rem;
@media only screen and (min-width : 768px) { @media only screen and (min-width: 768px) {
max-width: 560px; max-width: 560px;
position: absolute; position: absolute;
margin-left: auto; margin-left: auto;
...@@ -149,9 +149,9 @@ ...@@ -149,9 +149,9 @@
} }
&__summary-text { &__summary-text {
font-size: .75rem; font-size: 0.75rem;
} }
&--error { &--error {
color: $salmon-red; color: $salmon-red;
} }
...@@ -166,7 +166,7 @@ ...@@ -166,7 +166,7 @@
&__output-text { &__output-text {
font-size: 1.25rem; font-size: 1.25rem;
line-height: 1.5rem; line-height: 1.5rem;
padding: 1rem .75rem; padding: 1rem 0.75rem;
} }
&__output-plus { &__output-plus {
...@@ -174,4 +174,4 @@ ...@@ -174,4 +174,4 @@
line-height: 1.5rem; line-height: 1.5rem;
padding: 1rem 0; padding: 1rem 0;
} }
} }
\ No newline at end of file
This diff is collapsed.
@import "../../variables.scss"; @import '../../variables.scss';
.send { .send {
@extend %col-nowrap; @extend %col-nowrap;
......
This diff is collapsed.
@import "../../variables.scss"; @import '../../variables.scss';
.swap { .swap {
@extend %col-nowrap; @extend %col-nowrap;
...@@ -6,22 +6,22 @@ ...@@ -6,22 +6,22 @@
background-color: $white; background-color: $white;
&--inactive { &--inactive {
opacity: .5; opacity: 0.5;
} }
&__content { &__content {
padding: 1rem .75rem; padding: 1rem 0.75rem;
flex: 1 1 auto; flex: 1 1 auto;
height: 0; height: 0;
overflow-y: auto; overflow-y: auto;
} }
&__down-arrow { &__down-arrow {
width: .625rem; width: 0.625rem;
height: .625rem; height: 0.625rem;
position: relative; position: relative;
z-index: 200; z-index: 200;
padding: .875rem; padding: 0.875rem;
&--clickable { &--clickable {
cursor: pointer; cursor: pointer;
...@@ -38,8 +38,8 @@ ...@@ -38,8 +38,8 @@
@extend %row-nowrap; @extend %row-nowrap;
align-items: center; align-items: center;
color: $dove-gray; color: $dove-gray;
font-size: .75rem; font-size: 0.75rem;
padding: .5rem 1rem; padding: 0.5rem 1rem;
} }
&__exchange-rate { &__exchange-rate {
......
...@@ -13,28 +13,26 @@ const isLocalhost = Boolean( ...@@ -13,28 +13,26 @@ const isLocalhost = Boolean(
// [::1] is the IPv6 localhost address. // [::1] is the IPv6 localhost address.
window.location.hostname === '[::1]' || window.location.hostname === '[::1]' ||
// 127.0.0.1/8 is considered localhost for IPv4. // 127.0.0.1/8 is considered localhost for IPv4.
window.location.hostname.match( window.location.hostname.match(/^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/)
/^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/ )
)
);
export default function register() { export default function register() {
if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) { if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
// The URL constructor is available in all browsers that support SW. // The URL constructor is available in all browsers that support SW.
const publicUrl = new URL(process.env.PUBLIC_URL, window.location); const publicUrl = new URL(process.env.PUBLIC_URL, window.location)
if (publicUrl.origin !== window.location.origin) { if (publicUrl.origin !== window.location.origin) {
// Our service worker won't work if PUBLIC_URL is on a different origin // Our service worker won't work if PUBLIC_URL is on a different origin
// from what our page is served on. This might happen if a CDN is used to // from what our page is served on. This might happen if a CDN is used to
// serve assets; see https://github.com/facebookincubator/create-react-app/issues/2374 // serve assets; see https://github.com/facebookincubator/create-react-app/issues/2374
return; return
} }
window.addEventListener('load', () => { window.addEventListener('load', () => {
const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`; const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`
if (isLocalhost) { if (isLocalhost) {
// This is running on localhost. Lets check if a service worker still exists or not. // This is running on localhost. Lets check if a service worker still exists or not.
checkValidServiceWorker(swUrl); checkValidServiceWorker(swUrl)
// Add some additional logging to localhost, pointing developers to the // Add some additional logging to localhost, pointing developers to the
// service worker/PWA documentation. // service worker/PWA documentation.
...@@ -42,13 +40,13 @@ export default function register() { ...@@ -42,13 +40,13 @@ export default function register() {
console.log( console.log(
'This web app is being served cache-first by a service ' + 'This web app is being served cache-first by a service ' +
'worker. To learn more, visit https://goo.gl/SC7cgQ' 'worker. To learn more, visit https://goo.gl/SC7cgQ'
); )
}); })
} else { } else {
// Is not local host. Just register service worker // Is not local host. Just register service worker
registerValidSW(swUrl); registerValidSW(swUrl)
} }
}); })
} }
} }
...@@ -57,7 +55,7 @@ function registerValidSW(swUrl) { ...@@ -57,7 +55,7 @@ function registerValidSW(swUrl) {
.register(swUrl) .register(swUrl)
.then(registration => { .then(registration => {
registration.onupdatefound = () => { registration.onupdatefound = () => {
const installingWorker = registration.installing; const installingWorker = registration.installing
installingWorker.onstatechange = () => { installingWorker.onstatechange = () => {
if (installingWorker.state === 'installed') { if (installingWorker.state === 'installed') {
if (navigator.serviceWorker.controller) { if (navigator.serviceWorker.controller) {
...@@ -65,20 +63,20 @@ function registerValidSW(swUrl) { ...@@ -65,20 +63,20 @@ function registerValidSW(swUrl) {
// the fresh content will have been added to the cache. // the fresh content will have been added to the cache.
// It's the perfect time to display a "New content is // It's the perfect time to display a "New content is
// available; please refresh." message in your web app. // available; please refresh." message in your web app.
console.log('New content is available; please refresh.'); console.log('New content is available; please refresh.')
} else { } else {
// At this point, everything has been precached. // At this point, everything has been precached.
// It's the perfect time to display a // It's the perfect time to display a
// "Content is cached for offline use." message. // "Content is cached for offline use." message.
console.log('Content is cached for offline use.'); console.log('Content is cached for offline use.')
} }
} }
}; }
}; }
}) })
.catch(error => { .catch(error => {
console.error('Error during service worker registration:', error); console.error('Error during service worker registration:', error)
}); })
} }
function checkValidServiceWorker(swUrl) { function checkValidServiceWorker(swUrl) {
...@@ -86,32 +84,27 @@ function checkValidServiceWorker(swUrl) { ...@@ -86,32 +84,27 @@ function checkValidServiceWorker(swUrl) {
fetch(swUrl) fetch(swUrl)
.then(response => { .then(response => {
// Ensure service worker exists, and that we really are getting a JS file. // Ensure service worker exists, and that we really are getting a JS file.
if ( if (response.status === 404 || response.headers.get('content-type').indexOf('javascript') === -1) {
response.status === 404 ||
response.headers.get('content-type').indexOf('javascript') === -1
) {
// No service worker found. Probably a different app. Reload the page. // No service worker found. Probably a different app. Reload the page.
navigator.serviceWorker.ready.then(registration => { navigator.serviceWorker.ready.then(registration => {
registration.unregister().then(() => { registration.unregister().then(() => {
window.location.reload(); window.location.reload()
}); })
}); })
} else { } else {
// Service worker found. Proceed as normal. // Service worker found. Proceed as normal.
registerValidSW(swUrl); registerValidSW(swUrl)
} }
}) })
.catch(() => { .catch(() => {
console.log( console.log('No internet connection found. App is running in offline mode.')
'No internet connection found. App is running in offline mode.' })
);
});
} }
export function unregister() { export function unregister() {
if ('serviceWorker' in navigator) { if ('serviceWorker' in navigator) {
navigator.serviceWorker.ready.then(registration => { navigator.serviceWorker.ready.then(registration => {
registration.unregister(); registration.unregister()
}); })
} }
} }
import store from './store.dev'; import store from './store.dev'
export default store; export default store
\ No newline at end of file
export default {}; export default {}
import { applyMiddleware, compose, createStore } from 'redux'; import { applyMiddleware, compose, createStore } from 'redux'
import thunk from 'redux-thunk' import thunk from 'redux-thunk'
import initialState from './initial-state'; import initialState from './initial-state'
import reducer from '../ducks'; import reducer from '../ducks'
const middleware = [thunk]; const middleware = [thunk]
const enhancers = []; const enhancers = []
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose; const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose
const store = createStore( const store = createStore(reducer, initialState, composeEnhancers(applyMiddleware(...middleware), ...enhancers))
reducer,
initialState,
composeEnhancers(
applyMiddleware(...middleware),
...enhancers,
)
);
export default store; export default store
\ No newline at end of file
...@@ -2,26 +2,26 @@ $white: #fff; ...@@ -2,26 +2,26 @@ $white: #fff;
$black: #000; $black: #000;
// Gray // Gray
$concrete-gray: #FAFAFA; $concrete-gray: #fafafa;
$mercury-gray: #E1E1E1; $mercury-gray: #e1e1e1;
$silver-gray: #C4C4C4; $silver-gray: #c4c4c4;
$chalice-gray: #AEAEAE; $chalice-gray: #aeaeae;
$dove-gray: #737373; $dove-gray: #737373;
$mine-shaft-gray: #2B2B2B; $mine-shaft-gray: #2b2b2b;
// Blue // Blue
$zumthor-blue: #EBF4FF; $zumthor-blue: #ebf4ff;
$malibu-blue: #5CA2FF; $malibu-blue: #5ca2ff;
$royal-blue: #2F80ED; $royal-blue: #2f80ed;
// Purple // Purple
$wisteria-purple: #DC6BE5; $wisteria-purple: #dc6be5;
// Red // Red
$salmon-red: #FF6871; $salmon-red: #ff6871;
// Orange // Orange
$pizazz-orange: #FF8F05; $pizazz-orange: #ff8f05;
%col-nowrap { %col-nowrap {
display: flex; display: flex;
...@@ -57,7 +57,6 @@ $pizazz-orange: #FF8F05; ...@@ -57,7 +57,6 @@ $pizazz-orange: #FF8F05;
&:disabled { &:disabled {
background-color: $mercury-gray; background-color: $mercury-gray;
} }
} }
%borderless-input { %borderless-input {
...@@ -68,7 +67,6 @@ $pizazz-orange: #FF8F05; ...@@ -68,7 +67,6 @@ $pizazz-orange: #FF8F05;
flex: 1 1 auto; flex: 1 1 auto;
width: 0; width: 0;
&::placeholder { &::placeholder {
color: $mercury-gray; color: $mercury-gray;
} }
......
...@@ -9966,6 +9966,11 @@ preserve@^0.2.0: ...@@ -9966,6 +9966,11 @@ preserve@^0.2.0:
resolved "https://registry.yarnpkg.com/preserve/-/preserve-0.2.0.tgz#815ed1f6ebc65926f865b310c0713bcb3315ce4b" resolved "https://registry.yarnpkg.com/preserve/-/preserve-0.2.0.tgz#815ed1f6ebc65926f865b310c0713bcb3315ce4b"
integrity sha1-gV7R9uvGWSb4ZbMQwHE7yzMVzks= integrity sha1-gV7R9uvGWSb4ZbMQwHE7yzMVzks=
prettier@^1.17.0:
version "1.17.0"
resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.17.0.tgz#53b303676eed22cc14a9f0cec09b477b3026c008"
integrity sha512-sXe5lSt2WQlCbydGETgfm1YBShgOX4HxQkFPvbxkcwgDvGDeqVau8h+12+lmSVlP3rHPz0oavfddSZg/q+Szjw==
pretty-bytes@^4.0.2: pretty-bytes@^4.0.2:
version "4.0.2" version "4.0.2"
resolved "https://registry.yarnpkg.com/pretty-bytes/-/pretty-bytes-4.0.2.tgz#b2bf82e7350d65c6c33aa95aaa5a4f6327f61cd9" resolved "https://registry.yarnpkg.com/pretty-bytes/-/pretty-bytes-4.0.2.tgz#b2bf82e7350d65c6c33aa95aaa5a4f6327f61cd9"
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment