commit
6f32bdc216
|
@ -58,4 +58,7 @@ v8-compile-cache-0/
|
||||||
package-lock.json
|
package-lock.json
|
||||||
|
|
||||||
# Favicon cache
|
# Favicon cache
|
||||||
.wwp-cache
|
.wwp-cache
|
||||||
|
|
||||||
|
# MacOSX
|
||||||
|
.DS_Store
|
|
@ -1,4 +1,4 @@
|
||||||
# MyCrypto Beta (VISIT [MyCryptoHQ/mycrypto.com](https://github.com/MyCryptoHQ/mycrypto.com) for the current site)<br/>Just looking to download? Grab our [latest release](https://github.com/MyCryptoHQ/MyCrypto/releases)
|
# MyCrypto Beta RC (VISIT [MyCryptoHQ/mycrypto.com](https://github.com/MyCryptoHQ/mycrypto.com) for the current site)<br/>Just looking to download? Grab our [latest release](https://github.com/MyCryptoHQ/MyCrypto/releases)
|
||||||
|
|
||||||
[![Greenkeeper badge](https://badges.greenkeeper.io/MyCryptoHq/MyCrypto.svg)](https://greenkeeper.io/)
|
[![Greenkeeper badge](https://badges.greenkeeper.io/MyCryptoHq/MyCrypto.svg)](https://greenkeeper.io/)
|
||||||
[![Coverage Status](https://coveralls.io/repos/github/MyCryptoHQ/MyCrypto/badge.svg?branch=develop)](https://coveralls.io/github/MyCryptoHQ/MyCrypto?branch=develop)
|
[![Coverage Status](https://coveralls.io/repos/github/MyCryptoHQ/MyCrypto/badge.svg?branch=develop)](https://coveralls.io/github/MyCryptoHQ/MyCrypto?branch=develop)
|
||||||
|
|
|
@ -10,9 +10,11 @@ import Swap from 'containers/Tabs/Swap';
|
||||||
import SignAndVerifyMessage from 'containers/Tabs/SignAndVerifyMessage';
|
import SignAndVerifyMessage from 'containers/Tabs/SignAndVerifyMessage';
|
||||||
import BroadcastTx from 'containers/Tabs/BroadcastTx';
|
import BroadcastTx from 'containers/Tabs/BroadcastTx';
|
||||||
import CheckTransaction from 'containers/Tabs/CheckTransaction';
|
import CheckTransaction from 'containers/Tabs/CheckTransaction';
|
||||||
|
import SupportPage from 'containers/Tabs/SupportPage';
|
||||||
import ErrorScreen from 'components/ErrorScreen';
|
import ErrorScreen from 'components/ErrorScreen';
|
||||||
import PageNotFound from 'components/PageNotFound';
|
import PageNotFound from 'components/PageNotFound';
|
||||||
import LogOutPrompt from 'components/LogOutPrompt';
|
import LogOutPrompt from 'components/LogOutPrompt';
|
||||||
|
import QrSignerModal from 'containers/QrSignerModal';
|
||||||
import { TitleBar } from 'components/ui';
|
import { TitleBar } from 'components/ui';
|
||||||
import { Store } from 'redux';
|
import { Store } from 'redux';
|
||||||
import { pollOfflineStatus, TPollOfflineStatus } from 'actions/config';
|
import { pollOfflineStatus, TPollOfflineStatus } from 'actions/config';
|
||||||
|
@ -50,6 +52,7 @@ class RootClass extends Component<Props, State> {
|
||||||
public componentDidMount() {
|
public componentDidMount() {
|
||||||
this.props.pollOfflineStatus();
|
this.props.pollOfflineStatus();
|
||||||
this.props.setUnitMeta(this.props.networkUnit);
|
this.props.setUnitMeta(this.props.networkUnit);
|
||||||
|
this.addBodyClasses();
|
||||||
}
|
}
|
||||||
|
|
||||||
public componentDidCatch(error: Error) {
|
public componentDidCatch(error: Error) {
|
||||||
|
@ -84,6 +87,7 @@ class RootClass extends Component<Props, State> {
|
||||||
<Route path="/sign-and-verify-message" component={SignAndVerifyMessage} />
|
<Route path="/sign-and-verify-message" component={SignAndVerifyMessage} />
|
||||||
<Route path="/tx-status" component={CheckTransaction} exact={true} />
|
<Route path="/tx-status" component={CheckTransaction} exact={true} />
|
||||||
<Route path="/pushTx" component={BroadcastTx} />
|
<Route path="/pushTx" component={BroadcastTx} />
|
||||||
|
<Route path="/support-us" component={SupportPage} exact={true} />
|
||||||
<RouteNotFound />
|
<RouteNotFound />
|
||||||
</Switch>
|
</Switch>
|
||||||
</CaptureRouteNotFound>
|
</CaptureRouteNotFound>
|
||||||
|
@ -95,18 +99,40 @@ class RootClass extends Component<Props, State> {
|
||||||
: BrowserRouter;
|
: BrowserRouter;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Provider store={store} key={Math.random()}>
|
<React.Fragment>
|
||||||
<Router key={Math.random()}>
|
<Provider store={store} key={Math.random()}>
|
||||||
<React.Fragment>
|
<Router key={Math.random()}>
|
||||||
{process.env.BUILD_ELECTRON && <TitleBar />}
|
<React.Fragment>
|
||||||
{routes}
|
{process.env.BUILD_ELECTRON && <TitleBar />}
|
||||||
<LegacyRoutes />
|
{routes}
|
||||||
<LogOutPrompt />
|
<LegacyRoutes />
|
||||||
</React.Fragment>
|
<LogOutPrompt />
|
||||||
</Router>
|
<QrSignerModal />
|
||||||
</Provider>
|
</React.Fragment>
|
||||||
|
</Router>
|
||||||
|
</Provider>
|
||||||
|
<div id="ModalContainer" />
|
||||||
|
</React.Fragment>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private addBodyClasses() {
|
||||||
|
const classes = [];
|
||||||
|
|
||||||
|
if (process.env.BUILD_ELECTRON) {
|
||||||
|
classes.push('is-electron');
|
||||||
|
|
||||||
|
if (navigator.appVersion.includes('Win')) {
|
||||||
|
classes.push('is-windows');
|
||||||
|
} else if (navigator.appVersion.includes('Mac')) {
|
||||||
|
classes.push('is-osx');
|
||||||
|
} else {
|
||||||
|
classes.push('is-linux');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
document.body.className += ` ${classes.join(' ')}`;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const LegacyRoutes = withRouter(props => {
|
const LegacyRoutes = withRouter(props => {
|
||||||
|
|
|
@ -1,10 +1,15 @@
|
||||||
import * as interfaces from './actionTypes';
|
import * as interfaces from './actionTypes';
|
||||||
import { TypeKeys } from './constants';
|
import { TypeKeys } from './constants';
|
||||||
|
|
||||||
export type TToggleOffline = typeof toggleOffline;
|
export function setOnline(): interfaces.SetOnlineAction {
|
||||||
export function toggleOffline(): interfaces.ToggleOfflineAction {
|
|
||||||
return {
|
return {
|
||||||
type: TypeKeys.CONFIG_TOGGLE_OFFLINE
|
type: TypeKeys.CONFIG_SET_ONLINE
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function setOffline(): interfaces.SetOfflineAction {
|
||||||
|
return {
|
||||||
|
type: TypeKeys.CONFIG_SET_OFFLINE
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -48,6 +53,14 @@ export function changeNodeIntent(payload: string): interfaces.ChangeNodeIntentAc
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type TChangeNodeIntentOneTime = typeof changeNodeIntentOneTime;
|
||||||
|
export function changeNodeIntentOneTime(payload: string): interfaces.ChangeNodeIntentOneTimeAction {
|
||||||
|
return {
|
||||||
|
type: TypeKeys.CONFIG_NODE_CHANGE_INTENT_ONETIME,
|
||||||
|
payload
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export type TChangeNodeForce = typeof changeNodeForce;
|
export type TChangeNodeForce = typeof changeNodeForce;
|
||||||
export function changeNodeForce(payload: string): interfaces.ChangeNodeForceAction {
|
export function changeNodeForce(payload: string): interfaces.ChangeNodeForceAction {
|
||||||
return {
|
return {
|
||||||
|
|
|
@ -2,9 +2,12 @@ import { TypeKeys } from './constants';
|
||||||
import { CustomNodeConfig, StaticNodeConfig } from 'types/node';
|
import { CustomNodeConfig, StaticNodeConfig } from 'types/node';
|
||||||
import { CustomNetworkConfig } from 'types/network';
|
import { CustomNetworkConfig } from 'types/network';
|
||||||
|
|
||||||
/*** Toggle Offline ***/
|
export interface SetOnlineAction {
|
||||||
export interface ToggleOfflineAction {
|
type: TypeKeys.CONFIG_SET_ONLINE;
|
||||||
type: TypeKeys.CONFIG_TOGGLE_OFFLINE;
|
}
|
||||||
|
|
||||||
|
export interface SetOfflineAction {
|
||||||
|
type: TypeKeys.CONFIG_SET_OFFLINE;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ToggleAutoGasLimitAction {
|
export interface ToggleAutoGasLimitAction {
|
||||||
|
@ -36,6 +39,13 @@ export interface ChangeNodeIntentAction {
|
||||||
type: TypeKeys.CONFIG_NODE_CHANGE_INTENT;
|
type: TypeKeys.CONFIG_NODE_CHANGE_INTENT;
|
||||||
payload: string;
|
payload: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*** Change Node Onetime ***/
|
||||||
|
export interface ChangeNodeIntentOneTimeAction {
|
||||||
|
type: TypeKeys.CONFIG_NODE_CHANGE_INTENT_ONETIME;
|
||||||
|
payload: string;
|
||||||
|
}
|
||||||
|
|
||||||
/*** Force Change Node ***/
|
/*** Force Change Node ***/
|
||||||
export interface ChangeNodeForceAction {
|
export interface ChangeNodeForceAction {
|
||||||
type: TypeKeys.CONFIG_NODE_CHANGE_FORCE;
|
type: TypeKeys.CONFIG_NODE_CHANGE_FORCE;
|
||||||
|
@ -95,7 +105,8 @@ export type NodeAction =
|
||||||
|
|
||||||
export type MetaAction =
|
export type MetaAction =
|
||||||
| ChangeLanguageAction
|
| ChangeLanguageAction
|
||||||
| ToggleOfflineAction
|
| SetOnlineAction
|
||||||
|
| SetOfflineAction
|
||||||
| ToggleAutoGasLimitAction
|
| ToggleAutoGasLimitAction
|
||||||
| PollOfflineStatus
|
| PollOfflineStatus
|
||||||
| SetLatestBlockAction;
|
| SetLatestBlockAction;
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
export enum TypeKeys {
|
export enum TypeKeys {
|
||||||
CONFIG_LANGUAGE_CHANGE = 'CONFIG_LANGUAGE_CHANGE',
|
CONFIG_LANGUAGE_CHANGE = 'CONFIG_LANGUAGE_CHANGE',
|
||||||
|
|
||||||
|
CONFIG_SET_ONLINE = 'CONFIG_SET_ONLINE',
|
||||||
|
CONFIG_SET_OFFLINE = 'CONFIG_SET_OFFLINE',
|
||||||
|
|
||||||
CONFIG_TOGGLE_OFFLINE = 'CONFIG_TOGGLE_OFFLINE',
|
CONFIG_TOGGLE_OFFLINE = 'CONFIG_TOGGLE_OFFLINE',
|
||||||
CONFIG_TOGGLE_AUTO_GAS_LIMIT = 'CONFIG_TOGGLE_AUTO_GAS_LIMIT',
|
CONFIG_TOGGLE_AUTO_GAS_LIMIT = 'CONFIG_TOGGLE_AUTO_GAS_LIMIT',
|
||||||
CONFIG_POLL_OFFLINE_STATUS = 'CONFIG_POLL_OFFLINE_STATUS',
|
CONFIG_POLL_OFFLINE_STATUS = 'CONFIG_POLL_OFFLINE_STATUS',
|
||||||
|
@ -10,6 +13,7 @@ export enum TypeKeys {
|
||||||
CONFIG_NODE_WEB3_UNSET = 'CONFIG_NODE_WEB3_UNSET',
|
CONFIG_NODE_WEB3_UNSET = 'CONFIG_NODE_WEB3_UNSET',
|
||||||
CONFIG_NODE_CHANGE = 'CONFIG_NODE_CHANGE',
|
CONFIG_NODE_CHANGE = 'CONFIG_NODE_CHANGE',
|
||||||
CONFIG_NODE_CHANGE_INTENT = 'CONFIG_NODE_CHANGE_INTENT',
|
CONFIG_NODE_CHANGE_INTENT = 'CONFIG_NODE_CHANGE_INTENT',
|
||||||
|
CONFIG_NODE_CHANGE_INTENT_ONETIME = 'CONFIG_NODE_CHANGE_INTENT_ONETIME',
|
||||||
CONFIG_NODE_CHANGE_FORCE = 'CONFIG_NODE_CHANGE_FORCE',
|
CONFIG_NODE_CHANGE_FORCE = 'CONFIG_NODE_CHANGE_FORCE',
|
||||||
|
|
||||||
CONFIG_ADD_CUSTOM_NODE = 'CONFIG_ADD_CUSTOM_NODE',
|
CONFIG_ADD_CUSTOM_NODE = 'CONFIG_ADD_CUSTOM_NODE',
|
||||||
|
|
|
@ -0,0 +1,117 @@
|
||||||
|
import {
|
||||||
|
SetTimeBountyFieldAction,
|
||||||
|
SetWindowSizeFieldAction,
|
||||||
|
SetWindowStartFieldAction,
|
||||||
|
SetScheduleTimestampFieldAction,
|
||||||
|
SetScheduleTypeAction,
|
||||||
|
SetSchedulingToggleAction,
|
||||||
|
SetScheduleTimezoneAction,
|
||||||
|
SetScheduleGasPriceFieldAction,
|
||||||
|
SetScheduleGasLimitFieldAction,
|
||||||
|
SetScheduleDepositFieldAction,
|
||||||
|
SetScheduleParamsValidityAction
|
||||||
|
} from '../actionTypes';
|
||||||
|
import { TypeKeys } from 'actions/schedule/constants';
|
||||||
|
|
||||||
|
type TSetTimeBountyField = typeof setTimeBountyField;
|
||||||
|
const setTimeBountyField = (
|
||||||
|
payload: SetTimeBountyFieldAction['payload']
|
||||||
|
): SetTimeBountyFieldAction => ({
|
||||||
|
type: TypeKeys.TIME_BOUNTY_FIELD_SET,
|
||||||
|
payload
|
||||||
|
});
|
||||||
|
|
||||||
|
type TSetWindowSizeField = typeof setWindowSizeField;
|
||||||
|
const setWindowSizeField = (
|
||||||
|
payload: SetWindowSizeFieldAction['payload']
|
||||||
|
): SetWindowSizeFieldAction => ({
|
||||||
|
type: TypeKeys.WINDOW_SIZE_FIELD_SET,
|
||||||
|
payload
|
||||||
|
});
|
||||||
|
|
||||||
|
type TSetWindowStartField = typeof setWindowStartField;
|
||||||
|
const setWindowStartField = (
|
||||||
|
payload: SetWindowStartFieldAction['payload']
|
||||||
|
): SetWindowStartFieldAction => ({
|
||||||
|
type: TypeKeys.WINDOW_START_FIELD_SET,
|
||||||
|
payload
|
||||||
|
});
|
||||||
|
|
||||||
|
type TSetScheduleTimestampField = typeof setScheduleTimestampField;
|
||||||
|
const setScheduleTimestampField = (
|
||||||
|
payload: SetScheduleTimestampFieldAction['payload']
|
||||||
|
): SetScheduleTimestampFieldAction => ({
|
||||||
|
type: TypeKeys.SCHEDULE_TIMESTAMP_FIELD_SET,
|
||||||
|
payload
|
||||||
|
});
|
||||||
|
|
||||||
|
type TSetScheduleType = typeof setScheduleType;
|
||||||
|
const setScheduleType = (payload: SetScheduleTypeAction['payload']): SetScheduleTypeAction => ({
|
||||||
|
type: TypeKeys.SCHEDULE_TYPE_SET,
|
||||||
|
payload
|
||||||
|
});
|
||||||
|
|
||||||
|
type TSetSchedulingToggle = typeof setSchedulingToggle;
|
||||||
|
const setSchedulingToggle = (
|
||||||
|
payload: SetSchedulingToggleAction['payload']
|
||||||
|
): SetSchedulingToggleAction => ({
|
||||||
|
type: TypeKeys.SCHEDULING_TOGGLE_SET,
|
||||||
|
payload
|
||||||
|
});
|
||||||
|
|
||||||
|
type TSetScheduleTimezone = typeof setScheduleTimezone;
|
||||||
|
const setScheduleTimezone = (
|
||||||
|
payload: SetScheduleTimezoneAction['payload']
|
||||||
|
): SetScheduleTimezoneAction => ({
|
||||||
|
type: TypeKeys.SCHEDULE_TIMEZONE_SET,
|
||||||
|
payload
|
||||||
|
});
|
||||||
|
|
||||||
|
type TSetScheduleGasPriceField = typeof setScheduleGasPriceField;
|
||||||
|
const setScheduleGasPriceField = (payload: SetScheduleGasPriceFieldAction['payload']) => ({
|
||||||
|
type: TypeKeys.SCHEDULE_GAS_PRICE_FIELD_SET,
|
||||||
|
payload
|
||||||
|
});
|
||||||
|
|
||||||
|
type TSetScheduleGasLimitField = typeof setScheduleGasLimitField;
|
||||||
|
const setScheduleGasLimitField = (payload: SetScheduleGasLimitFieldAction['payload']) => ({
|
||||||
|
type: TypeKeys.SCHEDULE_GAS_LIMIT_FIELD_SET,
|
||||||
|
payload
|
||||||
|
});
|
||||||
|
|
||||||
|
type TSetScheduleDepositField = typeof setScheduleDepositField;
|
||||||
|
const setScheduleDepositField = (payload: SetScheduleDepositFieldAction['payload']) => ({
|
||||||
|
type: TypeKeys.SCHEDULE_DEPOSIT_FIELD_SET,
|
||||||
|
payload
|
||||||
|
});
|
||||||
|
|
||||||
|
type TSetScheduleParamsValidity = typeof setScheduleParamsValidity;
|
||||||
|
const setScheduleParamsValidity = (payload: SetScheduleParamsValidityAction['payload']) => ({
|
||||||
|
type: TypeKeys.SCHEDULE_PARAMS_VALIDITY_SET,
|
||||||
|
payload
|
||||||
|
});
|
||||||
|
|
||||||
|
export {
|
||||||
|
TSetWindowSizeField,
|
||||||
|
TSetWindowStartField,
|
||||||
|
TSetTimeBountyField,
|
||||||
|
TSetScheduleTimestampField,
|
||||||
|
TSetScheduleType,
|
||||||
|
TSetSchedulingToggle,
|
||||||
|
TSetScheduleTimezone,
|
||||||
|
TSetScheduleGasPriceField,
|
||||||
|
TSetScheduleGasLimitField,
|
||||||
|
TSetScheduleDepositField,
|
||||||
|
TSetScheduleParamsValidity,
|
||||||
|
setTimeBountyField,
|
||||||
|
setWindowSizeField,
|
||||||
|
setWindowStartField,
|
||||||
|
setScheduleTimestampField,
|
||||||
|
setScheduleType,
|
||||||
|
setSchedulingToggle,
|
||||||
|
setScheduleTimezone,
|
||||||
|
setScheduleGasPriceField,
|
||||||
|
setScheduleGasLimitField,
|
||||||
|
setScheduleDepositField,
|
||||||
|
setScheduleParamsValidity
|
||||||
|
};
|
|
@ -0,0 +1,7 @@
|
||||||
|
export * from './fields';
|
||||||
|
export * from './scheduleTimestamp';
|
||||||
|
export * from './scheduleType';
|
||||||
|
export * from './schedulingToggle';
|
||||||
|
export * from './timeBounty';
|
||||||
|
export * from './windowSize';
|
||||||
|
export * from './windowStart';
|
|
@ -0,0 +1,21 @@
|
||||||
|
import { TypeKeys } from 'actions/schedule';
|
||||||
|
import {
|
||||||
|
SetCurrentScheduleTimestampAction,
|
||||||
|
SetCurrentScheduleTimezoneAction
|
||||||
|
} from '../actionTypes/scheduleTimestamp';
|
||||||
|
|
||||||
|
export type TSetCurrentScheduleTimestamp = typeof setCurrentScheduleTimestamp;
|
||||||
|
export const setCurrentScheduleTimestamp = (
|
||||||
|
payload: SetCurrentScheduleTimestampAction['payload']
|
||||||
|
): SetCurrentScheduleTimestampAction => ({
|
||||||
|
type: TypeKeys.CURRENT_SCHEDULE_TIMESTAMP_SET,
|
||||||
|
payload
|
||||||
|
});
|
||||||
|
|
||||||
|
export type TSetCurrentScheduleTimezone = typeof setCurrentScheduleTimezone;
|
||||||
|
export const setCurrentScheduleTimezone = (
|
||||||
|
payload: SetCurrentScheduleTimezoneAction['payload']
|
||||||
|
): SetCurrentScheduleTimezoneAction => ({
|
||||||
|
type: TypeKeys.CURRENT_SCHEDULE_TIMEZONE_SET,
|
||||||
|
payload
|
||||||
|
});
|
|
@ -0,0 +1,10 @@
|
||||||
|
import { TypeKeys } from 'actions/schedule';
|
||||||
|
import { SetCurrentScheduleTypeAction } from '../actionTypes/scheduleType';
|
||||||
|
|
||||||
|
export type TSetCurrentScheduleType = typeof setCurrentScheduleType;
|
||||||
|
export const setCurrentScheduleType = (
|
||||||
|
payload: SetCurrentScheduleTypeAction['payload']
|
||||||
|
): SetCurrentScheduleTypeAction => ({
|
||||||
|
type: TypeKeys.CURRENT_SCHEDULE_TYPE,
|
||||||
|
payload
|
||||||
|
});
|
|
@ -0,0 +1,12 @@
|
||||||
|
import { SetCurrentSchedulingToggleAction } from '../actionTypes/schedulingToggle';
|
||||||
|
import { TypeKeys } from 'actions/schedule';
|
||||||
|
|
||||||
|
type TSetCurrentSchedulingToggle = typeof setCurrentSchedulingToggle;
|
||||||
|
const setCurrentSchedulingToggle = (
|
||||||
|
payload: SetCurrentSchedulingToggleAction['payload']
|
||||||
|
): SetCurrentSchedulingToggleAction => ({
|
||||||
|
type: TypeKeys.CURRENT_SCHEDULING_TOGGLE,
|
||||||
|
payload
|
||||||
|
});
|
||||||
|
|
||||||
|
export { TSetCurrentSchedulingToggle, setCurrentSchedulingToggle };
|
|
@ -0,0 +1,12 @@
|
||||||
|
import { SetCurrentTimeBountyAction } from '../actionTypes/timeBounty';
|
||||||
|
import { TypeKeys } from 'actions/schedule';
|
||||||
|
|
||||||
|
type TSetCurrentTimeBounty = typeof setCurrentTimeBounty;
|
||||||
|
const setCurrentTimeBounty = (
|
||||||
|
payload: SetCurrentTimeBountyAction['payload']
|
||||||
|
): SetCurrentTimeBountyAction => ({
|
||||||
|
type: TypeKeys.CURRENT_TIME_BOUNTY_SET,
|
||||||
|
payload
|
||||||
|
});
|
||||||
|
|
||||||
|
export { setCurrentTimeBounty, TSetCurrentTimeBounty };
|
|
@ -0,0 +1,12 @@
|
||||||
|
import { SetCurrentWindowSizeAction } from '../actionTypes/windowSize';
|
||||||
|
import { TypeKeys } from 'actions/schedule';
|
||||||
|
|
||||||
|
type TSetCurrentWindowSize = typeof setCurrentWindowSize;
|
||||||
|
const setCurrentWindowSize = (
|
||||||
|
payload: SetCurrentWindowSizeAction['payload']
|
||||||
|
): SetCurrentWindowSizeAction => ({
|
||||||
|
type: TypeKeys.CURRENT_WINDOW_SIZE_SET,
|
||||||
|
payload
|
||||||
|
});
|
||||||
|
|
||||||
|
export { setCurrentWindowSize, TSetCurrentWindowSize };
|
|
@ -0,0 +1,12 @@
|
||||||
|
import { SetCurrentWindowStartAction } from '../actionTypes/windowStart';
|
||||||
|
import { TypeKeys } from 'actions/schedule';
|
||||||
|
|
||||||
|
type TSetCurrentWindowStart = typeof setCurrentWindowStart;
|
||||||
|
const setCurrentWindowStart = (
|
||||||
|
payload: SetCurrentWindowStartAction['payload']
|
||||||
|
): SetCurrentWindowStartAction => ({
|
||||||
|
type: TypeKeys.CURRENT_WINDOW_START_SET,
|
||||||
|
payload
|
||||||
|
});
|
||||||
|
|
||||||
|
export { setCurrentWindowStart, TSetCurrentWindowStart };
|
|
@ -0,0 +1,5 @@
|
||||||
|
import { ScheduleFieldAction } from './fields';
|
||||||
|
|
||||||
|
export * from './fields';
|
||||||
|
|
||||||
|
export type ScheduleAction = ScheduleFieldAction;
|
|
@ -0,0 +1,116 @@
|
||||||
|
import { TypeKeys } from 'actions/schedule/constants';
|
||||||
|
import { Wei } from 'libs/units';
|
||||||
|
|
||||||
|
interface SetTimeBountyFieldAction {
|
||||||
|
type: TypeKeys.TIME_BOUNTY_FIELD_SET;
|
||||||
|
payload: {
|
||||||
|
raw: string;
|
||||||
|
value: Wei | null;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
interface SetWindowSizeFieldAction {
|
||||||
|
type: TypeKeys.WINDOW_SIZE_FIELD_SET;
|
||||||
|
payload: {
|
||||||
|
raw: string;
|
||||||
|
value: Wei | null;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
interface SetWindowStartFieldAction {
|
||||||
|
type: TypeKeys.WINDOW_START_FIELD_SET;
|
||||||
|
payload: {
|
||||||
|
raw: string;
|
||||||
|
value: number | null;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
interface SetScheduleTimestampFieldAction {
|
||||||
|
type: TypeKeys.SCHEDULE_TIMESTAMP_FIELD_SET;
|
||||||
|
payload: {
|
||||||
|
raw: string;
|
||||||
|
value: Date;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
interface SetScheduleTypeAction {
|
||||||
|
type: TypeKeys.SCHEDULE_TYPE_SET;
|
||||||
|
payload: {
|
||||||
|
raw: string;
|
||||||
|
value: string | null;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
interface SetSchedulingToggleAction {
|
||||||
|
type: TypeKeys.SCHEDULING_TOGGLE_SET;
|
||||||
|
payload: {
|
||||||
|
value: boolean;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
interface SetScheduleTimezoneAction {
|
||||||
|
type: TypeKeys.SCHEDULE_TIMEZONE_SET;
|
||||||
|
payload: {
|
||||||
|
raw: string;
|
||||||
|
value: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
interface SetScheduleGasPriceFieldAction {
|
||||||
|
type: TypeKeys.SCHEDULE_GAS_PRICE_FIELD_SET;
|
||||||
|
payload: {
|
||||||
|
raw: string;
|
||||||
|
value: Wei | null;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
interface SetScheduleGasLimitFieldAction {
|
||||||
|
type: TypeKeys.SCHEDULE_GAS_LIMIT_FIELD_SET;
|
||||||
|
payload: {
|
||||||
|
raw: string;
|
||||||
|
value: Wei | null;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
interface SetScheduleDepositFieldAction {
|
||||||
|
type: TypeKeys.SCHEDULE_DEPOSIT_FIELD_SET;
|
||||||
|
payload: {
|
||||||
|
raw: string;
|
||||||
|
value: Wei | null;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
interface SetScheduleParamsValidityAction {
|
||||||
|
type: TypeKeys.SCHEDULE_PARAMS_VALIDITY_SET;
|
||||||
|
payload: {
|
||||||
|
value: boolean;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
type ScheduleFieldAction =
|
||||||
|
| SetTimeBountyFieldAction
|
||||||
|
| SetWindowSizeFieldAction
|
||||||
|
| SetWindowStartFieldAction
|
||||||
|
| SetScheduleTimestampFieldAction
|
||||||
|
| SetScheduleTypeAction
|
||||||
|
| SetSchedulingToggleAction
|
||||||
|
| SetScheduleGasPriceFieldAction
|
||||||
|
| SetScheduleGasLimitFieldAction
|
||||||
|
| SetScheduleDepositFieldAction
|
||||||
|
| SetScheduleTimezoneAction
|
||||||
|
| SetScheduleParamsValidityAction;
|
||||||
|
|
||||||
|
export {
|
||||||
|
ScheduleFieldAction,
|
||||||
|
SetTimeBountyFieldAction,
|
||||||
|
SetWindowSizeFieldAction,
|
||||||
|
SetWindowStartFieldAction,
|
||||||
|
SetScheduleTimestampFieldAction,
|
||||||
|
SetScheduleTypeAction,
|
||||||
|
SetSchedulingToggleAction,
|
||||||
|
SetScheduleGasPriceFieldAction,
|
||||||
|
SetScheduleGasLimitFieldAction,
|
||||||
|
SetScheduleDepositFieldAction,
|
||||||
|
SetScheduleTimezoneAction,
|
||||||
|
SetScheduleParamsValidityAction
|
||||||
|
};
|
|
@ -0,0 +1 @@
|
||||||
|
export * from './actionTypes';
|
|
@ -0,0 +1,17 @@
|
||||||
|
import { TypeKeys } from 'actions/schedule';
|
||||||
|
|
||||||
|
/* user input */
|
||||||
|
|
||||||
|
interface SetCurrentScheduleTimestampAction {
|
||||||
|
type: TypeKeys.CURRENT_SCHEDULE_TIMESTAMP_SET;
|
||||||
|
payload: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
type CurrentAction = SetCurrentScheduleTimestampAction;
|
||||||
|
|
||||||
|
interface SetCurrentScheduleTimezoneAction {
|
||||||
|
type: TypeKeys.CURRENT_SCHEDULE_TIMEZONE_SET;
|
||||||
|
payload: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export { SetCurrentScheduleTimestampAction, CurrentAction, SetCurrentScheduleTimezoneAction };
|
|
@ -0,0 +1,12 @@
|
||||||
|
import { TypeKeys } from 'actions/schedule';
|
||||||
|
|
||||||
|
/* user input */
|
||||||
|
|
||||||
|
interface SetCurrentScheduleTypeAction {
|
||||||
|
type: TypeKeys.CURRENT_SCHEDULE_TYPE;
|
||||||
|
payload: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
type CurrentAction = SetCurrentScheduleTypeAction;
|
||||||
|
|
||||||
|
export { SetCurrentScheduleTypeAction, CurrentAction };
|
|
@ -0,0 +1,12 @@
|
||||||
|
import { TypeKeys } from 'actions/schedule';
|
||||||
|
|
||||||
|
/* user input */
|
||||||
|
|
||||||
|
interface SetCurrentSchedulingToggleAction {
|
||||||
|
type: TypeKeys.CURRENT_SCHEDULING_TOGGLE;
|
||||||
|
payload: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
type CurrentAction = SetCurrentSchedulingToggleAction;
|
||||||
|
|
||||||
|
export { SetCurrentSchedulingToggleAction, CurrentAction };
|
|
@ -0,0 +1,12 @@
|
||||||
|
import { TypeKeys } from 'actions/schedule';
|
||||||
|
|
||||||
|
/* user input */
|
||||||
|
|
||||||
|
interface SetCurrentTimeBountyAction {
|
||||||
|
type: TypeKeys.CURRENT_TIME_BOUNTY_SET;
|
||||||
|
payload: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
type CurrentAction = SetCurrentTimeBountyAction;
|
||||||
|
|
||||||
|
export { SetCurrentTimeBountyAction, CurrentAction };
|
|
@ -0,0 +1,12 @@
|
||||||
|
import { TypeKeys } from 'actions/schedule';
|
||||||
|
|
||||||
|
/* user input */
|
||||||
|
|
||||||
|
interface SetCurrentWindowSizeAction {
|
||||||
|
type: TypeKeys.CURRENT_WINDOW_SIZE_SET;
|
||||||
|
payload: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
type CurrentAction = SetCurrentWindowSizeAction;
|
||||||
|
|
||||||
|
export { SetCurrentWindowSizeAction, CurrentAction };
|
|
@ -0,0 +1,12 @@
|
||||||
|
import { TypeKeys } from 'actions/schedule';
|
||||||
|
|
||||||
|
/* user input */
|
||||||
|
|
||||||
|
interface SetCurrentWindowStartAction {
|
||||||
|
type: TypeKeys.CURRENT_WINDOW_START_SET;
|
||||||
|
payload: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
type CurrentAction = SetCurrentWindowStartAction;
|
||||||
|
|
||||||
|
export { SetCurrentWindowStartAction, CurrentAction };
|
|
@ -0,0 +1,21 @@
|
||||||
|
export enum TypeKeys {
|
||||||
|
CURRENT_TIME_BOUNTY_SET = 'CURRENT_TIME_BOUNTY_SET',
|
||||||
|
CURRENT_WINDOW_SIZE_SET = 'CURRENT_WINDOW_SIZE_SET',
|
||||||
|
CURRENT_WINDOW_START_SET = 'CURRENT_WINDOW_START_SET',
|
||||||
|
CURRENT_SCHEDULE_TIMESTAMP_SET = 'CURRENT_SCHEDULE_TIMESTAMP_SET',
|
||||||
|
CURRENT_SCHEDULE_TIMEZONE_SET = 'CURRENT_SCHEDULE_TIMEZONE_SET',
|
||||||
|
CURRENT_SCHEDULE_TYPE = 'CURRENT_SCHEDULE_TYPE',
|
||||||
|
CURRENT_SCHEDULING_TOGGLE = 'CURRENT_SCHEDULING_TOGGLE',
|
||||||
|
|
||||||
|
TIME_BOUNTY_FIELD_SET = 'TIME_BOUNTY_FIELD_SET',
|
||||||
|
WINDOW_SIZE_FIELD_SET = 'WINDOW_SIZE_FIELD_SET',
|
||||||
|
WINDOW_START_FIELD_SET = 'WINDOW_START_FIELD_SET',
|
||||||
|
SCHEDULE_GAS_PRICE_FIELD_SET = 'SCHEDULE_GAS_PRICE_SET',
|
||||||
|
SCHEDULE_GAS_LIMIT_FIELD_SET = 'SCHEDULE_GAS_LIMIT_SET',
|
||||||
|
SCHEDULE_TIMESTAMP_FIELD_SET = 'SCHEDULE_TIMESTAMP_FIELD_SET',
|
||||||
|
SCHEDULE_TIMEZONE_SET = 'SCHEDULE_TIMEZONE_SET',
|
||||||
|
SCHEDULE_TYPE_SET = 'SCHEDULE_TYPE_SET',
|
||||||
|
SCHEDULING_TOGGLE_SET = 'SCHEDULING_TOGGLE_SET',
|
||||||
|
SCHEDULE_DEPOSIT_FIELD_SET = 'SCHEDULE_DEPOSIT_FIELD_SET',
|
||||||
|
SCHEDULE_PARAMS_VALIDITY_SET = 'SCHEDULE_PARAMS_VALIDITY_SET'
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
export * from './actionCreators';
|
||||||
|
export * from './constants';
|
||||||
|
export * from './actionTypes';
|
|
@ -0,0 +1,92 @@
|
||||||
|
<?xml version="1.0"?>
|
||||||
|
<svg width="650" height="130" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg">
|
||||||
|
<style type="text/css">.st0{fill:#EBFFFE;}
|
||||||
|
.st1{clip-path:url(#SVGID_2_);}
|
||||||
|
.st2{fill:none;stroke:#CCCCCC;stroke-miterlimit:10;}
|
||||||
|
.st3{fill:#52CACB;}
|
||||||
|
.st4{fill:#263238;}
|
||||||
|
.st5{fill:#FFFFFF;}
|
||||||
|
.st6{fill:#B3B3B3;}
|
||||||
|
.st7{fill:url(#SVGID_3_);}
|
||||||
|
.st8{fill:#2E3192;}
|
||||||
|
.st9{fill:url(#SVGID_4_);}
|
||||||
|
.st10{opacity:0.3;}
|
||||||
|
.st11{clip-path:url(#SVGID_6_);}
|
||||||
|
.st12{fill:none;stroke:#FFFFFF;stroke-width:0.25;stroke-miterlimit:10;}
|
||||||
|
.st13{fill:#8CC63F;}
|
||||||
|
.st14{fill:url(#SVGID_7_);}
|
||||||
|
.st15{fill:url(#SVGID_8_);}
|
||||||
|
.st16{clip-path:url(#SVGID_10_);}
|
||||||
|
.st17{fill:#36CBFF;}</style>
|
||||||
|
<g>
|
||||||
|
<title>Layer 1</title>
|
||||||
|
<g id="svg_2">
|
||||||
|
<g id="svg_3">
|
||||||
|
<g id="svg_4">
|
||||||
|
<path id="svg_5" d="m38,121.800003l-27,0c-2.1,-0.5 -3.4,-1.900002 -3.7,-4.100006l6.8,-2.5l22.1,0l1.799999,6.600006zm-30.5,-5.800003l2.2,-21.300003l1.8,-1.599998l4.4,4.900002l-1.599999,15.5l-6.8,2.5zm11.299999,-45l-1.599998,15.5l-5.500001,4.900002l-1.4,-1.599998l2.2,-21.300003l6.299999,2.5zm22.200001,-1.599998l-22,0l-6.3,-2.5c0.8,-2.200005 2.3,-3.5 4.500001,-4.100002l26.899998,0l-3.099998,6.600002zm-2.799999,19.599998l2.899998,3.300003l-3.599998,3.299995l-21.4,0l-2.900001,-3.299995l3.7,-3.300003l21.300001,0z" class="st3"/>
|
||||||
|
<path id="svg_6" d="m61.599998,69.400002l-6.299999,-2.5c0.799999,-2.200005 2.299999,-3.5 4.5,-4.100002l26.100002,0c2.099998,0.5 3.400002,1.899998 3.699997,4.100002l-6.799995,2.5l-21.200005,0zm8,23.699997l2.900002,3.300003l-2.199997,21.299995c-0.800003,2.200005 -2.300003,3.5 -4.5,4.100006l-1.800003,-6.5l2,-18.800003l3.599998,-3.400002zm-2.699997,-4.900002l1.799995,-17.199997l6.5,0l-1.799995,17.199997l-3.700005,3.300003l-2.799995,-3.300003z" class="st3"/>
|
||||||
|
<path id="svg_7" d="m101,98l-1.800003,17.199997l-3.099998,6.5c-2.099998,-0.5 -3.400002,-1.899994 -3.699997,-4.099998l2.400002,-22.900002l1.799995,-1.599998l4.400002,4.900002zm-4.400002,-5.699997l-1.400002,-1.600006l2.400002,-22.899994c0.800003,-2.200005 2.300003,-3.5 4.5,-4.100002l1.800003,6.499996l-1.800003,17.200005l-5.5,4.900002zm26.599998,-3.300003l2.900002,3.300003l-3.599998,3.299995l-21.300003,0l-2.899994,-3.299995l3.699997,-3.300003l21.199997,0zm4.5,4.099998l1.5,1.599998l-2.399994,22.900002c-0.800003,2.200005 -2.300003,3.5 -4.5,4.099998l-1.800003,-6.5l1.800003,-17.199997l5.399994,-4.900002zm-4.199997,-6.5l1.800003,-17.199997l3.099991,-6.5c2.100006,0.5 3.400009,1.900002 3.700012,4.099998l-2.400009,22.900002l-1.899994,1.599998l-4.300003,-4.900002z" class="st3"/>
|
||||||
|
<path id="svg_8" d="m156.300003,108.699997l-6.5,0l0.699997,-6.5l6.5,0l-0.699997,6.5zm2.800003,-26.199997l-6.5,0l0.699997,-6.5l6.5,0l-0.699997,6.5z" class="st3"/>
|
||||||
|
<path id="svg_9" d="m186.100006,98l-1.800003,17.199997l-3.099991,6.5c-2.100006,-0.5 -3.400009,-1.899994 -3.699997,-4.099998l2.399994,-22.900002l1.800003,-1.599998l4.399994,4.900002zm2.799988,-27l-1.600006,15.5l-5.5,4.900002l-1.399994,-1.599998l2.199997,-21.300003l6.300003,2.5zm19.800003,-1.599998l-19.599991,0l-6.300003,-2.5c0.800003,-2.200005 2.300003,-3.5 4.5,-4.100002l24.5,0l-3.100006,6.600002zm-0.399994,19.599998l2.899994,3.300003l-3.599991,3.299995l-21.300003,0l-2.900009,-3.299995l3.700012,-3.300003l21.199997,0zm4.399994,4.099998l1.5,1.599998l-2.399994,22.900002c-0.800003,2.200005 -2.300003,3.5 -4.5,4.099998l-1.800003,-6.5l1.800003,-17.199997l5.399994,-4.900002zm-4.099991,-6.5l1.800003,-17.199997l3.100006,-6.5c2.099991,0.5 3.399994,1.900002 3.699997,4.099998l-2.400009,22.900002l-1.899994,1.599998l-4.300003,-4.900002z" class="st3"/>
|
||||||
|
<path id="svg_10" d="m228.600006,98l-1.800003,17.199997l-3.099991,6.5c-2.100006,-0.5 -3.400009,-1.899994 -3.699997,-4.099998l2.399994,-22.900002l1.800003,-1.599998l4.399994,4.900002zm-4.200012,-6.5l-1.400009,-1.599998l2.400009,-22.900002c0.800003,-2.199997 2.300003,-3.5 4.5,-4.099998l1.800003,6.5l-1.800003,17.199997l-5.5,4.900002zm4.100006,23.699997l19.600006,0l6.299988,2.5c-0.799988,2.200005 -2.299988,3.5 -4.5,4.100006l-24.5,0l3.100006,-6.600006z" class="st3"/>
|
||||||
|
<path id="svg_11" d="m271.200012,98l-1.799988,17.199997l-3.100006,6.5c-2.100006,-0.5 -3.399994,-1.899994 -3.700012,-4.099998l2.399994,-22.900002l1.800018,-1.599998l4.399994,4.900002zm2.799988,-27l-1.600006,15.5l-5.5,4.900002l-1.399994,-1.599998l2.200012,-21.300003l6.299988,2.5zm19.799988,-1.599998l-19.600006,0l-6.299988,-2.5c0.799988,-2.200005 2.299988,-3.5 4.5,-4.100002l24.5,0l-3.100006,6.600002zm-0.399994,19.599998l2.899994,3.300003l-3.600006,3.299995l-21.299988,0l-2.899994,-3.299995l3.699982,-3.300003l21.200012,0zm4.399994,4.099998l1.5,1.599998l-2.399994,22.900002c-0.800018,2.200005 -2.300018,3.5 -4.5,4.099998l-1.800018,-6.5l1.800018,-17.199997l5.399994,-4.900002zm-4.099976,-6.5l1.799988,-17.199997l3.100006,-6.5c2.100006,0.5 3.399994,1.900002 3.700012,4.099998l-2.399994,22.900002l-1.900024,1.599998l-4.299988,-4.900002z" class="st3"/>
|
||||||
|
<path id="svg_12" d="m313.700012,98l-1.799988,17.199997l-3.100006,6.5c-2.100006,-0.5 -3.399994,-1.899994 -3.700012,-4.099998l2.399994,-22.900002l1.800018,-1.599998l4.399994,4.900002zm2.899994,-27l-1.600006,15.5l-5.5,4.900002l-1.399994,-1.599998l2.200012,-21.300003l6.299988,2.5zm19.799988,-1.599998l-19.600006,0l-6.299988,-2.5c0.799988,-2.200005 2.299988,-3.5 4.5,-4.100002l24.5,0l-3.100006,6.600002zm-0.399994,19.599998l2.899994,3.300003l-3.600006,3.299995l-21.299988,0l-2.899994,-3.299995l3.699982,-3.300003l21.200012,0zm-20.600006,8.199997l4.899994,0l19.100006,21.300003c-1.300018,2.199997 -3.600006,3.300003 -6.899994,3.300003l-17.600006,-19.600006l0.5,-5zm20.800018,-10.599998l1.799988,-17.199997l3.100006,-6.5c2.100006,0.5 3.399994,1.900002 3.700012,4.099998l-2.399994,22.900002l-1.900024,1.599998l-4.299988,-4.900002z" class="st3"/>
|
||||||
|
<path id="svg_13" d="m356.299988,98l-1.799988,17.199997l-3.100006,6.5c-2.100006,-0.5 -3.399994,-1.899994 -3.700012,-4.099998l2.399994,-22.900002l1.800018,-1.599998l4.399994,4.900002zm2.800018,-27l-1.600006,15.5l-5.5,4.900002l-1.399994,-1.599998l2.200012,-21.300003l6.299988,2.5zm19.799988,-1.599998l-19.600006,0l-6.299988,-2.5c0.799988,-2.200005 2.299988,-3.5 4.5,-4.100002l24.5,0l-3.100006,6.600002zm-7.799988,20.5l-3.699982,3.299995l-2.900024,-3.299995l2,-18.800003l6.5,0l-1.899994,18.800003zm11.799988,3.199997l1.5,1.599998l-2.399994,22.900002c-0.800018,2.200005 -2.300018,3.5 -4.5,4.099998l-1.800018,-6.5l1.800018,-17.199997l5.399994,-4.900002zm-4.100006,-6.5l1.799988,-17.199997l3.100006,-6.5c2.100006,0.5 3.399994,1.900002 3.700012,4.099998l-2.399994,22.900002l-1.899994,1.599998l-4.300018,-4.900002z" class="st3"/>
|
||||||
|
<path id="svg_14" d="m411.600006,108.699997l-6.600006,0l0.700012,-6.5l6.5,0l-0.600006,6.5zm2.699982,-26.199997l-6.5,0l0.700012,-6.5l6.5,0l-0.700012,6.5z" class="st3"/>
|
||||||
|
<path id="svg_15" d="m463.399994,121.800003l-27,0c-2.100006,-0.5 -3.399994,-1.900002 -3.700012,-4.100006l6.800018,-2.5l22.099976,0l1.800018,6.600006zm-30.5,-5.800003l2.199982,-21.300003l1.800018,-1.599998l4.399994,4.900002l-1.600006,15.5l-6.799988,2.5zm11.300018,-45l-1.600006,15.5l-5.5,4.900002l-1.399994,-1.599998l2.200012,-21.300003l6.299988,2.5zm22.299988,-1.599998l-22,0l-6.299988,-2.5c0.799988,-2.200005 2.299988,-3.5 4.5,-4.100002l26.899994,0l-3.100006,6.600002z" class="st3"/>
|
||||||
|
<path id="svg_16" d="m483.899994,98l-1.800018,17.199997l-3.099976,6.5c-2.100006,-0.5 -3.400024,-1.899994 -3.700012,-4.099998l2.399994,-22.900002l1.800018,-1.599998l4.399994,4.900002zm-4.299988,-6.5l-1.399994,-1.599998l2.399994,-22.900002c0.800018,-2.199997 2.300018,-3.5 4.5,-4.099998l1.800018,6.5l-1.800018,17.199997l-5.5,4.900002zm4.100006,23.699997l19.600006,0l6.299988,2.5c-0.799988,2.200005 -2.299988,3.5 -4.5,4.100006l-24.5,0l3.100006,-6.600006z" class="st3"/>
|
||||||
|
<path id="svg_17" d="m526.400024,98l-1.799988,17.199997l-2.700012,2.5l-3.700012,-4.099998l2,-18.799995l1.799988,-1.600006l4.400024,4.800003zm-4.200012,-6.5l-1.400024,-1.599998l2,-18.800003l4.5,-4.099998l2.200012,2.5l-1.799988,17.199997l-5.5,4.800003zm25,27l-3.599976,3.300003l-18,0l-2.900024,-3.300003l3.599976,-3.300003l18,0l2.900024,3.300003zm1.899963,-49.099998l-18,0l-2.900024,-3.300003l3.600037,-3.299999l18,0l2.899963,3.299999l-3.599976,3.300003zm4,23.699997l1.5,1.599998l-2,18.800003l-4.5,4.099998l-2.200012,-2.5l1.799988,-17.199997l5.400024,-4.800003zm-4.199951,-6.5l1.799988,-17.199997l2.700012,-2.5l3.700012,4.099998l-2,18.800003l-1.900024,1.599998l-4.299988,-4.800003z" class="st3"/>
|
||||||
|
<path id="svg_18" d="m591.099976,121.800003l-27,0c-2.099976,-0.5 -3.400024,-1.900002 -3.700012,-4.100006l6.799988,-2.5l22.100037,0l1.799988,6.600006zm-30.599976,-5.800003l2.200012,-21.300003l1.799988,-1.599998l4.5,4.900002l-1.599976,15.5l-6.900024,2.5zm11.299988,-45l-1.599976,15.5l-5.5,4.900002l-1.400024,-1.599998l2.200012,-21.300003l6.299988,2.5zm22.299988,-1.599998l-22,0l-6.299988,-2.5c0.799988,-2.200005 2.299988,-3.5 4.5,-4.100002l26.899963,0l-3.099976,6.600002z" class="st3"/>
|
||||||
|
<path id="svg_19" d="m611.5,98l-1.799988,17.199997l-3.100037,6.5c-2.099976,-0.5 -3.399963,-1.899994 -3.699951,-4.099998l2.399963,-22.900002l1.799988,-1.599998l4.400024,4.900002zm-4.200012,-6.5l-1.400024,-1.599998l2.400024,-22.900002c0.799988,-2.199997 2.299988,-3.5 4.5,-4.099998l1.799988,6.5l-1.799988,17.199997l-5.5,4.900002zm26.5,-2.5l2.900024,3.300003l-3.600037,3.299995l-21.299988,0l-2.900024,-3.299995l3.700012,-3.300003l21.200012,0zm-19,-6.5l21.700012,-19.599998c3.299988,0 5.299988,1.099998 6.200012,3.299995l-23.5,21.300003l-4.900024,0l0.5,-5zm23.400024,10.599998l1.5,1.599998l-2.400024,22.900002c-0.799988,2.200005 -2.299988,3.5 -4.5,4.099998l-1.799988,-6.5l1.799988,-17.199997l5.400024,-4.900002z" class="st3"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g id="svg_20">
|
||||||
|
<g id="svg_21">
|
||||||
|
<path id="svg_22" d="m220.5,34.099998l-10.399994,0l-1.800003,10.600002l-3.300003,0l4.899994,-28l13.700012,0c3.599991,0 5.5,2.4 4.899994,6l-1,5.5c-0.600006,3.5 -3.399994,5.899998 -7,5.899998zm4.699997,-11.299999c0.400009,-2 -0.699997,-3.199999 -2.599991,-3.199999l-9.800003,0l-2.100006,11.6l9.800003,0c2,0 3.5,-1.200001 3.800003,-3.200001l0.899994,-5.200001z" class="st4"/>
|
||||||
|
<path id="svg_23" d="m238.899994,44.700001l-4.899994,0c-3.599991,0 -5.5,-2.400002 -4.899994,-6l1.599991,-8.900002c0.600006,-3.599998 3.400009,-6 7,-6l4.900009,0c3.599991,0 5.599991,2.400002 4.899994,6l-1.600006,8.900002c-0.599991,3.599998 -3.399994,6 -7,6zm5.500015,-14.900002c0.299988,-2 -0.600006,-3.199999 -2.600006,-3.199999l-4.199997,0c-2,0 -3.5,1.199999 -3.800003,3.199999l-1.600006,9c-0.399994,2 0.699997,3.200001 2.699997,3.200001l4.200012,0c2,0 3.399994,-1.299999 3.799988,-3.200001l1.500015,-9z" class="st4"/>
|
||||||
|
<path id="svg_24" d="m254.199997,44.700001l-2.799988,-20.800001l3,0l2.199997,17.000002l8.100006,-17.000002l3.099976,0l2.100006,17.000002l8.200012,-17.000002l3.200012,0l-10.200012,20.800001l-3.5,0l-2.200012,-16.1l-7.799988,16.1l-3.400009,0z" class="st4"/>
|
||||||
|
<path id="svg_25" d="m298.399994,38.099998l-0.099976,0.600002c-0.600006,3.599998 -3.400024,6 -7,6l-4.900024,0c-3.600006,0 -5.5,-2.400002 -4.899994,-6l1.600006,-8.900002c0.600006,-3.599998 3.399994,-6 7,-6l4.899994,0c3.600006,0 5.600006,2.400002 4.899994,6l-1,5.400002l-13.699982,0l-0.600006,3.5c-0.399994,2 0.700012,3.200001 2.700012,3.200001l4.199982,0c2,0 3.399994,-1.300003 3.800018,-3.200001l0.099976,-0.700001l3,0l0,0.099998zm-12.799988,-5.299999l10.600006,0l0.5,-3c0.299988,-2 -0.600006,-3.199999 -2.600006,-3.199999l-4.100006,0c-2,0 -3.5,1.199999 -3.799988,3.199999l-0.600006,3z" class="st4"/>
|
||||||
|
<path id="svg_26" d="m316.799988,26.6c-2,0 -2.899994,0.299999 -3.899994,1.199999l-5.599976,4.700001l-2.100006,12.200001l-3.100006,0l3.600006,-20.800001l2.399994,0l0.100006,3.4l-0.300018,1.800001l3.700012,-3.1c1.600006,-1.4 3.299988,-2 5.600006,-2l-0.400024,2.6z" class="st4"/>
|
||||||
|
<path id="svg_27" d="m333.600006,38.099998l-0.100006,0.600002c-0.599976,3.599998 -3.399994,6 -7,6l-4.899994,0c-3.599976,0 -5.5,-2.400002 -4.899994,-6l1.600006,-8.900002c0.600006,-3.599998 3.399994,-6 7,-6l4.899994,0c3.599976,0 5.599976,2.400002 4.899994,6l-1,5.400002l-13.699982,0l-0.600037,3.5c-0.399963,2 0.700012,3.200001 2.700012,3.200001l4.200012,0c2,0 3.399994,-1.300003 3.799988,-3.200001l0.100006,-0.700001l3,0l0,0.099998zm-12.800018,-5.299999l10.600006,0l0.5,-3c0.300018,-2 -0.599976,-3.199999 -2.599976,-3.199999l-4.200012,0c-2,0 -3.5,1.199999 -3.799988,3.199999l-0.500031,3z" class="st4"/>
|
||||||
|
<path id="svg_28" d="m351.5,39.5l-3.700012,3.200001c-1.699982,1.5 -3.299988,2 -5,2c-3.599976,0 -5.499969,-2.400002 -4.799988,-5.900002l1.600006,-8.9c0.600006,-3.6 3.399994,-6 7,-6l3,0c2.100006,0 3.700012,0.9 4.299988,2.6l2,-11.4l3.100006,0l-5.200012,29.6l-2.399994,0l-0.099976,-3.400002l0.199982,-1.799999zm1.700012,-9.9c0.199982,-1.9 -0.700012,-3.1 -2.600006,-3.1l-4.200012,0c-2,0 -3.399994,1.200001 -3.799988,3.200001l-1.600006,9c-0.399994,2 0.700012,3.200001 2.600006,3.200001c1,0 1.799988,-0.300003 2.899994,-1.200001l5.600006,-4.700001l1.100006,-6.4z" class="st4"/>
|
||||||
|
<path id="svg_29" d="m376.300018,29l3.699982,-3.200001c1.799988,-1.5 3.300018,-2 5,-2c3.600006,0 5.5,2.400002 4.899994,5.900002l-1.599976,8.899998c-0.600006,3.600002 -3.400024,6 -7,6l-3,0c-2.300018,0 -4,-1 -4.600006,-2.799999l-0.900024,2.799999l-2.399994,0l5.200012,-29.599998l3.100006,0l-2.399994,14zm-1.700012,9.799999c-0.299988,2 0.600006,3.200001 2.600006,3.200001l4.200012,0c2,0 3.5,-1.200001 3.799988,-3.200001l1.599976,-9c0.400024,-2 -0.699982,-3.199999 -2.599976,-3.199999c-1,0 -1.799988,0.299999 -2.899994,1.199999l-5.600006,4.799999l-1.100006,6.200001z" class="st4"/>
|
||||||
|
<path id="svg_30" d="m393,52.700001l4.5,-8l-0.199982,0l-4.300018,-20.800001l3.200012,0l3.599976,17.4l9.600006,-17.4l3.300018,0l-16.100006,28.800001l-3.600006,0z" class="st4"/>
|
||||||
|
<path id="svg_31" d="m444.700012,25.700001l0.5,-2.900002c0.399994,-2 -0.700012,-3.199999 -2.600006,-3.199999l-7.800018,0c-2,0 -3.499969,1.199999 -3.799988,3.199999l-2.799988,15.799999c-0.400024,2 0.699982,3.200001 2.599976,3.200001l7.800018,0c2,0 3.5,-1.299999 3.799988,-3.200001l0.5,-2.899998l3.300018,0l-0.600006,3c-0.600006,3.599998 -3.399994,6 -7,6l-9,0c-3.600006,0 -5.5,-2.400002 -4.899994,-6l2.799988,-16.1c0.600006,-3.6 3.399994,-6 7,-6l9,0c3.600006,0 5.5,2.4 4.899994,6l-0.5,3l-3.199982,0l0,0.1z" class="st4"/>
|
||||||
|
<path id="svg_32" d="m463.600006,44.700001l2.600006,-14.900002c0.399994,-2 -0.700012,-3.199999 -2.600006,-3.199999c-1,0 -1.799988,0.299999 -2.899994,1.199999l-5.600006,4.700001l-2.100006,12.200001l-3.100006,0l5.200012,-29.6l3.100006,0l-2.5,13.9l3.700012,-3.1c1.799988,-1.5 3.299988,-2 5,-2c3.599976,0 5.5,2.4 4.899994,5.9l-2.600006,14.900002l-3.100006,0z" class="st4"/>
|
||||||
|
<path id="svg_33" d="m486.600006,26.6c-2,0 -2.899994,0.299999 -3.899994,1.199999l-5.600006,4.700001l-2.100006,12.200001l-3.100006,0l3.600006,-20.800001l2.399994,0l0.100006,3.4l-0.299988,1.800001l3.699982,-3.1c1.600006,-1.4 3.300018,-2 5.600006,-2l-0.399994,2.6z" class="st4"/>
|
||||||
|
<path id="svg_34" d="m496.299988,44.700001l-4.899994,0c-3.600006,0 -5.5,-2.400002 -4.899994,-6l1.599976,-8.900002c0.600006,-3.599998 3.400024,-6 7,-6l4.900024,0c3.599976,0 5.599976,2.400002 4.899994,6l-1.600006,8.900002c-0.599976,3.599998 -3.399994,6 -7,6zm5.5,-14.900002c0.299988,-2 -0.600006,-3.199999 -2.600006,-3.199999l-4.199982,0c-2,0 -3.5,1.199999 -3.799988,3.199999l-1.600006,9c-0.399994,2 0.699982,3.200001 2.699982,3.200001l4.200012,0c2,0 3.399994,-1.299999 3.799988,-3.200001l1.5,-9z" class="st4"/>
|
||||||
|
<path id="svg_35" d="m520.799988,44.700001l2.600037,-14.900002c0.399963,-2 -0.700073,-3.199999 -2.600037,-3.199999c-1,0 -1.799988,0.299999 -2.900024,1.199999l-5.599976,4.700001l-2.100006,12.200001l-3.100006,0l3.600006,-20.800001l2.399994,0l0.099976,3.4l-0.299988,1.800001l3.700012,-3.1c1.700012,-1.5 3.299988,-2 5,-2c3.599976,0 5.5,2.4 4.900024,5.9l-2.600037,14.9l-3.099976,0l0,-0.099998z" class="st4"/>
|
||||||
|
<path id="svg_36" d="m539.600037,44.700001l-4.900024,0c-3.600037,0 -5.5,-2.400002 -4.899963,-6l1.599976,-8.900002c0.599976,-3.599998 3.400024,-6 7,-6l4.900024,0c3.599976,0 5.599976,2.400002 4.899963,6l-1.599976,8.900002c-0.600037,3.599998 -3.400024,6 -7,6zm5.499939,-14.900002c0.300049,-2 -0.599976,-3.199999 -2.599976,-3.199999l-4.199951,0c-2,0 -3.5,1.199999 -3.800049,3.199999l-1.599976,9c-0.400024,2 0.700012,3.200001 2.700012,3.200001l4.200012,0c2,0 3.399963,-1.299999 3.799988,-3.200001l1.499939,-9z" class="st4"/>
|
||||||
|
<path id="svg_37" d="m555.299988,16.700001l3.299988,0l-4.400024,25.099998l14.800049,0l-0.5,2.900002l-18.099976,0l4.899963,-28z" class="st4"/>
|
||||||
|
<path id="svg_38" d="m582.099976,44.700001l-4.899963,0c-3.599976,0 -5.5,-2.400002 -4.899963,-6l1.599976,-8.900002c0.599976,-3.599998 3.400024,-6 7,-6l4.900024,0c3.599976,0 5.599976,2.400002 4.899963,6l-1.600037,8.900002c-0.599976,3.599998 -3.399963,6 -7,6zm5.500061,-14.900002c0.299988,-2 -0.600037,-3.199999 -2.600037,-3.199999l-4.199951,0c-2,0 -3.5,1.199999 -3.800049,3.199999l-1.599976,9c-0.400024,2 0.699951,3.200001 2.699951,3.200001l4.200073,0c2,0 3.399963,-1.299999 3.799927,-3.200001l1.500061,-9z" class="st4"/>
|
||||||
|
<path id="svg_39" d="m607.099976,39.5l-3.699951,3.200001c-1.799988,1.5 -3.300049,2 -5,2c-3.599976,0 -5.5,-2.400002 -4.799988,-5.900002l1.599976,-8.9c0.600037,-3.6 3.400024,-6 7,-6l3,0c2.299988,0 4,1 4.5,2.800001l0.900024,-2.800001l2.399963,0l-4,22.800001c-0.599976,3.599998 -3.5,6 -7,6l-5,0c-3.599976,0 -5.599976,-2.400002 -4.900024,-6l0.100037,-0.600002l3.100037,0l-0.100037,0.700001c-0.399963,2 0.600037,3.200001 2.600037,3.200001l4.199951,0c2,0 3.5,-1.200001 3.800049,-3.200001l1.299927,-7.299999zm1.700012,-9.700001c0.299988,-2 -0.700012,-3.199999 -2.600037,-3.199999l-4.199951,0c-2,0 -3.399963,1.199999 -3.799988,3.199999l-1.599976,9c-0.400024,2 0.699951,3.200001 2.599976,3.200001c1,0 1.799988,-0.200001 2.899963,-1.200001l5.600037,-4.700001l1.099976,-6.299999z" class="st4"/>
|
||||||
|
<path id="svg_40" d="m618.200012,23.9l3.100037,0l-3.600037,20.800001l-3.099976,0l3.599976,-20.800001zm1.299988,-7.5l3.100037,0l-0.800049,4.5l-3.099976,0l0.799988,-4.5z" class="st4"/>
|
||||||
|
<path id="svg_41" d="m640.5,37.700001l-0.200012,1c-0.599976,3.599998 -3.400024,6 -7,6l-4.899963,0c-3.600037,0 -5.500061,-2.400002 -4.900024,-6l1.599976,-8.900002c0.600037,-3.599998 3.400024,-6 7,-6l4.900024,0c3.600037,0 5.600037,2.400002 4.899963,6l-0.199951,1l-3.099976,0l0.199951,-1.099998c0.299988,-2 -0.599976,-3.200001 -2.599976,-3.200001l-4.200012,0c-2,0 -3.5,1.200001 -3.799988,3.200001l-1.599976,9c-0.400024,2 0.699951,3.200001 2.699951,3.200001l4.200012,0c2,0 3.399963,-1.300003 3.799988,-3.200001l0.200012,-1.100002l3,0l0,0.100002z" class="st4"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g id="svg_42">
|
||||||
|
<g id="svg_43">
|
||||||
|
<g id="svg_44">
|
||||||
|
<g id="svg_45">
|
||||||
|
<path id="svg_46" d="m38,121.800003l-27,0c-2.1,-0.5 -3.4,-1.900002 -3.7,-4.100006l6.8,-2.5l22.1,0l1.799999,6.600006zm-30.5,-5.800003l2.2,-21.300003l1.8,-1.599998l4.4,4.900002l-1.599999,15.5l-6.8,2.5zm11.299999,-45l-1.599998,15.5l-5.500001,4.900002l-1.4,-1.599998l2.2,-21.300003l6.299999,2.5zm22.200001,-1.599998l-22,0l-6.3,-2.5c0.8,-2.200005 2.3,-3.5 4.500001,-4.100002l26.899998,0l-3.099998,6.600002zm-2.799999,19.599998l2.899998,3.300003l-3.599998,3.299995l-21.4,0l-2.900001,-3.299995l3.7,-3.300003l21.300001,0z" class="st3"/>
|
||||||
|
<path id="svg_47" d="m61.599998,69.400002l-6.299999,-2.5c0.799999,-2.200005 2.299999,-3.5 4.5,-4.100002l26.100002,0c2.099998,0.5 3.400002,1.899998 3.699997,4.100002l-6.799995,2.5l-21.200005,0zm8,23.699997l2.900002,3.300003l-2.199997,21.299995c-0.800003,2.200005 -2.300003,3.5 -4.5,4.100006l-1.800003,-6.5l2,-18.800003l3.599998,-3.400002zm-2.699997,-4.900002l1.799995,-17.199997l6.5,0l-1.799995,17.199997l-3.700005,3.300003l-2.799995,-3.300003z" class="st3"/>
|
||||||
|
<path id="svg_48" d="m101,98l-1.800003,17.199997l-3.099998,6.5c-2.099998,-0.5 -3.400002,-1.899994 -3.699997,-4.099998l2.400002,-22.900002l1.799995,-1.599998l4.400002,4.900002zm-4.400002,-5.699997l-1.400002,-1.600006l2.400002,-22.899994c0.800003,-2.200005 2.300003,-3.5 4.5,-4.100002l1.800003,6.499996l-1.800003,17.200005l-5.5,4.900002zm26.599998,-3.300003l2.900002,3.300003l-3.599998,3.299995l-21.300003,0l-2.899994,-3.299995l3.699997,-3.300003l21.199997,0zm4.5,4.099998l1.5,1.599998l-2.399994,22.900002c-0.800003,2.200005 -2.300003,3.5 -4.5,4.099998l-1.800003,-6.5l1.800003,-17.199997l5.399994,-4.900002zm-4.199997,-6.5l1.800003,-17.199997l3.099991,-6.5c2.100006,0.5 3.400009,1.900002 3.700012,4.099998l-2.400009,22.900002l-1.899994,1.599998l-4.300003,-4.900002z" class="st3"/>
|
||||||
|
<path id="svg_49" d="m156.300003,108.699997l-6.5,0l0.699997,-6.5l6.5,0l-0.699997,6.5zm2.800003,-26.199997l-6.5,0l0.699997,-6.5l6.5,0l-0.699997,6.5z" class="st3"/>
|
||||||
|
<path id="svg_50" d="m186.100006,98l-1.800003,17.199997l-3.099991,6.5c-2.100006,-0.5 -3.400009,-1.899994 -3.699997,-4.099998l2.399994,-22.900002l1.800003,-1.599998l4.399994,4.900002zm2.799988,-27l-1.600006,15.5l-5.5,4.900002l-1.399994,-1.599998l2.199997,-21.300003l6.300003,2.5zm19.800003,-1.599998l-19.599991,0l-6.300003,-2.5c0.800003,-2.200005 2.300003,-3.5 4.5,-4.100002l24.5,0l-3.100006,6.600002zm-0.399994,19.599998l2.899994,3.300003l-3.599991,3.299995l-21.300003,0l-2.900009,-3.299995l3.700012,-3.300003l21.199997,0zm4.399994,4.099998l1.5,1.599998l-2.399994,22.900002c-0.800003,2.200005 -2.300003,3.5 -4.5,4.099998l-1.800003,-6.5l1.800003,-17.199997l5.399994,-4.900002zm-4.099991,-6.5l1.800003,-17.199997l3.100006,-6.5c2.099991,0.5 3.399994,1.900002 3.699997,4.099998l-2.400009,22.900002l-1.899994,1.599998l-4.300003,-4.900002z" class="st3"/>
|
||||||
|
<path id="svg_51" d="m228.600006,98l-1.800003,17.199997l-3.099991,6.5c-2.100006,-0.5 -3.400009,-1.899994 -3.699997,-4.099998l2.399994,-22.900002l1.800003,-1.599998l4.399994,4.900002zm-4.200012,-6.5l-1.400009,-1.599998l2.400009,-22.900002c0.800003,-2.199997 2.300003,-3.5 4.5,-4.099998l1.800003,6.5l-1.800003,17.199997l-5.5,4.900002zm4.100006,23.699997l19.600006,0l6.299988,2.5c-0.799988,2.200005 -2.299988,3.5 -4.5,4.100006l-24.5,0l3.100006,-6.600006z" class="st3"/>
|
||||||
|
<path id="svg_52" d="m271.200012,98l-1.799988,17.199997l-3.100006,6.5c-2.100006,-0.5 -3.399994,-1.899994 -3.700012,-4.099998l2.399994,-22.900002l1.800018,-1.599998l4.399994,4.900002zm2.799988,-27l-1.600006,15.5l-5.5,4.900002l-1.399994,-1.599998l2.200012,-21.300003l6.299988,2.5zm19.799988,-1.599998l-19.600006,0l-6.299988,-2.5c0.799988,-2.200005 2.299988,-3.5 4.5,-4.100002l24.5,0l-3.100006,6.600002zm-0.399994,19.599998l2.899994,3.300003l-3.600006,3.299995l-21.299988,0l-2.899994,-3.299995l3.699982,-3.300003l21.200012,0zm4.399994,4.099998l1.5,1.599998l-2.399994,22.900002c-0.800018,2.200005 -2.300018,3.5 -4.5,4.099998l-1.800018,-6.5l1.800018,-17.199997l5.399994,-4.900002zm-4.099976,-6.5l1.799988,-17.199997l3.100006,-6.5c2.100006,0.5 3.399994,1.900002 3.700012,4.099998l-2.399994,22.900002l-1.900024,1.599998l-4.299988,-4.900002z" class="st3"/>
|
||||||
|
<path id="svg_53" d="m313.700012,98l-1.799988,17.199997l-3.100006,6.5c-2.100006,-0.5 -3.399994,-1.899994 -3.700012,-4.099998l2.399994,-22.900002l1.800018,-1.599998l4.399994,4.900002zm2.899994,-27l-1.600006,15.5l-5.5,4.900002l-1.399994,-1.599998l2.200012,-21.300003l6.299988,2.5zm19.799988,-1.599998l-19.600006,0l-6.299988,-2.5c0.799988,-2.200005 2.299988,-3.5 4.5,-4.100002l24.5,0l-3.100006,6.600002zm-0.399994,19.599998l2.899994,3.300003l-3.600006,3.299995l-21.299988,0l-2.899994,-3.299995l3.699982,-3.300003l21.200012,0zm-20.600006,8.199997l4.899994,0l19.100006,21.300003c-1.300018,2.199997 -3.600006,3.300003 -6.899994,3.300003l-17.600006,-19.600006l0.5,-5zm20.800018,-10.599998l1.799988,-17.199997l3.100006,-6.5c2.100006,0.5 3.399994,1.900002 3.700012,4.099998l-2.399994,22.900002l-1.900024,1.599998l-4.299988,-4.900002z" class="st3"/>
|
||||||
|
<path id="svg_54" d="m356.299988,98l-1.799988,17.199997l-3.100006,6.5c-2.100006,-0.5 -3.399994,-1.899994 -3.700012,-4.099998l2.399994,-22.900002l1.800018,-1.599998l4.399994,4.900002zm2.800018,-27l-1.600006,15.5l-5.5,4.900002l-1.399994,-1.599998l2.200012,-21.300003l6.299988,2.5zm19.799988,-1.599998l-19.600006,0l-6.299988,-2.5c0.799988,-2.200005 2.299988,-3.5 4.5,-4.100002l24.5,0l-3.100006,6.600002zm-7.799988,20.5l-3.699982,3.299995l-2.900024,-3.299995l2,-18.800003l6.5,0l-1.899994,18.800003zm11.799988,3.199997l1.5,1.599998l-2.399994,22.900002c-0.800018,2.200005 -2.300018,3.5 -4.5,4.099998l-1.800018,-6.5l1.800018,-17.199997l5.399994,-4.900002zm-4.100006,-6.5l1.799988,-17.199997l3.100006,-6.5c2.100006,0.5 3.399994,1.900002 3.700012,4.099998l-2.399994,22.900002l-1.899994,1.599998l-4.300018,-4.900002z" class="st3"/>
|
||||||
|
<path id="svg_55" d="m411.600006,108.699997l-6.600006,0l0.700012,-6.5l6.5,0l-0.600006,6.5zm2.699982,-26.199997l-6.5,0l0.700012,-6.5l6.5,0l-0.700012,6.5z" class="st3"/>
|
||||||
|
<path id="svg_56" d="m463.399994,121.800003l-27,0c-2.100006,-0.5 -3.399994,-1.900002 -3.700012,-4.100006l6.800018,-2.5l22.099976,0l1.800018,6.600006zm-30.5,-5.800003l2.199982,-21.300003l1.800018,-1.599998l4.399994,4.900002l-1.600006,15.5l-6.799988,2.5zm11.300018,-45l-1.600006,15.5l-5.5,4.900002l-1.399994,-1.599998l2.200012,-21.300003l6.299988,2.5zm22.299988,-1.599998l-22,0l-6.299988,-2.5c0.799988,-2.200005 2.299988,-3.5 4.5,-4.100002l26.899994,0l-3.100006,6.600002z" class="st3"/>
|
||||||
|
<path id="svg_57" d="m483.899994,98l-1.800018,17.199997l-3.099976,6.5c-2.100006,-0.5 -3.400024,-1.899994 -3.700012,-4.099998l2.399994,-22.900002l1.800018,-1.599998l4.399994,4.900002zm-4.299988,-6.5l-1.399994,-1.599998l2.399994,-22.900002c0.800018,-2.199997 2.300018,-3.5 4.5,-4.099998l1.800018,6.5l-1.800018,17.199997l-5.5,4.900002zm4.100006,23.699997l19.600006,0l6.299988,2.5c-0.799988,2.200005 -2.299988,3.5 -4.5,4.100006l-24.5,0l3.100006,-6.600006z" class="st3"/>
|
||||||
|
<path id="svg_58" d="m526.400024,98l-1.799988,17.199997l-2.700012,2.5l-3.700012,-4.099998l2,-18.799995l1.799988,-1.600006l4.400024,4.800003zm-4.200012,-6.5l-1.400024,-1.599998l2,-18.800003l4.5,-4.099998l2.200012,2.5l-1.799988,17.199997l-5.5,4.800003zm25,27l-3.599976,3.300003l-18,0l-2.900024,-3.300003l3.599976,-3.300003l18,0l2.900024,3.300003zm1.899963,-49.099998l-18,0l-2.900024,-3.300003l3.600037,-3.299999l18,0l2.899963,3.299999l-3.599976,3.300003zm4,23.699997l1.5,1.599998l-2,18.800003l-4.5,4.099998l-2.200012,-2.5l1.799988,-17.199997l5.400024,-4.800003zm-4.199951,-6.5l1.799988,-17.199997l2.700012,-2.5l3.700012,4.099998l-2,18.800003l-1.900024,1.599998l-4.299988,-4.800003z" class="st3"/>
|
||||||
|
<path id="svg_59" d="m591.099976,121.800003l-27,0c-2.099976,-0.5 -3.400024,-1.900002 -3.700012,-4.100006l6.799988,-2.5l22.100037,0l1.799988,6.600006zm-30.599976,-5.800003l2.200012,-21.300003l1.799988,-1.599998l4.5,4.900002l-1.599976,15.5l-6.900024,2.5zm11.299988,-45l-1.599976,15.5l-5.5,4.900002l-1.400024,-1.599998l2.200012,-21.300003l6.299988,2.5zm22.299988,-1.599998l-22,0l-6.299988,-2.5c0.799988,-2.200005 2.299988,-3.5 4.5,-4.100002l26.899963,0l-3.099976,6.600002z" class="st3"/>
|
||||||
|
<path id="svg_60" d="m611.5,98l-1.799988,17.199997l-3.100037,6.5c-2.099976,-0.5 -3.399963,-1.899994 -3.699951,-4.099998l2.399963,-22.900002l1.799988,-1.599998l4.400024,4.900002zm-4.200012,-6.5l-1.400024,-1.599998l2.400024,-22.900002c0.799988,-2.199997 2.299988,-3.5 4.5,-4.099998l1.799988,6.5l-1.799988,17.199997l-5.5,4.900002zm26.5,-2.5l2.900024,3.300003l-3.600037,3.299995l-21.299988,0l-2.900024,-3.299995l3.700012,-3.300003l21.200012,0zm-19,-6.5l21.700012,-19.599998c3.299988,0 5.299988,1.099998 6.200012,3.299995l-23.5,21.300003l-4.900024,0l0.5,-5zm23.400024,10.599998l1.5,1.599998l-2.400024,22.900002c-0.799988,2.200005 -2.299988,3.5 -4.5,4.099998l-1.799988,-6.5l1.799988,-17.199997l5.400024,-4.900002z" class="st3"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 27 KiB |
Binary file not shown.
After Width: | Height: | Size: 1.4 KiB |
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1023.44 246"><title>logo</title><path d="M131.9,2.82A120.18,120.18,0,1,0,252.08,123,120.18,120.18,0,0,0,131.9,2.82Zm43.46,159.47-30.92,31.33a17.23,17.23,0,0,1-24.57,0l-31-31.29a17.74,17.74,0,0,1,0-24.87h0a10.72,10.72,0,0,1,15.29,0l15.66,15.83a17.23,17.23,0,0,0,24.57,0l15.64-15.85a10.72,10.72,0,0,1,15.29,0h0A17.74,17.74,0,0,1,175.36,162.28Zm32.52-50.09h0a10.33,10.33,0,0,1-14.73.15L178.2,97.23a16.61,16.61,0,0,0-23.69.25L139.29,112.9a10.49,10.49,0,0,1-8.63,3.09,10.1,10.1,0,0,1-6.37-3L109.35,97.91a16.61,16.61,0,0,0-23.69.25L70.44,113.58a10.33,10.33,0,0,1-14.73.15h0a17.1,17.1,0,0,1,.22-24L86,59.27A16.61,16.61,0,0,1,109.7,59L132.1,81.67l22.78-23.08a16.61,16.61,0,0,1,23.69-.25L208.1,88.21A17.1,17.1,0,0,1,207.88,112.19Z" style="fill:#007896"/><path d="M415.29,84.35,382,127.22a1.06,1.06,0,0,1-.83.41h-4.07a1.06,1.06,0,0,1-.83-.4L342.94,84.7a1.06,1.06,0,0,0-1.89.65v79.76a1.06,1.06,0,0,1-1.06,1.06H326.43a1.06,1.06,0,0,1-1.06-1.06l.19-111.23a1.06,1.06,0,0,1,1.06-1.05h9.76a1.06,1.06,0,0,1,.83.41l41.16,52.92a1.06,1.06,0,0,0,1.67,0l41-52.92a1.06,1.06,0,0,1,.84-.41h9.76a1.06,1.06,0,0,1,1.06,1.06V165.11a1.06,1.06,0,0,1-1.06,1.06H418.24a1.06,1.06,0,0,1-1.06-1.06V85A1.06,1.06,0,0,0,415.29,84.35Z" style="fill:#007896"/><path d="M477.81,195.63H464a1,1,0,0,1-.94-1.47l14.8-32.11a1,1,0,0,0,0-.86L444.77,88.29a1,1,0,0,1,.94-1.46h14.7a1,1,0,0,1,1,.64L484,142.55a14.44,14.44,0,0,1,1.51,5.48h.76a18.26,18.26,0,0,1,1.12-5.44l0-.08,22.58-55a1,1,0,0,1,1-.64h13.59a1,1,0,0,1,.95,1.45L478.76,195A1,1,0,0,1,477.81,195.63Z" style="fill:#007896"/><path d="M633.4,153.73a1,1,0,0,1-.1,1.36l-.87.87a47.15,47.15,0,0,1-4.44,3.49,46.65,46.65,0,0,1-7.46,4.44A52.37,52.37,0,0,1,610,167.3a61.73,61.73,0,0,1-13.79,1.51q-25.12,0-41-17t-15.87-42.13q0-25.31,15.77-42.31t41.09-17A58.06,58.06,0,0,1,616.95,54q9.45,3.59,13.22,7.18l3.1,2.95a1,1,0,0,1,.12,1.38l-7.73,10.31a1,1,0,0,1-1.58.11l-.43-.39q-.85-.76-3.68-2.74a56.13,56.13,0,0,0-5.86-3.59,35.79,35.79,0,0,0-7.84-2.83,40,40,0,0,0-9.92-1.23q-17.95,0-29.47,12.85t-11.52,31.74q0,18.7,11.52,31.55t29.47,12.85a39.63,39.63,0,0,0,15.11-2.93q7.14-2.91,10.35-5.73l.07-.06,2.14-2.14a1,1,0,0,1,1.57.11Z" style="fill:#007896"/><path d="M690.48,98.25a1,1,0,0,1-1,1h-7.34a19.37,19.37,0,0,0-14.45,6.14,21.23,21.23,0,0,0-6,15.4v44.29a1,1,0,0,1-1,1H647.25a1,1,0,0,1-1-1V87.87a1,1,0,0,1,1-1h9.59a1,1,0,0,1,1,1l.41,12.4h.94a21.44,21.44,0,0,1,8.88-10,26.86,26.86,0,0,1,14-3.59h7.34a1,1,0,0,1,1,1Z" style="fill:#007896"/><path d="M731.49,195.63H717.74a1,1,0,0,1-.95-1.48l14.8-32.09a1,1,0,0,0,0-.87L698.46,88.3a1,1,0,0,1,.95-1.47h14.68a1,1,0,0,1,1,.65l22.59,55.08c1,2.27,2.41,4.09,2.41,5.48h-1c0-1.39,1.27-3.21,2-5.48l22.59-55.08a1,1,0,0,1,1-.65h13.57a1,1,0,0,1,1,1.46L732.45,195A1,1,0,0,1,731.49,195.63Z" style="fill:#007896"/><path d="M802.37,156.15v38.44a1,1,0,0,1-1,1H787.92a1,1,0,0,1-1-1V87.86a1,1,0,0,1,1-1h10.3a1,1,0,0,1,1,1l.09,10.49h.77a1,1,0,0,0,.86-.46l.64-1a26.55,26.55,0,0,1,3-3.21,29.86,29.86,0,0,1,5.48-4.16,33.81,33.81,0,0,1,8.22-3.21,41.84,41.84,0,0,1,11.05-1.42,34.54,34.54,0,0,1,26.45,11.43q10.58,11.43,10.58,30.13,0,18.32-10.48,29.94a33.58,33.58,0,0,1-26,11.62,45.53,45.53,0,0,1-10.86-1.23,26.8,26.8,0,0,1-7.84-3,41.82,41.82,0,0,1-4.82-3.5,15.84,15.84,0,0,1-2.71-2.79l-.06-.09-.73-1.27Zm6.23-8.78a23.27,23.27,0,0,0,19.12,7.81,22.13,22.13,0,0,0,14.32-6.1q8.69-8.38,8.69-22.58,0-12.47-6.89-20.59a21.93,21.93,0,0,0-17.47-8.12,23.11,23.11,0,0,0-17.76,7.84q-7.18,7.84-7.18,20.87T808.61,147.37Z" style="fill:#007896"/><path d="M884.9,85.77V63.7A1.05,1.05,0,0,1,886,62.65h13.38a1.05,1.05,0,0,1,1.05,1.05V85.77a1.05,1.05,0,0,0,1.05,1.05H922a1.05,1.05,0,0,1,1.05,1.05V98.05A1.05,1.05,0,0,1,922,99.1H901.45a1.05,1.05,0,0,0-1.05,1.05v42.77q0,11.9,11,11.9a17.55,17.55,0,0,0,5.2-.85,18.69,18.69,0,0,0,4.25-1.79l.51-.26a1.05,1.05,0,0,1,1.44.53l4,9.22a1,1,0,0,1-.31,1.24,19.85,19.85,0,0,1-2.12,1.36,28.6,28.6,0,0,1-5.09,2.07A33.89,33.89,0,0,1,907.73,168q-11.26-.35-16.88-6.59-6-6.61-6-18.32v-43a1.05,1.05,0,0,0-1.05-1.05H871.41a1.05,1.05,0,0,1-1.05-1.05V87.88a1.05,1.05,0,0,1,1.05-1.05h12.44A1.05,1.05,0,0,0,884.9,85.77Z" style="fill:#007896"/><path d="M1000,156.72q-11.71,11.33-28.71,11.33-17.19,0-28.71-11.33t-11.52-29.85q0-18.7,11.62-30.32t28.62-11.62A39,39,0,0,1,1000,96.65q11.71,11.71,11.71,30.22T1000,156.72Zm-11-9q7.18-7.84,7.18-21.06,0-13-7.18-21.16a22.76,22.76,0,0,0-17.76-8.12,23.16,23.16,0,0,0-17.85,8q-7.27,8-7.27,21.25t7.18,21.06a24.24,24.24,0,0,0,35.7,0Z" style="fill:#007896"/></svg>
|
After Width: | Height: | Size: 4.4 KiB |
|
@ -25,12 +25,13 @@ export default class BetaAgreement extends React.PureComponent<{}, State> {
|
||||||
return (
|
return (
|
||||||
<div className={`BetaAgreement ${isFading}`}>
|
<div className={`BetaAgreement ${isFading}`}>
|
||||||
<div className="BetaAgreement-content">
|
<div className="BetaAgreement-content">
|
||||||
<h2>Welcome to the New MyCrypto Beta!</h2>
|
<h2>Welcome to the New MyCrypto Beta Release Candidate!</h2>
|
||||||
<p>
|
<p>
|
||||||
You are about to use a version of MyCrypto that hasn't been released yet. While we are
|
You are about to use the new MyCrypto Beta Release Candidate. Although this is a release
|
||||||
confident that it is close to being production ready, you may want to use the current
|
candidate for production, we encourage caution while using this unreleased version of
|
||||||
production site for larger or more time-sensitive transactions.
|
MyCrypto.
|
||||||
</p>
|
</p>
|
||||||
|
<p>We hope to move this version of MyCrypto into production in the near future!</p>
|
||||||
<p>
|
<p>
|
||||||
Feedback and bug reports are greatly appreciated. You can file issues on our{' '}
|
Feedback and bug reports are greatly appreciated. You can file issues on our{' '}
|
||||||
<NewTabLink href="https://github.com/MyCryptoHQ/MyCrypto/issues">
|
<NewTabLink href="https://github.com/MyCryptoHQ/MyCrypto/issues">
|
||||||
|
@ -46,7 +47,7 @@ export default class BetaAgreement extends React.PureComponent<{}, State> {
|
||||||
className="BetaAgreement-content-buttons-btn is-continue"
|
className="BetaAgreement-content-buttons-btn is-continue"
|
||||||
onClick={this.doContinue}
|
onClick={this.doContinue}
|
||||||
>
|
>
|
||||||
Yes, continue to the Beta
|
Yes, continue to the Beta RC
|
||||||
</button>
|
</button>
|
||||||
<button className="BetaAgreement-content-buttons-btn is-reject" onClick={this.reject}>
|
<button className="BetaAgreement-content-buttons-btn is-reject" onClick={this.reject}>
|
||||||
No, take me to the production site
|
No, take me to the production site
|
||||||
|
|
|
@ -98,7 +98,7 @@ class CustomNodeModal extends React.Component<Props, State> {
|
||||||
const options = [...staticNetwrks, ...customNetwrks, CUSTOM];
|
const options = [...staticNetwrks, ...customNetwrks, CUSTOM];
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
title={translateRaw('NODE_Title')}
|
title={translateRaw('NODE_TITLE')}
|
||||||
isOpen={isOpen}
|
isOpen={isOpen}
|
||||||
buttons={buttons}
|
buttons={buttons}
|
||||||
handleClose={handleClose}
|
handleClose={handleClose}
|
||||||
|
@ -125,7 +125,7 @@ class CustomNodeModal extends React.Component<Props, State> {
|
||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
<label className="col-sm-3 input-group">
|
<label className="col-sm-3 input-group">
|
||||||
<div className="input-group-header">Network</div>
|
<div className="input-group-header">{translate('CUSTOM_NETWORK')}</div>
|
||||||
<Dropdown
|
<Dropdown
|
||||||
value={network}
|
value={network}
|
||||||
options={options}
|
options={options}
|
|
@ -0,0 +1,2 @@
|
||||||
|
import CustomNodeModal from './CustomNodeModal';
|
||||||
|
export default CustomNodeModal;
|
|
@ -0,0 +1,154 @@
|
||||||
|
@import 'common/sass/variables';
|
||||||
|
@import 'common/sass/mixins';
|
||||||
|
|
||||||
|
// Prefer pixels of rem in this file, Electron shouldn't be responsive in the
|
||||||
|
// same way the content is.
|
||||||
|
$branding-spacing-top: 12px;
|
||||||
|
$back-spacing: 10px;
|
||||||
|
|
||||||
|
.ElectronNav {
|
||||||
|
transition: transform 300ms ease;
|
||||||
|
|
||||||
|
&.is-panel-open {
|
||||||
|
transform: translateX(-100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
&-branding {
|
||||||
|
position: -webkit-sticky;
|
||||||
|
position: sticky;
|
||||||
|
top: 0;
|
||||||
|
padding-top: $branding-spacing-top;
|
||||||
|
background: #FFF;
|
||||||
|
z-index: 1;
|
||||||
|
border-bottom: 1px solid $gray-lighter;
|
||||||
|
|
||||||
|
.is-osx & {
|
||||||
|
padding-top: $electron-osx-control-spacing + $branding-spacing-top - 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-logo {
|
||||||
|
margin-left: -1px;
|
||||||
|
height: 35px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
background-image: url('~assets/images/logo-mycrypto-transparent.svg');
|
||||||
|
background-position: center;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-size: auto 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-beta {
|
||||||
|
text-align: center;
|
||||||
|
letter-spacing: 4px;
|
||||||
|
height: 20px;
|
||||||
|
line-height: 20px;
|
||||||
|
color: #FFF;
|
||||||
|
background: $brand-info;
|
||||||
|
text-transform: uppercase;
|
||||||
|
font-size: 8px;
|
||||||
|
opacity: 0.8;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-links {
|
||||||
|
padding: 0;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-status {
|
||||||
|
margin: 12px 0;
|
||||||
|
padding-left: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-controls {
|
||||||
|
margin: 10px 0;
|
||||||
|
padding: 0 10px;
|
||||||
|
|
||||||
|
&-btn {
|
||||||
|
@include reset-button;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
width: 100%;
|
||||||
|
color: $gray-light;
|
||||||
|
font-size: 12px;
|
||||||
|
padding: 6px 0;
|
||||||
|
|
||||||
|
&-icon {
|
||||||
|
font-size: 11px;
|
||||||
|
margin-left: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: $link-hover-color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-panel {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 100%;
|
||||||
|
width: 100%;
|
||||||
|
z-index: $zindex-navbar - 1;
|
||||||
|
|
||||||
|
&-back {
|
||||||
|
@include reset-button;
|
||||||
|
position: sticky;
|
||||||
|
top: 0;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
width: 100%;
|
||||||
|
text-align: left;
|
||||||
|
padding: $back-spacing 0 8px $back-spacing;
|
||||||
|
border-bottom: 1px solid $gray-lighter;
|
||||||
|
color: rgba($text-color, 0.4);
|
||||||
|
background: #FFF;
|
||||||
|
|
||||||
|
.is-osx & {
|
||||||
|
padding-top: $electron-osx-control-spacing + $back-spacing;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-icon {
|
||||||
|
font-size: 12px;
|
||||||
|
margin-right: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: $link-hover-color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Styling for common/components/NavigationLink, with custom classname
|
||||||
|
.ElectronNavLink {
|
||||||
|
display: block;
|
||||||
|
margin: 0;
|
||||||
|
@include ellipsis;
|
||||||
|
|
||||||
|
&-link {
|
||||||
|
display: block;
|
||||||
|
height: 48px;
|
||||||
|
line-height: 48px;
|
||||||
|
padding: 0 0 0 10px;
|
||||||
|
border-bottom: 1px solid $gray-lighter;
|
||||||
|
color: $text-color;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: normal;
|
||||||
|
|
||||||
|
&.is-active {
|
||||||
|
color: $link-color;
|
||||||
|
background: $gray-lightest;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: $link-hover-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-icon {
|
||||||
|
font-size: 12px;
|
||||||
|
margin-left: 3px;
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,103 @@
|
||||||
|
import React from 'react';
|
||||||
|
import classnames from 'classnames';
|
||||||
|
import translate from 'translations';
|
||||||
|
import { navigationLinks } from 'config';
|
||||||
|
import NavigationLink from 'components/NavigationLink';
|
||||||
|
import LanguageSelect from './LanguageSelect';
|
||||||
|
import NodeSelect from './NodeSelect';
|
||||||
|
import NetworkStatus from './NetworkStatus';
|
||||||
|
import './ElectronNav.scss';
|
||||||
|
|
||||||
|
interface State {
|
||||||
|
panelContent: React.ReactElement<any> | null;
|
||||||
|
isPanelOpen: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class ElectronNav extends React.Component<{}, State> {
|
||||||
|
public state: State = {
|
||||||
|
panelContent: null,
|
||||||
|
isPanelOpen: false
|
||||||
|
};
|
||||||
|
|
||||||
|
public render() {
|
||||||
|
const { panelContent, isPanelOpen } = this.state;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={classnames({
|
||||||
|
ElectronNav: true,
|
||||||
|
'is-panel-open': isPanelOpen
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
<div className="ElectronNav-branding">
|
||||||
|
<div className="ElectronNav-branding-logo" />
|
||||||
|
<div className="ElectronNav-branding-beta">Beta Release</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<ul className="ElectronNav-links">
|
||||||
|
{navigationLinks.map(link => (
|
||||||
|
<NavigationLink
|
||||||
|
key={link.to}
|
||||||
|
link={link}
|
||||||
|
isHomepage={link === navigationLinks[0]}
|
||||||
|
className="ElectronNavLink"
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<div className="ElectronNav-controls">
|
||||||
|
<button className="ElectronNav-controls-btn" onClick={this.openLanguageSelect}>
|
||||||
|
Change Language
|
||||||
|
<i className="ElectronNav-controls-btn-icon fa fa-arrow-circle-right" />
|
||||||
|
</button>
|
||||||
|
<button className="ElectronNav-controls-btn" onClick={this.openNodeSelect}>
|
||||||
|
Change Network
|
||||||
|
<i className="ElectronNav-controls-btn-icon fa fa-arrow-circle-right" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="ElectronNav-status">
|
||||||
|
<NetworkStatus />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="ElectronNav-panel">
|
||||||
|
<button className="ElectronNav-panel-back" onClick={this.closePanel}>
|
||||||
|
<i className="ElectronNav-panel-back-icon fa fa-arrow-circle-left" />
|
||||||
|
{translate('MODAL_BACK')}
|
||||||
|
</button>
|
||||||
|
<div className="ElectronNav-panel-content">{panelContent}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private openLanguageSelect = () => {
|
||||||
|
const panelContent = <LanguageSelect closePanel={this.closePanel} />;
|
||||||
|
this.setState({
|
||||||
|
panelContent,
|
||||||
|
isPanelOpen: true
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
private openNodeSelect = () => {
|
||||||
|
const panelContent = <NodeSelect closePanel={this.closePanel} />;
|
||||||
|
this.setState({
|
||||||
|
panelContent,
|
||||||
|
isPanelOpen: true
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
private closePanel = () => {
|
||||||
|
const { panelContent } = this.state;
|
||||||
|
|
||||||
|
// Start closing panel
|
||||||
|
this.setState({ isPanelOpen: false });
|
||||||
|
|
||||||
|
// Remove content when out of sight
|
||||||
|
setTimeout(() => {
|
||||||
|
if (this.state.panelContent === panelContent) {
|
||||||
|
this.setState({ panelContent: null });
|
||||||
|
}
|
||||||
|
}, 300);
|
||||||
|
};
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
@import 'common/sass/variables';
|
||||||
|
@import 'common/sass/mixins';
|
||||||
|
|
||||||
|
.LanguageSelect {
|
||||||
|
&-language {
|
||||||
|
@include reset-button;
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
height: 48px;
|
||||||
|
line-height: 48px;
|
||||||
|
padding: 0 10px;
|
||||||
|
color: $text-color;
|
||||||
|
border-bottom: 1px solid $gray-lighter;
|
||||||
|
text-align: left;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: $link-hover-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.is-selected {
|
||||||
|
color: $link-color;
|
||||||
|
background: $gray-lightest;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,58 @@
|
||||||
|
import React from 'react';
|
||||||
|
import classnames from 'classnames';
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
import { languages } from 'config';
|
||||||
|
import { TChangeLanguage, changeLanguage } from 'actions/config';
|
||||||
|
import { getLanguageSelection } from 'selectors/config';
|
||||||
|
import { AppState } from 'reducers';
|
||||||
|
import './LanguageSelect.scss';
|
||||||
|
|
||||||
|
interface OwnProps {
|
||||||
|
closePanel(): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface StateProps {
|
||||||
|
languageSelection: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface DispatchProps {
|
||||||
|
changeLanguage: TChangeLanguage;
|
||||||
|
}
|
||||||
|
|
||||||
|
type Props = OwnProps & StateProps & DispatchProps;
|
||||||
|
|
||||||
|
class LanguageSelect extends React.Component<Props> {
|
||||||
|
public render() {
|
||||||
|
const { languageSelection } = this.props;
|
||||||
|
return (
|
||||||
|
<div className="LanguageSelect">
|
||||||
|
{Object.entries(languages).map(lang => (
|
||||||
|
<button
|
||||||
|
key={lang[0]}
|
||||||
|
className={classnames({
|
||||||
|
'LanguageSelect-language': true,
|
||||||
|
'is-selected': languageSelection === lang[0]
|
||||||
|
})}
|
||||||
|
onClick={() => this.handleLanguageSelect(lang[0])}
|
||||||
|
>
|
||||||
|
{lang[1]}
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private handleLanguageSelect = (lang: string) => {
|
||||||
|
this.props.changeLanguage(lang);
|
||||||
|
this.props.closePanel();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export default connect(
|
||||||
|
(state: AppState): StateProps => ({
|
||||||
|
languageSelection: getLanguageSelection(state)
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
changeLanguage
|
||||||
|
}
|
||||||
|
)(LanguageSelect);
|
|
@ -0,0 +1,30 @@
|
||||||
|
@import 'common/sass/variables';
|
||||||
|
|
||||||
|
.NetworkStatus {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
&-icon {
|
||||||
|
width: 10px;
|
||||||
|
height: 10px;
|
||||||
|
margin-right: 4px;
|
||||||
|
border-radius: 100%;
|
||||||
|
|
||||||
|
&.is-online {
|
||||||
|
background: $brand-success;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.is-offline {
|
||||||
|
background: $brand-danger;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.is-connecting {
|
||||||
|
background: $brand-warning;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-text {
|
||||||
|
color: $gray-dark;
|
||||||
|
font-size: 9px;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,49 @@
|
||||||
|
import React from 'react';
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
import translate from 'translations';
|
||||||
|
import { getNetworkConfig, getOffline, isNodeChanging } from 'selectors/config';
|
||||||
|
import { NetworkConfig } from 'types/network';
|
||||||
|
import { AppState } from 'reducers';
|
||||||
|
import './NetworkStatus.scss';
|
||||||
|
|
||||||
|
enum NETWORK_STATUS {
|
||||||
|
CONNECTING = 'NETWORK_STATUS_CONNECTING',
|
||||||
|
OFFLINE = 'NETWORK_STATUS_OFFLINE',
|
||||||
|
ONLINE = 'NETWORK_STATUS_ONLINE'
|
||||||
|
}
|
||||||
|
|
||||||
|
interface StateProps {
|
||||||
|
network: NetworkConfig;
|
||||||
|
isOffline: boolean;
|
||||||
|
isChangingNode: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const NetworkStatus: React.SFC<StateProps> = ({ isOffline, isChangingNode, network }) => {
|
||||||
|
let statusClass: string;
|
||||||
|
let statusText: string;
|
||||||
|
const $network = network.isCustom ? network.unit : network.name;
|
||||||
|
|
||||||
|
if (isChangingNode) {
|
||||||
|
statusClass = 'is-connecting';
|
||||||
|
statusText = NETWORK_STATUS.CONNECTING;
|
||||||
|
} else if (isOffline) {
|
||||||
|
statusClass = 'is-offline';
|
||||||
|
statusText = NETWORK_STATUS.OFFLINE;
|
||||||
|
} else {
|
||||||
|
statusClass = 'is-online';
|
||||||
|
statusText = NETWORK_STATUS.ONLINE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="NetworkStatus">
|
||||||
|
<div className={`NetworkStatus-icon ${statusClass}`} />
|
||||||
|
<div className="NetworkStatus-text">{translate(statusText, { $network })}</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default connect((state: AppState): StateProps => ({
|
||||||
|
network: getNetworkConfig(state),
|
||||||
|
isOffline: getOffline(state),
|
||||||
|
isChangingNode: isNodeChanging(state)
|
||||||
|
}))(NetworkStatus);
|
|
@ -0,0 +1,31 @@
|
||||||
|
@import 'common/sass/variables';
|
||||||
|
@import 'common/sass/mixins';
|
||||||
|
|
||||||
|
.NodeSelect {
|
||||||
|
&-node {
|
||||||
|
@include reset-button;
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
height: 48px;
|
||||||
|
line-height: 48px;
|
||||||
|
padding: 0 10px;
|
||||||
|
color: $text-color;
|
||||||
|
border-bottom: 1px solid $gray-lighter;
|
||||||
|
border-left: 4px solid;
|
||||||
|
text-align: left;
|
||||||
|
@include ellipsis;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: $link-hover-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.is-active {
|
||||||
|
color: $link-color;
|
||||||
|
background: $gray-lightest;
|
||||||
|
}
|
||||||
|
|
||||||
|
small {
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,126 @@
|
||||||
|
import React from 'react';
|
||||||
|
import classnames from 'classnames';
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
import translate from 'translations';
|
||||||
|
import CustomNodeModal from 'components/CustomNodeModal';
|
||||||
|
import {
|
||||||
|
TChangeNodeIntent,
|
||||||
|
TAddCustomNode,
|
||||||
|
TRemoveCustomNode,
|
||||||
|
changeNodeIntent,
|
||||||
|
addCustomNode,
|
||||||
|
removeCustomNode,
|
||||||
|
AddCustomNodeAction
|
||||||
|
} from 'actions/config';
|
||||||
|
import {
|
||||||
|
isNodeChanging,
|
||||||
|
getNodeId,
|
||||||
|
CustomNodeOption,
|
||||||
|
NodeOption,
|
||||||
|
getNodeOptions
|
||||||
|
} from 'selectors/config';
|
||||||
|
import { AppState } from 'reducers';
|
||||||
|
import './NodeSelect.scss';
|
||||||
|
|
||||||
|
interface OwnProps {
|
||||||
|
closePanel(): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface StateProps {
|
||||||
|
nodeSelection: AppState['config']['nodes']['selectedNode']['nodeId'];
|
||||||
|
isChangingNode: AppState['config']['nodes']['selectedNode']['pending'];
|
||||||
|
nodeOptions: (CustomNodeOption | NodeOption)[];
|
||||||
|
}
|
||||||
|
|
||||||
|
interface DispatchProps {
|
||||||
|
changeNodeIntent: TChangeNodeIntent;
|
||||||
|
addCustomNode: TAddCustomNode;
|
||||||
|
removeCustomNode: TRemoveCustomNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
type Props = OwnProps & StateProps & DispatchProps;
|
||||||
|
|
||||||
|
interface State {
|
||||||
|
isAddingCustomNode: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
class NodeSelect extends React.Component<Props, State> {
|
||||||
|
public state: State = {
|
||||||
|
isAddingCustomNode: false
|
||||||
|
};
|
||||||
|
|
||||||
|
public render() {
|
||||||
|
const { nodeSelection, nodeOptions } = this.props;
|
||||||
|
const { isAddingCustomNode } = this.state;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="NodeSelect">
|
||||||
|
{nodeOptions.map(node => (
|
||||||
|
<button
|
||||||
|
key={node.value}
|
||||||
|
className={classnames({
|
||||||
|
'NodeSelect-node': true,
|
||||||
|
'is-active': node.value === nodeSelection
|
||||||
|
})}
|
||||||
|
onClick={() => this.handleNodeSelect(node.value)}
|
||||||
|
style={{ borderLeftColor: node.color }}
|
||||||
|
>
|
||||||
|
{this.renderNodeLabel(node)}
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
<button className="NodeSelect-node is-add" onClick={this.openCustomNodeModal}>
|
||||||
|
{translate('NODE_ADD')}
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<CustomNodeModal
|
||||||
|
isOpen={isAddingCustomNode}
|
||||||
|
addCustomNode={this.addCustomNode}
|
||||||
|
handleClose={this.closeCustomNodeModal}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private handleNodeSelect = (node: string) => {
|
||||||
|
this.props.changeNodeIntent(node);
|
||||||
|
this.props.closePanel();
|
||||||
|
};
|
||||||
|
|
||||||
|
private renderNodeLabel(node: CustomNodeOption | NodeOption) {
|
||||||
|
return node.isCustom ? (
|
||||||
|
<span>
|
||||||
|
{node.label.network} - {node.label.nodeName} <small>(custom)</small>
|
||||||
|
</span>
|
||||||
|
) : (
|
||||||
|
<span>
|
||||||
|
{node.label.network} - <small>({node.label.service})</small>
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private openCustomNodeModal = () => {
|
||||||
|
this.setState({ isAddingCustomNode: true });
|
||||||
|
};
|
||||||
|
|
||||||
|
private closeCustomNodeModal = () => {
|
||||||
|
this.setState({ isAddingCustomNode: false });
|
||||||
|
};
|
||||||
|
|
||||||
|
private addCustomNode = (payload: AddCustomNodeAction['payload']) => {
|
||||||
|
this.props.addCustomNode(payload);
|
||||||
|
this.closeCustomNodeModal();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export default connect(
|
||||||
|
(state: AppState): StateProps => ({
|
||||||
|
isChangingNode: isNodeChanging(state),
|
||||||
|
nodeSelection: getNodeId(state),
|
||||||
|
nodeOptions: getNodeOptions(state)
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
changeNodeIntent,
|
||||||
|
addCustomNode,
|
||||||
|
removeCustomNode
|
||||||
|
}
|
||||||
|
)(NodeSelect);
|
|
@ -0,0 +1,2 @@
|
||||||
|
import ElectronNav from './ElectronNav';
|
||||||
|
export default ElectronNav;
|
|
@ -3,14 +3,16 @@ import { Link } from 'react-router-dom';
|
||||||
import translate from 'translations';
|
import translate from 'translations';
|
||||||
import { NewTabLink } from 'components/ui';
|
import { NewTabLink } from 'components/ui';
|
||||||
import { BlockExplorerConfig } from 'types/network';
|
import { BlockExplorerConfig } from 'types/network';
|
||||||
|
import { getTXDetailsCheckURL } from 'libs/scheduling';
|
||||||
import { etherChainExplorerInst } from 'config/data';
|
import { etherChainExplorerInst } from 'config/data';
|
||||||
|
|
||||||
export interface TransactionSucceededProps {
|
export interface TransactionSucceededProps {
|
||||||
txHash: string;
|
txHash: string;
|
||||||
blockExplorer?: BlockExplorerConfig;
|
blockExplorer?: BlockExplorerConfig;
|
||||||
|
scheduling?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const TransactionSucceeded = ({ txHash, blockExplorer }: TransactionSucceededProps) => {
|
const TransactionSucceeded = ({ txHash, blockExplorer, scheduling }: TransactionSucceededProps) => {
|
||||||
let verifyBtn: React.ReactElement<string> | undefined;
|
let verifyBtn: React.ReactElement<string> | undefined;
|
||||||
let altVerifyBtn: React.ReactElement<string> | undefined;
|
let altVerifyBtn: React.ReactElement<string> | undefined;
|
||||||
if (blockExplorer) {
|
if (blockExplorer) {
|
||||||
|
@ -30,11 +32,21 @@ const TransactionSucceeded = ({ txHash, blockExplorer }: TransactionSucceededPro
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let scheduleDetailsBtn: React.ReactElement<string> | undefined;
|
||||||
|
if (scheduling) {
|
||||||
|
scheduleDetailsBtn = (
|
||||||
|
<NewTabLink className="btn btn-xs" href={getTXDetailsCheckURL(txHash)}>
|
||||||
|
{translate('SCHEDULE_CHECK')}
|
||||||
|
</NewTabLink>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<p>
|
<p>
|
||||||
{translate('SUCCESS_3')} {txHash}
|
{translate('SUCCESS_3')} {txHash}
|
||||||
</p>
|
</p>
|
||||||
|
{scheduleDetailsBtn}
|
||||||
{verifyBtn}
|
{verifyBtn}
|
||||||
{altVerifyBtn}
|
{altVerifyBtn}
|
||||||
<Link to={`/tx-status?txHash=${txHash}`} className="btn btn-xs">
|
<Link to={`/tx-status?txHash=${txHash}`} className="btn btn-xs">
|
||||||
|
|
|
@ -1,22 +1,22 @@
|
||||||
import logo from 'assets/images/logo-mycrypto.svg';
|
import logo from 'assets/images/logo-mycrypto.svg';
|
||||||
import {
|
import {
|
||||||
ledgerReferralURL,
|
|
||||||
trezorReferralURL,
|
|
||||||
ethercardReferralURL,
|
|
||||||
donationAddressMap,
|
donationAddressMap,
|
||||||
VERSION,
|
VERSION,
|
||||||
knowledgeBaseURL,
|
knowledgeBaseURL,
|
||||||
discordURL
|
socialMediaLinks,
|
||||||
|
productLinks,
|
||||||
|
affiliateLinks,
|
||||||
|
partnerLinks
|
||||||
} from 'config';
|
} from 'config';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PreFooter from './PreFooter';
|
import PreFooter from './PreFooter';
|
||||||
import DisclaimerModal from './DisclaimerModal';
|
import DisclaimerModal from 'components/DisclaimerModal';
|
||||||
import { NewTabLink } from 'components/ui';
|
import { NewTabLink } from 'components/ui';
|
||||||
import OnboardModal from 'containers/OnboardModal';
|
import OnboardModal from 'containers/OnboardModal';
|
||||||
import './index.scss';
|
import './index.scss';
|
||||||
import { translateRaw } from 'translations';
|
import { translateRaw } from 'translations';
|
||||||
|
|
||||||
const SocialMediaLink = ({ link, text }: Link) => {
|
const SocialMediaLink = ({ link, text }: { link: string; text: string }) => {
|
||||||
return (
|
return (
|
||||||
<NewTabLink className="SocialMediaLink" key={link} href={link} aria-label={text}>
|
<NewTabLink className="SocialMediaLink" key={link} href={link} aria-label={text}>
|
||||||
<i className={`sm-icon sm-logo-${text}`} />
|
<i className={`sm-icon sm-logo-${text}`} />
|
||||||
|
@ -24,101 +24,6 @@ const SocialMediaLink = ({ link, text }: Link) => {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const SOCIAL_MEDIA: Link[] = [
|
|
||||||
{
|
|
||||||
link: 'https://twitter.com/mycrypto',
|
|
||||||
text: 'twitter'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
link: 'https://www.facebook.com/MyCrypto/',
|
|
||||||
text: 'facebook'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
link: 'https://medium.com/@mycrypto',
|
|
||||||
text: 'medium'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
link: 'https://www.linkedin.com/company/mycrypto',
|
|
||||||
text: 'linkedin'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
link: 'https://github.com/MyCryptoHQ',
|
|
||||||
text: 'github'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
link: 'https://www.reddit.com/r/mycrypto/',
|
|
||||||
text: 'reddit'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
link: discordURL,
|
|
||||||
text: 'discord'
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
const PRODUCT_INFO: Link[] = [
|
|
||||||
{
|
|
||||||
link:
|
|
||||||
'https://chrome.google.com/webstore/detail/etheraddresslookup/pdknmigbbbhmllnmgdfalmedcmcefdfn',
|
|
||||||
text: translateRaw('ETHER_ADDRESS_LOOKUP')
|
|
||||||
},
|
|
||||||
{
|
|
||||||
link:
|
|
||||||
'https://chrome.google.com/webstore/detail/ethersecuritylookup/bhhfhgpgmifehjdghlbbijjaimhmcgnf',
|
|
||||||
text: translateRaw('ETHER_SECURITY_LOOKUP')
|
|
||||||
},
|
|
||||||
{
|
|
||||||
link: 'https://etherscamdb.info/',
|
|
||||||
text: translateRaw('ETHERSCAMDB')
|
|
||||||
},
|
|
||||||
{
|
|
||||||
link: 'https://www.mycrypto.com/helpers.html',
|
|
||||||
text: translateRaw('FOOTER_HELP_AND_DEBUGGING')
|
|
||||||
},
|
|
||||||
{
|
|
||||||
link: 'mailto:press@mycrypto.com',
|
|
||||||
text: translateRaw('FOOTER_PRESS')
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
const AFFILIATES: Link[] = [
|
|
||||||
{
|
|
||||||
link: ledgerReferralURL,
|
|
||||||
text: translateRaw('LEDGER_REFERRAL_1')
|
|
||||||
},
|
|
||||||
{
|
|
||||||
link: trezorReferralURL,
|
|
||||||
text: translateRaw('TREZOR_REFERAL')
|
|
||||||
},
|
|
||||||
{
|
|
||||||
link: ethercardReferralURL,
|
|
||||||
text: translateRaw('ETHERCARD_REFERAL')
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
const FRIENDS: Link[] = [
|
|
||||||
{
|
|
||||||
link: 'https://metamask.io/',
|
|
||||||
text: 'MetaMask'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
link: 'https://infura.io/',
|
|
||||||
text: 'Infura'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
link: 'https://etherscan.io/',
|
|
||||||
text: 'Etherscan'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
link: 'https://etherchain.org/',
|
|
||||||
text: 'Etherchain'
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
interface Link {
|
|
||||||
link: string;
|
|
||||||
text: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
latestBlock: string;
|
latestBlock: string;
|
||||||
}
|
}
|
||||||
|
@ -139,7 +44,7 @@ export default class Footer extends React.PureComponent<Props, State> {
|
||||||
<footer className="Footer" role="contentinfo" aria-label="footer">
|
<footer className="Footer" role="contentinfo" aria-label="footer">
|
||||||
<div className="Footer-links Footer-section">
|
<div className="Footer-links Footer-section">
|
||||||
<div className="Footer-links-social">
|
<div className="Footer-links-social">
|
||||||
{SOCIAL_MEDIA.map((socialMediaItem, idx) => (
|
{socialMediaLinks.map((socialMediaItem, idx) => (
|
||||||
<SocialMediaLink
|
<SocialMediaLink
|
||||||
link={socialMediaItem.link}
|
link={socialMediaItem.link}
|
||||||
key={idx}
|
key={idx}
|
||||||
|
@ -149,11 +54,14 @@ export default class Footer extends React.PureComponent<Props, State> {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="Footer-links-links">
|
<div className="Footer-links-links">
|
||||||
{PRODUCT_INFO.map((productInfoItem, idx) => (
|
{productLinks.map(link => (
|
||||||
<NewTabLink key={idx} href={productInfoItem.link}>
|
<NewTabLink key={link.link} href={link.link}>
|
||||||
{productInfoItem.text}
|
{link.text}
|
||||||
</NewTabLink>
|
</NewTabLink>
|
||||||
))}
|
))}
|
||||||
|
<NewTabLink href="mailto:press@mycrypto.com">
|
||||||
|
{translateRaw('FOOTER_PRESS')}
|
||||||
|
</NewTabLink>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -169,7 +77,7 @@ export default class Footer extends React.PureComponent<Props, State> {
|
||||||
</NewTabLink>
|
</NewTabLink>
|
||||||
|
|
||||||
<div className="Footer-about-links">
|
<div className="Footer-about-links">
|
||||||
<a href="https://mycrypto.com">MyCrypto.com</a>
|
<NewTabLink href="https://mycrypto.com">MyCrypto.com</NewTabLink>
|
||||||
<NewTabLink href={knowledgeBaseURL}>{translateRaw('FOOTER_SUPPORT')}</NewTabLink>
|
<NewTabLink href={knowledgeBaseURL}>{translateRaw('FOOTER_SUPPORT')}</NewTabLink>
|
||||||
<NewTabLink href="https://about.mycrypto.com">
|
<NewTabLink href="https://about.mycrypto.com">
|
||||||
{translateRaw('FOOTER_TEAM')}
|
{translateRaw('FOOTER_TEAM')}
|
||||||
|
@ -192,7 +100,7 @@ export default class Footer extends React.PureComponent<Props, State> {
|
||||||
<div className="Footer-support Footer-section">
|
<div className="Footer-support Footer-section">
|
||||||
<h5 className="Footer-support-title">{translateRaw('FOOTER_AFFILIATE_TITLE')}</h5>
|
<h5 className="Footer-support-title">{translateRaw('FOOTER_AFFILIATE_TITLE')}</h5>
|
||||||
<div className="Footer-support-affiliates">
|
<div className="Footer-support-affiliates">
|
||||||
{AFFILIATES.map((link, i) => (
|
{affiliateLinks.map((link, i) => (
|
||||||
<NewTabLink key={i} href={link.link}>
|
<NewTabLink key={i} href={link.link}>
|
||||||
{link.text}
|
{link.text}
|
||||||
</NewTabLink>
|
</NewTabLink>
|
||||||
|
@ -214,7 +122,7 @@ export default class Footer extends React.PureComponent<Props, State> {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="Footer-support-friends">
|
<div className="Footer-support-friends">
|
||||||
{FRIENDS.map((link, i) => (
|
{partnerLinks.map((link, i) => (
|
||||||
<NewTabLink key={i} href={link.link}>
|
<NewTabLink key={i} href={link.link}>
|
||||||
{link.text}
|
{link.text}
|
||||||
</NewTabLink>
|
</NewTabLink>
|
||||||
|
|
|
@ -9,9 +9,14 @@ import { Input } from 'components/ui';
|
||||||
interface Props {
|
interface Props {
|
||||||
customLabel?: string;
|
customLabel?: string;
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
|
hideGasCalculationSpinner?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const GasLimitField: React.SFC<Props> = ({ customLabel, disabled }) => (
|
export const GasLimitField: React.SFC<Props> = ({
|
||||||
|
customLabel,
|
||||||
|
disabled,
|
||||||
|
hideGasCalculationSpinner
|
||||||
|
}) => (
|
||||||
<GasLimitFieldFactory
|
<GasLimitFieldFactory
|
||||||
withProps={({ gasLimit: { raw }, onChange, readOnly, gasEstimationPending }) => (
|
withProps={({ gasLimit: { raw }, onChange, readOnly, gasEstimationPending }) => (
|
||||||
<div className="input-group-wrapper">
|
<div className="input-group-wrapper">
|
||||||
|
@ -19,7 +24,10 @@ export const GasLimitField: React.SFC<Props> = ({ customLabel, disabled }) => (
|
||||||
<div className="input-group-header">
|
<div className="input-group-header">
|
||||||
{customLabel ? customLabel : translate('TRANS_GAS')}
|
{customLabel ? customLabel : translate('TRANS_GAS')}
|
||||||
<div className="flex-spacer" />
|
<div className="flex-spacer" />
|
||||||
<InlineSpinner active={gasEstimationPending} text="Calculating" />
|
<InlineSpinner
|
||||||
|
active={!hideGasCalculationSpinner && gasEstimationPending}
|
||||||
|
text="Calculating"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<Input
|
<Input
|
||||||
className={gasLimitValidator(raw) ? 'is-valid' : 'is-invalid'}
|
className={gasLimitValidator(raw) ? 'is-valid' : 'is-invalid'}
|
||||||
|
|
|
@ -5,6 +5,7 @@ import { inputGasLimit, TInputGasLimit } from 'actions/transaction';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { AppState } from 'reducers';
|
import { AppState } from 'reducers';
|
||||||
import { sanitizeNumericalInput } from 'libs/values';
|
import { sanitizeNumericalInput } from 'libs/values';
|
||||||
|
import { getSchedulingToggle } from 'selectors/schedule/fields';
|
||||||
|
|
||||||
const defaultGasLimit = '21000';
|
const defaultGasLimit = '21000';
|
||||||
|
|
||||||
|
@ -18,16 +19,24 @@ export interface CallBackProps {
|
||||||
interface DispatchProps {
|
interface DispatchProps {
|
||||||
inputGasLimit: TInputGasLimit;
|
inputGasLimit: TInputGasLimit;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface OwnProps {
|
interface OwnProps {
|
||||||
gasLimit: string | null;
|
gasLimit: string | null;
|
||||||
|
scheduling: boolean;
|
||||||
|
|
||||||
withProps(props: CallBackProps): React.ReactElement<any> | null;
|
withProps(props: CallBackProps): React.ReactElement<any> | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
type Props = DispatchProps & OwnProps;
|
type Props = DispatchProps & OwnProps;
|
||||||
|
|
||||||
class GasLimitFieldClass extends Component<Props, {}> {
|
class GasLimitFieldClass extends Component<Props> {
|
||||||
public componentDidMount() {
|
public componentDidMount() {
|
||||||
const { gasLimit } = this.props;
|
const { gasLimit, scheduling } = this.props;
|
||||||
|
|
||||||
|
if (scheduling) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (gasLimit) {
|
if (gasLimit) {
|
||||||
this.props.inputGasLimit(gasLimit);
|
this.props.inputGasLimit(gasLimit);
|
||||||
} else {
|
} else {
|
||||||
|
@ -45,7 +54,12 @@ class GasLimitFieldClass extends Component<Props, {}> {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const GasLimitField = connect(null, { inputGasLimit })(GasLimitFieldClass);
|
const GasLimitField = connect(
|
||||||
|
(state: AppState) => ({
|
||||||
|
scheduling: getSchedulingToggle(state).value
|
||||||
|
}),
|
||||||
|
{ inputGasLimit }
|
||||||
|
)(GasLimitFieldClass);
|
||||||
|
|
||||||
interface DefaultGasLimitFieldProps {
|
interface DefaultGasLimitFieldProps {
|
||||||
withProps(props: CallBackProps): React.ReactElement<any> | null;
|
withProps(props: CallBackProps): React.ReactElement<any> | null;
|
||||||
|
|
|
@ -42,7 +42,7 @@ interface OwnProps {
|
||||||
|
|
||||||
type Props = OwnProps & StateProps;
|
type Props = OwnProps & StateProps;
|
||||||
|
|
||||||
class GenerateTransactionFactoryClass extends Component<Props> {
|
export class GenerateTransactionFactoryClass extends Component<Props> {
|
||||||
public render() {
|
public render() {
|
||||||
const {
|
const {
|
||||||
walletType,
|
walletType,
|
||||||
|
@ -61,6 +61,7 @@ class GenerateTransactionFactoryClass extends Component<Props> {
|
||||||
|
|
||||||
const isButtonDisabled =
|
const isButtonDisabled =
|
||||||
!isFullTransaction || networkRequestPending || !validGasPrice || !validGasLimit;
|
!isFullTransaction || networkRequestPending || !validGasPrice || !validGasLimit;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<WithSigner
|
<WithSigner
|
||||||
|
|
|
@ -78,3 +78,69 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Styling for common/components/NavigationLink.tsx, with custom class
|
||||||
|
.NavigationLink {
|
||||||
|
display: inline-block;
|
||||||
|
|
||||||
|
&-link {
|
||||||
|
color: darken($link-color, 15%);
|
||||||
|
display: block;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 300;
|
||||||
|
padding: 10px;
|
||||||
|
white-space: nowrap;
|
||||||
|
position: relative;
|
||||||
|
min-height: 2.75rem;
|
||||||
|
|
||||||
|
&-icon {
|
||||||
|
font-size: 0.7rem;
|
||||||
|
margin-left: 2px;
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:after {
|
||||||
|
content: '';
|
||||||
|
background: $brand-primary;
|
||||||
|
height: 2px;
|
||||||
|
width: 100%;
|
||||||
|
left: 0px;
|
||||||
|
position: absolute;
|
||||||
|
bottom: -1px;
|
||||||
|
transition: all 250ms ease 0s;
|
||||||
|
transform: scaleX(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.is-active,
|
||||||
|
&:hover,
|
||||||
|
&:focus {
|
||||||
|
color: $brand-primary;
|
||||||
|
text-decoration: none;
|
||||||
|
transition: all 250ms ease 0s;
|
||||||
|
|
||||||
|
&:after {
|
||||||
|
transform: scaleX(1);
|
||||||
|
transition: all 250ms ease 0s;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.is-disabled {
|
||||||
|
pointer-events: none;
|
||||||
|
opacity: 0.3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#NAV_SWAP.NavigationLink-link:before {
|
||||||
|
content: '';
|
||||||
|
display: inline-block;
|
||||||
|
margin-top: -0.1rem;
|
||||||
|
width: 1.3rem;
|
||||||
|
height: 1.3rem;
|
||||||
|
background-image: url('~assets/images/logo-shapeshift-no-text.svg');
|
||||||
|
background-position: center;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-size: contain;
|
||||||
|
vertical-align: middle;
|
||||||
|
margin-right: 4px;
|
||||||
|
}
|
||||||
|
|
|
@ -1,54 +1,8 @@
|
||||||
import React, { PureComponent } from 'react';
|
import React, { PureComponent } from 'react';
|
||||||
import NavigationLink from './NavigationLink';
|
import NavigationLink from 'components/NavigationLink';
|
||||||
import { knowledgeBaseURL } from 'config';
|
import { navigationLinks } from 'config';
|
||||||
import './Navigation.scss';
|
import './Navigation.scss';
|
||||||
|
|
||||||
export interface TabLink {
|
|
||||||
name: string;
|
|
||||||
to: string;
|
|
||||||
external?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
const tabs: TabLink[] = [
|
|
||||||
{
|
|
||||||
name: 'NAV_VIEW',
|
|
||||||
to: '/account'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'NAV_GENERATEWALLET',
|
|
||||||
to: '/generate'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'NAV_SWAP',
|
|
||||||
to: '/swap'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'NAV_CONTRACTS',
|
|
||||||
to: '/contracts'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'NAV_ENS',
|
|
||||||
to: '/ens'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'NAV_SIGN',
|
|
||||||
to: '/sign-and-verify-message'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'NAV_TXSTATUS',
|
|
||||||
to: '/tx-status'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'NAV_BROADCAST',
|
|
||||||
to: '/pushTx'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'NAV_HELP',
|
|
||||||
to: `${knowledgeBaseURL}`,
|
|
||||||
external: true
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
color?: string | false;
|
color?: string | false;
|
||||||
}
|
}
|
||||||
|
@ -97,9 +51,14 @@ export default class Navigation extends PureComponent<Props, State> {
|
||||||
|
|
||||||
<div className="Navigation-scroll container">
|
<div className="Navigation-scroll container">
|
||||||
<ul className="Navigation-links">
|
<ul className="Navigation-links">
|
||||||
{tabs.map(link => {
|
{navigationLinks.map(link => (
|
||||||
return <NavigationLink key={link.name} link={link} isHomepage={link === tabs[0]} />;
|
<NavigationLink
|
||||||
})}
|
key={link.name}
|
||||||
|
link={link}
|
||||||
|
isHomepage={link === navigationLinks[0]}
|
||||||
|
className="NavigationLink"
|
||||||
|
/>
|
||||||
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -1,60 +0,0 @@
|
||||||
@import 'common/sass/variables';
|
|
||||||
|
|
||||||
.NavigationLink {
|
|
||||||
display: inline-block;
|
|
||||||
|
|
||||||
&-link {
|
|
||||||
color: darken($link-color, 15%);
|
|
||||||
display: block;
|
|
||||||
font-size: 16px;
|
|
||||||
font-weight: 300;
|
|
||||||
padding: 10px;
|
|
||||||
white-space: nowrap;
|
|
||||||
position: relative;
|
|
||||||
min-height: 2.75rem;
|
|
||||||
|
|
||||||
&:after {
|
|
||||||
content: '';
|
|
||||||
background: $brand-primary;
|
|
||||||
height: 2px;
|
|
||||||
width: 100%;
|
|
||||||
left: 0px;
|
|
||||||
position: absolute;
|
|
||||||
bottom: -1px;
|
|
||||||
transition: all 250ms ease 0s;
|
|
||||||
transform: scaleX(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
&.is-active,
|
|
||||||
&:hover,
|
|
||||||
&:focus {
|
|
||||||
color: $brand-primary;
|
|
||||||
text-decoration: none;
|
|
||||||
transition: all 250ms ease 0s;
|
|
||||||
|
|
||||||
&:after {
|
|
||||||
transform: scaleX(1);
|
|
||||||
transition: all 250ms ease 0s;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.is-disabled {
|
|
||||||
pointer-events: none;
|
|
||||||
opacity: 0.3;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#NAV_SWAP a:before {
|
|
||||||
content: '';
|
|
||||||
display: inline-block;
|
|
||||||
margin-top: -0.1rem;
|
|
||||||
width: 1.3rem;
|
|
||||||
height: 1.3rem;
|
|
||||||
background-image: url('~assets/images/logo-shapeshift-no-text.svg');
|
|
||||||
background-position: center;
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
background-size: contain;
|
|
||||||
vertical-align: middle;
|
|
||||||
margin-right: 4px;
|
|
||||||
}
|
|
|
@ -1,12 +1,14 @@
|
||||||
import {
|
import {
|
||||||
TChangeLanguage,
|
TChangeLanguage,
|
||||||
TChangeNodeIntent,
|
TChangeNodeIntent,
|
||||||
|
TChangeNodeIntentOneTime,
|
||||||
TAddCustomNode,
|
TAddCustomNode,
|
||||||
TRemoveCustomNode,
|
TRemoveCustomNode,
|
||||||
TAddCustomNetwork,
|
TAddCustomNetwork,
|
||||||
AddCustomNodeAction,
|
AddCustomNodeAction,
|
||||||
changeLanguage,
|
changeLanguage,
|
||||||
changeNodeIntent,
|
changeNodeIntent,
|
||||||
|
changeNodeIntentOneTime,
|
||||||
addCustomNode,
|
addCustomNode,
|
||||||
removeCustomNode,
|
removeCustomNode,
|
||||||
addCustomNetwork
|
addCustomNetwork
|
||||||
|
@ -19,8 +21,8 @@ import { Link } from 'react-router-dom';
|
||||||
import { TSetGasPriceField, setGasPriceField } from 'actions/transaction';
|
import { TSetGasPriceField, setGasPriceField } from 'actions/transaction';
|
||||||
import { ANNOUNCEMENT_MESSAGE, ANNOUNCEMENT_TYPE, languages } from 'config';
|
import { ANNOUNCEMENT_MESSAGE, ANNOUNCEMENT_TYPE, languages } from 'config';
|
||||||
import Navigation from './components/Navigation';
|
import Navigation from './components/Navigation';
|
||||||
import CustomNodeModal from './components/CustomNodeModal';
|
|
||||||
import OnlineStatus from './components/OnlineStatus';
|
import OnlineStatus from './components/OnlineStatus';
|
||||||
|
import CustomNodeModal from 'components/CustomNodeModal';
|
||||||
import { getKeyByValue } from 'utils/helpers';
|
import { getKeyByValue } from 'utils/helpers';
|
||||||
import { NodeConfig } from 'types/node';
|
import { NodeConfig } from 'types/node';
|
||||||
import './index.scss';
|
import './index.scss';
|
||||||
|
@ -34,15 +36,21 @@ import {
|
||||||
CustomNodeOption,
|
CustomNodeOption,
|
||||||
NodeOption,
|
NodeOption,
|
||||||
getNodeOptions,
|
getNodeOptions,
|
||||||
getNetworkConfig
|
getNetworkConfig,
|
||||||
|
isStaticNodeId
|
||||||
} from 'selectors/config';
|
} from 'selectors/config';
|
||||||
import { NetworkConfig } from 'types/network';
|
import { NetworkConfig } from 'types/network';
|
||||||
import { connect } from 'react-redux';
|
import { connect, MapStateToProps } from 'react-redux';
|
||||||
import { stripWeb3Network } from 'libs/nodes';
|
import translate from 'translations';
|
||||||
|
|
||||||
|
interface OwnProps {
|
||||||
|
networkParam: string | null;
|
||||||
|
}
|
||||||
|
|
||||||
interface DispatchProps {
|
interface DispatchProps {
|
||||||
changeLanguage: TChangeLanguage;
|
changeLanguage: TChangeLanguage;
|
||||||
changeNodeIntent: TChangeNodeIntent;
|
changeNodeIntent: TChangeNodeIntent;
|
||||||
|
changeNodeIntentOneTime: TChangeNodeIntentOneTime;
|
||||||
setGasPriceField: TSetGasPriceField;
|
setGasPriceField: TSetGasPriceField;
|
||||||
addCustomNode: TAddCustomNode;
|
addCustomNode: TAddCustomNode;
|
||||||
removeCustomNode: TRemoveCustomNode;
|
removeCustomNode: TRemoveCustomNode;
|
||||||
|
@ -50,6 +58,7 @@ interface DispatchProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
interface StateProps {
|
interface StateProps {
|
||||||
|
shouldSetNodeFromQS: boolean;
|
||||||
network: NetworkConfig;
|
network: NetworkConfig;
|
||||||
languageSelection: AppState['config']['meta']['languageSelection'];
|
languageSelection: AppState['config']['meta']['languageSelection'];
|
||||||
node: NodeConfig;
|
node: NodeConfig;
|
||||||
|
@ -59,7 +68,11 @@ interface StateProps {
|
||||||
nodeOptions: (CustomNodeOption | NodeOption)[];
|
nodeOptions: (CustomNodeOption | NodeOption)[];
|
||||||
}
|
}
|
||||||
|
|
||||||
const mapStateToProps = (state: AppState): StateProps => ({
|
const mapStateToProps: MapStateToProps<StateProps, OwnProps, AppState> = (
|
||||||
|
state,
|
||||||
|
{ networkParam }
|
||||||
|
): StateProps => ({
|
||||||
|
shouldSetNodeFromQS: !!(networkParam && isStaticNodeId(state, networkParam)),
|
||||||
isOffline: getOffline(state),
|
isOffline: getOffline(state),
|
||||||
isChangingNode: isNodeChanging(state),
|
isChangingNode: isNodeChanging(state),
|
||||||
languageSelection: getLanguageSelection(state),
|
languageSelection: getLanguageSelection(state),
|
||||||
|
@ -73,6 +86,7 @@ const mapDispatchToProps: DispatchProps = {
|
||||||
setGasPriceField,
|
setGasPriceField,
|
||||||
changeLanguage,
|
changeLanguage,
|
||||||
changeNodeIntent,
|
changeNodeIntent,
|
||||||
|
changeNodeIntentOneTime,
|
||||||
addCustomNode,
|
addCustomNode,
|
||||||
removeCustomNode,
|
removeCustomNode,
|
||||||
addCustomNetwork
|
addCustomNetwork
|
||||||
|
@ -82,13 +96,17 @@ interface State {
|
||||||
isAddingCustomNode: boolean;
|
isAddingCustomNode: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
type Props = StateProps & DispatchProps;
|
type Props = OwnProps & StateProps & DispatchProps;
|
||||||
|
|
||||||
class Header extends Component<Props, State> {
|
class Header extends Component<Props, State> {
|
||||||
public state = {
|
public state = {
|
||||||
isAddingCustomNode: false
|
isAddingCustomNode: false
|
||||||
};
|
};
|
||||||
|
|
||||||
|
public componentDidMount() {
|
||||||
|
this.attemptSetNodeFromQueryParameter();
|
||||||
|
}
|
||||||
|
|
||||||
public render() {
|
public render() {
|
||||||
const {
|
const {
|
||||||
languageSelection,
|
languageSelection,
|
||||||
|
@ -120,7 +138,7 @@ class Header extends Component<Props, State> {
|
||||||
...rest,
|
...rest,
|
||||||
name: (
|
name: (
|
||||||
<span>
|
<span>
|
||||||
{stripWeb3Network(label.network)} <small>({label.service})</small>
|
{label.network} <small>({label.service})</small>
|
||||||
</span>
|
</span>
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
@ -161,7 +179,6 @@ class Header extends Component<Props, State> {
|
||||||
color="white"
|
color="white"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{console.log(nodeSelection)}
|
|
||||||
<div
|
<div
|
||||||
className={classnames({
|
className={classnames({
|
||||||
'Header-branding-right-dropdown': true,
|
'Header-branding-right-dropdown': true,
|
||||||
|
@ -177,7 +194,7 @@ class Header extends Component<Props, State> {
|
||||||
value={nodeSelection || ''}
|
value={nodeSelection || ''}
|
||||||
extra={
|
extra={
|
||||||
<li>
|
<li>
|
||||||
<a onClick={this.openCustomNodeModal}>Add Custom Node</a>
|
<a onClick={this.openCustomNodeModal}>{translate('NODE_ADD')}</a>
|
||||||
</li>
|
</li>
|
||||||
}
|
}
|
||||||
disabled={nodeSelection === 'web3'}
|
disabled={nodeSelection === 'web3'}
|
||||||
|
@ -221,6 +238,13 @@ class Header extends Component<Props, State> {
|
||||||
this.setState({ isAddingCustomNode: false });
|
this.setState({ isAddingCustomNode: false });
|
||||||
this.props.addCustomNode(payload);
|
this.props.addCustomNode(payload);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private attemptSetNodeFromQueryParameter() {
|
||||||
|
const { shouldSetNodeFromQS, networkParam } = this.props;
|
||||||
|
if (shouldSetNodeFromQS) {
|
||||||
|
this.props.changeNodeIntentOneTime(networkParam!);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default connect(mapStateToProps, mapDispatchToProps)(Header);
|
export default connect(mapStateToProps, mapDispatchToProps)(Header);
|
||||||
|
|
|
@ -70,7 +70,6 @@ class LogOutPromptClass extends React.Component<Props, State> {
|
||||||
private onConfirm = () => {
|
private onConfirm = () => {
|
||||||
const { nextLocation: next } = this.state;
|
const { nextLocation: next } = this.state;
|
||||||
this.props.resetWallet();
|
this.props.resetWallet();
|
||||||
this.props.web3UnsetNode();
|
|
||||||
this.setState(
|
this.setState(
|
||||||
{
|
{
|
||||||
openModal: false,
|
openModal: false,
|
||||||
|
@ -79,6 +78,7 @@ class LogOutPromptClass extends React.Component<Props, State> {
|
||||||
() => {
|
() => {
|
||||||
if (next) {
|
if (next) {
|
||||||
this.props.history.push(`${next.pathname}${next.search}${next.hash}`);
|
this.props.history.push(`${next.pathname}${next.search}${next.hash}`);
|
||||||
|
this.props.web3UnsetNode();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
|
@ -2,21 +2,20 @@ import classnames from 'classnames';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Link, withRouter, RouteComponentProps } from 'react-router-dom';
|
import { Link, withRouter, RouteComponentProps } from 'react-router-dom';
|
||||||
import translate, { translateRaw } from 'translations';
|
import translate, { translateRaw } from 'translations';
|
||||||
import { TabLink } from './Navigation';
|
import { NavigationLink } from 'config';
|
||||||
import './NavigationLink.scss';
|
|
||||||
|
|
||||||
interface Props extends RouteComponentProps<{}> {
|
interface Props extends RouteComponentProps<{}> {
|
||||||
link: TabLink;
|
link: NavigationLink;
|
||||||
isHomepage: boolean;
|
isHomepage: boolean;
|
||||||
|
className: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
class NavigationLink extends React.PureComponent<Props, {}> {
|
class NavigationLinkClass extends React.PureComponent<Props, {}> {
|
||||||
public render() {
|
public render() {
|
||||||
const { link, location, isHomepage } = this.props;
|
const { link, location, isHomepage, className } = this.props;
|
||||||
const isExternalLink = link.to.includes('http');
|
|
||||||
let isActive = false;
|
let isActive = false;
|
||||||
|
|
||||||
if (!isExternalLink) {
|
if (!link.external) {
|
||||||
// isActive if
|
// isActive if
|
||||||
// 1) Current path is the same as link
|
// 1) Current path is the same as link
|
||||||
// 2) the first path is the same for both links (/account and /account/send)
|
// 2) the first path is the same for both links (/account and /account/send)
|
||||||
|
@ -27,7 +26,7 @@ class NavigationLink extends React.PureComponent<Props, {}> {
|
||||||
}
|
}
|
||||||
|
|
||||||
const linkClasses = classnames({
|
const linkClasses = classnames({
|
||||||
'NavigationLink-link': true,
|
[`${className}-link`]: true,
|
||||||
'is-disabled': !link.to,
|
'is-disabled': !link.to,
|
||||||
'is-active': isActive
|
'is-active': isActive
|
||||||
});
|
});
|
||||||
|
@ -43,6 +42,7 @@ class NavigationLink extends React.PureComponent<Props, {}> {
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
>
|
>
|
||||||
{translate(link.name)}
|
{translate(link.name)}
|
||||||
|
<i className={`${className}-link-icon fa fa-external-link`} />
|
||||||
</a>
|
</a>
|
||||||
) : (
|
) : (
|
||||||
<Link className={linkClasses} to={(link as any).to} aria-label={linkLabel}>
|
<Link className={linkClasses} to={(link as any).to} aria-label={linkLabel}>
|
||||||
|
@ -51,7 +51,7 @@ class NavigationLink extends React.PureComponent<Props, {}> {
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<li id={link.name} className="NavigationLink">
|
<li id={link.name} className={className}>
|
||||||
{linkEl}
|
{linkEl}
|
||||||
</li>
|
</li>
|
||||||
);
|
);
|
||||||
|
@ -59,4 +59,4 @@ class NavigationLink extends React.PureComponent<Props, {}> {
|
||||||
}
|
}
|
||||||
|
|
||||||
// withRouter is a HOC which provides NavigationLink with a react-router location prop
|
// withRouter is a HOC which provides NavigationLink with a react-router location prop
|
||||||
export default withRouter<Props>(NavigationLink);
|
export default withRouter<Props>(NavigationLinkClass);
|
|
@ -41,7 +41,7 @@ interface OwnProps {
|
||||||
|
|
||||||
type Props = StateProps & OwnProps;
|
type Props = StateProps & OwnProps;
|
||||||
|
|
||||||
class SendButtonFactoryClass extends Component<Props> {
|
export class SendButtonFactoryClass extends Component<Props> {
|
||||||
public render() {
|
public render() {
|
||||||
const {
|
const {
|
||||||
signing,
|
signing,
|
||||||
|
|
|
@ -3,6 +3,10 @@
|
||||||
.SubTabs {
|
.SubTabs {
|
||||||
margin-top: $space-xs;
|
margin-top: $space-xs;
|
||||||
|
|
||||||
|
.is-electron & {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
&-tabs {
|
&-tabs {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
|
|
|
@ -50,6 +50,7 @@ interface OwnProps {
|
||||||
disableToggle?: boolean;
|
disableToggle?: boolean;
|
||||||
advancedGasOptions?: AdvancedOptions;
|
advancedGasOptions?: AdvancedOptions;
|
||||||
className?: string;
|
className?: string;
|
||||||
|
scheduling?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
type Props = DispatchProps & OwnProps & StateProps;
|
type Props = DispatchProps & OwnProps & StateProps;
|
||||||
|
@ -90,16 +91,19 @@ class TXMetaDataPanel extends React.Component<Props, State> {
|
||||||
}
|
}
|
||||||
|
|
||||||
public render() {
|
public render() {
|
||||||
const { offline, disableToggle, advancedGasOptions, className = '' } = this.props;
|
const { offline, disableToggle, advancedGasOptions, className = '', scheduling } = this.props;
|
||||||
const { gasPrice } = this.state;
|
const { gasPrice } = this.state;
|
||||||
const showAdvanced = this.state.sliderState === 'advanced' || offline;
|
const showAdvanced = this.state.sliderState === 'advanced' || offline;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={`Gas col-md-12 ${className}`}>
|
<div className={`Gas col-md-12 ${className}`}>
|
||||||
|
<br />
|
||||||
{showAdvanced ? (
|
{showAdvanced ? (
|
||||||
<AdvancedGas
|
<AdvancedGas
|
||||||
gasPrice={gasPrice}
|
gasPrice={gasPrice}
|
||||||
inputGasPrice={this.props.inputGasPrice}
|
inputGasPrice={this.props.inputGasPrice}
|
||||||
options={advancedGasOptions}
|
options={advancedGasOptions}
|
||||||
|
scheduling={scheduling}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<SimpleGas
|
<SimpleGas
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { translateRaw } from 'translations';
|
import { translateRaw } from 'translations';
|
||||||
import FeeSummary from './FeeSummary';
|
import FeeSummary, { RenderData } from './FeeSummary';
|
||||||
import './AdvancedGas.scss';
|
import './AdvancedGas.scss';
|
||||||
import { TToggleAutoGasLimit, toggleAutoGasLimit } from 'actions/config';
|
import { TToggleAutoGasLimit, toggleAutoGasLimit } from 'actions/config';
|
||||||
import { AppState } from 'reducers';
|
import { AppState } from 'reducers';
|
||||||
|
@ -11,6 +11,8 @@ import { getAutoGasLimitEnabled } from 'selectors/config';
|
||||||
import { isValidGasPrice } from 'selectors/transaction';
|
import { isValidGasPrice } from 'selectors/transaction';
|
||||||
import { sanitizeNumericalInput } from 'libs/values';
|
import { sanitizeNumericalInput } from 'libs/values';
|
||||||
import { Input } from 'components/ui';
|
import { Input } from 'components/ui';
|
||||||
|
import { EAC_SCHEDULING_CONFIG } from 'libs/scheduling';
|
||||||
|
import { getScheduleGasPrice, getTimeBounty } from 'selectors/schedule';
|
||||||
|
|
||||||
export interface AdvancedOptions {
|
export interface AdvancedOptions {
|
||||||
gasPriceField?: boolean;
|
gasPriceField?: boolean;
|
||||||
|
@ -24,6 +26,9 @@ interface OwnProps {
|
||||||
inputGasPrice: TInputGasPrice;
|
inputGasPrice: TInputGasPrice;
|
||||||
gasPrice: AppState['transaction']['fields']['gasPrice'];
|
gasPrice: AppState['transaction']['fields']['gasPrice'];
|
||||||
options?: AdvancedOptions;
|
options?: AdvancedOptions;
|
||||||
|
scheduling?: boolean;
|
||||||
|
scheduleGasPrice: AppState['schedule']['scheduleGasPrice'];
|
||||||
|
timeBounty: AppState['schedule']['timeBounty'];
|
||||||
}
|
}
|
||||||
|
|
||||||
interface StateProps {
|
interface StateProps {
|
||||||
|
@ -54,8 +59,9 @@ class AdvancedGas extends React.Component<Props, State> {
|
||||||
};
|
};
|
||||||
|
|
||||||
public render() {
|
public render() {
|
||||||
const { autoGasLimitEnabled, gasPrice, validGasPrice } = this.props;
|
const { autoGasLimitEnabled, gasPrice, scheduling, validGasPrice } = this.props;
|
||||||
const { gasPriceField, gasLimitField, nonceField, dataField, feeSummary } = this.state.options;
|
const { gasPriceField, gasLimitField, nonceField, dataField } = this.state.options;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="AdvancedGas row form-group">
|
<div className="AdvancedGas row form-group">
|
||||||
<div className="AdvancedGas-calculate-limit">
|
<div className="AdvancedGas-calculate-limit">
|
||||||
|
@ -78,7 +84,7 @@ class AdvancedGas extends React.Component<Props, State> {
|
||||||
{translateRaw('OFFLINE_STEP2_LABEL_3')} (gwei)
|
{translateRaw('OFFLINE_STEP2_LABEL_3')} (gwei)
|
||||||
</div>
|
</div>
|
||||||
<Input
|
<Input
|
||||||
className={!!gasPrice.raw && !validGasPrice ? 'is-invalid' : ''}
|
className={!!gasPrice.raw && !validGasPrice ? 'invalid' : ''}
|
||||||
type="number"
|
type="number"
|
||||||
placeholder="40"
|
placeholder="40"
|
||||||
value={gasPrice.raw}
|
value={gasPrice.raw}
|
||||||
|
@ -91,7 +97,11 @@ class AdvancedGas extends React.Component<Props, State> {
|
||||||
|
|
||||||
{gasLimitField && (
|
{gasLimitField && (
|
||||||
<div className="AdvancedGas-gas-limit">
|
<div className="AdvancedGas-gas-limit">
|
||||||
<GasLimitField customLabel={translateRaw('OFFLINE_STEP2_LABEL_4')} />
|
<GasLimitField
|
||||||
|
customLabel={translateRaw('OFFLINE_STEP2_LABEL_4')}
|
||||||
|
disabled={scheduling}
|
||||||
|
hideGasCalculationSpinner={scheduling}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{nonceField && (
|
{nonceField && (
|
||||||
|
@ -101,24 +111,62 @@ class AdvancedGas extends React.Component<Props, State> {
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{dataField && (
|
{!scheduling &&
|
||||||
<div className="AdvancedGas-data">
|
dataField && (
|
||||||
<DataField />
|
<div className="AdvancedGas-data">
|
||||||
</div>
|
<DataField />
|
||||||
)}
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
{feeSummary && (
|
{this.renderFee()}
|
||||||
<div className="AdvancedGas-fee-summary">
|
</div>
|
||||||
<FeeSummary
|
);
|
||||||
gasPrice={gasPrice}
|
}
|
||||||
render={({ gasPriceWei, gasLimit, fee, usd }) => (
|
|
||||||
<span>
|
private renderFee() {
|
||||||
{gasPriceWei} * {gasLimit} = {fee} {usd && <span>~= ${usd} USD</span>}
|
const { gasPrice, scheduleGasPrice } = this.props;
|
||||||
</span>
|
const { feeSummary } = this.state.options;
|
||||||
)}
|
|
||||||
/>
|
if (!feeSummary) {
|
||||||
</div>
|
return;
|
||||||
)}
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="AdvancedGas-fee-summary">
|
||||||
|
<FeeSummary
|
||||||
|
gasPrice={gasPrice}
|
||||||
|
scheduleGasPrice={scheduleGasPrice}
|
||||||
|
render={(data: RenderData) => this.printFeeFormula(data)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private printFeeFormula(data: RenderData) {
|
||||||
|
if (this.props.scheduling) {
|
||||||
|
return this.getScheduleFeeFormula(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.getStandardFeeFormula(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
private getStandardFeeFormula({ gasPriceWei, gasLimit, fee, usd }: RenderData) {
|
||||||
|
return (
|
||||||
|
<span>
|
||||||
|
{gasPriceWei} * {gasLimit} = {fee} {usd && <span>~= ${usd} USD</span>}
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private getScheduleFeeFormula({ gasPriceWei, scheduleGasLimit, fee, usd }: RenderData) {
|
||||||
|
const { scheduleGasPrice, timeBounty } = this.props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{timeBounty && timeBounty.value && timeBounty.value.toString()} + {gasPriceWei} *{' '}
|
||||||
|
{EAC_SCHEDULING_CONFIG.SCHEDULING_GAS_LIMIT.toString()} +{' '}
|
||||||
|
{scheduleGasPrice && scheduleGasPrice.value && scheduleGasPrice.value.toString()} * ({EAC_SCHEDULING_CONFIG.FUTURE_EXECUTION_COST.toString()}{' '}
|
||||||
|
+ {scheduleGasLimit}) = {fee} {usd && <span>~= ${usd} USD</span>}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -136,6 +184,8 @@ class AdvancedGas extends React.Component<Props, State> {
|
||||||
export default connect(
|
export default connect(
|
||||||
(state: AppState) => ({
|
(state: AppState) => ({
|
||||||
autoGasLimitEnabled: getAutoGasLimitEnabled(state),
|
autoGasLimitEnabled: getAutoGasLimitEnabled(state),
|
||||||
|
scheduleGasPrice: getScheduleGasPrice(state),
|
||||||
|
timeBounty: getTimeBounty(state),
|
||||||
validGasPrice: isValidGasPrice(state)
|
validGasPrice: isValidGasPrice(state)
|
||||||
}),
|
}),
|
||||||
{ toggleAutoGasLimit }
|
{ toggleAutoGasLimit }
|
||||||
|
|
|
@ -9,3 +9,9 @@
|
||||||
text-align: center;
|
text-align: center;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.SchedulingFeeSummary {
|
||||||
|
font-size: 12px;
|
||||||
|
height: auto;
|
||||||
|
min-height: 42px;
|
||||||
|
}
|
||||||
|
|
|
@ -2,17 +2,21 @@ import React from 'react';
|
||||||
import BN from 'bn.js';
|
import BN from 'bn.js';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { AppState } from 'reducers';
|
import { AppState } from 'reducers';
|
||||||
|
import classNames from 'classnames';
|
||||||
import { getNetworkConfig, getOffline } from 'selectors/config';
|
import { getNetworkConfig, getOffline } from 'selectors/config';
|
||||||
import { getIsEstimating } from 'selectors/gas';
|
import { getIsEstimating } from 'selectors/gas';
|
||||||
import { getGasLimit } from 'selectors/transaction';
|
import { getGasLimit } from 'selectors/transaction';
|
||||||
import { UnitDisplay, Spinner } from 'components/ui';
|
import { UnitDisplay, Spinner } from 'components/ui';
|
||||||
import { NetworkConfig } from 'types/network';
|
import { NetworkConfig } from 'types/network';
|
||||||
import './FeeSummary.scss';
|
import './FeeSummary.scss';
|
||||||
|
import { getScheduleGasLimit, getTimeBounty, getSchedulingToggle } from 'selectors/schedule';
|
||||||
|
import { calcEACTotalCost } from 'libs/scheduling';
|
||||||
|
|
||||||
interface RenderData {
|
export interface RenderData {
|
||||||
gasPriceWei: string;
|
gasPriceWei: string;
|
||||||
gasPriceGwei: string;
|
gasPriceGwei: string;
|
||||||
gasLimit: string;
|
gasLimit: string;
|
||||||
|
scheduleGasLimit: string;
|
||||||
fee: React.ReactElement<string>;
|
fee: React.ReactElement<string>;
|
||||||
usd: React.ReactElement<string> | null;
|
usd: React.ReactElement<string> | null;
|
||||||
}
|
}
|
||||||
|
@ -23,10 +27,15 @@ interface ReduxStateProps {
|
||||||
network: NetworkConfig;
|
network: NetworkConfig;
|
||||||
isOffline: AppState['config']['meta']['offline'];
|
isOffline: AppState['config']['meta']['offline'];
|
||||||
isGasEstimating: AppState['gas']['isEstimating'];
|
isGasEstimating: AppState['gas']['isEstimating'];
|
||||||
|
scheduleGasLimit: AppState['schedule']['scheduleGasLimit'];
|
||||||
|
timeBounty: AppState['schedule']['timeBounty'];
|
||||||
|
scheduling: AppState['schedule']['schedulingToggle']['value'];
|
||||||
}
|
}
|
||||||
|
|
||||||
interface OwnProps {
|
interface OwnProps {
|
||||||
gasPrice: AppState['transaction']['fields']['gasPrice'];
|
gasPrice: AppState['transaction']['fields']['gasPrice'];
|
||||||
|
scheduleGasPrice: AppState['schedule']['scheduleGasPrice'];
|
||||||
|
|
||||||
render(data: RenderData): React.ReactElement<string> | string;
|
render(data: RenderData): React.ReactElement<string> | string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,7 +43,16 @@ type Props = OwnProps & ReduxStateProps;
|
||||||
|
|
||||||
class FeeSummary extends React.Component<Props> {
|
class FeeSummary extends React.Component<Props> {
|
||||||
public render() {
|
public render() {
|
||||||
const { gasPrice, gasLimit, rates, network, isOffline, isGasEstimating } = this.props;
|
const {
|
||||||
|
gasPrice,
|
||||||
|
gasLimit,
|
||||||
|
rates,
|
||||||
|
network,
|
||||||
|
isOffline,
|
||||||
|
isGasEstimating,
|
||||||
|
scheduling,
|
||||||
|
scheduleGasLimit
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
if (isGasEstimating) {
|
if (isGasEstimating) {
|
||||||
return (
|
return (
|
||||||
|
@ -44,7 +62,7 @@ class FeeSummary extends React.Component<Props> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const feeBig = gasPrice.value && gasLimit.value && gasPrice.value.mul(gasLimit.value);
|
const feeBig = this.getFee();
|
||||||
const fee = (
|
const fee = (
|
||||||
<UnitDisplay
|
<UnitDisplay
|
||||||
value={feeBig}
|
value={feeBig}
|
||||||
|
@ -68,18 +86,56 @@ class FeeSummary extends React.Component<Props> {
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const feeSummaryClasses = classNames({
|
||||||
|
FeeSummary: true,
|
||||||
|
SchedulingFeeSummary: scheduling
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="FeeSummary">
|
<div className={feeSummaryClasses}>
|
||||||
{this.props.render({
|
{this.props.render({
|
||||||
gasPriceWei: gasPrice.value.toString(),
|
gasPriceWei: gasPrice.value.toString(),
|
||||||
gasPriceGwei: gasPrice.raw,
|
gasPriceGwei: gasPrice.raw,
|
||||||
fee,
|
fee,
|
||||||
usd,
|
usd,
|
||||||
gasLimit: gasLimit.raw
|
gasLimit: gasLimit.raw,
|
||||||
|
scheduleGasLimit: scheduleGasLimit.raw
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private getFee() {
|
||||||
|
const { scheduling } = this.props;
|
||||||
|
|
||||||
|
if (scheduling) {
|
||||||
|
return this.calculateSchedulingFee();
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.calculateStandardFee();
|
||||||
|
}
|
||||||
|
|
||||||
|
private calculateStandardFee() {
|
||||||
|
const { gasPrice, gasLimit } = this.props;
|
||||||
|
|
||||||
|
return gasPrice.value && gasLimit.value && gasPrice.value.mul(gasLimit.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private calculateSchedulingFee() {
|
||||||
|
const { gasPrice, scheduleGasLimit, scheduleGasPrice, timeBounty } = this.props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
gasPrice.value &&
|
||||||
|
scheduleGasLimit.value &&
|
||||||
|
timeBounty.value &&
|
||||||
|
calcEACTotalCost(
|
||||||
|
scheduleGasLimit.value,
|
||||||
|
gasPrice.value,
|
||||||
|
scheduleGasPrice.value,
|
||||||
|
timeBounty.value
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function mapStateToProps(state: AppState): ReduxStateProps {
|
function mapStateToProps(state: AppState): ReduxStateProps {
|
||||||
|
@ -88,7 +144,10 @@ function mapStateToProps(state: AppState): ReduxStateProps {
|
||||||
rates: state.rates.rates,
|
rates: state.rates.rates,
|
||||||
network: getNetworkConfig(state),
|
network: getNetworkConfig(state),
|
||||||
isOffline: getOffline(state),
|
isOffline: getOffline(state),
|
||||||
isGasEstimating: getIsEstimating(state)
|
isGasEstimating: getIsEstimating(state),
|
||||||
|
scheduling: getSchedulingToggle(state).value,
|
||||||
|
scheduleGasLimit: getScheduleGasLimit(state),
|
||||||
|
timeBounty: getTimeBounty(state)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import Slider, { createSliderWithTooltip } from 'rc-slider';
|
import Slider, { createSliderWithTooltip } from 'rc-slider';
|
||||||
import translate from 'translations';
|
import translate from 'translations';
|
||||||
import FeeSummary from './FeeSummary';
|
|
||||||
import './SimpleGas.scss';
|
import './SimpleGas.scss';
|
||||||
import { AppState } from 'reducers';
|
import { AppState } from 'reducers';
|
||||||
import {
|
import {
|
||||||
|
@ -17,11 +16,15 @@ import { Wei, fromWei } from 'libs/units';
|
||||||
import { gasPriceDefaults } from 'config';
|
import { gasPriceDefaults } from 'config';
|
||||||
import { InlineSpinner } from 'components/ui/InlineSpinner';
|
import { InlineSpinner } from 'components/ui/InlineSpinner';
|
||||||
import { TInputGasPrice } from 'actions/transaction';
|
import { TInputGasPrice } from 'actions/transaction';
|
||||||
|
import FeeSummary from './FeeSummary';
|
||||||
|
import { getScheduleGasPrice } from 'selectors/schedule';
|
||||||
|
|
||||||
const SliderWithTooltip = createSliderWithTooltip(Slider);
|
const SliderWithTooltip = createSliderWithTooltip(Slider);
|
||||||
|
|
||||||
interface OwnProps {
|
interface OwnProps {
|
||||||
gasPrice: AppState['transaction']['fields']['gasPrice'];
|
gasPrice: AppState['transaction']['fields']['gasPrice'];
|
||||||
setGasPrice: TInputGasPrice;
|
setGasPrice: TInputGasPrice;
|
||||||
|
|
||||||
inputGasPrice(rawGas: string): void;
|
inputGasPrice(rawGas: string): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -32,6 +35,7 @@ interface StateProps {
|
||||||
gasLimitPending: boolean;
|
gasLimitPending: boolean;
|
||||||
isWeb3Node: boolean;
|
isWeb3Node: boolean;
|
||||||
gasLimitEstimationTimedOut: boolean;
|
gasLimitEstimationTimedOut: boolean;
|
||||||
|
scheduleGasPrice: AppState['schedule']['scheduleGasPrice'];
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ActionProps {
|
interface ActionProps {
|
||||||
|
@ -68,7 +72,8 @@ class SimpleGas extends React.Component<Props> {
|
||||||
gasLimitEstimationTimedOut,
|
gasLimitEstimationTimedOut,
|
||||||
isWeb3Node,
|
isWeb3Node,
|
||||||
noncePending,
|
noncePending,
|
||||||
gasLimitPending
|
gasLimitPending,
|
||||||
|
scheduleGasPrice
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
const bounds = {
|
const bounds = {
|
||||||
|
@ -114,6 +119,7 @@ class SimpleGas extends React.Component<Props> {
|
||||||
</div>
|
</div>
|
||||||
<FeeSummary
|
<FeeSummary
|
||||||
gasPrice={gasPrice}
|
gasPrice={gasPrice}
|
||||||
|
scheduleGasPrice={scheduleGasPrice}
|
||||||
render={({ fee, usd }) => (
|
render={({ fee, usd }) => (
|
||||||
<span>
|
<span>
|
||||||
{fee} {usd && <span>/ ${usd}</span>}
|
{fee} {usd && <span>/ ${usd}</span>}
|
||||||
|
@ -151,7 +157,8 @@ export default connect(
|
||||||
noncePending: nonceRequestPending(state),
|
noncePending: nonceRequestPending(state),
|
||||||
gasLimitPending: getGasEstimationPending(state),
|
gasLimitPending: getGasEstimationPending(state),
|
||||||
gasLimitEstimationTimedOut: getGasLimitEstimationTimedOut(state),
|
gasLimitEstimationTimedOut: getGasLimitEstimationTimedOut(state),
|
||||||
isWeb3Node: getIsWeb3Node(state)
|
isWeb3Node: getIsWeb3Node(state),
|
||||||
|
scheduleGasPrice: getScheduleGasPrice(state)
|
||||||
}),
|
}),
|
||||||
{
|
{
|
||||||
fetchGasEstimates
|
fetchGasEstimates
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import Markdown from 'react-markdown';
|
import Markdown from 'react-markdown';
|
||||||
|
import NewTabLink from 'components/ui/NewTabLink';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
source: string;
|
source: string;
|
||||||
|
@ -11,7 +12,10 @@ const TranslateMarkdown = ({ source }: Props) => {
|
||||||
escapeHtml={true}
|
escapeHtml={true}
|
||||||
unwrapDisallowed={true}
|
unwrapDisallowed={true}
|
||||||
allowedTypes={['link', 'emphasis', 'strong', 'code', 'root', 'inlineCode']}
|
allowedTypes={['link', 'emphasis', 'strong', 'code', 'root', 'inlineCode']}
|
||||||
renderers={{ root: React.Fragment }}
|
renderers={{
|
||||||
|
root: React.Fragment,
|
||||||
|
link: NewTabLink
|
||||||
|
}}
|
||||||
source={source}
|
source={source}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
|
@ -26,6 +26,8 @@ $speed: 500ms;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
&-wallets {
|
&-wallets {
|
||||||
|
margin: 0 -$space-md;
|
||||||
|
|
||||||
&-title {
|
&-title {
|
||||||
@include decrypt-title;
|
@include decrypt-title;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
import React from 'react';
|
||||||
|
import translate from 'translations';
|
||||||
|
|
||||||
|
const DeprecationWarning: React.SFC<{}> = () => {
|
||||||
|
if (process.env.BUILD_DOWNLOADABLE) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return <div className="alert alert-warning">{translate('INSECURE_WALLET_DEPRECATION')}</div>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default DeprecationWarning;
|
|
@ -24,7 +24,6 @@ const WALLETS_PER_PAGE = 5;
|
||||||
interface Props {
|
interface Props {
|
||||||
// Passed props
|
// Passed props
|
||||||
isOpen?: boolean;
|
isOpen?: boolean;
|
||||||
walletType?: string;
|
|
||||||
dPath: string;
|
dPath: string;
|
||||||
dPaths: DPath[];
|
dPaths: DPath[];
|
||||||
publicKey?: string;
|
publicKey?: string;
|
||||||
|
@ -87,16 +86,7 @@ class DeterministicWalletsModalClass extends React.PureComponent<Props, State> {
|
||||||
}
|
}
|
||||||
|
|
||||||
public render() {
|
public render() {
|
||||||
const {
|
const { wallets, desiredToken, network, tokens, dPath, dPaths, onCancel } = this.props;
|
||||||
wallets,
|
|
||||||
desiredToken,
|
|
||||||
network,
|
|
||||||
tokens,
|
|
||||||
dPath,
|
|
||||||
dPaths,
|
|
||||||
onCancel,
|
|
||||||
walletType
|
|
||||||
} = this.props;
|
|
||||||
const { selectedAddress, customPath, page } = this.state;
|
const { selectedAddress, customPath, page } = this.state;
|
||||||
|
|
||||||
const buttons: IButton[] = [
|
const buttons: IButton[] = [
|
||||||
|
@ -115,7 +105,7 @@ class DeterministicWalletsModalClass extends React.PureComponent<Props, State> {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
title={translateRaw(`DECRYPT_PROMPT_UNLOCK_${walletType}`)}
|
title={translateRaw('DECRYPT_PROMPT_SELECT_ADDRESS')}
|
||||||
isOpen={this.props.isOpen}
|
isOpen={this.props.isOpen}
|
||||||
buttons={buttons}
|
buttons={buttons}
|
||||||
handleClose={onCancel}
|
handleClose={onCancel}
|
||||||
|
@ -209,15 +199,19 @@ class DeterministicWalletsModalClass extends React.PureComponent<Props, State> {
|
||||||
private getAddresses(props: Props = this.props) {
|
private getAddresses(props: Props = this.props) {
|
||||||
const { dPath, publicKey, chainCode, seed } = props;
|
const { dPath, publicKey, chainCode, seed } = props;
|
||||||
|
|
||||||
if (dPath && ((publicKey && chainCode) || seed) && isValidPath(dPath)) {
|
if (dPath && ((publicKey && chainCode) || seed)) {
|
||||||
this.props.getDeterministicWallets({
|
if (isValidPath(dPath)) {
|
||||||
seed,
|
this.props.getDeterministicWallets({
|
||||||
dPath,
|
seed,
|
||||||
publicKey,
|
dPath,
|
||||||
chainCode,
|
publicKey,
|
||||||
limit: WALLETS_PER_PAGE,
|
chainCode,
|
||||||
offset: WALLETS_PER_PAGE * this.state.page
|
limit: WALLETS_PER_PAGE,
|
||||||
});
|
offset: WALLETS_PER_PAGE * this.state.page
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
console.error('Invalid dPath provided', dPath);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -274,7 +268,7 @@ class DeterministicWalletsModalClass extends React.PureComponent<Props, State> {
|
||||||
|
|
||||||
private renderDPathOption(option: Option) {
|
private renderDPathOption(option: Option) {
|
||||||
if (option.value === customDPath.value) {
|
if (option.value === customDPath.value) {
|
||||||
return translate('ADD_Radio_5_PathCustom');
|
return translate('X_CUSTOM');
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -4,6 +4,7 @@ import translate, { translateRaw } from 'translations';
|
||||||
import Spinner from 'components/ui/Spinner';
|
import Spinner from 'components/ui/Spinner';
|
||||||
import { TShowNotification } from 'actions/notifications';
|
import { TShowNotification } from 'actions/notifications';
|
||||||
import { Input } from 'components/ui';
|
import { Input } from 'components/ui';
|
||||||
|
import DeprecationWarning from './DeprecationWarning';
|
||||||
|
|
||||||
export interface KeystoreValue {
|
export interface KeystoreValue {
|
||||||
file: string;
|
file: string;
|
||||||
|
@ -43,7 +44,8 @@ export class KeystoreDecrypt extends PureComponent {
|
||||||
const unlockDisabled = !file || (passReq && !password);
|
const unlockDisabled = !file || (passReq && !password);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<form id="selectedUploadKey" onSubmit={this.unlock}>
|
<form onSubmit={this.unlock}>
|
||||||
|
<DeprecationWarning />
|
||||||
<div className="form-group">
|
<div className="form-group">
|
||||||
<input
|
<input
|
||||||
className="hidden"
|
className="hidden"
|
||||||
|
|
|
@ -9,6 +9,8 @@ import { connect } from 'react-redux';
|
||||||
import { AppState } from 'reducers';
|
import { AppState } from 'reducers';
|
||||||
import { SecureWalletName, ledgerReferralURL } from 'config';
|
import { SecureWalletName, ledgerReferralURL } from 'config';
|
||||||
import { getPaths, getSingleDPath } from 'selectors/config/wallet';
|
import { getPaths, getSingleDPath } from 'selectors/config/wallet';
|
||||||
|
import { getNetworkConfig } from 'selectors/config';
|
||||||
|
import { NetworkConfig } from 'types/network';
|
||||||
import './LedgerNano.scss';
|
import './LedgerNano.scss';
|
||||||
|
|
||||||
interface OwnProps {
|
interface OwnProps {
|
||||||
|
@ -18,6 +20,7 @@ interface OwnProps {
|
||||||
interface StateProps {
|
interface StateProps {
|
||||||
dPath: DPath | undefined;
|
dPath: DPath | undefined;
|
||||||
dPaths: DPath[];
|
dPaths: DPath[];
|
||||||
|
network: NetworkConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface State {
|
interface State {
|
||||||
|
@ -54,6 +57,7 @@ class LedgerNanoSDecryptClass extends PureComponent<Props, State> {
|
||||||
}
|
}
|
||||||
|
|
||||||
public render() {
|
public render() {
|
||||||
|
const { network } = this.props;
|
||||||
const { dPath, publicKey, chainCode, error, isLoading, showTip } = this.state;
|
const { dPath, publicKey, chainCode, error, isLoading, showTip } = this.state;
|
||||||
const showErr = error ? 'is-showing' : '';
|
const showErr = error ? 'is-showing' : '';
|
||||||
|
|
||||||
|
@ -66,7 +70,7 @@ class LedgerNanoSDecryptClass extends PureComponent<Props, State> {
|
||||||
<div className="LedgerDecrypt">
|
<div className="LedgerDecrypt">
|
||||||
<div className="alert alert-danger">
|
<div className="alert alert-danger">
|
||||||
Unlocking a Ledger hardware wallet is only possible on pages served over HTTPS. You can
|
Unlocking a Ledger hardware wallet is only possible on pages served over HTTPS. You can
|
||||||
unlock your wallet at <a href="https://mycrypto.com">MyCrypto.com</a>
|
unlock your wallet at <NewTabLink href="https://mycrypto.com">MyCrypto.com</NewTabLink>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -74,12 +78,6 @@ class LedgerNanoSDecryptClass extends PureComponent<Props, State> {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="LedgerDecrypt">
|
<div className="LedgerDecrypt">
|
||||||
{showTip && (
|
|
||||||
<p>
|
|
||||||
<strong>Tip: </strong>Make sure you're logged into the ethereum app on your hardware
|
|
||||||
wallet
|
|
||||||
</p>
|
|
||||||
)}
|
|
||||||
<button
|
<button
|
||||||
className="LedgerDecrypt-decrypt btn btn-primary btn-lg btn-block"
|
className="LedgerDecrypt-decrypt btn btn-primary btn-lg btn-block"
|
||||||
onClick={this.handleNullConnect}
|
onClick={this.handleNullConnect}
|
||||||
|
@ -101,6 +99,10 @@ class LedgerNanoSDecryptClass extends PureComponent<Props, State> {
|
||||||
|
|
||||||
<div className={`LedgerDecrypt-error alert alert-danger ${showErr}`}>{error || '-'}</div>
|
<div className={`LedgerDecrypt-error alert alert-danger ${showErr}`}>{error || '-'}</div>
|
||||||
|
|
||||||
|
{showTip && (
|
||||||
|
<p className="LedgerDecrypt-tip">{translate('LEDGER_TIP', { $network: network.unit })}</p>
|
||||||
|
)}
|
||||||
|
|
||||||
<div className="LedgerDecrypt-help">
|
<div className="LedgerDecrypt-help">
|
||||||
<NewTabLink href="https://support.ledgerwallet.com/hc/en-us/articles/115005200009">
|
<NewTabLink href="https://support.ledgerwallet.com/hc/en-us/articles/115005200009">
|
||||||
{translate('HELP_ARTICLE_1')}
|
{translate('HELP_ARTICLE_1')}
|
||||||
|
@ -116,7 +118,6 @@ class LedgerNanoSDecryptClass extends PureComponent<Props, State> {
|
||||||
onCancel={this.handleCancel}
|
onCancel={this.handleCancel}
|
||||||
onConfirmAddress={this.handleUnlock}
|
onConfirmAddress={this.handleUnlock}
|
||||||
onPathChange={this.handlePathChange}
|
onPathChange={this.handlePathChange}
|
||||||
walletType={'LEDGER'}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -144,13 +145,34 @@ class LedgerNanoSDecryptClass extends PureComponent<Props, State> {
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
.catch((err: any) => {
|
.catch((err: any) => {
|
||||||
|
let showTip;
|
||||||
|
let errMsg;
|
||||||
|
// Timeout
|
||||||
if (err && err.metaData && err.metaData.code === 5) {
|
if (err && err.metaData && err.metaData.code === 5) {
|
||||||
this.showTip();
|
showTip = true;
|
||||||
|
errMsg = translateRaw('LEDGER_TIMEOUT');
|
||||||
}
|
}
|
||||||
|
// Wrong app logged into
|
||||||
|
if (err && err.includes && err.includes('6804')) {
|
||||||
|
showTip = true;
|
||||||
|
errMsg = translateRaw('LEDGER_WRONG_APP');
|
||||||
|
}
|
||||||
|
// Ledger locked
|
||||||
|
if (err && err.includes && err.includes('6801')) {
|
||||||
|
errMsg = translateRaw('LEDGER_LOCKED');
|
||||||
|
}
|
||||||
|
// Other
|
||||||
|
if (!errMsg) {
|
||||||
|
errMsg = err && err.metaData ? err.metaData.type : err.toString();
|
||||||
|
}
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
error: err && err.metaData ? err.metaData.type : err.toString(),
|
error: errMsg,
|
||||||
isLoading: false
|
isLoading: false
|
||||||
});
|
});
|
||||||
|
if (showTip) {
|
||||||
|
this.showTip();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
@ -180,7 +202,8 @@ class LedgerNanoSDecryptClass extends PureComponent<Props, State> {
|
||||||
function mapStateToProps(state: AppState): StateProps {
|
function mapStateToProps(state: AppState): StateProps {
|
||||||
return {
|
return {
|
||||||
dPath: getSingleDPath(state, SecureWalletName.LEDGER_NANO_S),
|
dPath: getSingleDPath(state, SecureWalletName.LEDGER_NANO_S),
|
||||||
dPaths: getPaths(state, SecureWalletName.LEDGER_NANO_S)
|
dPaths: getPaths(state, SecureWalletName.LEDGER_NANO_S),
|
||||||
|
network: getNetworkConfig(state)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,7 @@ import { connect } from 'react-redux';
|
||||||
import { getSingleDPath, getPaths } from 'selectors/config/wallet';
|
import { getSingleDPath, getPaths } from 'selectors/config/wallet';
|
||||||
import { TogglablePassword } from 'components';
|
import { TogglablePassword } from 'components';
|
||||||
import { Input } from 'components/ui';
|
import { Input } from 'components/ui';
|
||||||
|
import DeprecationWarning from './DeprecationWarning';
|
||||||
|
|
||||||
interface OwnProps {
|
interface OwnProps {
|
||||||
onUnlock(param: any): void;
|
onUnlock(param: any): void;
|
||||||
|
@ -49,7 +50,8 @@ class MnemonicDecryptClass extends PureComponent<Props, State> {
|
||||||
const isValidMnemonic = validateMnemonic(formattedPhrase);
|
const isValidMnemonic = validateMnemonic(formattedPhrase);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<React.Fragment>
|
||||||
|
<DeprecationWarning />
|
||||||
<div id="selectedTypeKey">
|
<div id="selectedTypeKey">
|
||||||
<div className="form-group">
|
<div className="form-group">
|
||||||
<TogglablePassword
|
<TogglablePassword
|
||||||
|
@ -91,9 +93,8 @@ class MnemonicDecryptClass extends PureComponent<Props, State> {
|
||||||
onCancel={this.handleCancel}
|
onCancel={this.handleCancel}
|
||||||
onConfirmAddress={this.handleUnlock}
|
onConfirmAddress={this.handleUnlock}
|
||||||
onPathChange={this.handlePathChange}
|
onPathChange={this.handlePathChange}
|
||||||
walletType={'MNEMONIC'}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</React.Fragment>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,7 @@ import React, { PureComponent } from 'react';
|
||||||
import translate, { translateRaw } from 'translations';
|
import translate, { translateRaw } from 'translations';
|
||||||
import { TogglablePassword } from 'components';
|
import { TogglablePassword } from 'components';
|
||||||
import { Input } from 'components/ui';
|
import { Input } from 'components/ui';
|
||||||
|
import DeprecationWarning from './DeprecationWarning';
|
||||||
|
|
||||||
export interface PrivateKeyValue {
|
export interface PrivateKeyValue {
|
||||||
key: string;
|
key: string;
|
||||||
|
@ -54,6 +55,7 @@ export class PrivateKeyDecrypt extends PureComponent<Props> {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<form id="selectedTypeKey" onSubmit={this.unlock}>
|
<form id="selectedTypeKey" onSubmit={this.unlock}>
|
||||||
|
<DeprecationWarning />
|
||||||
<div className="input-group-wrapper">
|
<div className="input-group-wrapper">
|
||||||
<label className="input-group">
|
<label className="input-group">
|
||||||
<TogglablePassword
|
<TogglablePassword
|
||||||
|
|
|
@ -93,7 +93,6 @@ class TrezorDecryptClass extends PureComponent<Props, State> {
|
||||||
onCancel={this.handleCancel}
|
onCancel={this.handleCancel}
|
||||||
onConfirmAddress={this.handleUnlock}
|
onConfirmAddress={this.handleUnlock}
|
||||||
onPathChange={this.handlePathChange}
|
onPathChange={this.handlePathChange}
|
||||||
walletType={'TREZOR'}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
@import 'common/sass/variables';
|
||||||
|
@import 'common/sass/mixins';
|
||||||
|
|
||||||
|
.ViewOnly {
|
||||||
|
&-recent {
|
||||||
|
text-align: left;
|
||||||
|
|
||||||
|
&-separator {
|
||||||
|
display: block;
|
||||||
|
margin: $space-sm 0;
|
||||||
|
text-align: center;
|
||||||
|
color: $gray-light;
|
||||||
|
}
|
||||||
|
|
||||||
|
.Select {
|
||||||
|
&-option {
|
||||||
|
@include ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
.Identicon {
|
||||||
|
display: inline-block;
|
||||||
|
margin-right: $space-sm;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,63 +1,101 @@
|
||||||
import React, { PureComponent } from 'react';
|
import React, { PureComponent } from 'react';
|
||||||
import translate from 'translations';
|
import { connect } from 'react-redux';
|
||||||
import { donationAddressMap } from 'config';
|
import Select, { Option } from 'react-select';
|
||||||
|
import translate, { translateRaw } from 'translations';
|
||||||
import { isValidETHAddress } from 'libs/validators';
|
import { isValidETHAddress } from 'libs/validators';
|
||||||
import { AddressOnlyWallet } from 'libs/wallet';
|
import { AddressOnlyWallet } from 'libs/wallet';
|
||||||
import { TextArea } from 'components/ui';
|
import { getRecentAddresses } from 'selectors/wallet';
|
||||||
|
import { AppState } from 'reducers';
|
||||||
|
import { Input, Identicon } from 'components/ui';
|
||||||
|
import './ViewOnly.scss';
|
||||||
|
|
||||||
interface Props {
|
interface OwnProps {
|
||||||
onUnlock(param: any): void;
|
onUnlock(param: any): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface StateProps {
|
||||||
|
recentAddresses: AppState['wallet']['recentAddresses'];
|
||||||
|
}
|
||||||
|
|
||||||
|
type Props = OwnProps & StateProps;
|
||||||
|
|
||||||
interface State {
|
interface State {
|
||||||
address: string;
|
address: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ViewOnlyDecrypt extends PureComponent<Props, State> {
|
class ViewOnlyDecryptClass extends PureComponent<Props, State> {
|
||||||
public state = {
|
public state = {
|
||||||
address: ''
|
address: ''
|
||||||
};
|
};
|
||||||
|
|
||||||
public render() {
|
public render() {
|
||||||
|
const { recentAddresses } = this.props;
|
||||||
const { address } = this.state;
|
const { address } = this.state;
|
||||||
const isValid = isValidETHAddress(address);
|
const isValid = isValidETHAddress(address);
|
||||||
|
|
||||||
|
const recentOptions = (recentAddresses.map(addr => ({
|
||||||
|
label: (
|
||||||
|
<React.Fragment>
|
||||||
|
<Identicon address={addr} />
|
||||||
|
{addr}
|
||||||
|
</React.Fragment>
|
||||||
|
),
|
||||||
|
value: addr
|
||||||
|
// I hate this assertion, but React elements definitely work as labels
|
||||||
|
})) as any) as Option[];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div id="selectedUploadKey">
|
<div className="ViewOnly">
|
||||||
<form className="form-group" onSubmit={this.openWallet}>
|
<form className="form-group" onSubmit={this.openWallet}>
|
||||||
<TextArea
|
{!!recentOptions.length && (
|
||||||
className={isValid ? 'is-valid' : 'is-invalid'}
|
<div className="ViewOnly-recent">
|
||||||
|
<Select
|
||||||
|
value={address}
|
||||||
|
onChange={this.handleSelectAddress}
|
||||||
|
options={recentOptions}
|
||||||
|
placeholder={translateRaw('VIEW_ONLY_RECENT')}
|
||||||
|
/>
|
||||||
|
<em className="ViewOnly-recent-separator">{translate('OR')}</em>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<Input
|
||||||
|
className={`ViewOnly-input ${isValid ? 'is-valid' : 'is-invalid'}`}
|
||||||
value={address}
|
value={address}
|
||||||
onChange={this.changeAddress}
|
onChange={this.changeAddress}
|
||||||
onKeyDown={this.handleEnterKey}
|
placeholder={translateRaw('VIEW_ONLY_ENTER')}
|
||||||
placeholder={donationAddressMap.ETH}
|
|
||||||
rows={3}
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<button className="btn btn-primary btn-block" disabled={!isValid}>
|
<button className="ViewOnly-submit btn btn-primary btn-block" disabled={!isValid}>
|
||||||
{translate('NAV_VIEWWALLET')}
|
{translate('VIEW_ADDR')}
|
||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private changeAddress = (ev: React.FormEvent<HTMLTextAreaElement>) => {
|
private changeAddress = (ev: React.FormEvent<HTMLInputElement>) => {
|
||||||
this.setState({ address: ev.currentTarget.value });
|
this.setState({ address: ev.currentTarget.value });
|
||||||
};
|
};
|
||||||
|
|
||||||
private handleEnterKey = (ev: React.KeyboardEvent<HTMLElement>) => {
|
private handleSelectAddress = (option: Option) => {
|
||||||
if (ev.keyCode === 13) {
|
const address = option && option.value ? option.value.toString() : '';
|
||||||
this.openWallet(ev);
|
this.setState({ address }, () => this.openWallet());
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
private openWallet = (ev: React.FormEvent<HTMLElement>) => {
|
private openWallet = (ev?: React.FormEvent<HTMLElement>) => {
|
||||||
|
if (ev) {
|
||||||
|
ev.preventDefault();
|
||||||
|
}
|
||||||
|
|
||||||
const { address } = this.state;
|
const { address } = this.state;
|
||||||
ev.preventDefault();
|
|
||||||
if (isValidETHAddress(address)) {
|
if (isValidETHAddress(address)) {
|
||||||
const wallet = new AddressOnlyWallet(address);
|
const wallet = new AddressOnlyWallet(address);
|
||||||
this.props.onUnlock(wallet);
|
this.props.onUnlock(wallet);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const ViewOnlyDecrypt = connect((state: AppState): StateProps => ({
|
||||||
|
recentAddresses: getRecentAddresses(state)
|
||||||
|
}))(ViewOnlyDecryptClass);
|
||||||
|
|
|
@ -8,6 +8,7 @@ export * from './CurrentCustomMessage';
|
||||||
export * from './GenerateTransaction';
|
export * from './GenerateTransaction';
|
||||||
export * from './SendButton';
|
export * from './SendButton';
|
||||||
export * from './SigningStatus';
|
export * from './SigningStatus';
|
||||||
|
export * from '../containers/Tabs/ScheduleTransaction/components';
|
||||||
export { default as NonceField } from './NonceField';
|
export { default as NonceField } from './NonceField';
|
||||||
export { default as Header } from './Header';
|
export { default as Header } from './Header';
|
||||||
export { default as Footer } from './Footer';
|
export { default as Footer } from './Footer';
|
||||||
|
@ -20,3 +21,4 @@ export { default as TogglablePassword } from './TogglablePassword';
|
||||||
export { default as GenerateKeystoreModal } from './GenerateKeystoreModal';
|
export { default as GenerateKeystoreModal } from './GenerateKeystoreModal';
|
||||||
export { default as TransactionStatus } from './TransactionStatus';
|
export { default as TransactionStatus } from './TransactionStatus';
|
||||||
export { default as ParityQrSigner } from './ParityQrSigner';
|
export { default as ParityQrSigner } from './ParityQrSigner';
|
||||||
|
export { default as ElectronNav } from './ElectronNav';
|
||||||
|
|
|
@ -13,7 +13,19 @@ interface IQueryResults {
|
||||||
[key: string]: string | null;
|
[key: string]: string | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type Param = 'to' | 'data' | 'readOnly' | 'tokenSymbol' | 'value' | 'gaslimit' | 'limit';
|
export type Param =
|
||||||
|
| 'to'
|
||||||
|
| 'data'
|
||||||
|
| 'readOnly'
|
||||||
|
| 'tokenSymbol'
|
||||||
|
| 'value'
|
||||||
|
| 'gaslimit'
|
||||||
|
| 'limit'
|
||||||
|
| 'windowSize'
|
||||||
|
| 'windowStart'
|
||||||
|
| 'scheduleTimestamp'
|
||||||
|
| 'timeBounty'
|
||||||
|
| 'network';
|
||||||
|
|
||||||
interface Props extends RouteComponentProps<{}> {
|
interface Props extends RouteComponentProps<{}> {
|
||||||
params: Param[];
|
params: Param[];
|
||||||
|
|
|
@ -5,13 +5,19 @@ import './Help.scss';
|
||||||
type Size = 'x1' | 'x2' | 'x3';
|
type Size = 'x1' | 'x2' | 'x3';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
link: string;
|
link?: string;
|
||||||
size?: Size;
|
size?: Size;
|
||||||
|
className?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Help = ({ size = 'x1', link }: Props) => {
|
const Help = ({ size = 'x1', link, className }: Props) => {
|
||||||
return (
|
return (
|
||||||
<a href={link} className={`Help Help-${size}`} target="_blank" rel="noopener noreferrer">
|
<a
|
||||||
|
href={link}
|
||||||
|
className={`Help Help-${size} ${className}`}
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
>
|
||||||
<img src={icon} alt="help" />
|
<img src={icon} alt="help" />
|
||||||
</a>
|
</a>
|
||||||
);
|
);
|
||||||
|
|
|
@ -78,7 +78,7 @@
|
||||||
padding: 0.5rem 0.75rem;
|
padding: 0.5rem 0.75rem;
|
||||||
}
|
}
|
||||||
&::placeholder {
|
&::placeholder {
|
||||||
color: rgba(0, 0, 0, 0.3);
|
color: $input-color-placeholder;
|
||||||
}
|
}
|
||||||
&:not([disabled]):not([readonly]) {
|
&:not([disabled]):not([readonly]) {
|
||||||
&.invalid.has-blurred.has-value {
|
&.invalid.has-blurred.has-value {
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import React, { PureComponent } from 'react';
|
import React, { PureComponent } from 'react';
|
||||||
|
import { createPortal } from 'react-dom';
|
||||||
import ModalBody from './ModalBody';
|
import ModalBody from './ModalBody';
|
||||||
import { TransitionGroup, CSSTransition } from 'react-transition-group';
|
import { TransitionGroup, CSSTransition } from 'react-transition-group';
|
||||||
import './index.scss';
|
import './index.scss';
|
||||||
|
@ -28,6 +29,7 @@ const Fade = ({ ...props }: any) => (
|
||||||
);
|
);
|
||||||
|
|
||||||
export default class Modal extends PureComponent<Props, {}> {
|
export default class Modal extends PureComponent<Props, {}> {
|
||||||
|
public modalParent: HTMLElement;
|
||||||
public modalBody: ModalBody;
|
public modalBody: ModalBody;
|
||||||
|
|
||||||
public componentDidUpdate(prevProps: Props) {
|
public componentDidUpdate(prevProps: Props) {
|
||||||
|
@ -36,8 +38,20 @@ export default class Modal extends PureComponent<Props, {}> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public componentDidMount() {
|
||||||
|
const modalEl = document.getElementById('ModalContainer');
|
||||||
|
if (modalEl) {
|
||||||
|
this.modalParent = document.createElement('div');
|
||||||
|
modalEl.appendChild(this.modalParent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public componentWillUnmount() {
|
public componentWillUnmount() {
|
||||||
document.body.classList.remove('no-scroll');
|
document.body.classList.remove('no-scroll');
|
||||||
|
const modalEl = document.getElementById('ModalContainer');
|
||||||
|
if (modalEl) {
|
||||||
|
modalEl.removeChild(this.modalParent);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public render() {
|
public render() {
|
||||||
|
@ -52,7 +66,7 @@ export default class Modal extends PureComponent<Props, {}> {
|
||||||
|
|
||||||
const modalBodyProps = { title, children, modalStyle, hasButtons, buttons, handleClose };
|
const modalBodyProps = { title, children, modalStyle, hasButtons, buttons, handleClose };
|
||||||
|
|
||||||
return (
|
const modal = (
|
||||||
<TransitionGroup>
|
<TransitionGroup>
|
||||||
{isOpen && (
|
{isOpen && (
|
||||||
// Trap focus in modal by focusing the first element after the animation is complete
|
// Trap focus in modal by focusing the first element after the animation is complete
|
||||||
|
@ -65,5 +79,11 @@ export default class Modal extends PureComponent<Props, {}> {
|
||||||
)}
|
)}
|
||||||
</TransitionGroup>
|
</TransitionGroup>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (this.modalParent) {
|
||||||
|
return createPortal(modal, this.modalParent);
|
||||||
|
} else {
|
||||||
|
return modal;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,8 +31,16 @@ export interface AAttributes {
|
||||||
|
|
||||||
interface NewTabLinkProps extends AAttributes {
|
interface NewTabLinkProps extends AAttributes {
|
||||||
href: string;
|
href: string;
|
||||||
content?: React.ReactElement<any> | string | string[] | number;
|
content?:
|
||||||
children?: React.ReactElement<any> | string | string[] | number;
|
| React.ReactElement<any>
|
||||||
|
| string
|
||||||
|
| number
|
||||||
|
| (string | number | React.ReactElement<any>)[];
|
||||||
|
children?:
|
||||||
|
| React.ReactElement<any>
|
||||||
|
| string
|
||||||
|
| number
|
||||||
|
| (string | number | React.ReactElement<any>)[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export class NewTabLink extends React.Component<NewTabLinkProps> {
|
export class NewTabLink extends React.Component<NewTabLinkProps> {
|
||||||
|
|
|
@ -0,0 +1,64 @@
|
||||||
|
$slider-radius: 26px;
|
||||||
|
$transition-time: .4s;
|
||||||
|
$toggle-color: #0C6482;
|
||||||
|
$travel-distance: 38px;
|
||||||
|
|
||||||
|
.Toggle {
|
||||||
|
$root: &;
|
||||||
|
|
||||||
|
position: relative;
|
||||||
|
display: inline-block;
|
||||||
|
width: 72px;
|
||||||
|
height: 34px;
|
||||||
|
margin: 5px 0;
|
||||||
|
|
||||||
|
&-input {
|
||||||
|
display: none;
|
||||||
|
|
||||||
|
&:checked + #{$root}-slider {
|
||||||
|
background-color: $toggle-color;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
-webkit-transform: translateX($travel-distance);
|
||||||
|
-ms-transform: translateX($travel-distance);
|
||||||
|
transform: translateX($travel-distance);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&:focus + #{$root}-slider {
|
||||||
|
box-shadow: 0 0 1px $toggle-color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-slider {
|
||||||
|
position: absolute;
|
||||||
|
cursor: pointer;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background-color: #ccc;
|
||||||
|
-webkit-transition: $transition-time;
|
||||||
|
transition: $transition-time;
|
||||||
|
|
||||||
|
&.round {
|
||||||
|
border-radius: 34px;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
position: absolute;
|
||||||
|
content: "";
|
||||||
|
height: $slider-radius;
|
||||||
|
width: $slider-radius;
|
||||||
|
left: 4px;
|
||||||
|
bottom: 4px;
|
||||||
|
background-color: white;
|
||||||
|
-webkit-transition: $transition-time;
|
||||||
|
transition: $transition-time;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
import React, { ChangeEvent } from 'react';
|
||||||
|
import './Toggle.scss';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
checked: boolean;
|
||||||
|
onChangeHandler(event: ChangeEvent<HTMLInputElement>): any;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Toggle: React.SFC<Props> = ({ checked, onChangeHandler }) => (
|
||||||
|
<label className="Toggle checkbox">
|
||||||
|
<input className="Toggle-input" type="checkbox" checked={checked} onChange={onChangeHandler} />
|
||||||
|
<span className="Toggle-slider round" />
|
||||||
|
</label>
|
||||||
|
);
|
||||||
|
|
||||||
|
export default Toggle;
|
|
@ -70,7 +70,7 @@ const UnitDisplay: React.SFC<EthProps | TokenProps> = params => {
|
||||||
element = (
|
element = (
|
||||||
<span>
|
<span>
|
||||||
{formattedValue}
|
{formattedValue}
|
||||||
<span>{symbol ? ` ${symbol}` : ''}</span>
|
<span>{symbol ? <> {symbol}</> : ''}</span>
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@ export { default as Input } from './Input';
|
||||||
export { default as TextArea } from './TextArea';
|
export { default as TextArea } from './TextArea';
|
||||||
export { default as Address } from './Address';
|
export { default as Address } from './Address';
|
||||||
export { default as CodeBlock } from './CodeBlock';
|
export { default as CodeBlock } from './CodeBlock';
|
||||||
|
export { default as Toggle } from './Toggle';
|
||||||
export * from './ConditionalInput';
|
export * from './ConditionalInput';
|
||||||
export * from './Expandable';
|
export * from './Expandable';
|
||||||
export * from './InlineSpinner';
|
export * from './InlineSpinner';
|
||||||
|
|
|
@ -9,7 +9,7 @@ export const languages = require('./languages.json');
|
||||||
export const discordURL = 'https://discord.gg/VSaTXEA';
|
export const discordURL = 'https://discord.gg/VSaTXEA';
|
||||||
|
|
||||||
// Displays in the footer
|
// Displays in the footer
|
||||||
export const VERSION = `${packageJson.version} (BETA)`;
|
export const VERSION = `${packageJson.version} (Release Candidate)`;
|
||||||
export const N_FACTOR = 8192;
|
export const N_FACTOR = 8192;
|
||||||
|
|
||||||
// Displays at the top of the site, make message empty string to remove.
|
// Displays at the top of the site, make message empty string to remove.
|
||||||
|
@ -18,7 +18,7 @@ export const N_FACTOR = 8192;
|
||||||
export const ANNOUNCEMENT_TYPE = '';
|
export const ANNOUNCEMENT_TYPE = '';
|
||||||
export const ANNOUNCEMENT_MESSAGE = (
|
export const ANNOUNCEMENT_MESSAGE = (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
This is a Beta version of MyCrypto. Please submit any bug reports to our{' '}
|
This is a Beta Release Candidate of the new MyCrypto. Please submit any bug reports to our{' '}
|
||||||
<NewTabLink href="https://github.com/MyCryptoHQ/MyCrypto/issues">GitHub</NewTabLink> and use{' '}
|
<NewTabLink href="https://github.com/MyCryptoHQ/MyCrypto/issues">GitHub</NewTabLink> and use{' '}
|
||||||
<NewTabLink href="https://hackerone.com/mycrypto">HackerOne</NewTabLink> for critical
|
<NewTabLink href="https://hackerone.com/mycrypto">HackerOne</NewTabLink> for critical
|
||||||
vulnerabilities. Join the discussion on <NewTabLink href={discordURL}>Discord</NewTabLink>.
|
vulnerabilities. Join the discussion on <NewTabLink href={discordURL}>Discord</NewTabLink>.
|
||||||
|
@ -65,6 +65,8 @@ export const bityReferralURL = 'https://bity.com/af/jshkb37v';
|
||||||
export const shapeshiftReferralURL = 'https://shapeshift.io';
|
export const shapeshiftReferralURL = 'https://shapeshift.io';
|
||||||
export const ethercardReferralURL =
|
export const ethercardReferralURL =
|
||||||
'https://ether.cards/?utm_source=mycrypto&utm_medium=cpm&utm_campaign=site';
|
'https://ether.cards/?utm_source=mycrypto&utm_medium=cpm&utm_campaign=site';
|
||||||
|
export const keepkeyReferralURL = 'https://keepkey.go2cloud.org/aff_c?offer_id=1&aff_id=4086';
|
||||||
|
export const steelyReferralURL = 'https://stee.ly/2Hcl4RE';
|
||||||
|
|
||||||
export enum SecureWalletName {
|
export enum SecureWalletName {
|
||||||
WEB3 = 'web3',
|
WEB3 = 'web3',
|
||||||
|
|
|
@ -15,7 +15,7 @@ export const ETH_LEDGER: DPath = {
|
||||||
|
|
||||||
export const ETC_LEDGER: DPath = {
|
export const ETC_LEDGER: DPath = {
|
||||||
label: 'Ledger (ETC)',
|
label: 'Ledger (ETC)',
|
||||||
value: "m/44'/60'/160720'/0"
|
value: "m/44'/60'/160720'/0'"
|
||||||
};
|
};
|
||||||
|
|
||||||
export const ETC_TREZOR: DPath = {
|
export const ETC_TREZOR: DPath = {
|
||||||
|
@ -86,5 +86,5 @@ export const EXTRA_PATHS = [ETH_SINGULAR];
|
||||||
|
|
||||||
// whitespace strings are evaluated the same way as nospace strings, except they allow optional spaces between each portion of the string
|
// whitespace strings are evaluated the same way as nospace strings, except they allow optional spaces between each portion of the string
|
||||||
// ie. "m / 44' / 0' / 0'" is valid, "m / 4 4' / 0' / 0'" is invalid
|
// ie. "m / 44' / 0' / 0'" is valid, "m / 4 4' / 0' / 0'" is invalid
|
||||||
export const dPathRegex = /m\/(44|0)'\/[0-9]+\'\/[0-9]+(\'+$|\'+(\/[0-1]+$))/;
|
export const dPathRegex = /m\/44'\/[0-9]+\'\/[0-9]+(\'+$|\'+(\/[0-1]+$))/;
|
||||||
// export const whitespaceDPathRegex = /m\s*\/\s*44'\s*\/\s*[0-9]+\'\s*\/\s*[0-9]+(\'+$|\'+\s*(\/\s*[0-1]+$))/;
|
// export const whitespaceDPathRegex = /m\s*\/\s*44'\s*\/\s*[0-9]+\'\s*\/\s*[0-9]+(\'+$|\'+\s*(\/\s*[0-1]+$))/;
|
||||||
|
|
|
@ -3,3 +3,5 @@ export * from './bity';
|
||||||
export * from './addressMessages';
|
export * from './addressMessages';
|
||||||
export * from './helpArticles';
|
export * from './helpArticles';
|
||||||
export * from './dpaths';
|
export * from './dpaths';
|
||||||
|
export * from './navigation';
|
||||||
|
export * from './links';
|
||||||
|
|
|
@ -0,0 +1,108 @@
|
||||||
|
import { translateRaw } from 'translations';
|
||||||
|
import {
|
||||||
|
discordURL,
|
||||||
|
ledgerReferralURL,
|
||||||
|
trezorReferralURL,
|
||||||
|
ethercardReferralURL,
|
||||||
|
keepkeyReferralURL,
|
||||||
|
steelyReferralURL
|
||||||
|
} from './data';
|
||||||
|
|
||||||
|
interface Link {
|
||||||
|
link: string;
|
||||||
|
text: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const socialMediaLinks: Link[] = [
|
||||||
|
{
|
||||||
|
link: 'https://twitter.com/mycrypto',
|
||||||
|
text: 'twitter'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
link: 'https://www.facebook.com/MyCrypto/',
|
||||||
|
text: 'facebook'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
link: 'https://medium.com/@mycrypto',
|
||||||
|
text: 'medium'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
link: 'https://www.linkedin.com/company/mycrypto',
|
||||||
|
text: 'linkedin'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
link: 'https://github.com/MyCryptoHQ',
|
||||||
|
text: 'github'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
link: 'https://www.reddit.com/r/mycrypto/',
|
||||||
|
text: 'reddit'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
link: discordURL,
|
||||||
|
text: 'discord'
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
export const productLinks: Link[] = [
|
||||||
|
{
|
||||||
|
link:
|
||||||
|
'https://chrome.google.com/webstore/detail/etheraddresslookup/pdknmigbbbhmllnmgdfalmedcmcefdfn',
|
||||||
|
text: translateRaw('ETHER_ADDRESS_LOOKUP')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
link:
|
||||||
|
'https://chrome.google.com/webstore/detail/ethersecuritylookup/bhhfhgpgmifehjdghlbbijjaimhmcgnf',
|
||||||
|
text: translateRaw('ETHER_SECURITY_LOOKUP')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
link: 'https://etherscamdb.info/',
|
||||||
|
text: translateRaw('ETHERSCAMDB')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
link: 'https://www.mycrypto.com/helpers.html',
|
||||||
|
text: translateRaw('FOOTER_HELP_AND_DEBUGGING')
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
export const affiliateLinks: Link[] = [
|
||||||
|
{
|
||||||
|
link: ledgerReferralURL,
|
||||||
|
text: translateRaw('LEDGER_REFERRAL_1')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
link: trezorReferralURL,
|
||||||
|
text: translateRaw('TREZOR_REFERAL')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
link: keepkeyReferralURL,
|
||||||
|
text: translateRaw('KEEPKEY_REFERRAL')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
link: steelyReferralURL,
|
||||||
|
text: translateRaw('STEELY_REFERRAL')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
link: ethercardReferralURL,
|
||||||
|
text: translateRaw('ETHERCARD_REFERAL')
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
export const partnerLinks: Link[] = [
|
||||||
|
{
|
||||||
|
link: 'https://metamask.io/',
|
||||||
|
text: 'MetaMask'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
link: 'https://infura.io/',
|
||||||
|
text: 'Infura'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
link: 'https://etherscan.io/',
|
||||||
|
text: 'Etherscan'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
link: 'https://etherchain.org/',
|
||||||
|
text: 'Etherchain'
|
||||||
|
}
|
||||||
|
];
|
|
@ -0,0 +1,53 @@
|
||||||
|
import { knowledgeBaseURL } from './data';
|
||||||
|
|
||||||
|
export interface NavigationLink {
|
||||||
|
name: string;
|
||||||
|
to: string;
|
||||||
|
external?: boolean;
|
||||||
|
disabled?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const navigationLinks: NavigationLink[] = [
|
||||||
|
{
|
||||||
|
name: 'NAV_VIEW',
|
||||||
|
to: '/account'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'NAV_GENERATEWALLET',
|
||||||
|
to: '/generate'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'NAV_SWAP',
|
||||||
|
to: '/swap'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'NAV_CONTRACTS',
|
||||||
|
to: '/contracts'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'NAV_ENS',
|
||||||
|
to: '/ens'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'NAV_SIGN',
|
||||||
|
to: '/sign-and-verify-message'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'NAV_TXSTATUS',
|
||||||
|
to: '/tx-status'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'NAV_BROADCAST',
|
||||||
|
to: '/pushTx'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'NAV_SUPPORT_US',
|
||||||
|
to: '/support-us',
|
||||||
|
disabled: !process.env.BUILD_ELECTRON
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'NAV_HELP',
|
||||||
|
to: knowledgeBaseURL,
|
||||||
|
external: true
|
||||||
|
}
|
||||||
|
].filter(link => !link.disabled);
|
File diff suppressed because it is too large
Load Diff
|
@ -29,11 +29,4 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (min-width: $screen-sm) {
|
|
||||||
.Modal {
|
|
||||||
max-width: 800px;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -123,6 +123,7 @@ class OnboardModal extends React.Component<Props, State> {
|
||||||
<Modal
|
<Modal
|
||||||
isOpen={isOpen}
|
isOpen={isOpen}
|
||||||
buttons={buttons}
|
buttons={buttons}
|
||||||
|
maxWidth={800}
|
||||||
handleClose={() => (slideNumber === NUMBER_OF_SLIDES ? this.closeModal : null)}
|
handleClose={() => (slideNumber === NUMBER_OF_SLIDES ? this.closeModal : null)}
|
||||||
>
|
>
|
||||||
<div className="OnboardModal-stepper">
|
<div className="OnboardModal-stepper">
|
||||||
|
|
|
@ -0,0 +1,46 @@
|
||||||
|
@import 'common/sass/variables';
|
||||||
|
|
||||||
|
.ElectronTemplate {
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
|
||||||
|
&-sidebar,
|
||||||
|
&-content {
|
||||||
|
position: relative;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-sidebar {
|
||||||
|
width: $electron-sidebar-width;
|
||||||
|
overflow-x: hidden;
|
||||||
|
background: #FFF;
|
||||||
|
border-right: 1px solid $gray-lighter;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-content {
|
||||||
|
padding: 10vh 30px;
|
||||||
|
flex: 1;
|
||||||
|
|
||||||
|
&-tab {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 1200px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-draggable {
|
||||||
|
display: none;
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
height: 16px;
|
||||||
|
-webkit-app-region: drag;
|
||||||
|
|
||||||
|
// Only needed on OSX, Linux and Windows have title bars
|
||||||
|
.is-osx & {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,48 @@
|
||||||
|
import React, { Component } from 'react';
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
import { AppState } from 'reducers';
|
||||||
|
import Notifications from './Notifications';
|
||||||
|
import OfflineTab from './OfflineTab';
|
||||||
|
import { getOffline } from 'selectors/config';
|
||||||
|
import { ElectronNav } from 'components';
|
||||||
|
import './ElectronTemplate.scss';
|
||||||
|
|
||||||
|
interface StateProps {
|
||||||
|
isOffline: AppState['config']['meta']['offline'];
|
||||||
|
}
|
||||||
|
|
||||||
|
interface OwnProps {
|
||||||
|
isUnavailableOffline?: boolean;
|
||||||
|
children: string | React.ReactElement<string> | React.ReactElement<string>[];
|
||||||
|
}
|
||||||
|
|
||||||
|
type Props = OwnProps & StateProps;
|
||||||
|
|
||||||
|
class ElectronTemplate extends Component<Props, {}> {
|
||||||
|
public render() {
|
||||||
|
const { isUnavailableOffline, children, isOffline } = this.props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="ElectronTemplate">
|
||||||
|
<div className="ElectronTemplate-sidebar">
|
||||||
|
<ElectronNav />
|
||||||
|
</div>
|
||||||
|
<div className="ElectronTemplate-content">
|
||||||
|
<div className="Tab ElectronTemplate-content-tab">
|
||||||
|
{isUnavailableOffline && isOffline ? <OfflineTab /> : children}
|
||||||
|
</div>
|
||||||
|
<Notifications />
|
||||||
|
</div>
|
||||||
|
<div className="ElectronTemplate-draggable" />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function mapStateToProps(state: AppState): StateProps {
|
||||||
|
return {
|
||||||
|
isOffline: getOffline(state)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export default connect(mapStateToProps, {})(ElectronTemplate);
|
|
@ -0,0 +1,10 @@
|
||||||
|
.WebTemplate {
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
flex-wrap: nowrap;
|
||||||
|
|
||||||
|
&-spacer {
|
||||||
|
flex-grow: 2;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,54 @@
|
||||||
|
import React, { Component } from 'react';
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
import { BetaAgreement, Footer, Header } from 'components';
|
||||||
|
import { AppState } from 'reducers';
|
||||||
|
import Notifications from './Notifications';
|
||||||
|
import OfflineTab from './OfflineTab';
|
||||||
|
import { getOffline, getLatestBlock } from 'selectors/config';
|
||||||
|
import { Query } from 'components/renderCbs';
|
||||||
|
import './WebTemplate.scss';
|
||||||
|
|
||||||
|
interface StateProps {
|
||||||
|
isOffline: AppState['config']['meta']['offline'];
|
||||||
|
latestBlock: AppState['config']['meta']['latestBlock'];
|
||||||
|
}
|
||||||
|
|
||||||
|
interface OwnProps {
|
||||||
|
isUnavailableOffline?: boolean;
|
||||||
|
children: string | React.ReactElement<string> | React.ReactElement<string>[];
|
||||||
|
}
|
||||||
|
|
||||||
|
type Props = OwnProps & StateProps;
|
||||||
|
|
||||||
|
class WebTemplate extends Component<Props, {}> {
|
||||||
|
public render() {
|
||||||
|
const { isUnavailableOffline, children, isOffline, latestBlock } = this.props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="WebTemplate">
|
||||||
|
<Query
|
||||||
|
params={['network']}
|
||||||
|
withQuery={({ network }) => (
|
||||||
|
<Header networkParam={network && `${network.toLowerCase()}_auto`} />
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<div className="Tab container">
|
||||||
|
{isUnavailableOffline && isOffline ? <OfflineTab /> : children}
|
||||||
|
</div>
|
||||||
|
<div className="WebTemplate-spacer" />
|
||||||
|
<Footer latestBlock={latestBlock} />
|
||||||
|
<Notifications />
|
||||||
|
<BetaAgreement />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function mapStateToProps(state: AppState): StateProps {
|
||||||
|
return {
|
||||||
|
isOffline: getOffline(state),
|
||||||
|
latestBlock: getLatestBlock(state)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export default connect(mapStateToProps, {})(WebTemplate);
|
|
@ -1,47 +1,6 @@
|
||||||
import React, { Component } from 'react';
|
import ElectronTemplate from './ElectronTemplate';
|
||||||
import { connect } from 'react-redux';
|
import WebTemplate from './WebTemplate';
|
||||||
import { BetaAgreement, Footer, Header } from 'components';
|
|
||||||
import { AppState } from 'reducers';
|
|
||||||
import Notifications from './Notifications';
|
|
||||||
import OfflineTab from './OfflineTab';
|
|
||||||
import { getOffline, getLatestBlock } from 'selectors/config';
|
|
||||||
|
|
||||||
interface StateProps {
|
const template = process.env.BUILD_ELECTRON ? ElectronTemplate : WebTemplate;
|
||||||
isOffline: AppState['config']['meta']['offline'];
|
|
||||||
latestBlock: AppState['config']['meta']['latestBlock'];
|
|
||||||
}
|
|
||||||
|
|
||||||
interface OwnProps {
|
export default template;
|
||||||
isUnavailableOffline?: boolean;
|
|
||||||
children: string | React.ReactElement<string> | React.ReactElement<string>[];
|
|
||||||
}
|
|
||||||
|
|
||||||
type Props = OwnProps & StateProps;
|
|
||||||
|
|
||||||
class TabSection extends Component<Props, {}> {
|
|
||||||
public render() {
|
|
||||||
const { isUnavailableOffline, children, isOffline, latestBlock } = this.props;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="page-layout">
|
|
||||||
<Header />
|
|
||||||
<div className="Tab container">
|
|
||||||
{isUnavailableOffline && isOffline ? <OfflineTab /> : children}
|
|
||||||
</div>
|
|
||||||
<div className="flex-spacer" />
|
|
||||||
<Footer latestBlock={latestBlock} />
|
|
||||||
<Notifications />
|
|
||||||
<BetaAgreement />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function mapStateToProps(state: AppState): StateProps {
|
|
||||||
return {
|
|
||||||
isOffline: getOffline(state),
|
|
||||||
latestBlock: getLatestBlock(state)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export default connect(mapStateToProps, {})(TabSection);
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { generateMnemonic } from 'bip39';
|
import { generateMnemonic } from 'bip39';
|
||||||
import translate from 'translations';
|
import translate from 'translations';
|
||||||
|
import shuffle from 'lodash/shuffle';
|
||||||
import Word from './Word';
|
import Word from './Word';
|
||||||
import FinalSteps from '../FinalSteps';
|
import FinalSteps from '../FinalSteps';
|
||||||
import Template from '../Template';
|
import Template from '../Template';
|
||||||
|
@ -10,8 +11,10 @@ import './Mnemonic.scss';
|
||||||
interface State {
|
interface State {
|
||||||
words: string[];
|
words: string[];
|
||||||
confirmValues: string[];
|
confirmValues: string[];
|
||||||
|
confirmWords: WordTuple[][];
|
||||||
isConfirming: boolean;
|
isConfirming: boolean;
|
||||||
isConfirmed: boolean;
|
isConfirmed: boolean;
|
||||||
|
isRevealingNextWord: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface WordTuple {
|
interface WordTuple {
|
||||||
|
@ -23,8 +26,10 @@ export default class GenerateMnemonic extends React.Component<{}, State> {
|
||||||
public state: State = {
|
public state: State = {
|
||||||
words: [],
|
words: [],
|
||||||
confirmValues: [],
|
confirmValues: [],
|
||||||
|
confirmWords: [],
|
||||||
isConfirming: false,
|
isConfirming: false,
|
||||||
isConfirmed: false
|
isConfirmed: false,
|
||||||
|
isRevealingNextWord: false
|
||||||
};
|
};
|
||||||
|
|
||||||
public componentDidMount() {
|
public componentDidMount() {
|
||||||
|
@ -32,63 +37,57 @@ export default class GenerateMnemonic extends React.Component<{}, State> {
|
||||||
}
|
}
|
||||||
|
|
||||||
public render() {
|
public render() {
|
||||||
const { words, isConfirming, isConfirmed } = this.state;
|
const { words, confirmWords, isConfirming, isConfirmed } = this.state;
|
||||||
let content;
|
const defaultBtnClassName = 'GenerateMnemonic-buttons-btn btn btn-default';
|
||||||
|
const canContinue = this.checkCanContinue();
|
||||||
|
const [firstHalf, lastHalf] =
|
||||||
|
confirmWords.length === 0 ? this.splitWordsIntoHalves(words) : confirmWords;
|
||||||
|
|
||||||
if (isConfirmed) {
|
const content = isConfirmed ? (
|
||||||
content = <FinalSteps walletType={WalletType.Mnemonic} />;
|
<FinalSteps walletType={WalletType.Mnemonic} />
|
||||||
} else {
|
) : (
|
||||||
const canContinue = this.checkCanContinue();
|
<div className="GenerateMnemonic">
|
||||||
const firstHalf: WordTuple[] = [];
|
<h1 className="GenerateMnemonic-title">{translate('GENERATE_MNEMONIC_TITLE')}</h1>
|
||||||
const lastHalf: WordTuple[] = [];
|
|
||||||
words.forEach((word, index) => {
|
|
||||||
if (index < words.length / 2) {
|
|
||||||
firstHalf.push({ word, index });
|
|
||||||
} else {
|
|
||||||
lastHalf.push({ word, index });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
content = (
|
<p className="GenerateMnemonic-help">
|
||||||
<div className="GenerateMnemonic">
|
{isConfirming ? translate('MNEMONIC_DESCRIPTION_1') : translate('MNEMONIC_DESCRIPTION_2')}
|
||||||
<h1 className="GenerateMnemonic-title">{translate('GENERATE_MNEMONIC_TITLE')}</h1>
|
</p>
|
||||||
|
|
||||||
<p className="GenerateMnemonic-help">
|
<div className="GenerateMnemonic-words">
|
||||||
{isConfirming
|
{[firstHalf, lastHalf].map((ws, i) => (
|
||||||
? translate('MNEMONIC_DESCRIPTION_1')
|
<div key={i} className="GenerateMnemonic-words-column">
|
||||||
: translate('MNEMONIC_DESCRIPTION_2')}
|
{ws.map(this.makeWord)}
|
||||||
</p>
|
</div>
|
||||||
|
))}
|
||||||
<div className="GenerateMnemonic-words">
|
|
||||||
{[firstHalf, lastHalf].map((ws, i) => (
|
|
||||||
<div key={i} className="GenerateMnemonic-words-column">
|
|
||||||
{ws.map(this.makeWord)}
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="GenerateMnemonic-buttons">
|
|
||||||
{!isConfirming && (
|
|
||||||
<button
|
|
||||||
className="GenerateMnemonic-buttons-btn btn btn-default"
|
|
||||||
onClick={this.regenerateWordArray}
|
|
||||||
>
|
|
||||||
<i className="fa fa-refresh" /> {translate('REGENERATE_MNEMONIC')}
|
|
||||||
</button>
|
|
||||||
)}
|
|
||||||
<button
|
|
||||||
className="GenerateMnemonic-buttons-btn btn btn-primary"
|
|
||||||
disabled={!canContinue}
|
|
||||||
onClick={this.goToNextStep}
|
|
||||||
>
|
|
||||||
{translate('CONFIRM_MNEMONIC')}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<button className="GenerateMnemonic-skip" onClick={this.skip} />
|
|
||||||
</div>
|
</div>
|
||||||
);
|
|
||||||
}
|
<div className="GenerateMnemonic-buttons">
|
||||||
|
{!isConfirming && (
|
||||||
|
<button className={defaultBtnClassName} onClick={this.regenerateWordArray}>
|
||||||
|
<i className="fa fa-refresh" /> {translate('REGENERATE_MNEMONIC')}
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
{isConfirming && (
|
||||||
|
<button
|
||||||
|
className={defaultBtnClassName}
|
||||||
|
disabled={canContinue}
|
||||||
|
onClick={this.revealNextWord}
|
||||||
|
>
|
||||||
|
<i className="fa fa-eye" /> {translate('REVEAL_NEXT_MNEMONIC')}
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
<button
|
||||||
|
className="GenerateMnemonic-buttons-btn btn btn-primary"
|
||||||
|
disabled={!canContinue}
|
||||||
|
onClick={this.goToNextStep}
|
||||||
|
>
|
||||||
|
{translate('CONFIRM_MNEMONIC')}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button className="GenerateMnemonic-skip" onClick={this.skip} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
return <Template>{content}</Template>;
|
return <Template>{content}</Template>;
|
||||||
}
|
}
|
||||||
|
@ -97,14 +96,6 @@ export default class GenerateMnemonic extends React.Component<{}, State> {
|
||||||
this.setState({ words: generateMnemonic().split(' ') });
|
this.setState({ words: generateMnemonic().split(' ') });
|
||||||
};
|
};
|
||||||
|
|
||||||
private handleConfirmChange = (index: number, value: string) => {
|
|
||||||
this.setState((state: State) => {
|
|
||||||
const confirmValues = [...state.confirmValues];
|
|
||||||
confirmValues[index] = value;
|
|
||||||
this.setState({ confirmValues });
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
private goToNextStep = () => {
|
private goToNextStep = () => {
|
||||||
if (!this.checkCanContinue()) {
|
if (!this.checkCanContinue()) {
|
||||||
return;
|
return;
|
||||||
|
@ -113,7 +104,13 @@ export default class GenerateMnemonic extends React.Component<{}, State> {
|
||||||
if (this.state.isConfirming) {
|
if (this.state.isConfirming) {
|
||||||
this.setState({ isConfirmed: true });
|
this.setState({ isConfirmed: true });
|
||||||
} else {
|
} else {
|
||||||
this.setState({ isConfirming: true });
|
const shuffledWords = shuffle(this.state.words);
|
||||||
|
const confirmWords = this.splitWordsIntoHalves(shuffledWords);
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
isConfirming: true,
|
||||||
|
confirmWords
|
||||||
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -129,18 +126,79 @@ export default class GenerateMnemonic extends React.Component<{}, State> {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private makeWord = (word: WordTuple) => (
|
private makeWord = (word: WordTuple) => {
|
||||||
<Word
|
const { words, confirmValues, isRevealingNextWord, isConfirming } = this.state;
|
||||||
key={`${word.word}${word.index}`}
|
const confirmIndex = words.indexOf(word.word);
|
||||||
index={word.index}
|
const nextIndex = confirmValues.length;
|
||||||
word={word.word}
|
const isNext = confirmIndex === nextIndex;
|
||||||
value={this.state.confirmValues[word.index] || ''}
|
const isRevealed = isRevealingNextWord && isNext;
|
||||||
isReadOnly={!this.state.isConfirming}
|
const hasBeenConfirmed = this.getWordConfirmed(word.word);
|
||||||
onChange={this.handleConfirmChange}
|
|
||||||
/>
|
return (
|
||||||
);
|
<Word
|
||||||
|
key={`${word.word}${word.index}`}
|
||||||
|
index={word.index}
|
||||||
|
confirmIndex={confirmIndex}
|
||||||
|
word={word.word}
|
||||||
|
value={confirmValues[word.index] || ''}
|
||||||
|
showIndex={!isConfirming}
|
||||||
|
isNext={isNext}
|
||||||
|
isBeingRevealed={isRevealed}
|
||||||
|
isConfirming={isConfirming}
|
||||||
|
hasBeenConfirmed={hasBeenConfirmed}
|
||||||
|
onClick={this.handleWordClick}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
private handleWordClick = (_: number, value: string) => {
|
||||||
|
const { confirmValues: previousConfirmValues, words, isConfirming } = this.state;
|
||||||
|
const wordAlreadyConfirmed = previousConfirmValues.includes(value);
|
||||||
|
const activeIndex = previousConfirmValues.length;
|
||||||
|
const isCorrectChoice = words[activeIndex] === value;
|
||||||
|
|
||||||
|
if (isConfirming && !wordAlreadyConfirmed && isCorrectChoice) {
|
||||||
|
const confirmValues = previousConfirmValues.concat(value);
|
||||||
|
|
||||||
|
this.setState({ confirmValues });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private getWordConfirmed = (word: string) => this.state.confirmValues.includes(word);
|
||||||
|
|
||||||
private skip = () => {
|
private skip = () => {
|
||||||
this.setState({ isConfirmed: true });
|
this.setState({ isConfirmed: true });
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private revealNextWord = () => {
|
||||||
|
const revealDuration = 400;
|
||||||
|
|
||||||
|
this.setState(
|
||||||
|
{
|
||||||
|
isRevealingNextWord: true
|
||||||
|
},
|
||||||
|
() =>
|
||||||
|
setTimeout(
|
||||||
|
() =>
|
||||||
|
this.setState({
|
||||||
|
isRevealingNextWord: false
|
||||||
|
}),
|
||||||
|
revealDuration
|
||||||
|
)
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
private splitWordsIntoHalves = (words: string[]) => {
|
||||||
|
const firstHalf: WordTuple[] = [];
|
||||||
|
const lastHalf: WordTuple[] = [];
|
||||||
|
|
||||||
|
words.forEach((word: string, index: number) => {
|
||||||
|
const inFirstColumn = index < words.length / 2;
|
||||||
|
const half = inFirstColumn ? firstHalf : lastHalf;
|
||||||
|
|
||||||
|
half.push({ word, index });
|
||||||
|
});
|
||||||
|
|
||||||
|
return [firstHalf, lastHalf];
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,21 +16,14 @@ $number-margin: 6px;
|
||||||
.MnemonicWord {
|
.MnemonicWord {
|
||||||
display: flex;
|
display: flex;
|
||||||
width: $width;
|
width: $width;
|
||||||
margin-bottom: $space-md;
|
margin-bottom: $space-xs;
|
||||||
|
|
||||||
&:last-child {
|
&:last-child {
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
&-number {
|
& .input-group-addon {
|
||||||
display: inline-block;
|
margin-bottom: $space-md;
|
||||||
width: $number-width;
|
|
||||||
margin-right: $number-margin;
|
|
||||||
text-align: right;
|
|
||||||
font-size: 26px;
|
|
||||||
font-weight: 100;
|
|
||||||
line-height: 40px;
|
|
||||||
vertical-align: bottom;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&-word {
|
&-word {
|
||||||
|
@ -39,14 +32,31 @@ $number-margin: 6px;
|
||||||
&-input {
|
&-input {
|
||||||
animation: word-fade 400ms ease 1;
|
animation: word-fade 400ms ease 1;
|
||||||
animation-fill-mode: both;
|
animation-fill-mode: both;
|
||||||
|
transition: border 0.2s ease-in;
|
||||||
|
margin-bottom: 0;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
&-toggle {
|
&-button {
|
||||||
color: $gray-light;
|
position: relative;
|
||||||
|
width: 300px;
|
||||||
|
margin-bottom: $space-sm;
|
||||||
|
|
||||||
&:hover {
|
&-index {
|
||||||
color: $gray;
|
position: absolute;
|
||||||
}
|
top: -4px;
|
||||||
|
left: -7px;
|
||||||
|
z-index: 1;
|
||||||
|
color: #fff;
|
||||||
|
width: 26px;
|
||||||
|
height: 26px;
|
||||||
|
border-radius: 100%;
|
||||||
|
background: linear-gradient(
|
||||||
|
to top,
|
||||||
|
lighten($brand-success, 10%),
|
||||||
|
lighten($brand-success, 5%)
|
||||||
|
);
|
||||||
|
line-height: 24px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,67 +1,98 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import { translateRaw } from 'translations';
|
|
||||||
import './Word.scss';
|
import './Word.scss';
|
||||||
import { Input } from 'components/ui';
|
import { Input } from 'components/ui';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
index: number;
|
index: number;
|
||||||
|
confirmIndex: number;
|
||||||
word: string;
|
word: string;
|
||||||
value: string;
|
value: string;
|
||||||
isReadOnly: boolean;
|
showIndex: boolean;
|
||||||
onChange(index: number, value: string): void;
|
isNext: boolean;
|
||||||
|
isBeingRevealed: boolean;
|
||||||
|
isConfirming: boolean;
|
||||||
|
hasBeenConfirmed: boolean;
|
||||||
|
onClick(index: number, value: string): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface State {
|
interface State {
|
||||||
isShowingWord: boolean;
|
flashingError: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class MnemonicWord extends React.Component<Props, State> {
|
export default class MnemonicWord extends React.Component<Props, State> {
|
||||||
public state = {
|
public state = {
|
||||||
isShowingWord: false
|
flashingError: false
|
||||||
};
|
};
|
||||||
|
|
||||||
public render() {
|
public render() {
|
||||||
const { index, word, value, isReadOnly } = this.props;
|
const {
|
||||||
const { isShowingWord } = this.state;
|
hasBeenConfirmed,
|
||||||
const readOnly = isReadOnly || isShowingWord;
|
isBeingRevealed,
|
||||||
|
showIndex,
|
||||||
|
index,
|
||||||
|
isConfirming,
|
||||||
|
confirmIndex,
|
||||||
|
word
|
||||||
|
} = this.props;
|
||||||
|
const { flashingError } = this.state;
|
||||||
|
const btnClassName = classnames({
|
||||||
|
btn: true,
|
||||||
|
'btn-default': !(isBeingRevealed || flashingError),
|
||||||
|
'btn-success': isBeingRevealed,
|
||||||
|
'btn-danger': flashingError
|
||||||
|
});
|
||||||
|
const indexClassName = 'input-group-addon input-group-addon--transparent';
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="input-group-wrapper MnemonicWord">
|
<div className="input-group-wrapper MnemonicWord">
|
||||||
<label className="input-group input-group-inline ENSInput-name">
|
<label className="input-group input-group-inline ENSInput-name">
|
||||||
<span className="input-group-addon input-group-addon--transparent">{index + 1}.</span>
|
{showIndex && <span className={indexClassName}>{index + 1}.</span>}
|
||||||
<Input
|
{hasBeenConfirmed && (
|
||||||
className={`MnemonicWord-word-input ${!isReadOnly && 'border-rad-right-0'}`}
|
<span className="MnemonicWord-button-index">{confirmIndex + 1}</span>
|
||||||
value={readOnly ? word : value}
|
)}
|
||||||
onChange={this.handleChange}
|
{isConfirming ? (
|
||||||
readOnly={readOnly}
|
<button
|
||||||
/>
|
className={`MnemonicWord-button ${btnClassName} ${
|
||||||
{!isReadOnly && (
|
hasBeenConfirmed ? 'disabled' : ''
|
||||||
<span
|
}`}
|
||||||
onClick={this.toggleShow}
|
onClick={() => this.handleClick(word)}
|
||||||
aria-label={translateRaw('GEN_ARIA_2')}
|
|
||||||
role="button"
|
|
||||||
className="MnemonicWord-word-toggle input-group-addon"
|
|
||||||
>
|
>
|
||||||
<i
|
{word}
|
||||||
className={classnames(
|
</button>
|
||||||
'fa',
|
) : (
|
||||||
isShowingWord && 'fa-eye-slash',
|
<Input className="MnemonicWord-word-input" value={word} readOnly={true} />
|
||||||
!isShowingWord && 'fa-eye'
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</span>
|
|
||||||
)}
|
)}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private handleChange = (ev: React.FormEvent<HTMLInputElement>) => {
|
private handleClick = (value: string) => {
|
||||||
this.props.onChange(this.props.index, ev.currentTarget.value);
|
const { isNext, index, onClick } = this.props;
|
||||||
|
|
||||||
|
if (!isNext) {
|
||||||
|
this.flashError();
|
||||||
|
}
|
||||||
|
|
||||||
|
onClick(index, value);
|
||||||
};
|
};
|
||||||
|
|
||||||
private toggleShow = () => {
|
private flashError = () => {
|
||||||
this.setState({ isShowingWord: !this.state.isShowingWord });
|
const errorDuration = 200;
|
||||||
|
|
||||||
|
this.setState(
|
||||||
|
{
|
||||||
|
flashingError: true
|
||||||
|
},
|
||||||
|
() =>
|
||||||
|
setTimeout(
|
||||||
|
() =>
|
||||||
|
this.setState({
|
||||||
|
flashingError: false
|
||||||
|
}),
|
||||||
|
errorDuration
|
||||||
|
)
|
||||||
|
);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,72 @@
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
import React, { Component } from 'react';
|
||||||
|
import { AppState } from 'reducers';
|
||||||
|
import { setScheduleDepositField, TSetScheduleDepositField } from 'actions/schedule';
|
||||||
|
import { translateRaw } from 'translations';
|
||||||
|
import { Input, Tooltip } from 'components/ui';
|
||||||
|
import { getDecimal } from 'selectors/transaction';
|
||||||
|
import { getScheduleDeposit, isValidScheduleDeposit } from 'selectors/schedule/fields';
|
||||||
|
import { toWei } from 'libs/units';
|
||||||
|
import Help from 'components/ui/Help';
|
||||||
|
|
||||||
|
interface OwnProps {
|
||||||
|
decimal: number;
|
||||||
|
scheduleDeposit: any;
|
||||||
|
validScheduleDeposit: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface DispatchProps {
|
||||||
|
setScheduleDepositField: TSetScheduleDepositField;
|
||||||
|
}
|
||||||
|
|
||||||
|
type Props = OwnProps & DispatchProps;
|
||||||
|
|
||||||
|
class ScheduleDepositFieldClass extends Component<Props> {
|
||||||
|
public render() {
|
||||||
|
const { scheduleDeposit, validScheduleDeposit } = this.props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="input-group-wrapper">
|
||||||
|
<label className="input-group">
|
||||||
|
<div className="input-group-header">
|
||||||
|
<span className="ScheduleFields-field-title-wrap">
|
||||||
|
{translateRaw('SCHEDULE_DEPOSIT')}
|
||||||
|
<div className="ScheduleFields-field-title-tooltip">
|
||||||
|
<Tooltip>{translateRaw('SCHEDULE_DEPOSIT_TOOLTIP')}</Tooltip>
|
||||||
|
<Help className="ScheduleFields-field-title-help" />
|
||||||
|
</div>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<Input
|
||||||
|
className={!!scheduleDeposit.raw && !validScheduleDeposit ? 'invalid' : ''}
|
||||||
|
type="number"
|
||||||
|
placeholder="0.00001"
|
||||||
|
value={scheduleDeposit.raw}
|
||||||
|
onChange={this.handleDepositChange}
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private handleDepositChange = (ev: React.FormEvent<HTMLInputElement>) => {
|
||||||
|
const { decimal } = this.props;
|
||||||
|
const { value } = ev.currentTarget;
|
||||||
|
|
||||||
|
this.props.setScheduleDepositField({
|
||||||
|
raw: value,
|
||||||
|
value: value ? toWei(value, decimal) : null
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ScheduleDepositField = connect(
|
||||||
|
(state: AppState) => ({
|
||||||
|
decimal: getDecimal(state),
|
||||||
|
scheduleDeposit: getScheduleDeposit(state),
|
||||||
|
validScheduleDeposit: isValidScheduleDeposit(state)
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
setScheduleDepositField
|
||||||
|
}
|
||||||
|
)(ScheduleDepositFieldClass);
|
|
@ -0,0 +1,67 @@
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
import React from 'react';
|
||||||
|
import { AppState } from 'reducers';
|
||||||
|
import { setScheduleGasLimitField, TSetScheduleGasLimitField } from 'actions/schedule';
|
||||||
|
import { translateRaw } from 'translations';
|
||||||
|
import { Input, InlineSpinner } from 'components/ui';
|
||||||
|
import { getGasEstimationPending } from 'selectors/transaction';
|
||||||
|
import { Wei } from 'libs/units';
|
||||||
|
import { EAC_SCHEDULING_CONFIG } from 'libs/scheduling';
|
||||||
|
import { getScheduleGasLimit, isValidScheduleGasLimit } from 'selectors/schedule/fields';
|
||||||
|
|
||||||
|
interface OwnProps {
|
||||||
|
gasEstimationPending: boolean;
|
||||||
|
scheduleGasLimit: any;
|
||||||
|
validScheduleGasLimit: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface DispatchProps {
|
||||||
|
setScheduleGasLimitField: TSetScheduleGasLimitField;
|
||||||
|
}
|
||||||
|
|
||||||
|
type Props = OwnProps & DispatchProps;
|
||||||
|
|
||||||
|
class ScheduleGasLimitFieldClass extends React.Component<Props> {
|
||||||
|
public render() {
|
||||||
|
const { gasEstimationPending, scheduleGasLimit, validScheduleGasLimit } = this.props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="input-group-wrapper">
|
||||||
|
<label className="input-group">
|
||||||
|
<div className="input-group-header">
|
||||||
|
{translateRaw('SCHEDULE_GAS_LIMIT')}
|
||||||
|
<div className="flex-spacer" />
|
||||||
|
<InlineSpinner active={gasEstimationPending} text="Calculating" />
|
||||||
|
</div>
|
||||||
|
<Input
|
||||||
|
className={!!scheduleGasLimit.raw && !validScheduleGasLimit ? 'invalid' : ''}
|
||||||
|
type="number"
|
||||||
|
placeholder={EAC_SCHEDULING_CONFIG.SCHEDULE_GAS_LIMIT_FALLBACK.toString()}
|
||||||
|
value={scheduleGasLimit.raw}
|
||||||
|
onChange={this.handleGasLimitChange}
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private handleGasLimitChange = (ev: React.FormEvent<HTMLInputElement>) => {
|
||||||
|
const { value } = ev.currentTarget;
|
||||||
|
|
||||||
|
this.props.setScheduleGasLimitField({
|
||||||
|
raw: value,
|
||||||
|
value: Wei(value)
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ScheduleGasLimitField = connect(
|
||||||
|
(state: AppState) => ({
|
||||||
|
gasEstimationPending: getGasEstimationPending(state),
|
||||||
|
scheduleGasLimit: getScheduleGasLimit(state),
|
||||||
|
validScheduleGasLimit: isValidScheduleGasLimit(state)
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
setScheduleGasLimitField
|
||||||
|
}
|
||||||
|
)(ScheduleGasLimitFieldClass);
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue