Merge in develop, update a lotta modules.

This commit is contained in:
Will O'Beirne 2018-03-07 17:52:44 -05:00
commit 3e76edf0c8
No known key found for this signature in database
GPG Key ID: 44C190DB5DEAF9F6
186 changed files with 2068 additions and 1474 deletions

View File

@ -1,5 +1,5 @@
import React, { Component } from 'react';
import { Provider } from 'react-redux';
import { Provider, connect } from 'react-redux';
import { withRouter, Switch, Redirect, HashRouter, Route, BrowserRouter } from 'react-router-dom';
// Components
import Contracts from 'containers/Tabs/Contracts';
@ -15,27 +15,41 @@ import PageNotFound from 'components/PageNotFound';
import LogOutPrompt from 'components/LogOutPrompt';
import { TitleBar } from 'components/ui';
import { Store } from 'redux';
import { pollOfflineStatus } from 'actions/config';
import { pollOfflineStatus, TPollOfflineStatus } from 'actions/config';
import { AppState } from 'reducers';
import { RouteNotFound } from 'components/RouteNotFound';
import { RedirectWithQuery } from 'components/RedirectWithQuery';
import 'what-input';
import { setUnitMeta, TSetUnitMeta } from 'actions/transaction';
import { getNetworkUnit } from 'selectors/config';
interface Props {
interface OwnProps {
store: Store<AppState>;
}
interface StateProps {
networkUnit: string;
}
interface DispatchProps {
pollOfflineStatus: TPollOfflineStatus;
setUnitMeta: TSetUnitMeta;
}
type Props = OwnProps & StateProps & DispatchProps;
interface State {
error: Error | null;
}
export default class Root extends Component<Props, State> {
class RootClass extends Component<Props, State> {
public state = {
error: null
};
public componentDidMount() {
this.props.store.dispatch(pollOfflineStatus());
this.props.pollOfflineStatus();
this.props.setUnitMeta(this.props.networkUnit);
}
public componentDidCatch(error: Error) {
@ -134,3 +148,14 @@ const LegacyRoutes = withRouter(props => {
</Switch>
);
});
const mapStateToProps = (state: AppState) => {
return {
networkUnit: getNetworkUnit(state)
};
};
export default connect(mapStateToProps, {
pollOfflineStatus,
setUnitMeta
})(RootClass);

View File

@ -48,6 +48,14 @@ export function changeNodeIntent(payload: string): interfaces.ChangeNodeIntentAc
};
}
export type TChangeNodeForce = typeof changeNodeForce;
export function changeNodeForce(payload: string): interfaces.ChangeNodeForceAction {
return {
type: TypeKeys.CONFIG_NODE_CHANGE_FORCE,
payload
};
}
export type TAddCustomNode = typeof addCustomNode;
export function addCustomNode(
payload: interfaces.AddCustomNodeAction['payload']

View File

@ -36,6 +36,11 @@ export interface ChangeNodeIntentAction {
type: TypeKeys.CONFIG_NODE_CHANGE_INTENT;
payload: string;
}
/*** Force Change Node ***/
export interface ChangeNodeForceAction {
type: TypeKeys.CONFIG_NODE_CHANGE_FORCE;
payload: string;
}
/*** Add Custom Node ***/
export interface AddCustomNodeAction {

View File

@ -10,6 +10,7 @@ export enum TypeKeys {
CONFIG_NODE_WEB3_UNSET = 'CONFIG_NODE_WEB3_UNSET',
CONFIG_NODE_CHANGE = 'CONFIG_NODE_CHANGE',
CONFIG_NODE_CHANGE_INTENT = 'CONFIG_NODE_CHANGE_INTENT',
CONFIG_NODE_CHANGE_FORCE = 'CONFIG_NODE_CHANGE_FORCE',
CONFIG_ADD_CUSTOM_NODE = 'CONFIG_ADD_CUSTOM_NODE',
CONFIG_REMOVE_CUSTOM_NODE = 'CONFIG_REMOVE_CUSTOM_NODE',

View File

@ -8,7 +8,7 @@ export interface Pairs {
}
export interface SwapInput {
id: string;
label: string;
amount: number | string;
}

View File

@ -81,7 +81,10 @@ const setGasPriceField = (payload: SetGasPriceFieldAction['payload']): SetGasPri
});
type TReset = typeof reset;
const reset = (): ResetAction => ({ type: TypeKeys.RESET });
const reset = (payload: ResetAction['payload'] = { include: {}, exclude: {} }): ResetAction => ({
type: TypeKeys.RESET,
payload
});
export {
TInputGasLimit,

View File

@ -5,23 +5,22 @@ import {
SetTokenToMetaAction
} from 'actions/transaction';
type TSetTokenBalance = typeof setTokenValue;
type TSetUnitMeta = typeof setUnitMeta;
type TSetTokenTo = typeof setTokenTo;
const setTokenTo = (payload: SetTokenToMetaAction['payload']): SetTokenToMetaAction => ({
export type TSetTokenTo = typeof setTokenTo;
export const setTokenTo = (payload: SetTokenToMetaAction['payload']): SetTokenToMetaAction => ({
type: TypeKeys.TOKEN_TO_META_SET,
payload
});
const setTokenValue = (payload: SetTokenValueMetaAction['payload']): SetTokenValueMetaAction => ({
export type TSetTokenValue = typeof setTokenValue;
export const setTokenValue = (
payload: SetTokenValueMetaAction['payload']
): SetTokenValueMetaAction => ({
type: TypeKeys.TOKEN_VALUE_META_SET,
payload
});
const setUnitMeta = (payload: SetUnitMetaAction['payload']): SetUnitMetaAction => ({
export type TSetUnitMeta = typeof setUnitMeta;
export const setUnitMeta = (payload: SetUnitMetaAction['payload']): SetUnitMetaAction => ({
type: TypeKeys.UNIT_META_SET,
payload
});
export { TSetUnitMeta, TSetTokenBalance, TSetTokenTo, setUnitMeta, setTokenValue, setTokenTo };

View File

@ -7,6 +7,9 @@ import { SignAction } from './sign';
import { SwapAction } from './swap';
import { CurrentAction } from './current';
import { SendEverythingAction } from './sendEverything';
import { State as FieldState } from 'reducers/transaction/fields';
import { State as MetaState } from 'reducers/transaction/meta';
import { State as SignState } from 'reducers/transaction/sign';
export * from './broadcast';
export * from './fields';
@ -19,6 +22,18 @@ export * from './sendEverything';
export interface ResetAction {
type: TypeKeys.RESET;
payload: {
include: {
fields?: (keyof FieldState)[];
meta?: (keyof MetaState)[];
sign?: (keyof SignState)[];
};
exclude: {
fields?: (keyof FieldState)[];
meta?: (keyof MetaState)[];
sign?: (keyof SignState)[];
};
};
}
export type TransactionAction =

Binary file not shown.

Before

Width:  |  Height:  |  Size: 51 KiB

After

Width:  |  Height:  |  Size: 47 KiB

View File

@ -1,3 +0,0 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version='1.1' xmlns='http://www.w3.org/2000/svg' x='0px' y='0px' width='20px' height='20px' viewBox='0 0 79.536 79.536' style='enable-background:new 0 0 79.536 79.536;' xml:space='preserve'><path fill='#999' d='M39.769,0C17.8,0,0,17.8,0,39.768c0,21.965,17.8,39.768,39.769,39.768 c21.965,0,39.768-17.803,39.768-39.768C79.536,17.8,61.733,0,39.769,0z M34.142,58.513L15.397,39.768l7.498-7.498l11.247,11.247 l22.497-22.493l7.498,7.498L34.142,58.513z'/></svg>

Before

Width:  |  Height:  |  Size: 602 B

View File

@ -1,3 +0,0 @@
<?xml version="1.0" encoding="iso-8859-1"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="20px" height="20px" viewBox="0 0 528.899 528.899" style="enable-background:new 0 0 528.899 528.899;" xml:space="preserve"><path fill="#21a4ce" d="M328.883,89.125l107.59,107.589l-272.34,272.34L56.604,361.465L328.883,89.125z M518.113,63.177l-47.981-47.981 c-18.543-18.543-48.653-18.543-67.259,0l-45.961,45.961l107.59,107.59l53.611-53.611 C532.495,100.753,532.495,77.559,518.113,63.177z M0.3,512.69c-1.958,8.812,5.998,16.708,14.811,14.565l119.891-29.069 L27.473,390.597L0.3,512.69z"/></svg>

Before

Width:  |  Height:  |  Size: 761 B

View File

@ -1 +0,0 @@
<svg width="20" height="20" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 92 92"><path fill="#ECECEC" d="M45.386.004C19.983.344-.333 21.215.005 46.619c.34 25.393 21.209 45.715 46.611 45.377 25.398-.342 45.718-21.213 45.38-46.615-.34-25.395-21.21-45.716-46.61-45.377zM45.25 74l-.254-.004c-3.912-.116-6.67-2.998-6.559-6.852.109-3.788 2.934-6.538 6.717-6.538l.227.004c4.021.119 6.748 2.972 6.635 6.937C51.904 71.346 49.123 74 45.25 74zm16.455-32.659c-.92 1.307-2.943 2.93-5.492 4.916l-2.807 1.938c-1.541 1.198-2.471 2.325-2.82 3.434-.275.873-.41 1.104-.434 2.88l-.004.451H39.43l.031-.907c.131-3.728.223-5.921 1.768-7.733 2.424-2.846 7.771-6.289 7.998-6.435.766-.577 1.412-1.234 1.893-1.936 1.125-1.551 1.623-2.772 1.623-3.972a7.74 7.74 0 0 0-1.471-4.576c-.939-1.323-2.723-1.993-5.303-1.993-2.559 0-4.311.812-5.359 2.478-1.078 1.713-1.623 3.512-1.623 5.35v.457H27.936l.02-.477c.285-6.769 2.701-11.643 7.178-14.487C37.947 18.918 41.447 18 45.531 18c5.346 0 9.859 1.299 13.412 3.861 3.6 2.596 5.426 6.484 5.426 11.556 0 2.837-.896 5.502-2.664 7.924z"/></svg>

Before

Width:  |  Height:  |  Size: 1.0 KiB

View File

@ -1 +1 @@
<svg width="20" height="20" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 92 92"><path fill="#0e97c0" d="M45.386.004C19.983.344-.333 21.215.005 46.619c.34 25.393 21.209 45.715 46.611 45.377 25.398-.342 45.718-21.213 45.38-46.615-.34-25.395-21.21-45.716-46.61-45.377zM45.25 74l-.254-.004c-3.912-.116-6.67-2.998-6.559-6.852.109-3.788 2.934-6.538 6.717-6.538l.227.004c4.021.119 6.748 2.972 6.635 6.937C51.904 71.346 49.123 74 45.25 74zm16.455-32.659c-.92 1.307-2.943 2.93-5.492 4.916l-2.807 1.938c-1.541 1.198-2.471 2.325-2.82 3.434-.275.873-.41 1.104-.434 2.88l-.004.451H39.43l.031-.907c.131-3.728.223-5.921 1.768-7.733 2.424-2.846 7.771-6.289 7.998-6.435.766-.577 1.412-1.234 1.893-1.936 1.125-1.551 1.623-2.772 1.623-3.972a7.74 7.74 0 0 0-1.471-4.576c-.939-1.323-2.723-1.993-5.303-1.993-2.559 0-4.311.812-5.359 2.478-1.078 1.713-1.623 3.512-1.623 5.35v.457H27.936l.02-.477c.285-6.769 2.701-11.643 7.178-14.487C37.947 18.918 41.447 18 45.531 18c5.346 0 9.859 1.299 13.412 3.861 3.6 2.596 5.426 6.484 5.426 11.556 0 2.837-.896 5.502-2.664 7.924z"/></svg>
<svg width="20" height="20" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 92 92"><path fill="#007896" d="M45.386.004C19.983.344-.333 21.215.005 46.619c.34 25.393 21.209 45.715 46.611 45.377 25.398-.342 45.718-21.213 45.38-46.615-.34-25.395-21.21-45.716-46.61-45.377zM45.25 74l-.254-.004c-3.912-.116-6.67-2.998-6.559-6.852.109-3.788 2.934-6.538 6.717-6.538l.227.004c4.021.119 6.748 2.972 6.635 6.937C51.904 71.346 49.123 74 45.25 74zm16.455-32.659c-.92 1.307-2.943 2.93-5.492 4.916l-2.807 1.938c-1.541 1.198-2.471 2.325-2.82 3.434-.275.873-.41 1.104-.434 2.88l-.004.451H39.43l.031-.907c.131-3.728.223-5.921 1.768-7.733 2.424-2.846 7.771-6.289 7.998-6.435.766-.577 1.412-1.234 1.893-1.936 1.125-1.551 1.623-2.772 1.623-3.972a7.74 7.74 0 0 0-1.471-4.576c-.939-1.323-2.723-1.993-5.303-1.993-2.559 0-4.311.812-5.359 2.478-1.078 1.713-1.623 3.512-1.623 5.35v.457H27.936l.02-.477c.285-6.769 2.701-11.643 7.178-14.487C37.947 18.918 41.447 18 45.531 18c5.346 0 9.859 1.299 13.412 3.861 3.6 2.596 5.426 6.484 5.426 11.556 0 2.837-.896 5.502-2.664 7.924z"/></svg>

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@ -1 +0,0 @@
<svg version="1.1" width="20px" height="20px" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 92 92" style="enable-background:new 0 0 92 92;" xml:space="preserve"><path fill="#2bb2dd" d="M45.386,0.004C19.983,0.344-0.333,21.215,0.005,46.619c0.34,25.393,21.209,45.715,46.611,45.377 c25.398-0.342,45.718-21.213,45.38-46.615C91.656,19.986,70.786-0.335,45.386,0.004z M45.25,74l-0.254-0.004 c-3.912-0.116-6.67-2.998-6.559-6.852c0.109-3.788,2.934-6.538,6.717-6.538l0.227,0.004c4.021,0.119,6.748,2.972,6.635,6.937 C51.904,71.346,49.123,74,45.25,74z M61.705,41.341c-0.92,1.307-2.943,2.93-5.492,4.916l-2.807,1.938 c-1.541,1.198-2.471,2.325-2.82,3.434c-0.275,0.873-0.41,1.104-0.434,2.88l-0.004,0.451H39.43l0.031-0.907 c0.131-3.728,0.223-5.921,1.768-7.733c2.424-2.846,7.771-6.289,7.998-6.435c0.766-0.577,1.412-1.234,1.893-1.936 c1.125-1.551,1.623-2.772,1.623-3.972c0-1.665-0.494-3.205-1.471-4.576c-0.939-1.323-2.723-1.993-5.303-1.993 c-2.559,0-4.311,0.812-5.359,2.478c-1.078,1.713-1.623,3.512-1.623,5.35v0.457H27.936l0.02-0.477 c0.285-6.769,2.701-11.643,7.178-14.487C37.947,18.918,41.447,18,45.531,18c5.346,0,9.859,1.299,13.412,3.861 c3.6,2.596,5.426,6.484,5.426,11.556C64.369,36.254,63.473,38.919,61.705,41.341z"/></svg>

Before

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -1,3 +0,0 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version='1.1' xmlns='http://www.w3.org/2000/svg' x='0px' y='0px' width='20px' height='20px' viewBox='0 0 511.626 511.626' style='enable-background:new 0 0 511.626 511.626;' xml:space='preserve'><path fill='#f0ad4e' d='M505.918,236.117c-26.651-43.587-62.485-78.609-107.497-105.065c-45.015-26.457-92.549-39.687-142.608-39.687 c-50.059,0-97.595,13.225-142.61,39.687C68.187,157.508,32.355,192.53,5.708,236.117C1.903,242.778,0,249.345,0,255.818 c0,6.473,1.903,13.04,5.708,19.699c26.647,43.589,62.479,78.614,107.495,105.064c45.015,26.46,92.551,39.68,142.61,39.68 c50.06,0,97.594-13.176,142.608-39.536c45.012-26.361,80.852-61.432,107.497-105.208c3.806-6.659,5.708-13.223,5.708-19.699 C511.626,249.345,509.724,242.778,505.918,236.117z M194.568,158.03c17.034-17.034,37.447-25.554,61.242-25.554 c3.805,0,7.043,1.336,9.709,3.999c2.662,2.664,4,5.901,4,9.707c0,3.809-1.338,7.044-3.994,9.704 c-2.662,2.667-5.902,3.999-9.708,3.999c-16.368,0-30.362,5.808-41.971,17.416c-11.613,11.615-17.416,25.603-17.416,41.971 c0,3.811-1.336,7.044-3.999,9.71c-2.667,2.668-5.901,3.999-9.707,3.999c-3.809,0-7.044-1.334-9.71-3.999 c-2.667-2.666-3.999-5.903-3.999-9.71C169.015,195.482,177.535,175.065,194.568,158.03z M379.867,349.04 c-38.164,23.12-79.514,34.687-124.054,34.687c-44.539,0-85.889-11.56-124.051-34.687s-69.901-54.2-95.215-93.222 c28.931-44.921,65.19-78.518,108.777-100.783c-11.61,19.792-17.417,41.207-17.417,64.236c0,35.216,12.517,65.329,37.544,90.362 s55.151,37.544,90.362,37.544c35.214,0,65.329-12.518,90.362-37.544s37.545-55.146,37.545-90.362 c0-23.029-5.808-44.447-17.419-64.236c43.585,22.265,79.846,55.865,108.776,100.783C449.767,294.84,418.031,325.913,379.867,349.04 z'/></svg>

Before

Width:  |  Height:  |  Size: 1.8 KiB

View File

@ -1,8 +0,0 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version='1.1' xmlns='http://www.w3.org/2000/svg' width='20px' height='20px'
xmlns:xlink='http://www.w3.org/1999/xlink' x='0px' y='0px' viewBox='0 0 27.965 27.965'
style='enable-background:new 0 0 27.965 27.965;'>
<path fill='#999'
d='M13.98,0C6.259,0,0,6.261,0,13.983c0,7.721,6.259,13.982,13.98,13.982c7.725,0,13.985-6.262,13.985-13.982 C27.965,6.261,21.705,0,13.98,0z M19.992,17.769l-2.227,2.224c0,0-3.523-3.78-3.786-3.78c-0.259,0-3.783,3.78-3.783,3.78 l-2.228-2.224c0,0,3.784-3.472,3.784-3.781c0-0.314-3.784-3.787-3.784-3.787l2.228-2.229c0,0,3.553,3.782,3.783,3.782 c0.232,0,3.786-3.782,3.786-3.782l2.227,2.229c0,0-3.785,3.523-3.785,3.787C16.207,14.239,19.992,17.769,19.992,17.769z'/>
</svg>

Before

Width:  |  Height:  |  Size: 866 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

View File

@ -8,7 +8,7 @@
<g id="Artboard" fill-rule="nonzero">
<g id="logo-mycrypto">
<g id="Group">
<circle id="Oval" fill="#0e97c0" cx="120.18" cy="121" r="120.18"></circle>
<circle id="Oval" fill="#007896" cx="120.18" cy="121" r="120.18"></circle>
<g transform="translate(39.000000, 51.000000)" fill="#FFFFFF" id="Shape">
<path d="M93.7,100.26 C90.4608521,103.553832 86.034676,105.408954 81.415,105.408954 C76.795324,105.408954 72.3691479,103.553832 69.13,100.26 L53.47,84.43 C51.4545763,82.3796602 48.700035,81.2248104 45.825,81.2248104 C42.949965,81.2248104 40.1954237,82.3796602 38.18,84.43 C31.3962777,91.3322169 31.3962777,102.397783 38.18,109.3 L69.18,140.59 C72.4191479,143.883832 76.845324,145.738954 81.465,145.738954 C86.084676,145.738954 90.5108521,143.883832 93.75,140.59 L124.67,109.26 C131.453722,102.357783 131.453722,91.2922169 124.67,84.39 C122.654576,82.3396602 119.900035,81.1848104 117.025,81.1848104 C114.149965,81.1848104 111.395424,82.3396602 109.38,84.39 L93.7,100.26 Z"></path>
<g>

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

View File

@ -0,0 +1,97 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="81px" height="120px" viewBox="0 0 81 120" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 48.2 (47327) - http://www.bohemiancoding.com/sketch -->
<title>Artboard</title>
<desc>Created with Sketch.</desc>
<defs>
<linearGradient x1="100.099854%" y1="-16.122731%" x2="44.1579795%" y2="77.6511455%" id="linearGradient-1">
<stop stop-color="#2B415B" offset="13.45%"></stop>
<stop stop-color="#3B5676" offset="37.62%"></stop>
<stop stop-color="#54769E" offset="69.23%"></stop>
<stop stop-color="#52749B" offset="79.01%"></stop>
<stop stop-color="#4D6C92" offset="86.14%"></stop>
<stop stop-color="#436082" offset="92.44%"></stop>
<stop stop-color="#364F6C" offset="98.22%"></stop>
<stop stop-color="#314863" offset="100%"></stop>
</linearGradient>
<linearGradient x1="99.7887646%" y1="61.4941149%" x2="-4.60729499%" y2="13.3460499%" id="linearGradient-2">
<stop stop-color="#54769E" offset="0%"></stop>
<stop stop-color="#53749C" offset="48.02%"></stop>
<stop stop-color="#4F6F95" offset="68.78%"></stop>
<stop stop-color="#486588" offset="84.23%"></stop>
<stop stop-color="#435F80" offset="90.95%"></stop>
</linearGradient>
<linearGradient x1="725.856983%" y1="50.0229457%" x2="-347.124022%" y2="50.0229457%" id="linearGradient-3">
<stop stop-color="#20344C" offset="25.39%"></stop>
<stop stop-color="#273D57" offset="40.72%"></stop>
<stop stop-color="#395373" offset="67.33%"></stop>
<stop stop-color="#54769E" offset="100%"></stop>
</linearGradient>
<linearGradient x1="-653.788268%" y1="50.0214562%" x2="455.494413%" y2="50.0214562%" id="linearGradient-4">
<stop stop-color="#54769E" offset="25.39%"></stop>
<stop stop-color="#4D6E93" offset="41.33%"></stop>
<stop stop-color="#3C5777" offset="68.97%"></stop>
<stop stop-color="#233850" offset="100%"></stop>
</linearGradient>
<linearGradient x1="50.0370086%" y1="-209.335792%" x2="49.9575834%" y2="235.965027%" id="linearGradient-5">
<stop stop-color="#54769E" offset="0.6545247%"></stop>
<stop stop-color="#507198" offset="19.93%"></stop>
<stop stop-color="#466488" offset="45.02%"></stop>
<stop stop-color="#354F6D" offset="73.18%"></stop>
<stop stop-color="#21354D" offset="100%"></stop>
</linearGradient>
<linearGradient x1="163.859644%" y1="-95.5767241%" x2="-53.1396713%" y2="173.049483%" id="linearGradient-6">
<stop stop-color="#54769E" offset="25.39%"></stop>
<stop stop-color="#4D6E93" offset="41.02%"></stop>
<stop stop-color="#3C5777" offset="68.13%"></stop>
<stop stop-color="#22364E" offset="100%"></stop>
</linearGradient>
<linearGradient x1="-44.1116757%" y1="-60.1463542%" x2="137.687762%" y2="146.957292%" id="linearGradient-7">
<stop stop-color="#54769E" offset="25.39%"></stop>
<stop stop-color="#4D6E93" offset="41.02%"></stop>
<stop stop-color="#3C5777" offset="68.13%"></stop>
<stop stop-color="#22364E" offset="100%"></stop>
</linearGradient>
<linearGradient x1="-1.43584837%" y1="31.6007047%" x2="127.701745%" y2="103.798097%" id="linearGradient-8">
<stop stop-color="#54769E" offset="26.64%"></stop>
<stop stop-color="#425E7F" offset="100%"></stop>
</linearGradient>
<linearGradient x1="-71.276129%" y1="-65.3137535%" x2="149.174581%" y2="96.3311507%" id="linearGradient-9">
<stop stop-color="#54769E" stop-opacity="0" offset="46.09%"></stop>
<stop stop-color="#52739A" stop-opacity="0.2156" offset="56.99%"></stop>
<stop stop-color="#4A698E" stop-opacity="0.4266" offset="67.64%"></stop>
<stop stop-color="#3D597B" stop-opacity="0.6356" offset="78.2%"></stop>
<stop stop-color="#2C435F" stop-opacity="0.8422" offset="88.63%"></stop>
<stop stop-color="#1B2E45" offset="96.61%"></stop>
</linearGradient>
<linearGradient x1="50.309375%" y1="295.997443%" x2="50.309375%" y2="-124.649242%" id="linearGradient-10">
<stop stop-color="#54769E" offset="25.39%"></stop>
<stop stop-color="#4D6E93" offset="41.02%"></stop>
<stop stop-color="#3C5777" offset="68.13%"></stop>
<stop stop-color="#22364E" offset="100%"></stop>
</linearGradient>
</defs>
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="Artboard" fill-rule="nonzero">
<g id="logo-shapeshift">
<polygon id="Shape" fill="#273C51" points="75.9045553 29.0113736 81 0.0349956255 59.7045553 8.88888889 36.3006508 8.88888889 15.0052061 0 20.1357918 29.0113736 15.3214751 45.1793526 19.8546638 48.0139983 0 65.3718285 0 81.67979 22.7010846 112.825897 44.1019523 119.965004 44.1370933 120 60.8642082 111.461067 60.8993492 111.426072 60.8993492 98.7926509 51.4112798 93.6132983 51.4112798 93.6132983 51.4112798 93.6132983 66.3110629 54.1032371 66.2056399 54.1032371 80.683731 45.1793526"></polygon>
<g id="Group">
<polygon id="Shape" fill="url(#linearGradient-1)" points="34.2584416 35.3372093 0 65.2325581 47.7233766 92.5465116 47.8987013 36.244186"></polygon>
<polygon id="Shape" fill="#466284" points="29.3844156 53.8255814 47.7584416 102.697674 47.8987013 56.5465116"></polygon>
<polygon id="Shape" fill="#354D6A" points="66.1675325 54 47.7584416 102.697674 47.8987013 56.5465116"></polygon>
<polygon id="Shape" fill="url(#linearGradient-2)" points="80.8246753 0.104651163 62.5558442 9.6627907 47.9337662 12.8372093 33.1714286 9.6627907 14.9727273 0.0697674419 21.2493506 28.9186047 15.2883117 45.1046512 36.187013 58.0116279 47.7935065 70.5697674 47.7935065 70.6744186 47.8636364 70.6046512 47.9337662 70.6744186 47.9337662 70.5697674 59.5402597 58.0116279 80.5090909 45.1046512 74.5480519 28.9534884"></polygon>
<polygon id="Shape" fill="url(#linearGradient-3)" points="80.8246753 0.104651163 75.7402597 28.9883721 80.5090909 45.1046512 74.5480519 28.9534884"></polygon>
<polygon id="Shape" fill="url(#linearGradient-4)" points="14.9727273 0.0697674419 20.0922078 28.9883721 15.2883117 45.1046512 21.2493506 28.9186047"></polygon>
<polygon id="Shape" fill="url(#linearGradient-5)" points="14.9727273 0.0697674419 36.2220779 8.93023256 59.5753247 8.93023256 80.8246753 0.104651163 62.5558442 9.6627907 47.9337662 12.8372093 33.1714286 9.6627907"></polygon>
<polygon id="Shape" fill="url(#linearGradient-6)" points="21.2493506 28.9186047 20.1974026 31.8139535 42.2532468 11.5813953"></polygon>
<polygon id="Shape" fill="url(#linearGradient-7)" points="74.5480519 28.9534884 53.6493506 11.5813953 75.5649351 31.6744186"></polygon>
<polygon id="Shape" fill="url(#linearGradient-8)" points="0 65.2325581 51.3 93.3837209 57.787013 109.534884 44.0415584 114.732558 22.6519481 112.534884 0 81.4883721"></polygon>
<polygon id="Shape" fill="#FFFFFF" points="42.4636364 88.5348837 22.7220779 112.465116 22.6519481 112.534884 44.0064935 119.651163 44.0415584 119.686047 60.7324675 111.174419 60.7675325 111.139535 60.7675325 98.5465116"></polygon>
<polygon id="Shape" fill="url(#linearGradient-9)" points="74.5480519 28.9534884 80.8246753 0.104651163 62.5558442 9.6627907 53.6493506 11.5813953"></polygon>
<polygon id="Shape" fill="#FFFFFF" points="47.8987013 70.6744186 42.112987 64.3604651 47.8987013 58.0116279 53.7194805 64.3604651"></polygon>
<polygon id="Shape" fill="url(#linearGradient-10)" points="47.3376623 12.6976744 47.9337662 12.8372093 48.4597403 12.7325581 47.8987013 49.5348837"></polygon>
</g>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 8.1 KiB

View File

@ -54,7 +54,7 @@
<path d="M82.77,744.06 L87.55,710.06 C87.8071361,708.226119 86.9908966,706.407071 85.45,705.38 C78.3717357,700.699197 75.198423,691.93208 77.6408657,683.805198 C80.0833084,675.678315 87.5640273,670.113147 96.05,670.11 C96.64,670.11 97.2,670.23 97.78,670.28 C88.9158298,664.439589 76.9954116,666.89083 71.1550005,675.755 C65.3145894,684.61917 67.7658301,696.539589 76.63,702.38 C78.1708966,703.407071 78.9871361,705.226119 78.73,707.06 L73.53,744.06 L82.77,744.06 Z" id="Shape" fill="#000000" opacity="0.1"></path>
<g id="mycrypto__circ__blue-on-navy__64" transform="translate(116.000000, 0.000000)">
<g id="Oval">
<use fill="#0E97C0" fill-rule="evenodd" xlink:href="#path-1"></use>
<use fill="#007896" fill-rule="evenodd" xlink:href="#path-1"></use>
<circle stroke="#000000" stroke-width="10" cx="145" cy="145" r="140"></circle>
</g>
<g id="Shape">
@ -69,4 +69,4 @@
</g>
</g>
</g>
</svg>
</svg>

Before

Width:  |  Height:  |  Size: 29 KiB

After

Width:  |  Height:  |  Size: 29 KiB

View File

@ -32,7 +32,7 @@
<rect id="Rectangle-path" fill="#FFCE00" x="40.72" y="256.52" width="16" height="48"></rect>
</g>
<g id="mycrypto__circ__blue-on-navy__64" transform="translate(216.000000, 191.000000)">
<circle id="Oval" fill="#0E97C0" cx="37" cy="37" r="37"></circle>
<circle id="Oval" fill="#007896" cx="37" cy="37" r="37"></circle>
<g id="Shape">
<use fill="#FFFFFF" fill-rule="evenodd" xlink:href="#path-1"></use>
<path stroke="#000000" stroke-width="2" d="M48.7644965,40.0113785 C49.7178355,40.010501 50.6345076,40.3752222 51.3145109,41.0314555 C53.5618297,43.2182135 53.5618297,46.7390341 51.3110373,48.9291558 L41.5069096,58.3771804 C40.4445637,59.4168045 38.9938635,60.0003162 37.4823951,59.9999999 C35.9705083,60.0003162 34.5198081,59.4168045 33.4616659,58.3812702 L23.6854891,48.9257921 C21.4381703,46.7390341 21.4381703,43.2182135 23.6838532,41.033051 C24.3665268,40.3657303 25.2975124,39.9945561 26.2579466,40.0000451 C27.2198046,39.9987497 28.1441246,40.3696137 28.8202797,41.0305404 L33.7601132,45.8246431 C34.7441958,46.7780599 36.0942246,47.317442 37.505938,47.317442 C38.9176514,47.317442 40.2676802,46.7780599 41.2528603,45.8235816 L46.2009116,41.0443696 C46.8793196,40.3812496 47.8031089,40.0104213 48.7644965,40.0113785 Z"></path>
@ -44,4 +44,4 @@
</g>
</g>
</g>
</svg>
</svg>

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 67 KiB

After

Width:  |  Height:  |  Size: 70 KiB

View File

@ -13,7 +13,7 @@
<mask id="mask-2" fill="white">
<use xlink:href="#path-1"></use>
</mask>
<circle stroke="#0E97C0" stroke-width="20" cx="170" cy="170" r="160"></circle>
<circle stroke="#007896" stroke-width="20" cx="170" cy="170" r="160"></circle>
<rect id="Rectangle" fill="#FFE14D" mask="url(#mask-2)" x="-1.13333333" y="1.13333333" width="173.4" height="340"></rect>
<rect id="Rectangle" fill="#FFCC33" mask="url(#mask-2)" x="171.133333" y="1.13333333" width="173.4" height="340"></rect>
<circle id="Oval-2" stroke="#333333" stroke-width="20" mask="url(#mask-2)" cx="170" cy="170" r="160"></circle>
@ -23,7 +23,7 @@
<mask id="mask-4" fill="white">
<use xlink:href="#path-3"></use>
</mask>
<circle stroke="#0E97C0" stroke-width="20" cx="170" cy="170" r="160"></circle>
<circle stroke="#007896" stroke-width="20" cx="170" cy="170" r="160"></circle>
<rect id="Rectangle" fill="#6EA6E8" mask="url(#mask-4)" x="-1.13333333" y="1.13333333" width="173.4" height="340"></rect>
<rect id="Rectangle" fill="#5C9BE4" mask="url(#mask-4)" x="171.133333" y="1.13333333" width="173.4" height="340"></rect>
<circle id="Oval-2" stroke="#333333" stroke-width="20" mask="url(#mask-4)" cx="170" cy="170" r="160"></circle>
@ -41,4 +41,4 @@
</g>
</g>
</g>
</svg>
</svg>

Before

Width:  |  Height:  |  Size: 3.9 KiB

After

Width:  |  Height:  |  Size: 3.9 KiB

View File

@ -1,6 +1,8 @@
import React from 'react';
import { AddressFieldFactory } from './AddressFieldFactory';
import { donationAddressMap } from 'config';
import translate from 'translations';
import { Input } from 'components/ui';
interface Props {
isReadOnly?: boolean;
@ -9,16 +11,20 @@ interface Props {
export const AddressField: React.SFC<Props> = ({ isReadOnly }) => (
<AddressFieldFactory
withProps={({ currentTo, isValid, onChange, readOnly }) => (
<React.Fragment>
<input
className={`form-control ${isValid ? 'is-valid' : 'is-invalid'}`}
type="text"
value={currentTo.raw}
placeholder={donationAddressMap.ETH}
readOnly={!!(isReadOnly || readOnly)}
onChange={onChange}
/>
</React.Fragment>
<div className="input-group-wrapper">
<label className="input-group">
<div className="input-group-header">{translate('SEND_addr')}</div>
<Input
className={`input-group-input ${isValid ? '' : 'invalid'}`}
type="text"
value={currentTo.raw}
placeholder={donationAddressMap.ETH}
readOnly={!!(isReadOnly || readOnly)}
spellCheck={false}
onChange={onChange}
/>
</label>
</div>
)}
/>
);

View File

@ -1,6 +1,5 @@
import React, { Component } from 'react';
import { Identicon, Spinner } from 'components/ui';
import translate from 'translations';
import { Query } from 'components/renderCbs';
import { ICurrentTo, getCurrentTo, isValidCurrentTo } from 'selectors/transaction';
import { connect } from 'react-redux';
@ -49,7 +48,6 @@ class AddressInputFactoryClass extends Component<Props> {
return (
<div className="row form-group">
<div className="col-xs-11">
<label>{translate('SEND_addr')}:</label>
<Query
params={['readOnly']}
withQuery={({ readOnly }) =>

View File

@ -1,66 +0,0 @@
import React from 'react';
import './index.scss';
const LS_KEY = 'acknowledged-alpha';
interface State {
isFading: boolean;
hasAcknowledged: boolean;
}
export default class AlphaAgreement extends React.PureComponent<{}, State> {
public state = {
hasAcknowledged: !!localStorage.getItem(LS_KEY),
isFading: false
};
public render() {
if (this.state.hasAcknowledged) {
return null;
}
const isFading = this.state.isFading ? 'is-fading' : '';
return (
<div className={`AlphaAgreement ${isFading}`}>
<div className="AlphaAgreement-content">
<h2>This is an Unstable Version of MyCrypto</h2>
<p>
You are about to access a beta version of MyCrypto that is currently in development. In
its current state, it should only be used for testing, not for important transactions.
</p>
<p>
Any wallets you generate should not hold a significant value, and any transactions you
make should be for small amounts. MyCrypto does not claim responsibility for any issues
that happen while using the beta version.
</p>
<p>Are you sure you would like to continue?</p>
<div className="AlphaAgreement-content-buttons">
<button className="AlphaAgreement-content-buttons-btn is-reject" onClick={this.reject}>
No, take me to prod
</button>
<button
className="AlphaAgreement-content-buttons-btn is-continue"
onClick={this.doContinue}
>
Yes, continue to beta
</button>
</div>
</div>
</div>
);
}
private doContinue = () => {
localStorage.setItem(LS_KEY, 'true');
this.setState({ isFading: true });
setTimeout(() => {
this.setState({ hasAcknowledged: true });
}, 1000);
};
private reject = () => {
window.location.assign('https://mycrypto.com');
};
}

View File

@ -2,6 +2,7 @@ import React from 'react';
import { AmountFieldFactory } from './AmountFieldFactory';
import { UnitDropDown } from 'components';
import translate, { translateRaw } from 'translations';
import { Input } from 'components/ui';
interface Props {
hasUnitDropdown?: boolean;
@ -16,12 +17,12 @@ export const AmountField: React.SFC<Props> = ({
}) => (
<AmountFieldFactory
withProps={({ currentValue: { raw }, isValid, onChange, readOnly }) => (
<React.Fragment>
<label>{translate('SEND_amount')}</label>
<div className="input-group">
<input
className={`form-control ${
isAmountValid(raw, customValidator, isValid) ? 'is-valid' : 'is-invalid'
<div className="input-group-wrapper">
<label className="input-group input-group-inline-dropdown">
<div className="input-group-header">{translate('SEND_amount')}</div>
<Input
className={`input-group-input ${
isAmountValid(raw, customValidator, isValid) ? '' : 'invalid'
}`}
type="number"
placeholder={translateRaw('SEND_amount_short')}
@ -30,8 +31,8 @@ export const AmountField: React.SFC<Props> = ({
onChange={onChange}
/>
{hasUnitDropdown && <UnitDropDown showAllTokens={showAllTokens} />}
</div>
</React.Fragment>
</label>
</div>
)}
/>
);

View File

@ -2,24 +2,28 @@
@import 'common/sass/mixins';
.AccountInfo {
&-copy-icon{
display: inline;
color: $gray-light;
.fa{
margin-right: 3px;
}
&-copied{
color: $brand-success;
cursor: pointer;
.fa{
margin-right: 3px;
}
}
&-copy {
display: inline-block;
cursor: pointer;
color: $text-color;
font-size: $font-size-xs;
opacity: 0.5;
transition: $transition;
&:hover{
cursor: pointer;
color: $text-color;
opacity: 1;
}
&.is-copied{
color: $brand-success;
opacity: 1;
}
.fa {
margin-right: $space-xs;
}
}
&-section {
margin-top: $space * 1.5;
@ -81,6 +85,7 @@
}
&-addr {
margin-top: -$space-xs;
word-wrap: break-word;
@include mono;
}

View File

@ -1,15 +1,16 @@
import { Identicon, UnitDisplay } from 'components/ui';
import { IWallet, Balance, TrezorWallet, LedgerWallet } from 'libs/wallet';
import React from 'react';
import { connect } from 'react-redux';
import { toChecksumAddress } from 'ethereumjs-util';
import { CopyToClipboard } from 'react-copy-to-clipboard';
import { Identicon, UnitDisplay, Address, NewTabLink } from 'components/ui';
import { IWallet, Balance, TrezorWallet, LedgerWallet } from 'libs/wallet';
import translate from 'translations';
import './AccountInfo.scss';
import Spinner from 'components/ui/Spinner';
import { getNetworkConfig, getOffline } from 'selectors/config';
import { AppState } from 'reducers';
import { connect } from 'react-redux';
import { NetworkConfig } from 'types/network';
import { TSetAccountBalance, setAccountBalance } from 'actions/wallet';
import { CopyToClipboard } from 'react-copy-to-clipboard';
import './AccountInfo.scss';
interface OwnProps {
wallet: IWallet;
@ -103,11 +104,13 @@ class AccountInfo extends React.Component<Props, State> {
<Identicon address={address} size="100%" />
</div>
<div className="AccountInfo-address-wrapper">
<div className="AccountInfo-address-addr">{address}</div>
<CopyToClipboard onCopy={this.onCopy} text={address}>
<div className="AccountInfo-address-addr">
<Address address={address} />
</div>
<CopyToClipboard onCopy={this.onCopy} text={toChecksumAddress(address)}>
<div
className={`AccountInfo-copy-icon${this.state.copied ? '-copied' : ''}`}
title="Copy To Clipboard"
className={`AccountInfo-copy ${this.state.copied ? 'is-copied' : ''}`}
title="Copy To clipboard"
>
<i className="fa fa-copy" />
<span>{this.state.copied ? 'copied!' : 'copy address'}</span>
@ -183,24 +186,16 @@ class AccountInfo extends React.Component<Props, State> {
<ul className="AccountInfo-list">
{!!blockExplorer && (
<li className="AccountInfo-list-item">
<a
href={blockExplorer.addressUrl(address)}
target="_blank"
rel="noopener noreferrer"
>
<NewTabLink href={blockExplorer.addressUrl(address)}>
{`${network.name} (${blockExplorer.origin})`}
</a>
</NewTabLink>
</li>
)}
{!!tokenExplorer && (
<li className="AccountInfo-list-item">
<a
href={tokenExplorer.address(address)}
target="_blank"
rel="noopener noreferrer"
>
<NewTabLink href={tokenExplorer.address(address)}>
{`Tokens (${tokenExplorer.name})`}
</a>
</NewTabLink>
</li>
)}
</ul>

View File

@ -41,7 +41,6 @@
flex-wrap: nowrap;
align-items: center;
&-fiat-symbol {
height: 18px;
width: 18px;
margin-right: 0.5rem;
text-align: center;

View File

@ -2,10 +2,14 @@ import React from 'react';
import CoinbaseLogo from 'assets/images/logo-coinbase.svg';
import { NewTabLink } from 'components/ui';
export const Coinbase: React.SFC = () => (
interface Props {
address: string;
}
export const Coinbase: React.SFC<Props> = ({ address }) => (
<NewTabLink
className="Promos-promo Promos--coinbase"
href="https://buy.coinbase.com?code=60c05061-3a76-57be-b1cd-a7afa97bcb8c&address=0xA7DeFf12461661212734dB35AdE9aE7d987D648c&crypto_currency=ETH&currency=USD"
href={`https://buy.coinbase.com?code=60c05061-3a76-57be-b1cd-a7afa97bcb8c&address=${address}&crypto_currency=ETH&currency=USD`}
>
<div className="Promos-promo-inner">
<div className="Promos-promo-text">

View File

@ -2,8 +2,8 @@ import React from 'react';
import { TransitionGroup, CSSTransition } from 'react-transition-group';
import { HardwareWallets, Coinbase, Shapeshift } from './PromoComponents';
import './Promos.scss';
const promos = [HardwareWallets, Coinbase, Shapeshift];
import { connect } from 'react-redux';
import { AppState } from '../../reducers';
const CarouselAnimation = ({ children, ...props }) => (
<CSSTransition {...props} timeout={300} classNames="carousel">
@ -11,12 +11,20 @@ const CarouselAnimation = ({ children, ...props }) => (
</CSSTransition>
);
// Don't change Coinbase index
const promos = [HardwareWallets, Coinbase, Shapeshift];
interface State {
activePromo: number;
}
export default class Promos extends React.PureComponent<{}, State> {
interface StateProps {
wallet: AppState['wallet']['inst'];
}
class PromosClass extends React.PureComponent<StateProps, State> {
public timer: any = null;
public promos = [HardwareWallets, Coinbase, Shapeshift];
public state = {
activePromo: parseInt(String(Math.random() * promos.length), 10)
@ -30,13 +38,27 @@ export default class Promos extends React.PureComponent<{}, State> {
clearInterval(this.timer);
}
public getPromo() {
const { activePromo } = this.state;
const { wallet } = this.props;
if (activePromo === 1) {
if (wallet) {
return <Coinbase address={wallet.getAddressString()} />;
} else {
return <Shapeshift />;
}
} else {
return promos[activePromo];
}
}
public render() {
const { activePromo } = this.state;
return (
<div className="Promos">
<TransitionGroup className="Promos-promo-wrapper">
<CarouselAnimation key={Math.random()}>{promos[activePromo]}</CarouselAnimation>
<CarouselAnimation key={Math.random()}>{this.getPromo()}</CarouselAnimation>
</TransitionGroup>
<div className="Promos-nav">
{promos.map((_, index) => {
@ -64,3 +86,11 @@ export default class Promos extends React.PureComponent<{}, State> {
this.setState({ activePromo });
};
}
function mapStateToProps(state: AppState): StateProps {
return {
wallet: state.wallet.inst
};
}
export default connect(mapStateToProps, {})(PromosClass);

View File

@ -1,9 +1,8 @@
import React from 'react';
import classnames from 'classnames';
import { HELP_ARTICLE } from 'config';
import { isPositiveIntegerOrZero, isValidETHAddress } from 'libs/validators';
import translate from 'translations';
import { HelpLink } from 'components/ui';
import { HelpLink, Input } from 'components/ui';
import './AddCustomTokenForm.scss';
import { Token } from 'types/network';
@ -42,7 +41,6 @@ export default class AddCustomTokenForm extends React.PureComponent<Props, State
public render() {
const { address, symbol, decimal } = this.state;
const inputClasses = 'AddCustom-field-input form-control input-sm';
const errors = this.getErrors();
const fields = [
@ -69,11 +67,10 @@ export default class AddCustomTokenForm extends React.PureComponent<Props, State
return (
<label className="AddCustom-field form-group" key={field.name}>
<span className="AddCustom-field-label">{field.label}</span>
<input
className={classnames(
inputClasses,
errors[field.name] ? 'is-invalid' : field.value ? 'is-valid' : ''
)}
<Input
className={`${
errors[field.name] ? 'invalid' : field.value ? 'valid' : ''
} AddCustom-field-input input-sm`}
type="text"
name={field.name}
value={field.value}

View File

@ -1,43 +1,50 @@
@import "common/sass/variables";
@import "common/sass/mixins";
.AlphaAgreement {
.BetaAgreement {
@include cover-message;
background: $brand-info;
&-content {
h2 {
text-align: center;
}
&-buttons {
padding-top: 20px;
&-btn {
display: block;
width: 100%;
max-width: 240px;
max-width: 280px;
margin: 0 auto;
border: none;
padding: 0;
outline: none;
transition: $transition;
&.is-reject {
&.is-continue {
height: 60px;
line-height: 60px;
font-size: 22px;
background: rgba(#fff, 0.96);
color: $gray;
color: $gray-dark;
border-radius: 4px;
margin-bottom: 20px;
&:hover {
background: #fff;
color: $gray-darker;
}
}
&.is-continue {
&.is-reject {
background: none;
color: #fff;
opacity: 0.7;
&:hover {
text-decoration: underline;
opacity: 1;
}
}
}
@ -51,7 +58,7 @@
background: #fff;
transition: all 500ms ease 400ms;
.AlphaAgreement-content {
.BetaAgreement-content {
opacity: 0;
transform: translateY(15px);
transition: all 500ms ease;

View File

@ -0,0 +1,72 @@
import React from 'react';
import { NewTabLink } from 'components/ui';
import { discordURL } from 'config';
import './index.scss';
const LS_KEY = 'acknowledged-beta';
interface State {
isFading: boolean;
hasAcknowledged: boolean;
}
export default class BetaAgreement extends React.PureComponent<{}, State> {
public state = {
hasAcknowledged: !!localStorage.getItem(LS_KEY),
isFading: false
};
public render() {
if (this.state.hasAcknowledged) {
return null;
}
const isFading = this.state.isFading ? 'is-fading' : '';
return (
<div className={`BetaAgreement ${isFading}`}>
<div className="BetaAgreement-content">
<h2>Welcome to the New MyCrypto Beta!</h2>
<p>
You are about to use a version of MyCrypto that hasn't been released yet. While we are
confident that it is close to being production ready, you may want to use the current
production site for larger or more time-sensitive transactions.
</p>
<p>
Feedback and bug reports are greatly appreciated. You can file issues on our{' '}
<NewTabLink href="https://github.com/MyCryptoHQ/MyCrypto/issues">
GitHub repository
</NewTabLink>{' '}
or join our <NewTabLink href={discordURL}>Discord server</NewTabLink> to discuss the
beta.
</p>
<p>Are you sure you would like to continue?</p>
<div className="BetaAgreement-content-buttons">
<button
className="BetaAgreement-content-buttons-btn is-continue"
onClick={this.doContinue}
>
Yes, continue to the Beta
</button>
<button className="BetaAgreement-content-buttons-btn is-reject" onClick={this.reject}>
No, take me to the production site
</button>
</div>
</div>
</div>
);
}
private doContinue = () => {
localStorage.setItem(LS_KEY, 'true');
this.setState({ isFading: true });
setTimeout(() => {
this.setState({ hasAcknowledged: true });
}, 1000);
};
private reject = () => {
window.location.assign('https://mycrypto.com');
};
}

View File

@ -5,6 +5,7 @@ import { getCurrentTo, ICurrentTo } from 'selectors/transaction';
import { getAllTokens } from 'selectors/config';
import { getWalletInst } from 'selectors/wallet';
import { getAddressMessage } from 'config';
import { Address } from 'components/ui';
import { Token } from 'types/network';
interface ReduxProps {
@ -72,7 +73,10 @@ class CurrentCustomMessageClass extends PureComponent<Props, State> {
<React.Fragment>
<p>
<small>
A message regarding <strong>{address}</strong>:
A message regarding{' '}
<strong>
<Address address={address} />
</strong>:
</small>
</p>
<p>{msg.msg}</p>

View File

@ -2,21 +2,24 @@ import { DataFieldFactory } from './DataFieldFactory';
import React from 'react';
import translate from 'translations';
import { donationAddressMap } from 'config';
import { Input } from 'components/ui';
export const DataField: React.SFC<{}> = () => (
<DataFieldFactory
withProps={({ data: { raw }, dataExists, onChange, readOnly }) => (
<>
<label>{translate('OFFLINE_Step2_Label_6')}</label>
<input
className={`form-control ${dataExists ? 'is-valid' : 'is-invalid'}`}
type="text"
placeholder={donationAddressMap.ETH}
value={raw}
readOnly={!!readOnly}
onChange={onChange}
/>
</>
<div className="input-group-wrapper">
<label className="input-group">
<div className="input-group-header">{translate('OFFLINE_Step2_Label_6')}</div>
<Input
className={dataExists ? 'is-valid' : 'is-invalid'}
type="text"
placeholder={donationAddressMap.ETH}
value={raw}
readOnly={!!readOnly}
onChange={onChange}
/>
</label>
</div>
)}
/>
);

View File

@ -1,4 +1,5 @@
import React from 'react';
import { NewTabLink } from 'components/ui';
import './index.scss';
const SUBJECT = 'Error!';
@ -24,14 +25,17 @@ const ErrorScreen: React.SFC<Props> = ({ error }) => {
>
support@mycrypto.com
</a>{' '}
if a refresh doesn't fix it (or click it anyway to open a ticket 😊). If you attach the
following error, you'll make it a ton easier to fix the issue.
if a refresh doesn't fix it (or click it anyway to open a ticket 😊). You can also submit
an issue on our{' '}
<NewTabLink href="https://github.com/MyCryptoHQ/MyCrypto/issues">
GitHub Repository
</NewTabLink>. Please attach the following error to help our team solve your issue.
</p>
<code>{error.message}</code>
<h5>
Please make sure the error message does not include any sensitive information before
sending it us. We don't want your private keys!
sending it to us. We don't want your private keys!
</h5>
<code>{error.message}</code>
</div>
</div>
);

View File

@ -1,26 +1,33 @@
import React from 'react';
import { translateRaw } from 'translations';
import { Link } from 'react-router-dom';
import translate from 'translations';
import { NewTabLink } from 'components/ui';
import { BlockExplorerConfig } from 'types/network';
export interface TransactionSucceededProps {
txHash: string;
blockExplorer: BlockExplorerConfig;
blockExplorer?: BlockExplorerConfig;
}
const TransactionSucceeded = ({ txHash, blockExplorer }: TransactionSucceededProps) => {
const txHashLink = blockExplorer.txUrl(txHash);
let verifyBtn: React.ReactElement<string> | undefined;
if (blockExplorer) {
verifyBtn = (
<NewTabLink className="btn btn-xs" href={blockExplorer.txUrl(txHash)}>
Verify Transaction on {blockExplorer.name}
</NewTabLink>
);
}
return (
<div>
<p>{translateRaw('SUCCESS_3') + txHash}</p>
<a
className="btn btn-xs btn-info string"
href={txHashLink}
target="_blank"
rel="noopener noreferrer"
>
Verify Transaction
</a>
<p>
{translate('SUCCESS_3')} {txHash}
</p>
{verifyBtn}
<Link to={`/tx-status?txHash=${txHash}`} className="btn btn-xs">
{translate('NAV_CheckTxStatus')}
</Link>
</div>
);
};

View File

@ -41,7 +41,11 @@ const DisclaimerModal: React.SFC<Props> = ({ isOpen, handleClose }) => {
English and, because of this, the English version of our website is the official text.
</p>
<p>
<b>MIT License</b> Copyright © 2015-2017 MyCrypto LLC
<b>MIT License</b>
<br />
Copyright (c) 2015-2017 MyEtherWallet LLC
<br />
Copyright (c) 2018 MyCrypto, Inc.
</p>
<p>
Permission is hereby granted, free of charge, to any person obtaining a copy of this

View File

@ -158,10 +158,10 @@ $footer-link-color: #fff;
.SocialMediaLink {
transition: opacity 0.3s;
color: #fff;
margin: 0.65rem;
margin: 0.55rem;
> i {
font-size: 1.2rem;
font-size: 1.15rem;
}
&:hover,

View File

@ -5,7 +5,8 @@ import {
ethercardReferralURL,
donationAddressMap,
VERSION,
knowledgeBaseURL
knowledgeBaseURL,
discordURL
} from 'config';
import React from 'react';
import PreFooter from './PreFooter';
@ -46,6 +47,10 @@ const SOCIAL_MEDIA: Link[] = [
{
link: 'https://www.reddit.com/r/mycrypto/',
text: 'reddit'
},
{
link: discordURL,
text: 'discord'
}
];

View File

@ -4,6 +4,7 @@ import translate from 'translations';
import { gasLimitValidator } from 'libs/validators';
import { InlineSpinner } from 'components/ui/InlineSpinner';
import './GasLimitField.scss';
import { Input } from 'components/ui';
interface Props {
customLabel?: string;
@ -13,22 +14,24 @@ interface Props {
export const GasLimitField: React.SFC<Props> = ({ customLabel, disabled }) => (
<GasLimitFieldFactory
withProps={({ gasLimit: { raw }, onChange, readOnly, gasEstimationPending }) => (
<React.Fragment>
<div className="gaslimit-label-wrapper flex-wrapper">
{customLabel ? <label>{customLabel} </label> : <label>{translate('TRANS_gas')} </label>}
<div className="flex-spacer" />
<InlineSpinner active={gasEstimationPending} text="Calculating" />
</div>
<input
className={`form-control ${gasLimitValidator(raw) ? 'is-valid' : 'is-invalid'}`}
type="number"
placeholder="e.g. 21000"
readOnly={!!readOnly}
value={raw}
onChange={onChange}
disabled={disabled}
/>
</React.Fragment>
<div className="input-group-wrapper AdvancedGas-gas-price">
<label className="input-group">
<div className="input-group-header">
{customLabel ? customLabel : translate('TRANS_gas')}
<div className="flex-spacer" />
<InlineSpinner active={gasEstimationPending} text="Calculating" />
</div>
<Input
className={gasLimitValidator(raw) ? 'is-valid' : 'is-invalid'}
type="number"
placeholder="e.g. 21000"
readOnly={!!readOnly}
value={raw}
onChange={onChange}
disabled={disabled}
/>
</label>
</div>
)}
/>
);

View File

@ -60,27 +60,31 @@ export default class GenerateKeystoreModal extends React.Component<Props, State>
handleClose={this.handleClose}
>
<form className="GenKeystore" onSubmit={this.handleSubmit}>
<label className="GenKeystore-field">
<h4 className="GenKeystore-field-label">Private Key</h4>
<TogglablePassword
name="privateKey"
value={privateKey}
disabled={!!privateKey}
onChange={this.handleInput}
placeholder="f1d0e0789c6d40f39..."
isValid={isPrivateKeyValid}
/>
</label>
<label className="GenKeystore-field">
<h4 className="GenKeystore-field-label">Password</h4>
<TogglablePassword
name="password"
value={password}
onChange={this.handleInput}
placeholder={translateRaw('Minimum 9 characters')}
isValid={isPasswordValid}
/>
</label>
<div className="input-group-wrapper GenKeystore-field">
<label className="input-group input-group-inline-dropdown">
<div className="input-group-header">Private Key</div>
<TogglablePassword
name="privateKey"
value={privateKey}
disabled={!!privateKey}
onChange={this.handleInput}
placeholder="f1d0e0789c6d40f39..."
isValid={isPrivateKeyValid}
/>
</label>
</div>
<div className="input-group-wrapper GenKeystore-field">
<label className="input-group input-group-inline-dropdown">
<div className="input-group-header">Password</div>
<TogglablePassword
name="password"
value={password}
onChange={this.handleInput}
placeholder={translateRaw('Minimum 9 characters')}
isValid={isPasswordValid}
/>
</label>
</div>
{!keystoreFile ? (
<button

View File

@ -1,5 +1,4 @@
import React from 'react';
import classnames from 'classnames';
import Modal, { IButton } from 'components/ui/Modal';
import translate from 'translations';
import { CustomNetworkConfig } from 'types/network';
@ -13,10 +12,11 @@ import {
getStaticNetworkConfigs
} from 'selectors/config';
import { CustomNode } from 'libs/nodes';
import { Input } from 'components/ui';
const CUSTOM = 'custom';
interface Input {
interface InputProps {
name: string;
placeholder?: string;
type?: string;
@ -228,13 +228,10 @@ class CustomNodeModal extends React.Component<Props, State> {
);
}
private renderInput(input: Input, invalids: { [key: string]: boolean }) {
private renderInput(input: InputProps, invalids: { [key: string]: boolean }) {
return (
<input
className={classnames({
'form-control': true,
'is-invalid': this.state[input.name] && invalids[input.name]
})}
<Input
className={`${this.state[input.name] && invalids[input.name] ? 'invalid' : ''}`}
value={this.state[input.name]}
onChange={this.handleChange}
autoComplete="off"

View File

@ -51,7 +51,7 @@
margin-top: -.1rem;
width: 1.3rem;
height: 1.3rem;
background-image: url('~assets/images/swap.svg');
background-image: url('~assets/images/logo-shapeshift-no-text.svg');
background-position: center;
background-repeat: no-repeat;
background-size: contain;

View File

@ -41,6 +41,7 @@ $small-size: 900px;
font-weight: 300;
color: #fff;
transition: background-color 80ms ease;
background: $gray-darker;
a {
color: #fff;
@ -54,7 +55,6 @@ $small-size: 900px;
}
// Colors
&,
&.is-primary {
background: $brand-primary;
}
@ -81,8 +81,7 @@ $small-size: 900px;
color: white;
padding: 0;
background: #274e7e;
background-image: url('~assets/images/header-bg.jpg'),
linear-gradient(130deg, #37709e, #274e7e);
background-image: url('~assets/images/header-bg.jpg'), linear-gradient(130deg, #37709e, #274e7e);
background-size: cover;
background-position: left;
@ -111,8 +110,7 @@ $small-size: 900px;
width: auto;
padding: 8px;
padding-right: 0;
filter: drop-shadow(0 1px 0 rgba(#000, 0.12))
drop-shadow(1px 1px 0 rgba(#000, 0.12));
filter: drop-shadow(0 1px 0 rgba(#000, 0.12)) drop-shadow(1px 1px 0 rgba(#000, 0.12));
}
}

View File

@ -12,7 +12,7 @@ import {
addCustomNetwork
} from 'actions/config';
import logo from 'assets/images/logo-mycrypto.svg';
import { Dropdown, ColorDropdown } from 'components/ui';
import { OldDropDown, ColorDropdown } from 'components/ui';
import React, { Component } from 'react';
import classnames from 'classnames';
import { Link } from 'react-router-dom';
@ -100,7 +100,7 @@ class Header extends Component<Props, State> {
} = this.props;
const { isAddingCustomNode } = this.state;
const selectedLanguage = languageSelection;
const LanguageDropDown = Dropdown as new () => Dropdown<typeof selectedLanguage>;
const LanguageDropDown = OldDropDown as new () => OldDropDown<typeof selectedLanguage>;
const options = nodeOptions.map(n => {
if (n.isCustom) {
const { label, isCustom, id, ...rest } = n;
@ -155,13 +155,6 @@ class Header extends Component<Props, State> {
ariaLabel={`change language. current language ${languages[selectedLanguage]}`}
options={Object.values(languages)}
value={languages[selectedLanguage]}
extra={
<li key="disclaimer">
<a data-toggle="modal" data-target="#disclaimerModal">
Disclaimer
</a>
</li>
}
onChange={this.changeLanguage}
size="smr"
color="white"

View File

@ -62,7 +62,7 @@ class LogOutPromptClass extends React.Component<Props, State> {
};
private onConfirm = () => {
const { nextLocation } = this.state;
const { nextLocation: next } = this.state;
this.props.resetWallet();
this.setState(
{
@ -70,8 +70,8 @@ class LogOutPromptClass extends React.Component<Props, State> {
nextLocation: null
},
() => {
if (nextLocation) {
this.props.history.push(nextLocation.pathname);
if (next) {
this.props.history.push(`${next.pathname}${next.search}${next.hash}`);
}
}
);

View File

@ -15,7 +15,6 @@
&-refresh {
@include reset-button;
height: $input-height-base;
opacity: 0.3;
transition: opacity 300ms;
@ -31,16 +30,11 @@
}
}
&-spinner {
height: 1rem;
}
&-spinner,
&-refresh {
position: absolute;
right: 0;
top: 50%;
transform: translateY(-50%) translateZ(0);
margin: 0 1rem;
bottom: 1rem;
padding: 0.75rem 1rem;
}
}

View File

@ -2,7 +2,7 @@ import React from 'react';
import translate from 'translations';
import { NonceFieldFactory } from 'components/NonceFieldFactory';
import Help from 'components/ui/Help';
import { Spinner } from 'components/ui';
import { Spinner, Input } from 'components/ui';
import { connect } from 'react-redux';
import { getNonceRequested, TGetNonceRequested } from 'actions/transaction';
import { nonceRequestPending } from 'selectors/transaction';
@ -32,19 +32,17 @@ class NonceField extends React.Component<Props> {
<NonceFieldFactory
withProps={({ nonce: { raw, value }, onChange, readOnly, shouldDisplay }) => {
return alwaysDisplay || shouldDisplay ? (
<React.Fragment>
<div className="Nonce-label flex-wrapper">
<label className="Nonce-label-text">{translate('OFFLINE_Step2_Label_5')}</label>
<Help
size="x1"
link="https://support.mycrypto.com/transactions/what-is-nonce.html"
/>
</div>
<div className="Nonce-field">
<input
className={`Nonce-field-input form-control ${
!!value ? 'is-valid' : 'is-invalid'
}`}
<div className="input-group-wrapper Nonce-label">
<label className="input-group">
<div className="input-group-header">
{translate('OFFLINE_Step2_Label_5')}
<Help
size="x1"
link="https://support.mycrypto.com/transactions/what-is-nonce.html"
/>
</div>
<Input
className={`Nonce-field-input ${!!value ? 'is-valid' : 'is-invalid'}`}
type="number"
placeholder="e.g. 7"
value={raw}
@ -63,8 +61,8 @@ class NonceField extends React.Component<Props> {
</button>
)
)}
</div>
</React.Fragment>
</label>
</div>
) : null;
}}
/>

View File

@ -9,6 +9,7 @@ import React, { Component } from 'react';
import { connect } from 'react-redux';
import { AppState } from 'reducers';
import { ConfirmationModal } from 'components/ConfirmationModal';
import { TextArea } from 'components/ui';
export interface CallbackProps {
onClick(): void;
@ -42,12 +43,7 @@ class SendButtonFactoryClass extends Component<Props> {
? 'Transaction Parameters'
: translate('SEND_raw')}
</label>
<textarea
className="form-control"
value={getStringifiedTx(serializedTransaction)}
rows={4}
readOnly={true}
/>
<TextArea value={getStringifiedTx(serializedTransaction)} rows={4} readOnly={true} />
</div>
{!onlyTransactionParameters && (
<div className="col-sm-6">
@ -56,12 +52,7 @@ class SendButtonFactoryClass extends Component<Props> {
? 'Serialized Transaction Parameters'
: translate('SEND_signed')}
</label>
<textarea
className="form-control"
value={addHexPrefix(serializedTransaction)}
rows={4}
readOnly={true}
/>
<TextArea value={addHexPrefix(serializedTransaction)} rows={4} readOnly={true} />
</div>
)}
<OfflineBroadcast />

View File

@ -27,7 +27,7 @@ class SendEverythingClass extends Component<Props> {
!readOnly ? (
<span className="help-block">
<a onClick={this.onSendEverything}>
<span className="strong">{translate('SEND_TransferTotal')}</span>
<span className="">{translate('SEND_TransferTotal')}</span>
</a>
</span>
) : null

View File

@ -16,7 +16,7 @@
transition-duration: 100ms;
&.is-active {
border-bottom-color: $ether-blue;
border-bottom-color: $brand-primary;
}
&.is-disabled {

View File

@ -10,7 +10,8 @@ import {
getNonceRequested,
TGetNonceRequested,
reset,
TReset
TReset,
ResetAction
} from 'actions/transaction';
import { fetchCCRatesRequested, TFetchCCRatesRequested } from 'actions/rates';
import { getNetworkConfig, getOffline } from 'selectors/config';
@ -44,6 +45,7 @@ interface DefaultProps {
}
interface OwnProps {
resetIncludeExcludeProperties?: ResetAction['payload'];
initialState?: SliderStates;
disableToggle?: boolean;
advancedGasOptions?: AdvancedOptions;
@ -69,7 +71,7 @@ class TXMetaDataPanel extends React.Component<Props, State> {
public componentDidMount() {
if (!this.props.offline) {
this.props.reset();
this.props.reset(this.props.resetIncludeExcludeProperties);
this.props.fetchCCRates([this.props.network.unit]);
this.props.getNonceRequested();
}
@ -111,11 +113,9 @@ class TXMetaDataPanel extends React.Component<Props, State> {
!disableToggle && (
<div className="help-block">
<a className="Gas-toggle" onClick={this.toggleAdvanced}>
<strong>
{showAdvanced
? `- ${translateRaw('Back to simple')}`
: `+ ${translateRaw('Advanced Settings')}`}
</strong>
{showAdvanced
? `- ${translateRaw('Back to simple')}`
: `+ ${translateRaw('Advanced Settings')}`}
</a>
</div>
)}

View File

@ -1,5 +1,4 @@
import React from 'react';
import classnames from 'classnames';
import translate, { translateRaw } from 'translations';
import FeeSummary from './FeeSummary';
import './AdvancedGas.scss';
@ -11,6 +10,7 @@ import { connect } from 'react-redux';
import { getAutoGasLimitEnabled } from 'selectors/config';
import { isValidGasPrice } from 'selectors/transaction';
import { sanitizeNumericalInput } from 'libs/values';
import { Input } from 'components/ui';
export interface AdvancedOptions {
gasPriceField?: boolean;
@ -71,17 +71,19 @@ class AdvancedGas extends React.Component<Props, State> {
<div className="AdvancedGas-flex-wrapper flex-wrapper">
{gasPriceField && (
<div className="AdvancedGas-gas-price">
<label>{translate('OFFLINE_Step2_Label_3')} (gwei)</label>
<input
className={classnames('form-control', {
'is-invalid': !!gasPrice.raw && !validGasPrice
})}
type="number"
placeholder="40"
value={gasPrice.raw}
onChange={this.handleGasPriceChange}
/>
<div className="input-group-wrapper AdvancedGas-gas-price">
<label className="input-group">
<div className="input-group-header">
{translate('OFFLINE_Step2_Label_3')} (gwei)
</div>
<Input
className={!!gasPrice.raw && !validGasPrice ? 'is-invalid' : ''}
type="number"
placeholder="40"
value={gasPrice.raw}
onChange={this.handleGasPriceChange}
/>
</label>
</div>
)}

View File

@ -4,9 +4,11 @@
// yourself, otherwise all visibiility changes are managed in internal state.
import React from 'react';
import './TogglablePassword.scss';
import { Input, TextArea } from 'components/ui';
interface Props {
// Shared props
className?: string;
value: string;
placeholder?: string;
name?: string;
@ -46,6 +48,7 @@ export default class TogglablePassword extends React.PureComponent<Props, State>
public render() {
const {
className,
value,
placeholder,
name,
@ -66,10 +69,10 @@ export default class TogglablePassword extends React.PureComponent<Props, State>
: isValid === null || isValid === undefined ? '' : isValid ? 'is-valid' : 'is-invalid';
return (
<div className="TogglablePassword input-group">
<div className={`TogglablePassword input-group input-group-inline-dropdown ${className}`}>
{isTextareaWhenVisible && isVisible ? (
<textarea
className={`form-control ${validClass}`}
<TextArea
className={validClass}
value={value}
name={name}
disabled={disabled}
@ -82,12 +85,12 @@ export default class TogglablePassword extends React.PureComponent<Props, State>
aria-label={ariaLabel}
/>
) : (
<input
<Input
value={value}
name={name}
disabled={disabled}
type={isVisible ? 'text' : 'password'}
className={`form-control ${validClass}`}
className={`${validClass}`}
placeholder={placeholder}
onChange={onChange}
onFocus={onFocus}

View File

@ -1,6 +1,6 @@
import React from 'react';
import translate from 'translations';
import { Identicon, UnitDisplay, NewTabLink } from 'components/ui';
import { Identicon, UnitDisplay, NewTabLink, TextArea, Address } from 'components/ui';
import { TransactionData, TransactionReceipt } from 'libs/nodes';
import { NetworkConfig } from 'types/network';
import './TransactionDataTable.scss';
@ -98,7 +98,7 @@ const TransactionDataTable: React.SFC<Props> = ({ data, receipt, network }) => {
data: (
<MaybeLink href={explorer.from}>
<Identicon address={data.from} size="26px" />
{data.from}
<Address address={data.from} />
</MaybeLink>
)
},
@ -107,7 +107,7 @@ const TransactionDataTable: React.SFC<Props> = ({ data, receipt, network }) => {
data: (
<MaybeLink href={explorer.to}>
<Identicon address={data.to} size="26px" />
{data.to}
<Address address={data.to} />
</MaybeLink>
)
},
@ -141,7 +141,9 @@ const TransactionDataTable: React.SFC<Props> = ({ data, receipt, network }) => {
label: translate('New contract address'),
data: receipt &&
receipt.contractAddress && (
<MaybeLink href={explorer.contract}>{receipt.contractAddress}</MaybeLink>
<MaybeLink href={explorer.contract}>
<Address address={receipt.contractAddress} />
</MaybeLink>
)
},
{
@ -150,9 +152,7 @@ const TransactionDataTable: React.SFC<Props> = ({ data, receipt, network }) => {
},
{
label: translate('TRANS_data'),
data: hasInputData ? (
<textarea className="form-control" value={data.input} disabled={true} />
) : null
data: hasInputData ? <TextArea value={data.input} disabled={true} /> : null
}
];

View File

@ -1,14 +1,12 @@
import React, { Component } from 'react';
import { setUnitMeta, TSetUnitMeta } from 'actions/transaction';
import Dropdown from 'components/ui/Dropdown';
import { withConditional } from 'components/hocs';
import { TokenBalance, MergedToken, getShownTokenBalances, getTokens } from 'selectors/wallet';
import { Query } from 'components/renderCbs';
import { connect } from 'react-redux';
import { AppState } from 'reducers';
import { getUnit } from 'selectors/transaction';
import { getNetworkConfig } from 'selectors/config';
import { NetworkConfig } from 'types/network';
import { getNetworkUnit } from 'selectors/config';
interface DispatchProps {
setUnitMeta: TSetUnitMeta;
@ -19,37 +17,32 @@ interface StateProps {
tokens: TokenBalance[];
allTokens: MergedToken[];
showAllTokens?: boolean;
network: NetworkConfig;
networkUnit: string;
}
const StringDropdown = Dropdown as new () => Dropdown<string>;
const ConditionalStringDropDown = withConditional(StringDropdown);
class UnitDropdownClass extends Component<DispatchProps & StateProps> {
public render() {
const { tokens, allTokens, showAllTokens, unit, network } = this.props;
const { tokens, allTokens, showAllTokens, unit, networkUnit } = this.props;
const focusedTokens = showAllTokens ? allTokens : tokens;
const options = [networkUnit, ...getTokenSymbols(focusedTokens)];
return (
<div className="input-group-btn">
<Query
params={['readOnly']}
withQuery={({ readOnly }) => (
<ConditionalStringDropDown
options={[network.unit, ...getTokenSymbols(focusedTokens)]}
value={unit === 'ether' ? network.unit : unit}
condition={!readOnly}
conditionalProps={{
onChange: this.handleOnChange
}}
ariaLabel={'dropdown'}
/>
)}
/>
</div>
<Query
params={['readOnly']}
withQuery={({ readOnly }) => (
<Dropdown
options={options}
value={unit === 'ether' ? networkUnit : unit}
onChange={this.handleOnChange}
clearable={false}
searchable={options.length > 10}
disabled={!!readOnly}
/>
)}
/>
);
}
private handleOnChange = (unit: string) => {
this.props.setUnitMeta(unit);
private handleOnChange = unit => {
this.props.setUnitMeta(unit.value);
};
}
const getTokenSymbols = (tokens: (TokenBalance | MergedToken)[]) => tokens.map(t => t.symbol);
@ -59,7 +52,7 @@ function mapStateToProps(state: AppState) {
tokens: getShownTokenBalances(state, true),
allTokens: getTokens(state),
unit: getUnit(state),
network: getNetworkConfig(state)
networkUnit: getNetworkUnit(state)
};
}

View File

@ -17,7 +17,7 @@ import {
resetWallet,
TResetWallet
} from 'actions/wallet';
import { reset, TReset } from 'actions/transaction';
import { reset, TReset, ResetAction } from 'actions/transaction';
import translate from 'translations';
import {
KeystoreDecrypt,
@ -55,6 +55,7 @@ interface OwnProps {
hidden?: boolean;
disabledWallets?: DisabledWallets;
showGenerateLink?: boolean;
resetIncludeExcludeProperties?: ResetAction['payload'];
}
interface DispatchProps {
@ -348,7 +349,7 @@ export class WalletDecrypt extends Component<Props, State> {
{this.props.showGenerateLink && (
<div className="WalletDecrypt-wallets-generate">
Dont have a wallet? <Link to="/generate">Click here to get one</Link>.
Dont have an account yet? <Link to="/generate">Click here to get one</Link>.
</div>
)}
</div>
@ -430,7 +431,7 @@ export class WalletDecrypt extends Component<Props, State> {
// the payload to contain the unlocked wallet info.
const unlockValue = value && !isEmpty(value) ? value : payload;
this.WALLETS[selectedWalletKey].unlock(unlockValue);
this.props.resetTransactionState();
this.props.resetTransactionState(this.props.resetIncludeExcludeProperties);
};
private isWalletDisabled = (walletKey: WalletName) => {

View File

@ -13,7 +13,7 @@ import React from 'react';
import { connect } from 'react-redux';
import { getNetworkConfig } from 'selectors/config';
import { getTokens, MergedToken } from 'selectors/wallet';
import { UnitDisplay } from 'components/ui';
import { UnitDisplay, Input } from 'components/ui';
import './DeterministicWalletsModal.scss';
import { StaticNetworkConfig } from 'types/network';
import Select from 'react-select';
@ -97,7 +97,6 @@ class DeterministicWalletsModalClass extends React.PureComponent<Props, State> {
walletType
} = this.props;
const { selectedAddress, customPath, page } = this.state;
const validPathClass = isValidPath(customPath) ? 'is-valid' : 'is-invalid';
const buttons: IButton[] = [
{
@ -137,8 +136,8 @@ class DeterministicWalletsModalClass extends React.PureComponent<Props, State> {
/>
{/* TODO/Hack - Custom Paths are temporarily disabled. `false` is used for smallest diff */}
{false && (
<input
className={`form-control ${validPathClass}`}
<Input
className={isValidPath(customPath) ? '' : 'invalid'}
value={customPath}
placeholder="m/44'/60'/0'/0"
onChange={this.handleChangeCustomPath}

View File

@ -3,6 +3,7 @@ import React, { PureComponent } from 'react';
import translate, { translateRaw } from 'translations';
import Spinner from 'components/ui/Spinner';
import { TShowNotification } from 'actions/notifications';
import { Input } from 'components/ui';
export interface KeystoreValue {
file: string;
@ -36,7 +37,7 @@ export class KeystoreDecrypt extends PureComponent {
};
public render() {
const { isWalletPending, isPasswordPending, value: { file, password } } = this.props;
const { isWalletPending, value: { file, password } } = this.props;
const passReq = isPassRequired(file);
const unlockDisabled = !file || (passReq && !password);
@ -44,7 +45,7 @@ export class KeystoreDecrypt extends PureComponent {
<form id="selectedUploadKey" onSubmit={this.unlock}>
<div className="form-group">
<input
className={'hidden'}
className="hidden"
type="file"
id="fselector"
onChange={this.handleFileSelection}
@ -55,17 +56,16 @@ export class KeystoreDecrypt extends PureComponent {
</a>
</label>
{isWalletPending ? <Spinner /> : ''}
<div className={file.length && isPasswordPending ? '' : 'hidden'}>
<p>{translate('ADD_Label_3')}</p>
<input
className={`form-control ${password.length > 0 ? 'is-valid' : 'is-invalid'}`}
value={password}
onChange={this.onPasswordChange}
onKeyDown={this.onKeyDown}
placeholder={translateRaw('x_Password')}
type="password"
/>
</div>
<Input
className={`${password.length > 0 ? 'is-valid' : 'is-invalid'} ${
file.length && isWalletPending ? 'hidden' : ''
}`}
value={password}
onChange={this.onPasswordChange}
onKeyDown={this.onKeyDown}
placeholder={translateRaw('x_Password')}
type="password"
/>
</div>
<button className="btn btn-primary btn-block" disabled={unlockDisabled}>

View File

@ -8,6 +8,7 @@ import { AppState } from 'reducers';
import { connect } from 'react-redux';
import { getSingleDPath, getPaths } from 'selectors/config/wallet';
import { TogglablePassword } from 'components';
import { Input } from 'components/ui';
interface OwnProps {
onUnlock(param: any): void;
@ -63,8 +64,7 @@ class MnemonicDecryptClass extends PureComponent<Props, State> {
</div>
<div className="form-group">
<p>Password (optional):</p>
<input
className="form-control"
<Input
value={pass}
onChange={this.onPasswordChange}
placeholder={translateRaw('x_Password')}

View File

@ -3,6 +3,7 @@ import { stripHexPrefix } from 'libs/values';
import React, { PureComponent } from 'react';
import translate, { translateRaw } from 'translations';
import { TogglablePassword } from 'components';
import { Input } from 'components/ui';
export interface PrivateKeyValue {
key: string;
@ -53,29 +54,32 @@ export class PrivateKeyDecrypt extends PureComponent<Props> {
return (
<form id="selectedTypeKey" onSubmit={this.unlock}>
<div className="form-group">
<TogglablePassword
value={key}
rows={4}
placeholder={translateRaw('x_PrivKey2')}
isValid={isValidPkey}
isTextareaWhenVisible={true}
onChange={this.onPkeyChange}
onEnter={this.props.onUnlock}
/>
<div className="input-group-wrapper">
<label className="input-group">
<TogglablePassword
value={key}
rows={4}
placeholder={translateRaw('x_PrivKey2')}
isValid={isValidPkey}
onChange={this.onPkeyChange}
onEnter={this.props.onUnlock}
/>
</label>
</div>
{isValidPkey &&
isPassRequired && (
<div className="form-group">
<p>{translate('ADD_Label_3')}</p>
<input
className={`form-control ${password.length > 0 ? 'is-valid' : 'is-invalid'}`}
value={password}
onChange={this.onPasswordChange}
onKeyDown={this.onKeyDown}
placeholder={translateRaw('x_Password')}
type="password"
/>
<div className="input-group-wrapper">
<label className="input-group">
<div className="input-group-header">{translate('ADD_Label_3')}</div>
<Input
className={`form-control ${password.length > 0 ? 'is-valid' : 'is-invalid'}`}
value={password}
onChange={this.onPasswordChange}
onKeyDown={this.onKeyDown}
placeholder={translateRaw('x_Password')}
type="password"
/>
</label>
</div>
)}
<button className="btn btn-block btn-primary" disabled={unlockDisabled}>

View File

@ -3,6 +3,7 @@ import translate from 'translations';
import { donationAddressMap } from 'config';
import { isValidETHAddress } from 'libs/validators';
import { AddressOnlyWallet } from 'libs/wallet';
import { TextArea } from 'components/ui';
interface Props {
onUnlock(param: any): void;
@ -24,10 +25,8 @@ export class ViewOnlyDecrypt extends PureComponent<Props, State> {
return (
<div id="selectedUploadKey">
<form className="form-group" onSubmit={this.openWallet}>
<textarea
className={`form-control
${isValid ? 'is-valid' : 'is-invalid'}
`}
<TextArea
className={isValid ? 'is-valid' : 'is-invalid'}
value={address}
onChange={this.changeAddress}
onKeyDown={this.handleEnterKey}

View File

@ -13,7 +13,7 @@ export { default as Header } from './Header';
export { default as Footer } from './Footer';
export { default as BalanceSidebar } from './BalanceSidebar';
export { default as PaperWallet } from './PaperWallet';
export { default as AlphaAgreement } from './AlphaAgreement';
export { default as BetaAgreement } from './BetaAgreement';
export { default as TXMetaDataPanel } from './TXMetaDataPanel';
export { default as WalletDecrypt } from './WalletDecrypt';
export { default as TogglablePassword } from './TogglablePassword';

View File

@ -0,0 +1,40 @@
import React from 'react';
import { toChecksumAddress } from 'ethereumjs-util';
import NewTabLink from './NewTabLink';
import { IWallet } from 'libs/wallet';
import { BlockExplorerConfig } from 'types/network';
interface BaseProps {
explorer?: BlockExplorerConfig | null;
}
interface AddressProps extends BaseProps {
address: string;
}
interface WalletProps extends BaseProps {
wallet: IWallet;
}
type Props = AddressProps | WalletProps;
const isAddressProps = (props: Props): props is AddressProps =>
typeof (props as AddressProps).address === 'string';
const Address: React.SFC<Props> = props => {
let addr = '';
if (isAddressProps(props)) {
addr = props.address;
} else {
addr = props.wallet.getAddressString();
}
addr = toChecksumAddress(addr);
if (props.explorer) {
return <NewTabLink href={props.explorer.addressUrl(addr)}>{addr}</NewTabLink>;
} else {
return <React.Fragment>{addr}</React.Fragment>;
}
};
export default Address;

View File

@ -1,5 +1,6 @@
import React from 'react';
import { withConditional } from 'components/hocs';
import { Input } from 'components/ui';
const Input: React.SFC<React.InputHTMLAttributes<any>> = props => <input {...props} />;
export const ConditionalInput = withConditional(Input);
const inpt: React.SFC<React.InputHTMLAttributes<any>> = props => <Input {...props} />;
export const ConditionalInput = withConditional(inpt);

View File

@ -1,123 +1,63 @@
import React, { PureComponent } from 'react';
import classnames from 'classnames';
import DropdownShell from './DropdownShell';
import React from 'react';
import Select, { ReactSelectProps } from 'react-select';
interface Props<T> {
value: T | undefined;
options: T[];
ariaLabel: string;
label?: string;
extra?: any;
size?: string;
color?: string;
menuAlign?: string;
formatTitle?(option: T): any;
onChange?(value: T): void;
interface Props extends ReactSelectProps {
className?: string;
options: any;
onChange: any;
}
interface State {
search: string;
}
export default class DropdownComponent<T> extends PureComponent<Props<T>, State> {
export default class Dropdown extends React.Component<Props> {
public state = {
search: ''
selectedOption: { value: '', label: '' },
hasBlurred: false
};
private dropdownShell: DropdownShell | null;
public handleChange = selectedOption => {
this.setState({ selectedOption });
};
public formatOptions = options => {
if (typeof options[0] === 'object') {
return options;
}
const formatted = options.map(opt => {
return { value: opt, label: opt };
});
return formatted;
};
public render() {
const { ariaLabel, color, size } = this.props;
const { onChange } = this.props;
const { selectedOption } = this.state;
const value = selectedOption && selectedOption.value;
const options = this.formatOptions(this.props.options);
return (
<DropdownShell
renderLabel={this.renderLabel}
renderOptions={this.renderOptions}
size={size}
color={color}
ariaLabel={ariaLabel}
ref={el => (this.dropdownShell = el)}
<Select
// use ref to prevent <label /> from stealing focus when used inline with an input
ref={el => {
if (!!el && !!(el as any).control) {
(el as any).control.addEventListener('click', e => {
e.preventDefault();
});
}
}}
className={`${this.props.className} ${this.state.hasBlurred ? 'has-blurred' : ''}`}
value={value}
onChange={obj => {
this.handleChange(obj);
onChange();
}}
{...this.props}
onBlur={e => {
this.setState({ hasBlurred: true });
if (this.props && this.props.onBlur) {
this.props.onBlur(e);
}
}}
options={options}
/>
);
}
private renderLabel = () => {
const { value } = this.props;
const labelStr = this.props.label ? `${this.props.label}:` : '';
return (
<span>
{labelStr} {this.formatTitle(value)}
</span>
);
};
private renderOptions = () => {
const { options, value, menuAlign, extra } = this.props;
const { search } = this.state;
const searchable = options.length > 20;
const menuClass = classnames({
'dropdown-menu': true,
[`dropdown-menu-${menuAlign || ''}`]: !!menuAlign
});
const searchableStyle = {
maxHeight: '300px',
overflowY: 'auto'
};
const searchRegex = new RegExp(search, 'gi');
const onSearchChange = e => {
this.setState({ search: e.target.value });
};
return (
<ul className={menuClass} style={searchable ? searchableStyle : {}}>
{searchable && (
<input
className="form-control"
placeholder={'Search'}
onChange={onSearchChange}
value={search}
/>
)}
{options
.filter(option => {
if (searchable && search.length) {
return option.toString().match(searchRegex);
}
return true;
})
.map((option, i) => {
return (
<li key={i}>
<a
className={option === value ? 'active' : ''}
onClick={this.onChange.bind(null, option)}
>
{this.props.formatTitle ? this.formatTitle(option) : option}
</a>
</li>
);
})}
{extra && <li key={'separator'} role="separator" className="divider" />}
{extra}
</ul>
);
};
private formatTitle = (option: any) => {
if (this.props.formatTitle) {
return this.props.formatTitle(option);
} else {
return option;
}
};
private onChange = (value: any) => {
if (this.props.onChange) {
this.props.onChange(value);
}
if (this.dropdownShell) {
this.dropdownShell.close();
}
};
}

View File

@ -1,8 +1,7 @@
.Help {
display: inline-block;
margin-left: 8px;
padding: 4px;
box-sizing: content-box;
box-sizing: border-box;
line-height: inherit;
vertical-align: top;
transition: opacity 0.3s;
@ -14,17 +13,17 @@
}
&-x1 {
height: 16px;
width: 16px;
height: 1rem;
width: 1rem;
}
&-x2 {
height: 24px;
width: 24px;
height: 1.5rem;
width: 1.5rem;
}
&-x3 {
height: 36px;
width: 36px;
height: 2rem;
width: 2rem;
}
}

View File

@ -0,0 +1,84 @@
@import 'common/sass/variables';
.example-form {
max-width: 744px;
margin: auto;
}
.input-group {
margin: auto;
display: flex;
flex-direction: column;
> .TogglablePassword {
width: 100%;
}
&-header {
display: flex;
font-size: 1rem;
margin-bottom: 8px;
font-weight: 400;
align-items: center;
flex-wrap: wrap;
> *:first-child {
margin-right: 8px;
}
> .flex-spacer {
flex-grow: 1;
}
> .small {
color: rgba(0, 0, 0, 0.54);
}
}
&-input {
width: 100%;
border: 1px solid #e5ecf3;
border-radius: 2px;
padding: 0.75rem 1rem;
font-weight: 400;
font-size: 1rem;
color: rgba(0, 0, 0, 0.87);
box-shadow: inset 0 1px 0 0 rgba(63, 63, 68, 0.05);
transition: border-color 120ms, box-shadow 120ms;
margin-bottom: 1rem;
&::placeholder {
color: rgba(0, 0, 0, 0.3);
}
&:not([disabled]):not([readonly]) {
&.invalid.has-blurred.has-value {
border-color: $brand-danger;
box-shadow: inset 0px 0px 0px 1px $brand-danger;
}
&:focus {
border-color: #4295bc;
box-shadow: inset 0px 0px 0px 1px #4295bc;
&.valid {
border-color: #8dd17b;
box-shadow: inset 0px 0px 0px 1px #8dd17b;
}
}
}
}
}
.input-group-inline-dropdown {
display: flex;
flex-direction: row;
font-size: 1rem;
flex-wrap: wrap;
> .input-group-header {
width: 100%;
}
> .input-group-input {
flex-grow: 1;
width: auto;
}
> .Select {
margin-left: 8px;
}
}
.Swap-dropdown {
.Select-input {
left: 24px;
}
}

View File

@ -0,0 +1,40 @@
import React, { HTMLProps } from 'react';
import './Input.scss';
interface State {
hasBlurred: boolean;
}
class Input extends React.Component<HTMLProps<HTMLInputElement>, State> {
public state: State = {
hasBlurred: false
};
public render() {
return (
<input
{...this.props}
onBlur={e => {
this.setState({ hasBlurred: true });
if (this.props && this.props.onBlur) {
this.props.onBlur(e);
}
}}
onWheel={this.props.type === 'number' ? this.preventNumberScroll : undefined}
className={`input-group-input ${this.props.className} ${
this.state.hasBlurred ? 'has-blurred' : ''
} ${!!this.props.value && this.props.value.toString().length > 0 ? 'has-value' : ''}`}
/>
);
}
// When number inputs are scrolled on while in focus, the number changes. So we blur
// it if it's focused to prevent that behavior, without preventing the scroll.
private preventNumberScroll(ev: React.WheelEvent<HTMLInputElement>) {
if (document.activeElement === ev.currentTarget) {
ev.currentTarget.blur();
}
}
}
export default Input;

View File

@ -4,6 +4,8 @@
$m-background: #fff;
$m-window-padding-w: 20px;
$m-window-padding-h: 30px;
$m-window-padding-w-mobile: 10px;
$m-window-padding-h-mobile: 10px;
$m-header-height: 62px;
$m-header-padding: 1rem 2rem 0.5rem 2rem;
$m-content-padding: 1.5rem 2rem;
@ -24,7 +26,7 @@ $m-anim-speed: 400ms;
.Modal {
position: fixed;
top: 50%;
top: $m-window-padding-h;
left: 50%;
width: initial;
max-width: 95%;
@ -33,7 +35,7 @@ $m-anim-speed: 400ms;
max-height: calc(100% - #{$m-window-padding-h * 2});
background: $m-background;
border-radius: 2px;
transform: translate(-50%, -50%);
transform: translateX(-50%);
z-index: $zindex-modal;
overflow: hidden;
display: flex;
@ -111,7 +113,10 @@ $m-anim-speed: 400ms;
// Mobile styles
@media(max-width: $screen-sm) {
width: calc(100% - 40px) !important;
top: $m-window-padding-h-mobile;
width: calc(100% - #{$m-window-padding-w-mobile}) !important;
max-width: calc(100% - #{$m-window-padding-w-mobile * 2});
max-height: calc(100% - #{$m-window-padding-h-mobile * 2});
}
}

View File

@ -97,6 +97,10 @@ export default class Modal extends PureComponent<Props, {}> {
};
private escapeListner = (ev: KeyboardEvent) => {
if (!this.props.isOpen) {
return;
}
// Don't trigger if they hit escape while on an input
if (ev.target) {
if (

View File

@ -0,0 +1,123 @@
import React, { PureComponent } from 'react';
import classnames from 'classnames';
import DropdownShell from './DropdownShell';
interface Props<T> {
value: T | undefined;
options: T[];
ariaLabel: string;
label?: string;
extra?: any;
size?: string;
color?: string;
menuAlign?: string;
formatTitle?(option: T): any;
onChange?(value: T): void;
}
interface State {
search: string;
}
export default class DropdownComponent<T> extends PureComponent<Props<T>, State> {
public state = {
search: ''
};
private dropdownShell: DropdownShell | null;
public render() {
const { ariaLabel, color, size } = this.props;
return (
<DropdownShell
renderLabel={this.renderLabel}
renderOptions={this.renderOptions}
size={size}
color={color}
ariaLabel={ariaLabel}
ref={el => (this.dropdownShell = el)}
/>
);
}
private renderLabel = () => {
const { value } = this.props;
const labelStr = this.props.label ? `${this.props.label}:` : '';
return (
<span>
{labelStr} {this.formatTitle(value)}
</span>
);
};
private renderOptions = () => {
const { options, value, menuAlign, extra } = this.props;
const { search } = this.state;
const searchable = options.length > 20;
const menuClass = classnames({
'dropdown-menu': true,
[`dropdown-menu-${menuAlign || ''}`]: !!menuAlign
});
const searchableStyle = {
maxHeight: '300px',
overflowY: 'auto'
};
const searchRegex = new RegExp(search, 'gi');
const onSearchChange = e => {
this.setState({ search: e.target.value });
};
return (
<ul className={menuClass} style={searchable ? searchableStyle : {}}>
{searchable && (
<input
className="form-control"
placeholder={'Search'}
onChange={onSearchChange}
value={search}
/>
)}
{options
.filter(option => {
if (searchable && search.length) {
return option.toString().match(searchRegex);
}
return true;
})
.map((option, i) => {
return (
<li key={i}>
<a
className={option === value ? 'active' : ''}
onClick={this.onChange.bind(null, option)}
>
{this.props.formatTitle ? this.formatTitle(option) : option}
</a>
</li>
);
})}
{extra && <li key={'separator'} role="separator" className="divider" />}
{extra}
</ul>
);
};
private formatTitle = (option: any) => {
if (this.props.formatTitle) {
return this.props.formatTitle(option);
} else {
return option;
}
};
private onChange = (value: any) => {
if (this.props.onChange) {
this.props.onChange(value);
}
if (this.dropdownShell) {
this.dropdownShell.close();
}
};
}

View File

@ -0,0 +1,7 @@
.SimpleButton {
display: flex;
align-items: center;
> .Spinner {
margin-right: 16px;
}
}

View File

@ -1,5 +1,6 @@
import React, { Component } from 'react';
import Spinner from './Spinner';
import './SimpleButton.scss';
const DEFAULT_BUTTON_TYPE = 'primary';
const DEFAULT_BUTTON_SIZE = 'lg';
@ -29,8 +30,8 @@ export default class SimpleButton extends Component<Props, {}> {
<div>
<button onClick={onClick} disabled={loading || disabled} className={this.computedClass()}>
{loading ? (
<div>
<Spinner /> {loadingText || text}
<div className="SimpleButton">
<Spinner light={true} /> {loadingText || text}
</div>
) : (
<div>{text}</div>

View File

@ -1,26 +0,0 @@
import React, { PureComponent } from 'react';
import Dropdown from './Dropdown';
interface Props {
value: string | undefined;
options: string[];
ariaLabel?: string;
onChange(value: string): void;
}
export default class SimpleDropdown extends PureComponent<Props, void> {
public render() {
const { options, value, onChange, ariaLabel } = this.props;
const StringDropdown = Dropdown as new () => Dropdown<string>;
return (
<StringDropdown
options={options}
value={value}
onChange={onChange}
ariaLabel={ariaLabel || 'dropdown'}
/>
);
}
}

View File

@ -112,3 +112,16 @@
padding-right: 1px;
}
}
.swap-option {
&-wrapper {
font-size: 1rem;
display: flex;
align-items: center;
padding: 0.75rem 1rem;
}
&-img {
width: 1rem;
margin-right: 8px;
}
}

View File

@ -1,6 +1,6 @@
import React, { PureComponent } from 'react';
import './SwapDropdown.scss';
import classnames from 'classnames';
import { DropDown } from 'components/ui';
export interface SingleCoin {
id: string;
@ -10,91 +10,70 @@ export interface SingleCoin {
}
interface Props<T> {
ariaLabel: string;
options: SingleCoin[];
value: string;
onChange(value: T): void;
}
class SwapDropdown<T> extends PureComponent<Props<T>, {}> {
public state = {
open: false
const ValueComp: React.SFC = (props: any) => {
return (
<div className={`${props.className} swap-option-wrapper`}>
<img src={props.value.img} className="swap-option-img" />
<span className="swap-option-label">{props.value.label}</span>
</div>
);
};
const OptionComp: React.SFC = (props: any) => {
const handleMouseDown = event => {
event.preventDefault();
event.stopPropagation();
props.onSelect(props.option, event);
};
private dropdown: HTMLElement | null;
public componentDidMount() {
document.addEventListener('click', this.clickHandler);
}
public componentWillUnmount() {
document.removeEventListener('click', this.clickHandler);
}
public handleClickOutside() {
this.toggleDropdown();
}
public render() {
const { open } = this.state;
const { options, value } = this.props;
const dropdownGrid = classnames(open && 'open', 'SwapDropdown-grid');
const mappedCoins = options.sort((a, b) => (a.id > b.id ? 1 : -1)).map((coin: SingleCoin) => {
const cn = classnames(coin.status !== 'available' && 'inactive', 'SwapDropdown-item');
return (
<li className={cn} key={coin.id}>
<a onClick={coin.status === 'available' ? this.onChange.bind(null, coin.id) : null}>
<img src={coin.image} height="20" width="20" />
{/* <div className="SwapDropdown-desc"> */}
<strong>{coin.id}</strong>
<br />
<small>{coin.name}</small>
{/* </div> */}
</a>
</li>
);
});
return (
<div className="SwapDropdown" ref={el => (this.dropdown = el)}>
<button onClick={this.toggleDropdown}>
{value}
<i className="caret" />
</button>
<ul className={dropdownGrid}>{mappedCoins}</ul>
</div>
);
}
private toggleDropdown = () => {
this.setState({
open: !this.state.open
});
const handleMouseEnter = event => {
props.onFocus(props.option, event);
};
private onChange = (value: any) => {
this.props.onChange(value);
if (this.state.open) {
this.setState({
open: false
});
}
};
private clickHandler = (ev: Event) => {
if (!this.state.open || !this.dropdown) {
const handleMouseMove = event => {
if (props.isFocused) {
return;
}
if (
this.dropdown !== ev.target &&
ev.target instanceof HTMLElement &&
!this.dropdown.contains(ev.target)
) {
this.setState({
open: false
});
}
props.onFocus(props.option, event);
};
return (
<div
className={`${props.className} swap-option-wrapper`}
onMouseDown={handleMouseDown}
onMouseEnter={handleMouseEnter}
onMouseMove={handleMouseMove}
>
<img src={props.option.img} className="swap-option-img" />
<span className="swap-option-label">{props.option.label}</span>
</div>
);
};
class SwapDropdown<T> extends PureComponent<Props<T>> {
public render() {
const { options, value, onChange } = this.props;
const mappedOptions = options.map(opt => {
return { label: opt.id, value: opt.name, img: opt.image, status: opt.status };
});
return (
<DropDown
className="Swap-dropdown"
options={mappedOptions}
optionComponent={(props: any) => {
return <OptionComp {...props} />;
}}
value={value}
clearable={false}
onChange={onChange}
valueComponent={(props: any) => {
return <ValueComp {...props} />;
}}
/>
);
}
}
export default SwapDropdown;

View File

@ -0,0 +1,30 @@
import React, { HTMLProps } from 'react';
import './Input.scss';
interface State {
hasBlurred: boolean;
}
class TextArea extends React.Component<HTMLProps<HTMLTextAreaElement>, State> {
public state: State = {
hasBlurred: false
};
public render() {
return (
<textarea
{...this.props}
onBlur={e => {
this.setState({ hasBlurred: true });
if (this.props && this.props.onBlur) {
this.props.onBlur(e);
}
}}
className={`input-group-input ${this.props.className} ${
this.state.hasBlurred ? 'has-blurred' : ''
}`}
/>
);
}
}
export default TextArea;

View File

@ -1,5 +1,6 @@
export { default as ColorDropdown } from './ColorDropdown';
export { default as Dropdown } from './Dropdown';
export { default as OldDropDown } from './OldDropdown';
export { default as DropDown } from './Dropdown';
export { default as DropdownShell } from './DropdownShell';
export { default as Identicon } from './Identicon';
export { default as Modal } from './Modal';
@ -12,6 +13,9 @@ export { default as SwapDropdown } from './SwapDropdown';
export { default as Tooltip } from './Tooltip';
export { default as TitleBar } from './TitleBar';
export { default as HelpLink } from './HelpLink';
export { default as Input } from './Input';
export { default as TextArea } from './TextArea';
export { default as Address } from './Address';
export * from './ConditionalInput';
export * from './Expandable';
export * from './InlineSpinner';

View File

@ -1,31 +1,26 @@
import React from 'react'; // For ANNOUNCEMENT_MESSAGE jsx
import NewTabLink from 'components/ui/NewTabLink';
import { getValues } from '../utils/helpers';
import packageJson from '../../package.json';
import { GasPriceSetting } from 'types/network';
export const languages = require('./languages.json');
export const discordURL = 'https://discord.gg/VSaTXEA';
// Displays in the footer
export const VERSION = `${packageJson.version} (BETA)`;
export const N_FACTOR = 8192;
// Displays at the top of the site, make message empty string to remove.
// Type can be primary, warning, danger, success, or info.
// Type can be primary, warning, danger, success, info, or blank for grey.
// Message must be a JSX element if you want to use HTML.
export const ANNOUNCEMENT_TYPE = 'warning';
export const ANNOUNCEMENT_TYPE = '';
export const ANNOUNCEMENT_MESSAGE = (
<div>
This is an early build of MyCrypto Beta. Please only use for testing, or use production at{' '}
<a href="https://mycrypto.com">{'https://mycrypto.com'}</a>.
<br />
<span className="hidden-xs">
If you're interested in recieving updates about the MyCrypto Beta, you can subscribe via{' '}
<a href="https://mycrypto.us17.list-manage.com/subscribe?u=6092be560275431280b0ffbc1&id=a03ef384e4">
mailchimp
</a>{' '}
:)
</span>
</div>
<React.Fragment>
This is a Beta version of MyCrypto. Please submit any bug reports to our{' '}
<NewTabLink href="https://github.com/MyCryptoHQ/MyCrypto/issues">GitHub</NewTabLink>, and join
the discussion on <NewTabLink href={discordURL}>Discord</NewTabLink>.
</React.Fragment>
);
const etherScan = 'https://etherscan.io';

View File

@ -1,6 +1,6 @@
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { AlphaAgreement, Footer, Header } from 'components';
import { BetaAgreement, Footer, Header } from 'components';
import { AppState } from 'reducers';
import Notifications from './Notifications';
import OfflineTab from './OfflineTab';
@ -31,7 +31,7 @@ class TabSection extends Component<Props, {}> {
<div className="flex-spacer" />
<Footer latestBlock={latestBlock} />
<Notifications />
<AlphaAgreement />
<BetaAgreement />
</div>
);
}

View File

@ -9,9 +9,8 @@ import {
TSignTransactionFailed
} from 'actions/transaction';
import { computeIndexingHash } from 'libs/transaction';
import { QRCode } from 'components/ui';
import { QRCode, TextArea } from 'components/ui';
import EthTx from 'ethereumjs-tx';
import classnames from 'classnames';
import { SendButton } from 'components/SendButton';
import { toBuffer, bufferToHex } from 'ethereumjs-util';
import { getSerializedTransaction } from 'selectors/transaction';
@ -40,11 +39,6 @@ class BroadcastTx extends Component<Props> {
public render() {
const { userInput } = this.state;
const { stateTransaction } = this.props;
const inputClasses = classnames({
'form-control': true,
'is-valid': !!stateTransaction,
'is-invalid': !stateTransaction
});
const currentPath = this.props.match.url;
return (
<TabSection isUnavailableOffline={true}>
@ -59,13 +53,19 @@ class BroadcastTx extends Component<Props> {
<p className="BroadcastTx-help">
Paste a signed transaction and press the "SEND TRANSACTION" button.
</p>
<label>{translateRaw('SEND_signed')}</label>
<textarea
className={inputClasses}
rows={7}
value={userInput}
onChange={this.handleChange}
/>
<div className="input-group-wrapper InteractForm-interface">
<label className="input-group">
<div className="input-group-header">{translateRaw('SEND_signed')}</div>
<TextArea
className={stateTransaction ? '' : 'invalid'}
rows={7}
value={userInput}
onChange={this.handleChange}
/>
</label>
</div>
<SendButton onlyTransactionParameters={true} />
<div className="BroadcastTx-qr">

View File

@ -2,6 +2,7 @@ import React from 'react';
import translate from 'translations';
import { isValidTxHash, isValidETHAddress } from 'libs/validators';
import './TxHashInput.scss';
import { Input } from 'components/ui';
interface Props {
hash?: string;
@ -26,14 +27,14 @@ export default class TxHashInput extends React.Component<Props, State> {
public render() {
const { hash } = this.state;
const validClass = hash ? (isValidTxHash(hash) ? 'is-valid' : 'is-invalid') : '';
const validClass = hash ? (isValidTxHash(hash) ? '' : 'invalid') : '';
return (
<form className="TxHashInput" onSubmit={this.handleSubmit}>
<input
<Input
value={hash}
placeholder="0x16e521..."
className={`TxHashInput-field form-control ${validClass}`}
className={`TxHashInput-field ${validClass}`}
onChange={this.handleChange}
/>

View File

@ -1,15 +1,17 @@
import React from 'react';
import { connect } from 'react-redux';
import { RouteComponentProps } from 'react-router';
import TabSection from 'containers/TabSection';
import TxHashInput from './components/TxHashInput';
import { TransactionStatus as TransactionStatusComponent } from 'components';
import { NewTabLink } from 'components/ui';
import { getNetworkConfig } from 'selectors/config';
import { getParamFromURL } from 'utils/helpers';
import { AppState } from 'reducers';
import { NetworkConfig } from 'types/network';
import './index.scss';
interface Props {
interface StateProps {
network: NetworkConfig;
}
@ -17,11 +19,20 @@ interface State {
hash: string;
}
type Props = StateProps & RouteComponentProps<{}>;
class CheckTransaction extends React.Component<Props, State> {
public state: State = {
hash: ''
};
public componentDidMount() {
const hash = getParamFromURL(this.props.location.search, 'txHash');
if (hash) {
this.setState({ hash });
}
}
public render() {
const { network } = this.props;
const { hash } = this.state;
@ -43,7 +54,7 @@ class CheckTransaction extends React.Component<Props, State> {
</React.Fragment>
)}
</p>
<TxHashInput onSubmit={this.handleHashSubmit} />
<TxHashInput hash={hash} onSubmit={this.handleHashSubmit} />
</section>
{hash && (
@ -64,6 +75,6 @@ class CheckTransaction extends React.Component<Props, State> {
};
}
export default connect((state: AppState) => ({
export default connect((state: AppState): StateProps => ({
network: getNetworkConfig(state)
}))(CheckTransaction);

View File

@ -2,13 +2,10 @@
&-field {
margin-top: 0;
&-label {
float: left;
}
&-reset {
float: right;
display: block;
margin: auto;
margin-bottom: 0.25rem;
.fa {
margin-right: 8px;
}

View File

@ -13,6 +13,7 @@ import { FullWalletOnly } from 'components/renderCbs';
import { NonceField, TXMetaDataPanel } from 'components';
import './Deploy.scss';
import { ConfirmationModal } from 'components/ConfirmationModal';
import { TextArea } from 'components/ui';
interface DispatchProps {
setToField: TSetToField;
@ -23,27 +24,30 @@ class DeployClass extends Component<DispatchProps> {
public render() {
const makeContent = () => (
<main className="Deploy Tab-content-pane" role="main">
<div className="Deploy-field form-group">
<h3 className="Deploy-field-label">{translate('CONTRACT_ByteCode')}</h3>
<button className="Deploy-field-reset btn btn-default btn-sm" onClick={this.changeWallet}>
<i className="fa fa-refresh" />
{translate('Change Wallet')}
</button>
<DataFieldFactory
withProps={({ data: { raw, value }, onChange, readOnly }) => (
<textarea
name="byteCode"
placeholder="0x8f87a973e..."
rows={6}
onChange={onChange}
disabled={readOnly}
className={classnames('Deploy-field-input', 'form-control', {
'is-valid': value && value.length > 0
})}
value={raw}
/>
)}
/>
<button className="Deploy-field-reset btn btn-default btn-sm" onClick={this.changeWallet}>
<i className="fa fa-refresh" />
{translate('Change Wallet')}
</button>
<div className="input-group-wrapper Deploy-field">
<label className="input-group">
<div className="input-group-header">{translate('CONTRACT_ByteCode')}</div>
<DataFieldFactory
withProps={({ data: { raw, value }, onChange, readOnly }) => (
<TextArea
name="byteCode"
placeholder="0x8f87a973e..."
rows={6}
onChange={onChange}
disabled={readOnly}
className={classnames('Deploy-field-input', 'form-control', {
'is-valid': value && value.length > 0
})}
value={raw}
/>
)}
/>
</label>
</div>
<div className="row form-group">

View File

@ -1,22 +1,25 @@
import { AmountFieldFactory } from 'components/AmountFieldFactory';
import React from 'react';
import classnames from 'classnames';
import { Input } from 'components/ui';
export const AmountField: React.SFC<{}> = () => (
<label className="InteractExplorer-field form-group">
<h4 className="InteractExplorer-field-label">Value</h4>
<AmountFieldFactory
withProps={({ currentValue: { raw }, isValid, onChange, readOnly }) => (
<input
name="value"
value={raw}
onChange={onChange}
readOnly={readOnly}
className={classnames('InteractExplorer-field-input', 'form-control', {
'is-invalid': !(isValid || raw === '')
})}
/>
)}
/>
</label>
export const AmountField: React.SFC = () => (
<div className="input-group-wrapper InteractExplorer-field">
<label className="input-group">
<div className="input-group-header">Value</div>
<AmountFieldFactory
withProps={({ currentValue: { raw }, isValid, onChange, readOnly }) => (
<Input
name="value"
value={raw}
onChange={onChange}
readOnly={readOnly}
className={classnames('InteractExplorer-field-input', 'form-control', {
'is-invalid': !(isValid || raw === '')
})}
/>
)}
/>
</label>
</div>
);

View File

@ -7,6 +7,7 @@ import { FullWalletOnly } from 'components/renderCbs';
interface OwnProps {
button: React.ReactElement<any>;
}
export class Fields extends Component<OwnProps> {
public render() {
const makeContent = () => (
@ -17,6 +18,7 @@ export class Fields extends Component<OwnProps> {
initialState="advanced"
disableToggle={true}
advancedGasOptions={{ dataField: false }}
resetIncludeExcludeProperties={{ exclude: { fields: ['to'] }, include: {} }}
/>
{this.props.button}
<SigningStatus />
@ -24,7 +26,12 @@ export class Fields extends Component<OwnProps> {
</React.Fragment>
);
const makeDecrypt = () => <WalletDecrypt disabledWallets={DISABLE_WALLETS.READ_ONLY} />;
const makeDecrypt = () => (
<WalletDecrypt
disabledWallets={DISABLE_WALLETS.READ_ONLY}
resetIncludeExcludeProperties={{ exclude: { fields: ['to'] }, include: {} }}
/>
);
return <FullWalletOnly withFullWallet={makeContent} withoutFullWallet={makeDecrypt} />;
}

View File

@ -10,9 +10,10 @@ import { connect } from 'react-redux';
import { Fields } from './components';
import { setDataField, TSetDataField } from 'actions/transaction';
import { Data } from 'libs/units';
import Select from 'react-select';
import { Web3Node } from 'libs/nodes';
import RpcNode from 'libs/nodes/rpc';
import { Input } from 'components/ui';
import Dropdown from 'components/ui/Dropdown';
interface StateProps {
nodeLib: RpcNode | Web3Node;
@ -83,22 +84,25 @@ class InteractExplorerClass extends Component<Props, State> {
return (
<div className="InteractExplorer">
<h3 className="InteractExplorer-title">
{translate('CONTRACT_Interact_Title')}
<span className="InteractExplorer-title-address">{to.raw}</span>
</h3>
<Select
name="exploreContract"
value={selectedFunction as any}
placeholder="Please select a function..."
onChange={this.handleFunctionSelect}
options={contractFunctionsOptions}
clearable={false}
searchable={false}
labelKey="name"
valueKey="contract"
/>
<div className="input-group-wrapper">
<label className="input-group">
<div className="input-group-header">
{translate('CONTRACT_Interact_Title')}
<div className="flex-spacer" />
<span className="small">{to.raw}</span>
</div>
<Dropdown
name="exploreContract"
value={selectedFunction as any}
placeholder="Please select a function..."
onChange={this.handleFunctionSelect}
options={contractFunctionsOptions}
clearable={false}
labelKey="name"
valueKey="contract"
/>
</label>
</div>
{selectedFunction && (
<div key={selectedFunction.name} className="InteractExplorer-func">
@ -107,18 +111,17 @@ class InteractExplorerClass extends Component<Props, State> {
const { type, name } = input;
return (
<label key={name} className="InteractExplorer-func-in form-group">
<h4 className="InteractExplorer-func-in-label">
{name}
<span className="InteractExplorer-func-in-label-type">{type}</span>
</h4>
<input
className="InteractExplorer-func-in-input form-control"
name={name}
value={(inputs[name] && inputs[name].rawData) || ''}
onChange={this.handleInputChange}
/>
</label>
<div key={name} className="input-group-wrapper InteractExplorer-func-in">
<label className="input-group">
<div className="input-group-header">{name + ' ' + type}</div>
<Input
className="InteractExplorer-func-in-input"
name={name}
value={(inputs[name] && inputs[name].rawData) || ''}
onChange={this.handleInputChange}
/>
</label>
</div>
);
})}
{selectedFunction.contract.outputs.map((output, index) => {
@ -126,17 +129,16 @@ class InteractExplorerClass extends Component<Props, State> {
const parsedName = name === '' ? index : name;
return (
<label key={parsedName} className="InteractExplorer-func-out form-group">
<h4 className="InteractExplorer-func-out-label">
{name}
<span className="InteractExplorer-func-out-label-type">{type}</span>
</h4>
<input
className="InteractExplorer-func-out-input form-control"
value={outputs[parsedName] || ''}
disabled={true}
/>
</label>
<div key={parsedName} className="input-group-wrapper InteractExplorer-func-out">
<label className="input-group">
<div className="input-group-header"> {name + ' ' + type}</div>
<Input
className="InteractExplorer-func-out-input "
value={outputs[parsedName] || ''}
disabled={true}
/>
</label>
</div>
);
})}

View File

@ -5,9 +5,10 @@ import { connect } from 'react-redux';
import { AppState } from 'reducers';
import { isValidETHAddress, isValidAbiJson } from 'libs/validators';
import classnames from 'classnames';
import Select from 'react-select';
import { NetworkContract } from 'types/network';
import { donationAddressMap } from 'config';
import { Input, TextArea } from 'components/ui';
import Dropdown from 'components/ui/Dropdown';
interface ContractOption {
name: string;
@ -83,45 +84,44 @@ class InteractForm extends Component<Props, State> {
return (
<div className="InteractForm">
<div className="InteractForm-address row">
<label className="InteractForm-address-field form-group col-sm-6">
<h4>{translate('CONTRACT_Title')}</h4>
<input
placeholder={`ensdomain.eth or ${donationAddressMap.ETH}`}
name="contract_address"
autoComplete="off"
value={address}
className={classnames('InteractForm-address-field-input', 'form-control', {
'is-invalid': !validEthAddress
})}
onChange={this.handleInput('address')}
/>
</label>
<div className="input-group-wrapper InteractForm-address-field col-sm-6">
<label className="input-group">
<div className="input-group-header">{translate('CONTRACT_Title')}</div>
<Input
placeholder={`ensdomain.eth or ${donationAddressMap.ETH}`}
name="contract_address"
autoComplete="off"
value={address}
className={classnames('InteractForm-address-field-input', {
invalid: !validEthAddress
})}
onChange={this.handleInput('address')}
/>
</label>
</div>
<label className="InteractForm-address-contract form-group col-sm-6">
<h4>{translate('CONTRACT_Title_2')}</h4>
<Select
name="interactContract"
className={`${!contract ? 'is-invalid' : ''}`}
value={contract as any}
placeholder={this.state.contractPlaceholder}
onChange={this.handleSelectContract}
options={contractOptions}
clearable={false}
searchable={false}
labelKey="name"
/>
</label>
<div className="input-group-wrapper InteractForm-address-field col-sm-6">
<label className="input-group">
<div className="input-group-header">{translate('CONTRACT_Title_2')}</div>
<Dropdown
className={`${!contract ? 'invalid' : ''}`}
value={contract as any}
placeholder={this.state.contractPlaceholder}
onChange={this.handleSelectContract}
options={contractOptions}
clearable={false}
labelKey="name"
/>
</label>
</div>
</div>
<div className="InteractForm-interface">
<label className="InteractForm-interface-field form-group">
<h4 className="InteractForm-interface-field-label">{translate('CONTRACT_Json')}</h4>
<textarea
<div className="input-group-wrapper InteractForm-interface">
<label className="input-group">
<div className="input-group-header">{translate('CONTRACT_Json')}</div>
<TextArea
placeholder={this.abiJsonPlaceholder}
name="abiJson"
className={classnames('InteractForm-interface-field-input', 'form-control', {
'is-invalid': !validAbiJson
})}
className={`InteractForm-interface-field-input ${validAbiJson ? '' : 'invalid'}`}
onChange={this.handleInput('abiJson')}
value={abiJson}
rows={6}

View File

@ -1,30 +0,0 @@
@import 'common/sass/variables';
@import 'common/sass/mixins';
.Contracts {
&-header {
margin: 0;
text-align: center;
&-tab {
@include reset-button;
color: $ether-blue;
&:hover,
&:active {
opacity: 0.8;
}
&.is-active {
&,
&:hover,
&:active {
color: $text-color;
cursor: default;
opacity: 1;
font-weight: 500;
}
}
}
}
}

View File

@ -1,7 +1,6 @@
import translate from 'translations';
import { Interact } from './components/Interact';
import { Deploy } from './components/Deploy';
import './index.scss';
import { reset, TReset } from 'actions/transaction';
import { resetWallet, TResetWallet } from 'actions/wallet';
import TabSection from 'containers/TabSection';

View File

@ -5,10 +5,6 @@
padding: $space-sm 0;
margin: 0 auto;
&-name {
margin-bottom: $space-md;
}
&-button {
.Spinner {
margin-left: $space-md;

View File

@ -1,10 +1,10 @@
import React, { Component } from 'react';
import classnames from 'classnames';
import { connect } from 'react-redux';
import { AppState } from 'reducers';
import { resolveDomainRequested, TResolveDomainRequested } from 'actions/ens';
import { isValidENSName } from 'libs/validators';
import './NameInput.scss';
import { Input } from 'components/ui';
interface State {
domainToCheck: string;
@ -32,21 +32,20 @@ class NameInput extends Component<Props, State> {
return (
<form className="ENSInput" onSubmit={this.onSubmit}>
<div className="ENSInput-name input-group">
<input
value={domainToCheck}
className={classnames(
'form-control',
!domainToCheck ? '' : isValidDomain ? 'is-valid' : 'is-invalid'
)}
type="text"
placeholder="mycrypto"
onChange={this.onChange}
onFocus={this.onFocus}
onBlur={this.onBlur}
disabled={isLoading}
/>
<span className="input-group-addon">.eth</span>
<div className="input-group-wrapper">
<label className="input-group input-group-inline-dropdown ENSInput-name">
<Input
value={domainToCheck}
className={!domainToCheck ? '' : isValidDomain ? 'is-valid' : 'is-invalid'}
type="text"
placeholder="mycrypto"
onChange={this.onChange}
onFocus={this.onFocus}
onBlur={this.onBlur}
disabled={isLoading}
/>
<span className="input-group-addon">.eth</span>
</label>
</div>
{domainToCheck &&
!isValidDomain &&

View File

@ -1,6 +1,6 @@
import React from 'react';
import { IOwnedDomainRequest } from 'libs/ens';
import { NewTabLink } from 'components/ui';
import { NewTabLink, Address } from 'components/ui';
const lookupLink = name => `https://etherscan.io/enslookup?q=${name}`;
type ChildrenProps = any;
@ -40,7 +40,9 @@ export const NameOwned: React.SFC<IOwnedDomainRequest> = ({
</tr>
<tr>
<td>Owner:</td>
<MonoTd>{ownerAddress}</MonoTd>
<MonoTd>
<Address address={ownerAddress} />
</MonoTd>
</tr>
<tr>
<td>Highest Bidder (Deed Owner): </td>
@ -50,7 +52,9 @@ export const NameOwned: React.SFC<IOwnedDomainRequest> = ({
</tr>
<tr>
<td>Resolved Address: </td>
<MonoTd>{resolvedAddress}</MonoTd>
<MonoTd>
<Address address={resolvedAddress} />
</MonoTd>
</tr>
</tbody>
</table>

Some files were not shown because too many files have changed in this diff Show More