Merge in develop, update a lotta modules.
|
@ -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);
|
||||
|
|
|
@ -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']
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -8,7 +8,7 @@ export interface Pairs {
|
|||
}
|
||||
|
||||
export interface SwapInput {
|
||||
id: string;
|
||||
label: string;
|
||||
amount: number | string;
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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 };
|
||||
|
|
|
@ -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 =
|
||||
|
|
Before Width: | Height: | Size: 51 KiB After Width: | Height: | Size: 47 KiB |
|
@ -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 |
|
@ -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 |
|
@ -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 |
|
@ -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 |
|
@ -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 |
|
@ -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 |
|
@ -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 |
Before Width: | Height: | Size: 2.8 KiB |
|
@ -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 |
|
@ -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 |
|
@ -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 |
|
@ -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 |
Before Width: | Height: | Size: 67 KiB After Width: | Height: | Size: 70 KiB |
|
@ -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 |
|
@ -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>
|
||||
)}
|
||||
/>
|
||||
);
|
||||
|
|
|
@ -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 }) =>
|
||||
|
|
|
@ -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');
|
||||
};
|
||||
}
|
|
@ -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>
|
||||
)}
|
||||
/>
|
||||
);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -41,7 +41,6 @@
|
|||
flex-wrap: nowrap;
|
||||
align-items: center;
|
||||
&-fiat-symbol {
|
||||
height: 18px;
|
||||
width: 18px;
|
||||
margin-right: 0.5rem;
|
||||
text-align: center;
|
||||
|
|
|
@ -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¤cy=USD"
|
||||
href={`https://buy.coinbase.com?code=60c05061-3a76-57be-b1cd-a7afa97bcb8c&address=${address}&crypto_currency=ETH¤cy=USD`}
|
||||
>
|
||||
<div className="Promos-promo-inner">
|
||||
<div className="Promos-promo-text">
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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;
|
|
@ -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');
|
||||
};
|
||||
}
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
)}
|
||||
/>
|
||||
);
|
||||
|
|
|
@ -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>
|
||||
);
|
||||
|
|
|
@ -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>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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'
|
||||
}
|
||||
];
|
||||
|
||||
|
|
|
@ -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>
|
||||
)}
|
||||
/>
|
||||
);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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}`);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}}
|
||||
/>
|
||||
|
|
|
@ -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 />
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
transition-duration: 100ms;
|
||||
|
||||
&.is-active {
|
||||
border-bottom-color: $ether-blue;
|
||||
border-bottom-color: $brand-primary;
|
||||
}
|
||||
|
||||
&.is-disabled {
|
||||
|
|
|
@ -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>
|
||||
)}
|
||||
|
|
|
@ -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>
|
||||
)}
|
||||
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
];
|
||||
|
||||
|
|
|
@ -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)
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -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">
|
||||
Don’t have a wallet? <Link to="/generate">Click here to get one</Link>.
|
||||
Don’t 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) => {
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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}>
|
||||
|
|
|
@ -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')}
|
||||
|
|
|
@ -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}>
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -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;
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
|
@ -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});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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 (
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
};
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
.SimpleButton {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
> .Spinner {
|
||||
margin-right: 16px;
|
||||
}
|
||||
}
|
|
@ -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>
|
||||
|
|
|
@ -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'}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
|
@ -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';
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -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>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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">
|
||||
|
|
|
@ -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}
|
||||
/>
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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">
|
||||
|
|
|
@ -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>
|
||||
);
|
||||
|
|
|
@ -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} />;
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
);
|
||||
})}
|
||||
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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';
|
||||
|
|
|
@ -5,10 +5,6 @@
|
|||
padding: $space-sm 0;
|
||||
margin: 0 auto;
|
||||
|
||||
&-name {
|
||||
margin-bottom: $space-md;
|
||||
}
|
||||
|
||||
&-button {
|
||||
.Spinner {
|
||||
margin-left: $space-md;
|
||||
|
|
|
@ -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 &&
|
||||
|
|
|
@ -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>
|
||||
|
|