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