commit
6f32bdc216
|
@ -58,4 +58,7 @@ v8-compile-cache-0/
|
|||
package-lock.json
|
||||
|
||||
# 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/)
|
||||
[![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 BroadcastTx from 'containers/Tabs/BroadcastTx';
|
||||
import CheckTransaction from 'containers/Tabs/CheckTransaction';
|
||||
import SupportPage from 'containers/Tabs/SupportPage';
|
||||
import ErrorScreen from 'components/ErrorScreen';
|
||||
import PageNotFound from 'components/PageNotFound';
|
||||
import LogOutPrompt from 'components/LogOutPrompt';
|
||||
import QrSignerModal from 'containers/QrSignerModal';
|
||||
import { TitleBar } from 'components/ui';
|
||||
import { Store } from 'redux';
|
||||
import { pollOfflineStatus, TPollOfflineStatus } from 'actions/config';
|
||||
|
@ -50,6 +52,7 @@ class RootClass extends Component<Props, State> {
|
|||
public componentDidMount() {
|
||||
this.props.pollOfflineStatus();
|
||||
this.props.setUnitMeta(this.props.networkUnit);
|
||||
this.addBodyClasses();
|
||||
}
|
||||
|
||||
public componentDidCatch(error: Error) {
|
||||
|
@ -84,6 +87,7 @@ class RootClass extends Component<Props, State> {
|
|||
<Route path="/sign-and-verify-message" component={SignAndVerifyMessage} />
|
||||
<Route path="/tx-status" component={CheckTransaction} exact={true} />
|
||||
<Route path="/pushTx" component={BroadcastTx} />
|
||||
<Route path="/support-us" component={SupportPage} exact={true} />
|
||||
<RouteNotFound />
|
||||
</Switch>
|
||||
</CaptureRouteNotFound>
|
||||
|
@ -95,18 +99,40 @@ class RootClass extends Component<Props, State> {
|
|||
: BrowserRouter;
|
||||
|
||||
return (
|
||||
<Provider store={store} key={Math.random()}>
|
||||
<Router key={Math.random()}>
|
||||
<React.Fragment>
|
||||
{process.env.BUILD_ELECTRON && <TitleBar />}
|
||||
{routes}
|
||||
<LegacyRoutes />
|
||||
<LogOutPrompt />
|
||||
</React.Fragment>
|
||||
</Router>
|
||||
</Provider>
|
||||
<React.Fragment>
|
||||
<Provider store={store} key={Math.random()}>
|
||||
<Router key={Math.random()}>
|
||||
<React.Fragment>
|
||||
{process.env.BUILD_ELECTRON && <TitleBar />}
|
||||
{routes}
|
||||
<LegacyRoutes />
|
||||
<LogOutPrompt />
|
||||
<QrSignerModal />
|
||||
</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 => {
|
||||
|
|
|
@ -1,10 +1,15 @@
|
|||
import * as interfaces from './actionTypes';
|
||||
import { TypeKeys } from './constants';
|
||||
|
||||
export type TToggleOffline = typeof toggleOffline;
|
||||
export function toggleOffline(): interfaces.ToggleOfflineAction {
|
||||
export function setOnline(): interfaces.SetOnlineAction {
|
||||
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 function changeNodeForce(payload: string): interfaces.ChangeNodeForceAction {
|
||||
return {
|
||||
|
|
|
@ -2,9 +2,12 @@ import { TypeKeys } from './constants';
|
|||
import { CustomNodeConfig, StaticNodeConfig } from 'types/node';
|
||||
import { CustomNetworkConfig } from 'types/network';
|
||||
|
||||
/*** Toggle Offline ***/
|
||||
export interface ToggleOfflineAction {
|
||||
type: TypeKeys.CONFIG_TOGGLE_OFFLINE;
|
||||
export interface SetOnlineAction {
|
||||
type: TypeKeys.CONFIG_SET_ONLINE;
|
||||
}
|
||||
|
||||
export interface SetOfflineAction {
|
||||
type: TypeKeys.CONFIG_SET_OFFLINE;
|
||||
}
|
||||
|
||||
export interface ToggleAutoGasLimitAction {
|
||||
|
@ -36,6 +39,13 @@ export interface ChangeNodeIntentAction {
|
|||
type: TypeKeys.CONFIG_NODE_CHANGE_INTENT;
|
||||
payload: string;
|
||||
}
|
||||
|
||||
/*** Change Node Onetime ***/
|
||||
export interface ChangeNodeIntentOneTimeAction {
|
||||
type: TypeKeys.CONFIG_NODE_CHANGE_INTENT_ONETIME;
|
||||
payload: string;
|
||||
}
|
||||
|
||||
/*** Force Change Node ***/
|
||||
export interface ChangeNodeForceAction {
|
||||
type: TypeKeys.CONFIG_NODE_CHANGE_FORCE;
|
||||
|
@ -95,7 +105,8 @@ export type NodeAction =
|
|||
|
||||
export type MetaAction =
|
||||
| ChangeLanguageAction
|
||||
| ToggleOfflineAction
|
||||
| SetOnlineAction
|
||||
| SetOfflineAction
|
||||
| ToggleAutoGasLimitAction
|
||||
| PollOfflineStatus
|
||||
| SetLatestBlockAction;
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
export enum TypeKeys {
|
||||
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_AUTO_GAS_LIMIT = 'CONFIG_TOGGLE_AUTO_GAS_LIMIT',
|
||||
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_CHANGE = 'CONFIG_NODE_CHANGE',
|
||||
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_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 (
|
||||
<div className={`BetaAgreement ${isFading}`}>
|
||||
<div className="BetaAgreement-content">
|
||||
<h2>Welcome to the New MyCrypto Beta!</h2>
|
||||
<h2>Welcome to the New MyCrypto Beta Release Candidate!</h2>
|
||||
<p>
|
||||
You are about to use a version of MyCrypto that hasn't been released yet. While we are
|
||||
confident that it is close to being production ready, you may want to use the current
|
||||
production site for larger or more time-sensitive transactions.
|
||||
You are about to use the new MyCrypto Beta Release Candidate. Although this is a release
|
||||
candidate for production, we encourage caution while using this unreleased version of
|
||||
MyCrypto.
|
||||
</p>
|
||||
<p>We hope to move this version of MyCrypto into production in the near future!</p>
|
||||
<p>
|
||||
Feedback and bug reports are greatly appreciated. You can file issues on our{' '}
|
||||
<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"
|
||||
onClick={this.doContinue}
|
||||
>
|
||||
Yes, continue to the Beta
|
||||
Yes, continue to the Beta RC
|
||||
</button>
|
||||
<button className="BetaAgreement-content-buttons-btn is-reject" onClick={this.reject}>
|
||||
No, take me to the production site
|
||||
|
|
|
@ -98,7 +98,7 @@ class CustomNodeModal extends React.Component<Props, State> {
|
|||
const options = [...staticNetwrks, ...customNetwrks, CUSTOM];
|
||||
return (
|
||||
<Modal
|
||||
title={translateRaw('NODE_Title')}
|
||||
title={translateRaw('NODE_TITLE')}
|
||||
isOpen={isOpen}
|
||||
buttons={buttons}
|
||||
handleClose={handleClose}
|
||||
|
@ -125,7 +125,7 @@ class CustomNodeModal extends React.Component<Props, State> {
|
|||
/>
|
||||
</label>
|
||||
<label className="col-sm-3 input-group">
|
||||
<div className="input-group-header">Network</div>
|
||||
<div className="input-group-header">{translate('CUSTOM_NETWORK')}</div>
|
||||
<Dropdown
|
||||
value={network}
|
||||
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 { NewTabLink } from 'components/ui';
|
||||
import { BlockExplorerConfig } from 'types/network';
|
||||
import { getTXDetailsCheckURL } from 'libs/scheduling';
|
||||
import { etherChainExplorerInst } from 'config/data';
|
||||
|
||||
export interface TransactionSucceededProps {
|
||||
txHash: string;
|
||||
blockExplorer?: BlockExplorerConfig;
|
||||
scheduling?: boolean;
|
||||
}
|
||||
|
||||
const TransactionSucceeded = ({ txHash, blockExplorer }: TransactionSucceededProps) => {
|
||||
const TransactionSucceeded = ({ txHash, blockExplorer, scheduling }: TransactionSucceededProps) => {
|
||||
let verifyBtn: React.ReactElement<string> | undefined;
|
||||
let altVerifyBtn: React.ReactElement<string> | undefined;
|
||||
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 (
|
||||
<div>
|
||||
<p>
|
||||
{translate('SUCCESS_3')} {txHash}
|
||||
</p>
|
||||
{scheduleDetailsBtn}
|
||||
{verifyBtn}
|
||||
{altVerifyBtn}
|
||||
<Link to={`/tx-status?txHash=${txHash}`} className="btn btn-xs">
|
||||
|
|
|
@ -1,22 +1,22 @@
|
|||
import logo from 'assets/images/logo-mycrypto.svg';
|
||||
import {
|
||||
ledgerReferralURL,
|
||||
trezorReferralURL,
|
||||
ethercardReferralURL,
|
||||
donationAddressMap,
|
||||
VERSION,
|
||||
knowledgeBaseURL,
|
||||
discordURL
|
||||
socialMediaLinks,
|
||||
productLinks,
|
||||
affiliateLinks,
|
||||
partnerLinks
|
||||
} from 'config';
|
||||
import React from 'react';
|
||||
import PreFooter from './PreFooter';
|
||||
import DisclaimerModal from './DisclaimerModal';
|
||||
import DisclaimerModal from 'components/DisclaimerModal';
|
||||
import { NewTabLink } from 'components/ui';
|
||||
import OnboardModal from 'containers/OnboardModal';
|
||||
import './index.scss';
|
||||
import { translateRaw } from 'translations';
|
||||
|
||||
const SocialMediaLink = ({ link, text }: Link) => {
|
||||
const SocialMediaLink = ({ link, text }: { link: string; text: string }) => {
|
||||
return (
|
||||
<NewTabLink className="SocialMediaLink" key={link} href={link} aria-label={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 {
|
||||
latestBlock: string;
|
||||
}
|
||||
|
@ -139,7 +44,7 @@ export default class Footer extends React.PureComponent<Props, State> {
|
|||
<footer className="Footer" role="contentinfo" aria-label="footer">
|
||||
<div className="Footer-links Footer-section">
|
||||
<div className="Footer-links-social">
|
||||
{SOCIAL_MEDIA.map((socialMediaItem, idx) => (
|
||||
{socialMediaLinks.map((socialMediaItem, idx) => (
|
||||
<SocialMediaLink
|
||||
link={socialMediaItem.link}
|
||||
key={idx}
|
||||
|
@ -149,11 +54,14 @@ export default class Footer extends React.PureComponent<Props, State> {
|
|||
</div>
|
||||
|
||||
<div className="Footer-links-links">
|
||||
{PRODUCT_INFO.map((productInfoItem, idx) => (
|
||||
<NewTabLink key={idx} href={productInfoItem.link}>
|
||||
{productInfoItem.text}
|
||||
{productLinks.map(link => (
|
||||
<NewTabLink key={link.link} href={link.link}>
|
||||
{link.text}
|
||||
</NewTabLink>
|
||||
))}
|
||||
<NewTabLink href="mailto:press@mycrypto.com">
|
||||
{translateRaw('FOOTER_PRESS')}
|
||||
</NewTabLink>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -169,7 +77,7 @@ export default class Footer extends React.PureComponent<Props, State> {
|
|||
</NewTabLink>
|
||||
|
||||
<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="https://about.mycrypto.com">
|
||||
{translateRaw('FOOTER_TEAM')}
|
||||
|
@ -192,7 +100,7 @@ export default class Footer extends React.PureComponent<Props, State> {
|
|||
<div className="Footer-support Footer-section">
|
||||
<h5 className="Footer-support-title">{translateRaw('FOOTER_AFFILIATE_TITLE')}</h5>
|
||||
<div className="Footer-support-affiliates">
|
||||
{AFFILIATES.map((link, i) => (
|
||||
{affiliateLinks.map((link, i) => (
|
||||
<NewTabLink key={i} href={link.link}>
|
||||
{link.text}
|
||||
</NewTabLink>
|
||||
|
@ -214,7 +122,7 @@ export default class Footer extends React.PureComponent<Props, State> {
|
|||
</div>
|
||||
|
||||
<div className="Footer-support-friends">
|
||||
{FRIENDS.map((link, i) => (
|
||||
{partnerLinks.map((link, i) => (
|
||||
<NewTabLink key={i} href={link.link}>
|
||||
{link.text}
|
||||
</NewTabLink>
|
||||
|
|
|
@ -9,9 +9,14 @@ import { Input } from 'components/ui';
|
|||
interface Props {
|
||||
customLabel?: string;
|
||||
disabled?: boolean;
|
||||
hideGasCalculationSpinner?: boolean;
|
||||
}
|
||||
|
||||
export const GasLimitField: React.SFC<Props> = ({ customLabel, disabled }) => (
|
||||
export const GasLimitField: React.SFC<Props> = ({
|
||||
customLabel,
|
||||
disabled,
|
||||
hideGasCalculationSpinner
|
||||
}) => (
|
||||
<GasLimitFieldFactory
|
||||
withProps={({ gasLimit: { raw }, onChange, readOnly, gasEstimationPending }) => (
|
||||
<div className="input-group-wrapper">
|
||||
|
@ -19,7 +24,10 @@ export const GasLimitField: React.SFC<Props> = ({ customLabel, disabled }) => (
|
|||
<div className="input-group-header">
|
||||
{customLabel ? customLabel : translate('TRANS_GAS')}
|
||||
<div className="flex-spacer" />
|
||||
<InlineSpinner active={gasEstimationPending} text="Calculating" />
|
||||
<InlineSpinner
|
||||
active={!hideGasCalculationSpinner && gasEstimationPending}
|
||||
text="Calculating"
|
||||
/>
|
||||
</div>
|
||||
<Input
|
||||
className={gasLimitValidator(raw) ? 'is-valid' : 'is-invalid'}
|
||||
|
|
|
@ -5,6 +5,7 @@ import { inputGasLimit, TInputGasLimit } from 'actions/transaction';
|
|||
import { connect } from 'react-redux';
|
||||
import { AppState } from 'reducers';
|
||||
import { sanitizeNumericalInput } from 'libs/values';
|
||||
import { getSchedulingToggle } from 'selectors/schedule/fields';
|
||||
|
||||
const defaultGasLimit = '21000';
|
||||
|
||||
|
@ -18,16 +19,24 @@ export interface CallBackProps {
|
|||
interface DispatchProps {
|
||||
inputGasLimit: TInputGasLimit;
|
||||
}
|
||||
|
||||
interface OwnProps {
|
||||
gasLimit: string | null;
|
||||
scheduling: boolean;
|
||||
|
||||
withProps(props: CallBackProps): React.ReactElement<any> | null;
|
||||
}
|
||||
|
||||
type Props = DispatchProps & OwnProps;
|
||||
|
||||
class GasLimitFieldClass extends Component<Props, {}> {
|
||||
class GasLimitFieldClass extends Component<Props> {
|
||||
public componentDidMount() {
|
||||
const { gasLimit } = this.props;
|
||||
const { gasLimit, scheduling } = this.props;
|
||||
|
||||
if (scheduling) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (gasLimit) {
|
||||
this.props.inputGasLimit(gasLimit);
|
||||
} 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 {
|
||||
withProps(props: CallBackProps): React.ReactElement<any> | null;
|
||||
|
|
|
@ -42,7 +42,7 @@ interface OwnProps {
|
|||
|
||||
type Props = OwnProps & StateProps;
|
||||
|
||||
class GenerateTransactionFactoryClass extends Component<Props> {
|
||||
export class GenerateTransactionFactoryClass extends Component<Props> {
|
||||
public render() {
|
||||
const {
|
||||
walletType,
|
||||
|
@ -61,6 +61,7 @@ class GenerateTransactionFactoryClass extends Component<Props> {
|
|||
|
||||
const isButtonDisabled =
|
||||
!isFullTransaction || networkRequestPending || !validGasPrice || !validGasLimit;
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<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 NavigationLink from './NavigationLink';
|
||||
import { knowledgeBaseURL } from 'config';
|
||||
import NavigationLink from 'components/NavigationLink';
|
||||
import { navigationLinks } from 'config';
|
||||
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 {
|
||||
color?: string | false;
|
||||
}
|
||||
|
@ -97,9 +51,14 @@ export default class Navigation extends PureComponent<Props, State> {
|
|||
|
||||
<div className="Navigation-scroll container">
|
||||
<ul className="Navigation-links">
|
||||
{tabs.map(link => {
|
||||
return <NavigationLink key={link.name} link={link} isHomepage={link === tabs[0]} />;
|
||||
})}
|
||||
{navigationLinks.map(link => (
|
||||
<NavigationLink
|
||||
key={link.name}
|
||||
link={link}
|
||||
isHomepage={link === navigationLinks[0]}
|
||||
className="NavigationLink"
|
||||
/>
|
||||
))}
|
||||
</ul>
|
||||
</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 {
|
||||
TChangeLanguage,
|
||||
TChangeNodeIntent,
|
||||
TChangeNodeIntentOneTime,
|
||||
TAddCustomNode,
|
||||
TRemoveCustomNode,
|
||||
TAddCustomNetwork,
|
||||
AddCustomNodeAction,
|
||||
changeLanguage,
|
||||
changeNodeIntent,
|
||||
changeNodeIntentOneTime,
|
||||
addCustomNode,
|
||||
removeCustomNode,
|
||||
addCustomNetwork
|
||||
|
@ -19,8 +21,8 @@ import { Link } from 'react-router-dom';
|
|||
import { TSetGasPriceField, setGasPriceField } from 'actions/transaction';
|
||||
import { ANNOUNCEMENT_MESSAGE, ANNOUNCEMENT_TYPE, languages } from 'config';
|
||||
import Navigation from './components/Navigation';
|
||||
import CustomNodeModal from './components/CustomNodeModal';
|
||||
import OnlineStatus from './components/OnlineStatus';
|
||||
import CustomNodeModal from 'components/CustomNodeModal';
|
||||
import { getKeyByValue } from 'utils/helpers';
|
||||
import { NodeConfig } from 'types/node';
|
||||
import './index.scss';
|
||||
|
@ -34,15 +36,21 @@ import {
|
|||
CustomNodeOption,
|
||||
NodeOption,
|
||||
getNodeOptions,
|
||||
getNetworkConfig
|
||||
getNetworkConfig,
|
||||
isStaticNodeId
|
||||
} from 'selectors/config';
|
||||
import { NetworkConfig } from 'types/network';
|
||||
import { connect } from 'react-redux';
|
||||
import { stripWeb3Network } from 'libs/nodes';
|
||||
import { connect, MapStateToProps } from 'react-redux';
|
||||
import translate from 'translations';
|
||||
|
||||
interface OwnProps {
|
||||
networkParam: string | null;
|
||||
}
|
||||
|
||||
interface DispatchProps {
|
||||
changeLanguage: TChangeLanguage;
|
||||
changeNodeIntent: TChangeNodeIntent;
|
||||
changeNodeIntentOneTime: TChangeNodeIntentOneTime;
|
||||
setGasPriceField: TSetGasPriceField;
|
||||
addCustomNode: TAddCustomNode;
|
||||
removeCustomNode: TRemoveCustomNode;
|
||||
|
@ -50,6 +58,7 @@ interface DispatchProps {
|
|||
}
|
||||
|
||||
interface StateProps {
|
||||
shouldSetNodeFromQS: boolean;
|
||||
network: NetworkConfig;
|
||||
languageSelection: AppState['config']['meta']['languageSelection'];
|
||||
node: NodeConfig;
|
||||
|
@ -59,7 +68,11 @@ interface StateProps {
|
|||
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),
|
||||
isChangingNode: isNodeChanging(state),
|
||||
languageSelection: getLanguageSelection(state),
|
||||
|
@ -73,6 +86,7 @@ const mapDispatchToProps: DispatchProps = {
|
|||
setGasPriceField,
|
||||
changeLanguage,
|
||||
changeNodeIntent,
|
||||
changeNodeIntentOneTime,
|
||||
addCustomNode,
|
||||
removeCustomNode,
|
||||
addCustomNetwork
|
||||
|
@ -82,13 +96,17 @@ interface State {
|
|||
isAddingCustomNode: boolean;
|
||||
}
|
||||
|
||||
type Props = StateProps & DispatchProps;
|
||||
type Props = OwnProps & StateProps & DispatchProps;
|
||||
|
||||
class Header extends Component<Props, State> {
|
||||
public state = {
|
||||
isAddingCustomNode: false
|
||||
};
|
||||
|
||||
public componentDidMount() {
|
||||
this.attemptSetNodeFromQueryParameter();
|
||||
}
|
||||
|
||||
public render() {
|
||||
const {
|
||||
languageSelection,
|
||||
|
@ -120,7 +138,7 @@ class Header extends Component<Props, State> {
|
|||
...rest,
|
||||
name: (
|
||||
<span>
|
||||
{stripWeb3Network(label.network)} <small>({label.service})</small>
|
||||
{label.network} <small>({label.service})</small>
|
||||
</span>
|
||||
)
|
||||
};
|
||||
|
@ -161,7 +179,6 @@ class Header extends Component<Props, State> {
|
|||
color="white"
|
||||
/>
|
||||
</div>
|
||||
{console.log(nodeSelection)}
|
||||
<div
|
||||
className={classnames({
|
||||
'Header-branding-right-dropdown': true,
|
||||
|
@ -177,7 +194,7 @@ class Header extends Component<Props, State> {
|
|||
value={nodeSelection || ''}
|
||||
extra={
|
||||
<li>
|
||||
<a onClick={this.openCustomNodeModal}>Add Custom Node</a>
|
||||
<a onClick={this.openCustomNodeModal}>{translate('NODE_ADD')}</a>
|
||||
</li>
|
||||
}
|
||||
disabled={nodeSelection === 'web3'}
|
||||
|
@ -221,6 +238,13 @@ class Header extends Component<Props, State> {
|
|||
this.setState({ isAddingCustomNode: false });
|
||||
this.props.addCustomNode(payload);
|
||||
};
|
||||
|
||||
private attemptSetNodeFromQueryParameter() {
|
||||
const { shouldSetNodeFromQS, networkParam } = this.props;
|
||||
if (shouldSetNodeFromQS) {
|
||||
this.props.changeNodeIntentOneTime(networkParam!);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(Header);
|
||||
|
|
|
@ -70,7 +70,6 @@ class LogOutPromptClass extends React.Component<Props, State> {
|
|||
private onConfirm = () => {
|
||||
const { nextLocation: next } = this.state;
|
||||
this.props.resetWallet();
|
||||
this.props.web3UnsetNode();
|
||||
this.setState(
|
||||
{
|
||||
openModal: false,
|
||||
|
@ -79,6 +78,7 @@ class LogOutPromptClass extends React.Component<Props, State> {
|
|||
() => {
|
||||
if (next) {
|
||||
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 { Link, withRouter, RouteComponentProps } from 'react-router-dom';
|
||||
import translate, { translateRaw } from 'translations';
|
||||
import { TabLink } from './Navigation';
|
||||
import './NavigationLink.scss';
|
||||
import { NavigationLink } from 'config';
|
||||
|
||||
interface Props extends RouteComponentProps<{}> {
|
||||
link: TabLink;
|
||||
link: NavigationLink;
|
||||
isHomepage: boolean;
|
||||
className: string;
|
||||
}
|
||||
|
||||
class NavigationLink extends React.PureComponent<Props, {}> {
|
||||
class NavigationLinkClass extends React.PureComponent<Props, {}> {
|
||||
public render() {
|
||||
const { link, location, isHomepage } = this.props;
|
||||
const isExternalLink = link.to.includes('http');
|
||||
const { link, location, isHomepage, className } = this.props;
|
||||
let isActive = false;
|
||||
|
||||
if (!isExternalLink) {
|
||||
if (!link.external) {
|
||||
// isActive if
|
||||
// 1) Current path is the same as link
|
||||
// 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({
|
||||
'NavigationLink-link': true,
|
||||
[`${className}-link`]: true,
|
||||
'is-disabled': !link.to,
|
||||
'is-active': isActive
|
||||
});
|
||||
|
@ -43,6 +42,7 @@ class NavigationLink extends React.PureComponent<Props, {}> {
|
|||
rel="noopener noreferrer"
|
||||
>
|
||||
{translate(link.name)}
|
||||
<i className={`${className}-link-icon fa fa-external-link`} />
|
||||
</a>
|
||||
) : (
|
||||
<Link className={linkClasses} to={(link as any).to} aria-label={linkLabel}>
|
||||
|
@ -51,7 +51,7 @@ class NavigationLink extends React.PureComponent<Props, {}> {
|
|||
);
|
||||
|
||||
return (
|
||||
<li id={link.name} className="NavigationLink">
|
||||
<li id={link.name} className={className}>
|
||||
{linkEl}
|
||||
</li>
|
||||
);
|
||||
|
@ -59,4 +59,4 @@ class NavigationLink extends React.PureComponent<Props, {}> {
|
|||
}
|
||||
|
||||
// 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;
|
||||
|
||||
class SendButtonFactoryClass extends Component<Props> {
|
||||
export class SendButtonFactoryClass extends Component<Props> {
|
||||
public render() {
|
||||
const {
|
||||
signing,
|
||||
|
|
|
@ -3,6 +3,10 @@
|
|||
.SubTabs {
|
||||
margin-top: $space-xs;
|
||||
|
||||
.is-electron & {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
&-tabs {
|
||||
display: inline-block;
|
||||
white-space: nowrap;
|
||||
|
|
|
@ -50,6 +50,7 @@ interface OwnProps {
|
|||
disableToggle?: boolean;
|
||||
advancedGasOptions?: AdvancedOptions;
|
||||
className?: string;
|
||||
scheduling?: boolean;
|
||||
}
|
||||
|
||||
type Props = DispatchProps & OwnProps & StateProps;
|
||||
|
@ -90,16 +91,19 @@ class TXMetaDataPanel extends React.Component<Props, State> {
|
|||
}
|
||||
|
||||
public render() {
|
||||
const { offline, disableToggle, advancedGasOptions, className = '' } = this.props;
|
||||
const { offline, disableToggle, advancedGasOptions, className = '', scheduling } = this.props;
|
||||
const { gasPrice } = this.state;
|
||||
const showAdvanced = this.state.sliderState === 'advanced' || offline;
|
||||
|
||||
return (
|
||||
<div className={`Gas col-md-12 ${className}`}>
|
||||
<br />
|
||||
{showAdvanced ? (
|
||||
<AdvancedGas
|
||||
gasPrice={gasPrice}
|
||||
inputGasPrice={this.props.inputGasPrice}
|
||||
options={advancedGasOptions}
|
||||
scheduling={scheduling}
|
||||
/>
|
||||
) : (
|
||||
<SimpleGas
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import React from 'react';
|
||||
import { translateRaw } from 'translations';
|
||||
import FeeSummary from './FeeSummary';
|
||||
import FeeSummary, { RenderData } from './FeeSummary';
|
||||
import './AdvancedGas.scss';
|
||||
import { TToggleAutoGasLimit, toggleAutoGasLimit } from 'actions/config';
|
||||
import { AppState } from 'reducers';
|
||||
|
@ -11,6 +11,8 @@ import { getAutoGasLimitEnabled } from 'selectors/config';
|
|||
import { isValidGasPrice } from 'selectors/transaction';
|
||||
import { sanitizeNumericalInput } from 'libs/values';
|
||||
import { Input } from 'components/ui';
|
||||
import { EAC_SCHEDULING_CONFIG } from 'libs/scheduling';
|
||||
import { getScheduleGasPrice, getTimeBounty } from 'selectors/schedule';
|
||||
|
||||
export interface AdvancedOptions {
|
||||
gasPriceField?: boolean;
|
||||
|
@ -24,6 +26,9 @@ interface OwnProps {
|
|||
inputGasPrice: TInputGasPrice;
|
||||
gasPrice: AppState['transaction']['fields']['gasPrice'];
|
||||
options?: AdvancedOptions;
|
||||
scheduling?: boolean;
|
||||
scheduleGasPrice: AppState['schedule']['scheduleGasPrice'];
|
||||
timeBounty: AppState['schedule']['timeBounty'];
|
||||
}
|
||||
|
||||
interface StateProps {
|
||||
|
@ -54,8 +59,9 @@ class AdvancedGas extends React.Component<Props, State> {
|
|||
};
|
||||
|
||||
public render() {
|
||||
const { autoGasLimitEnabled, gasPrice, validGasPrice } = this.props;
|
||||
const { gasPriceField, gasLimitField, nonceField, dataField, feeSummary } = this.state.options;
|
||||
const { autoGasLimitEnabled, gasPrice, scheduling, validGasPrice } = this.props;
|
||||
const { gasPriceField, gasLimitField, nonceField, dataField } = this.state.options;
|
||||
|
||||
return (
|
||||
<div className="AdvancedGas row form-group">
|
||||
<div className="AdvancedGas-calculate-limit">
|
||||
|
@ -78,7 +84,7 @@ class AdvancedGas extends React.Component<Props, State> {
|
|||
{translateRaw('OFFLINE_STEP2_LABEL_3')} (gwei)
|
||||
</div>
|
||||
<Input
|
||||
className={!!gasPrice.raw && !validGasPrice ? 'is-invalid' : ''}
|
||||
className={!!gasPrice.raw && !validGasPrice ? 'invalid' : ''}
|
||||
type="number"
|
||||
placeholder="40"
|
||||
value={gasPrice.raw}
|
||||
|
@ -91,7 +97,11 @@ class AdvancedGas extends React.Component<Props, State> {
|
|||
|
||||
{gasLimitField && (
|
||||
<div className="AdvancedGas-gas-limit">
|
||||
<GasLimitField customLabel={translateRaw('OFFLINE_STEP2_LABEL_4')} />
|
||||
<GasLimitField
|
||||
customLabel={translateRaw('OFFLINE_STEP2_LABEL_4')}
|
||||
disabled={scheduling}
|
||||
hideGasCalculationSpinner={scheduling}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{nonceField && (
|
||||
|
@ -101,24 +111,62 @@ class AdvancedGas extends React.Component<Props, State> {
|
|||
)}
|
||||
</div>
|
||||
|
||||
{dataField && (
|
||||
<div className="AdvancedGas-data">
|
||||
<DataField />
|
||||
</div>
|
||||
)}
|
||||
{!scheduling &&
|
||||
dataField && (
|
||||
<div className="AdvancedGas-data">
|
||||
<DataField />
|
||||
</div>
|
||||
)}
|
||||
|
||||
{feeSummary && (
|
||||
<div className="AdvancedGas-fee-summary">
|
||||
<FeeSummary
|
||||
gasPrice={gasPrice}
|
||||
render={({ gasPriceWei, gasLimit, fee, usd }) => (
|
||||
<span>
|
||||
{gasPriceWei} * {gasLimit} = {fee} {usd && <span>~= ${usd} USD</span>}
|
||||
</span>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{this.renderFee()}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
private renderFee() {
|
||||
const { gasPrice, scheduleGasPrice } = this.props;
|
||||
const { feeSummary } = this.state.options;
|
||||
|
||||
if (!feeSummary) {
|
||||
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>
|
||||
);
|
||||
}
|
||||
|
@ -136,6 +184,8 @@ class AdvancedGas extends React.Component<Props, State> {
|
|||
export default connect(
|
||||
(state: AppState) => ({
|
||||
autoGasLimitEnabled: getAutoGasLimitEnabled(state),
|
||||
scheduleGasPrice: getScheduleGasPrice(state),
|
||||
timeBounty: getTimeBounty(state),
|
||||
validGasPrice: isValidGasPrice(state)
|
||||
}),
|
||||
{ toggleAutoGasLimit }
|
||||
|
|
|
@ -9,3 +9,9 @@
|
|||
text-align: center;
|
||||
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 { connect } from 'react-redux';
|
||||
import { AppState } from 'reducers';
|
||||
import classNames from 'classnames';
|
||||
import { getNetworkConfig, getOffline } from 'selectors/config';
|
||||
import { getIsEstimating } from 'selectors/gas';
|
||||
import { getGasLimit } from 'selectors/transaction';
|
||||
import { UnitDisplay, Spinner } from 'components/ui';
|
||||
import { NetworkConfig } from 'types/network';
|
||||
import './FeeSummary.scss';
|
||||
import { getScheduleGasLimit, getTimeBounty, getSchedulingToggle } from 'selectors/schedule';
|
||||
import { calcEACTotalCost } from 'libs/scheduling';
|
||||
|
||||
interface RenderData {
|
||||
export interface RenderData {
|
||||
gasPriceWei: string;
|
||||
gasPriceGwei: string;
|
||||
gasLimit: string;
|
||||
scheduleGasLimit: string;
|
||||
fee: React.ReactElement<string>;
|
||||
usd: React.ReactElement<string> | null;
|
||||
}
|
||||
|
@ -23,10 +27,15 @@ interface ReduxStateProps {
|
|||
network: NetworkConfig;
|
||||
isOffline: AppState['config']['meta']['offline'];
|
||||
isGasEstimating: AppState['gas']['isEstimating'];
|
||||
scheduleGasLimit: AppState['schedule']['scheduleGasLimit'];
|
||||
timeBounty: AppState['schedule']['timeBounty'];
|
||||
scheduling: AppState['schedule']['schedulingToggle']['value'];
|
||||
}
|
||||
|
||||
interface OwnProps {
|
||||
gasPrice: AppState['transaction']['fields']['gasPrice'];
|
||||
scheduleGasPrice: AppState['schedule']['scheduleGasPrice'];
|
||||
|
||||
render(data: RenderData): React.ReactElement<string> | string;
|
||||
}
|
||||
|
||||
|
@ -34,7 +43,16 @@ type Props = OwnProps & ReduxStateProps;
|
|||
|
||||
class FeeSummary extends React.Component<Props> {
|
||||
public render() {
|
||||
const { gasPrice, gasLimit, rates, network, isOffline, isGasEstimating } = this.props;
|
||||
const {
|
||||
gasPrice,
|
||||
gasLimit,
|
||||
rates,
|
||||
network,
|
||||
isOffline,
|
||||
isGasEstimating,
|
||||
scheduling,
|
||||
scheduleGasLimit
|
||||
} = this.props;
|
||||
|
||||
if (isGasEstimating) {
|
||||
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 = (
|
||||
<UnitDisplay
|
||||
value={feeBig}
|
||||
|
@ -68,18 +86,56 @@ class FeeSummary extends React.Component<Props> {
|
|||
/>
|
||||
);
|
||||
|
||||
const feeSummaryClasses = classNames({
|
||||
FeeSummary: true,
|
||||
SchedulingFeeSummary: scheduling
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="FeeSummary">
|
||||
<div className={feeSummaryClasses}>
|
||||
{this.props.render({
|
||||
gasPriceWei: gasPrice.value.toString(),
|
||||
gasPriceGwei: gasPrice.raw,
|
||||
fee,
|
||||
usd,
|
||||
gasLimit: gasLimit.raw
|
||||
gasLimit: gasLimit.raw,
|
||||
scheduleGasLimit: scheduleGasLimit.raw
|
||||
})}
|
||||
</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 {
|
||||
|
@ -88,7 +144,10 @@ function mapStateToProps(state: AppState): ReduxStateProps {
|
|||
rates: state.rates.rates,
|
||||
network: getNetworkConfig(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 Slider, { createSliderWithTooltip } from 'rc-slider';
|
||||
import translate from 'translations';
|
||||
import FeeSummary from './FeeSummary';
|
||||
import './SimpleGas.scss';
|
||||
import { AppState } from 'reducers';
|
||||
import {
|
||||
|
@ -17,11 +16,15 @@ import { Wei, fromWei } from 'libs/units';
|
|||
import { gasPriceDefaults } from 'config';
|
||||
import { InlineSpinner } from 'components/ui/InlineSpinner';
|
||||
import { TInputGasPrice } from 'actions/transaction';
|
||||
import FeeSummary from './FeeSummary';
|
||||
import { getScheduleGasPrice } from 'selectors/schedule';
|
||||
|
||||
const SliderWithTooltip = createSliderWithTooltip(Slider);
|
||||
|
||||
interface OwnProps {
|
||||
gasPrice: AppState['transaction']['fields']['gasPrice'];
|
||||
setGasPrice: TInputGasPrice;
|
||||
|
||||
inputGasPrice(rawGas: string): void;
|
||||
}
|
||||
|
||||
|
@ -32,6 +35,7 @@ interface StateProps {
|
|||
gasLimitPending: boolean;
|
||||
isWeb3Node: boolean;
|
||||
gasLimitEstimationTimedOut: boolean;
|
||||
scheduleGasPrice: AppState['schedule']['scheduleGasPrice'];
|
||||
}
|
||||
|
||||
interface ActionProps {
|
||||
|
@ -68,7 +72,8 @@ class SimpleGas extends React.Component<Props> {
|
|||
gasLimitEstimationTimedOut,
|
||||
isWeb3Node,
|
||||
noncePending,
|
||||
gasLimitPending
|
||||
gasLimitPending,
|
||||
scheduleGasPrice
|
||||
} = this.props;
|
||||
|
||||
const bounds = {
|
||||
|
@ -114,6 +119,7 @@ class SimpleGas extends React.Component<Props> {
|
|||
</div>
|
||||
<FeeSummary
|
||||
gasPrice={gasPrice}
|
||||
scheduleGasPrice={scheduleGasPrice}
|
||||
render={({ fee, usd }) => (
|
||||
<span>
|
||||
{fee} {usd && <span>/ ${usd}</span>}
|
||||
|
@ -151,7 +157,8 @@ export default connect(
|
|||
noncePending: nonceRequestPending(state),
|
||||
gasLimitPending: getGasEstimationPending(state),
|
||||
gasLimitEstimationTimedOut: getGasLimitEstimationTimedOut(state),
|
||||
isWeb3Node: getIsWeb3Node(state)
|
||||
isWeb3Node: getIsWeb3Node(state),
|
||||
scheduleGasPrice: getScheduleGasPrice(state)
|
||||
}),
|
||||
{
|
||||
fetchGasEstimates
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import React from 'react';
|
||||
import Markdown from 'react-markdown';
|
||||
import NewTabLink from 'components/ui/NewTabLink';
|
||||
|
||||
interface Props {
|
||||
source: string;
|
||||
|
@ -11,7 +12,10 @@ const TranslateMarkdown = ({ source }: Props) => {
|
|||
escapeHtml={true}
|
||||
unwrapDisallowed={true}
|
||||
allowedTypes={['link', 'emphasis', 'strong', 'code', 'root', 'inlineCode']}
|
||||
renderers={{ root: React.Fragment }}
|
||||
renderers={{
|
||||
root: React.Fragment,
|
||||
link: NewTabLink
|
||||
}}
|
||||
source={source}
|
||||
/>
|
||||
);
|
||||
|
|
|
@ -26,6 +26,8 @@ $speed: 500ms;
|
|||
position: relative;
|
||||
|
||||
&-wallets {
|
||||
margin: 0 -$space-md;
|
||||
|
||||
&-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 {
|
||||
// Passed props
|
||||
isOpen?: boolean;
|
||||
walletType?: string;
|
||||
dPath: string;
|
||||
dPaths: DPath[];
|
||||
publicKey?: string;
|
||||
|
@ -87,16 +86,7 @@ class DeterministicWalletsModalClass extends React.PureComponent<Props, State> {
|
|||
}
|
||||
|
||||
public render() {
|
||||
const {
|
||||
wallets,
|
||||
desiredToken,
|
||||
network,
|
||||
tokens,
|
||||
dPath,
|
||||
dPaths,
|
||||
onCancel,
|
||||
walletType
|
||||
} = this.props;
|
||||
const { wallets, desiredToken, network, tokens, dPath, dPaths, onCancel } = this.props;
|
||||
const { selectedAddress, customPath, page } = this.state;
|
||||
|
||||
const buttons: IButton[] = [
|
||||
|
@ -115,7 +105,7 @@ class DeterministicWalletsModalClass extends React.PureComponent<Props, State> {
|
|||
|
||||
return (
|
||||
<Modal
|
||||
title={translateRaw(`DECRYPT_PROMPT_UNLOCK_${walletType}`)}
|
||||
title={translateRaw('DECRYPT_PROMPT_SELECT_ADDRESS')}
|
||||
isOpen={this.props.isOpen}
|
||||
buttons={buttons}
|
||||
handleClose={onCancel}
|
||||
|
@ -209,15 +199,19 @@ class DeterministicWalletsModalClass extends React.PureComponent<Props, State> {
|
|||
private getAddresses(props: Props = this.props) {
|
||||
const { dPath, publicKey, chainCode, seed } = props;
|
||||
|
||||
if (dPath && ((publicKey && chainCode) || seed) && isValidPath(dPath)) {
|
||||
this.props.getDeterministicWallets({
|
||||
seed,
|
||||
dPath,
|
||||
publicKey,
|
||||
chainCode,
|
||||
limit: WALLETS_PER_PAGE,
|
||||
offset: WALLETS_PER_PAGE * this.state.page
|
||||
});
|
||||
if (dPath && ((publicKey && chainCode) || seed)) {
|
||||
if (isValidPath(dPath)) {
|
||||
this.props.getDeterministicWallets({
|
||||
seed,
|
||||
dPath,
|
||||
publicKey,
|
||||
chainCode,
|
||||
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) {
|
||||
if (option.value === customDPath.value) {
|
||||
return translate('ADD_Radio_5_PathCustom');
|
||||
return translate('X_CUSTOM');
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
|
@ -4,6 +4,7 @@ import translate, { translateRaw } from 'translations';
|
|||
import Spinner from 'components/ui/Spinner';
|
||||
import { TShowNotification } from 'actions/notifications';
|
||||
import { Input } from 'components/ui';
|
||||
import DeprecationWarning from './DeprecationWarning';
|
||||
|
||||
export interface KeystoreValue {
|
||||
file: string;
|
||||
|
@ -43,7 +44,8 @@ export class KeystoreDecrypt extends PureComponent {
|
|||
const unlockDisabled = !file || (passReq && !password);
|
||||
|
||||
return (
|
||||
<form id="selectedUploadKey" onSubmit={this.unlock}>
|
||||
<form onSubmit={this.unlock}>
|
||||
<DeprecationWarning />
|
||||
<div className="form-group">
|
||||
<input
|
||||
className="hidden"
|
||||
|
|
|
@ -9,6 +9,8 @@ import { connect } from 'react-redux';
|
|||
import { AppState } from 'reducers';
|
||||
import { SecureWalletName, ledgerReferralURL } from 'config';
|
||||
import { getPaths, getSingleDPath } from 'selectors/config/wallet';
|
||||
import { getNetworkConfig } from 'selectors/config';
|
||||
import { NetworkConfig } from 'types/network';
|
||||
import './LedgerNano.scss';
|
||||
|
||||
interface OwnProps {
|
||||
|
@ -18,6 +20,7 @@ interface OwnProps {
|
|||
interface StateProps {
|
||||
dPath: DPath | undefined;
|
||||
dPaths: DPath[];
|
||||
network: NetworkConfig;
|
||||
}
|
||||
|
||||
interface State {
|
||||
|
@ -54,6 +57,7 @@ class LedgerNanoSDecryptClass extends PureComponent<Props, State> {
|
|||
}
|
||||
|
||||
public render() {
|
||||
const { network } = this.props;
|
||||
const { dPath, publicKey, chainCode, error, isLoading, showTip } = this.state;
|
||||
const showErr = error ? 'is-showing' : '';
|
||||
|
||||
|
@ -66,7 +70,7 @@ class LedgerNanoSDecryptClass extends PureComponent<Props, State> {
|
|||
<div className="LedgerDecrypt">
|
||||
<div className="alert alert-danger">
|
||||
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>
|
||||
);
|
||||
|
@ -74,12 +78,6 @@ class LedgerNanoSDecryptClass extends PureComponent<Props, State> {
|
|||
|
||||
return (
|
||||
<div className="LedgerDecrypt">
|
||||
{showTip && (
|
||||
<p>
|
||||
<strong>Tip: </strong>Make sure you're logged into the ethereum app on your hardware
|
||||
wallet
|
||||
</p>
|
||||
)}
|
||||
<button
|
||||
className="LedgerDecrypt-decrypt btn btn-primary btn-lg btn-block"
|
||||
onClick={this.handleNullConnect}
|
||||
|
@ -101,6 +99,10 @@ class LedgerNanoSDecryptClass extends PureComponent<Props, State> {
|
|||
|
||||
<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">
|
||||
<NewTabLink href="https://support.ledgerwallet.com/hc/en-us/articles/115005200009">
|
||||
{translate('HELP_ARTICLE_1')}
|
||||
|
@ -116,7 +118,6 @@ class LedgerNanoSDecryptClass extends PureComponent<Props, State> {
|
|||
onCancel={this.handleCancel}
|
||||
onConfirmAddress={this.handleUnlock}
|
||||
onPathChange={this.handlePathChange}
|
||||
walletType={'LEDGER'}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
@ -144,13 +145,34 @@ class LedgerNanoSDecryptClass extends PureComponent<Props, State> {
|
|||
});
|
||||
})
|
||||
.catch((err: any) => {
|
||||
let showTip;
|
||||
let errMsg;
|
||||
// Timeout
|
||||
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({
|
||||
error: err && err.metaData ? err.metaData.type : err.toString(),
|
||||
error: errMsg,
|
||||
isLoading: false
|
||||
});
|
||||
if (showTip) {
|
||||
this.showTip();
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
@ -180,7 +202,8 @@ class LedgerNanoSDecryptClass extends PureComponent<Props, State> {
|
|||
function mapStateToProps(state: AppState): StateProps {
|
||||
return {
|
||||
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 { TogglablePassword } from 'components';
|
||||
import { Input } from 'components/ui';
|
||||
import DeprecationWarning from './DeprecationWarning';
|
||||
|
||||
interface OwnProps {
|
||||
onUnlock(param: any): void;
|
||||
|
@ -49,7 +50,8 @@ class MnemonicDecryptClass extends PureComponent<Props, State> {
|
|||
const isValidMnemonic = validateMnemonic(formattedPhrase);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<React.Fragment>
|
||||
<DeprecationWarning />
|
||||
<div id="selectedTypeKey">
|
||||
<div className="form-group">
|
||||
<TogglablePassword
|
||||
|
@ -91,9 +93,8 @@ class MnemonicDecryptClass extends PureComponent<Props, State> {
|
|||
onCancel={this.handleCancel}
|
||||
onConfirmAddress={this.handleUnlock}
|
||||
onPathChange={this.handlePathChange}
|
||||
walletType={'MNEMONIC'}
|
||||
/>
|
||||
</div>
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@ import React, { PureComponent } from 'react';
|
|||
import translate, { translateRaw } from 'translations';
|
||||
import { TogglablePassword } from 'components';
|
||||
import { Input } from 'components/ui';
|
||||
import DeprecationWarning from './DeprecationWarning';
|
||||
|
||||
export interface PrivateKeyValue {
|
||||
key: string;
|
||||
|
@ -54,6 +55,7 @@ export class PrivateKeyDecrypt extends PureComponent<Props> {
|
|||
|
||||
return (
|
||||
<form id="selectedTypeKey" onSubmit={this.unlock}>
|
||||
<DeprecationWarning />
|
||||
<div className="input-group-wrapper">
|
||||
<label className="input-group">
|
||||
<TogglablePassword
|
||||
|
|
|
@ -93,7 +93,6 @@ class TrezorDecryptClass extends PureComponent<Props, State> {
|
|||
onCancel={this.handleCancel}
|
||||
onConfirmAddress={this.handleUnlock}
|
||||
onPathChange={this.handlePathChange}
|
||||
walletType={'TREZOR'}
|
||||
/>
|
||||
</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 translate from 'translations';
|
||||
import { donationAddressMap } from 'config';
|
||||
import { connect } from 'react-redux';
|
||||
import Select, { Option } from 'react-select';
|
||||
import translate, { translateRaw } from 'translations';
|
||||
import { isValidETHAddress } from 'libs/validators';
|
||||
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;
|
||||
}
|
||||
|
||||
interface StateProps {
|
||||
recentAddresses: AppState['wallet']['recentAddresses'];
|
||||
}
|
||||
|
||||
type Props = OwnProps & StateProps;
|
||||
|
||||
interface State {
|
||||
address: string;
|
||||
}
|
||||
|
||||
export class ViewOnlyDecrypt extends PureComponent<Props, State> {
|
||||
class ViewOnlyDecryptClass extends PureComponent<Props, State> {
|
||||
public state = {
|
||||
address: ''
|
||||
};
|
||||
|
||||
public render() {
|
||||
const { recentAddresses } = this.props;
|
||||
const { address } = this.state;
|
||||
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 (
|
||||
<div id="selectedUploadKey">
|
||||
<div className="ViewOnly">
|
||||
<form className="form-group" onSubmit={this.openWallet}>
|
||||
<TextArea
|
||||
className={isValid ? 'is-valid' : 'is-invalid'}
|
||||
{!!recentOptions.length && (
|
||||
<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}
|
||||
onChange={this.changeAddress}
|
||||
onKeyDown={this.handleEnterKey}
|
||||
placeholder={donationAddressMap.ETH}
|
||||
rows={3}
|
||||
placeholder={translateRaw('VIEW_ONLY_ENTER')}
|
||||
/>
|
||||
|
||||
<button className="btn btn-primary btn-block" disabled={!isValid}>
|
||||
{translate('NAV_VIEWWALLET')}
|
||||
<button className="ViewOnly-submit btn btn-primary btn-block" disabled={!isValid}>
|
||||
{translate('VIEW_ADDR')}
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
private changeAddress = (ev: React.FormEvent<HTMLTextAreaElement>) => {
|
||||
private changeAddress = (ev: React.FormEvent<HTMLInputElement>) => {
|
||||
this.setState({ address: ev.currentTarget.value });
|
||||
};
|
||||
|
||||
private handleEnterKey = (ev: React.KeyboardEvent<HTMLElement>) => {
|
||||
if (ev.keyCode === 13) {
|
||||
this.openWallet(ev);
|
||||
}
|
||||
private handleSelectAddress = (option: Option) => {
|
||||
const address = option && option.value ? option.value.toString() : '';
|
||||
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;
|
||||
ev.preventDefault();
|
||||
if (isValidETHAddress(address)) {
|
||||
const wallet = new AddressOnlyWallet(address);
|
||||
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 './SendButton';
|
||||
export * from './SigningStatus';
|
||||
export * from '../containers/Tabs/ScheduleTransaction/components';
|
||||
export { default as NonceField } from './NonceField';
|
||||
export { default as Header } from './Header';
|
||||
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 TransactionStatus } from './TransactionStatus';
|
||||
export { default as ParityQrSigner } from './ParityQrSigner';
|
||||
export { default as ElectronNav } from './ElectronNav';
|
||||
|
|
|
@ -13,7 +13,19 @@ interface IQueryResults {
|
|||
[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<{}> {
|
||||
params: Param[];
|
||||
|
|
|
@ -5,13 +5,19 @@ import './Help.scss';
|
|||
type Size = 'x1' | 'x2' | 'x3';
|
||||
|
||||
interface Props {
|
||||
link: string;
|
||||
link?: string;
|
||||
size?: Size;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const Help = ({ size = 'x1', link }: Props) => {
|
||||
const Help = ({ size = 'x1', link, className }: Props) => {
|
||||
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" />
|
||||
</a>
|
||||
);
|
||||
|
|
|
@ -78,7 +78,7 @@
|
|||
padding: 0.5rem 0.75rem;
|
||||
}
|
||||
&::placeholder {
|
||||
color: rgba(0, 0, 0, 0.3);
|
||||
color: $input-color-placeholder;
|
||||
}
|
||||
&:not([disabled]):not([readonly]) {
|
||||
&.invalid.has-blurred.has-value {
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import React, { PureComponent } from 'react';
|
||||
import { createPortal } from 'react-dom';
|
||||
import ModalBody from './ModalBody';
|
||||
import { TransitionGroup, CSSTransition } from 'react-transition-group';
|
||||
import './index.scss';
|
||||
|
@ -28,6 +29,7 @@ const Fade = ({ ...props }: any) => (
|
|||
);
|
||||
|
||||
export default class Modal extends PureComponent<Props, {}> {
|
||||
public modalParent: HTMLElement;
|
||||
public modalBody: ModalBody;
|
||||
|
||||
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() {
|
||||
document.body.classList.remove('no-scroll');
|
||||
const modalEl = document.getElementById('ModalContainer');
|
||||
if (modalEl) {
|
||||
modalEl.removeChild(this.modalParent);
|
||||
}
|
||||
}
|
||||
|
||||
public render() {
|
||||
|
@ -52,7 +66,7 @@ export default class Modal extends PureComponent<Props, {}> {
|
|||
|
||||
const modalBodyProps = { title, children, modalStyle, hasButtons, buttons, handleClose };
|
||||
|
||||
return (
|
||||
const modal = (
|
||||
<TransitionGroup>
|
||||
{isOpen && (
|
||||
// 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>
|
||||
);
|
||||
|
||||
if (this.modalParent) {
|
||||
return createPortal(modal, this.modalParent);
|
||||
} else {
|
||||
return modal;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,8 +31,16 @@ export interface AAttributes {
|
|||
|
||||
interface NewTabLinkProps extends AAttributes {
|
||||
href: string;
|
||||
content?: React.ReactElement<any> | string | string[] | number;
|
||||
children?: React.ReactElement<any> | string | string[] | number;
|
||||
content?:
|
||||
| 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> {
|
||||
|
|
|
@ -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 = (
|
||||
<span>
|
||||
{formattedValue}
|
||||
<span>{symbol ? ` ${symbol}` : ''}</span>
|
||||
<span>{symbol ? <> {symbol}</> : ''}</span>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@ export { default as Input } from './Input';
|
|||
export { default as TextArea } from './TextArea';
|
||||
export { default as Address } from './Address';
|
||||
export { default as CodeBlock } from './CodeBlock';
|
||||
export { default as Toggle } from './Toggle';
|
||||
export * from './ConditionalInput';
|
||||
export * from './Expandable';
|
||||
export * from './InlineSpinner';
|
||||
|
|
|
@ -9,7 +9,7 @@ export const languages = require('./languages.json');
|
|||
export const discordURL = 'https://discord.gg/VSaTXEA';
|
||||
|
||||
// Displays in the footer
|
||||
export const VERSION = `${packageJson.version} (BETA)`;
|
||||
export const VERSION = `${packageJson.version} (Release Candidate)`;
|
||||
export const N_FACTOR = 8192;
|
||||
|
||||
// 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_MESSAGE = (
|
||||
<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://hackerone.com/mycrypto">HackerOne</NewTabLink> for critical
|
||||
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 ethercardReferralURL =
|
||||
'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 {
|
||||
WEB3 = 'web3',
|
||||
|
|
|
@ -15,7 +15,7 @@ export const ETH_LEDGER: DPath = {
|
|||
|
||||
export const ETC_LEDGER: DPath = {
|
||||
label: 'Ledger (ETC)',
|
||||
value: "m/44'/60'/160720'/0"
|
||||
value: "m/44'/60'/160720'/0'"
|
||||
};
|
||||
|
||||
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
|
||||
// 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]+$))/;
|
||||
|
|
|
@ -3,3 +3,5 @@ export * from './bity';
|
|||
export * from './addressMessages';
|
||||
export * from './helpArticles';
|
||||
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
|
||||
isOpen={isOpen}
|
||||
buttons={buttons}
|
||||
maxWidth={800}
|
||||
handleClose={() => (slideNumber === NUMBER_OF_SLIDES ? this.closeModal : null)}
|
||||
>
|
||||
<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 { 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 ElectronTemplate from './ElectronTemplate';
|
||||
import WebTemplate from './WebTemplate';
|
||||
|
||||
interface StateProps {
|
||||
isOffline: AppState['config']['meta']['offline'];
|
||||
latestBlock: AppState['config']['meta']['latestBlock'];
|
||||
}
|
||||
const template = process.env.BUILD_ELECTRON ? ElectronTemplate : WebTemplate;
|
||||
|
||||
interface OwnProps {
|
||||
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);
|
||||
export default template;
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import React from 'react';
|
||||
import { generateMnemonic } from 'bip39';
|
||||
import translate from 'translations';
|
||||
import shuffle from 'lodash/shuffle';
|
||||
import Word from './Word';
|
||||
import FinalSteps from '../FinalSteps';
|
||||
import Template from '../Template';
|
||||
|
@ -10,8 +11,10 @@ import './Mnemonic.scss';
|
|||
interface State {
|
||||
words: string[];
|
||||
confirmValues: string[];
|
||||
confirmWords: WordTuple[][];
|
||||
isConfirming: boolean;
|
||||
isConfirmed: boolean;
|
||||
isRevealingNextWord: boolean;
|
||||
}
|
||||
|
||||
interface WordTuple {
|
||||
|
@ -23,8 +26,10 @@ export default class GenerateMnemonic extends React.Component<{}, State> {
|
|||
public state: State = {
|
||||
words: [],
|
||||
confirmValues: [],
|
||||
confirmWords: [],
|
||||
isConfirming: false,
|
||||
isConfirmed: false
|
||||
isConfirmed: false,
|
||||
isRevealingNextWord: false
|
||||
};
|
||||
|
||||
public componentDidMount() {
|
||||
|
@ -32,63 +37,57 @@ export default class GenerateMnemonic extends React.Component<{}, State> {
|
|||
}
|
||||
|
||||
public render() {
|
||||
const { words, isConfirming, isConfirmed } = this.state;
|
||||
let content;
|
||||
const { words, confirmWords, isConfirming, isConfirmed } = this.state;
|
||||
const defaultBtnClassName = 'GenerateMnemonic-buttons-btn btn btn-default';
|
||||
const canContinue = this.checkCanContinue();
|
||||
const [firstHalf, lastHalf] =
|
||||
confirmWords.length === 0 ? this.splitWordsIntoHalves(words) : confirmWords;
|
||||
|
||||
if (isConfirmed) {
|
||||
content = <FinalSteps walletType={WalletType.Mnemonic} />;
|
||||
} else {
|
||||
const canContinue = this.checkCanContinue();
|
||||
const firstHalf: WordTuple[] = [];
|
||||
const lastHalf: WordTuple[] = [];
|
||||
words.forEach((word, index) => {
|
||||
if (index < words.length / 2) {
|
||||
firstHalf.push({ word, index });
|
||||
} else {
|
||||
lastHalf.push({ word, index });
|
||||
}
|
||||
});
|
||||
const content = isConfirmed ? (
|
||||
<FinalSteps walletType={WalletType.Mnemonic} />
|
||||
) : (
|
||||
<div className="GenerateMnemonic">
|
||||
<h1 className="GenerateMnemonic-title">{translate('GENERATE_MNEMONIC_TITLE')}</h1>
|
||||
|
||||
content = (
|
||||
<div className="GenerateMnemonic">
|
||||
<h1 className="GenerateMnemonic-title">{translate('GENERATE_MNEMONIC_TITLE')}</h1>
|
||||
<p className="GenerateMnemonic-help">
|
||||
{isConfirming ? translate('MNEMONIC_DESCRIPTION_1') : translate('MNEMONIC_DESCRIPTION_2')}
|
||||
</p>
|
||||
|
||||
<p className="GenerateMnemonic-help">
|
||||
{isConfirming
|
||||
? translate('MNEMONIC_DESCRIPTION_1')
|
||||
: translate('MNEMONIC_DESCRIPTION_2')}
|
||||
</p>
|
||||
|
||||
<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 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={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>;
|
||||
}
|
||||
|
@ -97,14 +96,6 @@ export default class GenerateMnemonic extends React.Component<{}, State> {
|
|||
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 = () => {
|
||||
if (!this.checkCanContinue()) {
|
||||
return;
|
||||
|
@ -113,7 +104,13 @@ export default class GenerateMnemonic extends React.Component<{}, State> {
|
|||
if (this.state.isConfirming) {
|
||||
this.setState({ isConfirmed: true });
|
||||
} 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) => (
|
||||
<Word
|
||||
key={`${word.word}${word.index}`}
|
||||
index={word.index}
|
||||
word={word.word}
|
||||
value={this.state.confirmValues[word.index] || ''}
|
||||
isReadOnly={!this.state.isConfirming}
|
||||
onChange={this.handleConfirmChange}
|
||||
/>
|
||||
);
|
||||
private makeWord = (word: WordTuple) => {
|
||||
const { words, confirmValues, isRevealingNextWord, isConfirming } = this.state;
|
||||
const confirmIndex = words.indexOf(word.word);
|
||||
const nextIndex = confirmValues.length;
|
||||
const isNext = confirmIndex === nextIndex;
|
||||
const isRevealed = isRevealingNextWord && isNext;
|
||||
const hasBeenConfirmed = this.getWordConfirmed(word.word);
|
||||
|
||||
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 = () => {
|
||||
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 {
|
||||
display: flex;
|
||||
width: $width;
|
||||
margin-bottom: $space-md;
|
||||
margin-bottom: $space-xs;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
&-number {
|
||||
display: inline-block;
|
||||
width: $number-width;
|
||||
margin-right: $number-margin;
|
||||
text-align: right;
|
||||
font-size: 26px;
|
||||
font-weight: 100;
|
||||
line-height: 40px;
|
||||
vertical-align: bottom;
|
||||
& .input-group-addon {
|
||||
margin-bottom: $space-md;
|
||||
}
|
||||
|
||||
&-word {
|
||||
|
@ -39,14 +32,31 @@ $number-margin: 6px;
|
|||
&-input {
|
||||
animation: word-fade 400ms ease 1;
|
||||
animation-fill-mode: both;
|
||||
transition: border 0.2s ease-in;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&-toggle {
|
||||
color: $gray-light;
|
||||
&-button {
|
||||
position: relative;
|
||||
width: 300px;
|
||||
margin-bottom: $space-sm;
|
||||
|
||||
&:hover {
|
||||
color: $gray;
|
||||
}
|
||||
&-index {
|
||||
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 classnames from 'classnames';
|
||||
import { translateRaw } from 'translations';
|
||||
import './Word.scss';
|
||||
import { Input } from 'components/ui';
|
||||
|
||||
interface Props {
|
||||
index: number;
|
||||
confirmIndex: number;
|
||||
word: string;
|
||||
value: string;
|
||||
isReadOnly: boolean;
|
||||
onChange(index: number, value: string): void;
|
||||
showIndex: boolean;
|
||||
isNext: boolean;
|
||||
isBeingRevealed: boolean;
|
||||
isConfirming: boolean;
|
||||
hasBeenConfirmed: boolean;
|
||||
onClick(index: number, value: string): void;
|
||||
}
|
||||
|
||||
interface State {
|
||||
isShowingWord: boolean;
|
||||
flashingError: boolean;
|
||||
}
|
||||
|
||||
export default class MnemonicWord extends React.Component<Props, State> {
|
||||
public state = {
|
||||
isShowingWord: false
|
||||
flashingError: false
|
||||
};
|
||||
|
||||
public render() {
|
||||
const { index, word, value, isReadOnly } = this.props;
|
||||
const { isShowingWord } = this.state;
|
||||
const readOnly = isReadOnly || isShowingWord;
|
||||
const {
|
||||
hasBeenConfirmed,
|
||||
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 (
|
||||
<div className="input-group-wrapper MnemonicWord">
|
||||
<label className="input-group input-group-inline ENSInput-name">
|
||||
<span className="input-group-addon input-group-addon--transparent">{index + 1}.</span>
|
||||
<Input
|
||||
className={`MnemonicWord-word-input ${!isReadOnly && 'border-rad-right-0'}`}
|
||||
value={readOnly ? word : value}
|
||||
onChange={this.handleChange}
|
||||
readOnly={readOnly}
|
||||
/>
|
||||
{!isReadOnly && (
|
||||
<span
|
||||
onClick={this.toggleShow}
|
||||
aria-label={translateRaw('GEN_ARIA_2')}
|
||||
role="button"
|
||||
className="MnemonicWord-word-toggle input-group-addon"
|
||||
{showIndex && <span className={indexClassName}>{index + 1}.</span>}
|
||||
{hasBeenConfirmed && (
|
||||
<span className="MnemonicWord-button-index">{confirmIndex + 1}</span>
|
||||
)}
|
||||
{isConfirming ? (
|
||||
<button
|
||||
className={`MnemonicWord-button ${btnClassName} ${
|
||||
hasBeenConfirmed ? 'disabled' : ''
|
||||
}`}
|
||||
onClick={() => this.handleClick(word)}
|
||||
>
|
||||
<i
|
||||
className={classnames(
|
||||
'fa',
|
||||
isShowingWord && 'fa-eye-slash',
|
||||
!isShowingWord && 'fa-eye'
|
||||
)}
|
||||
/>
|
||||
</span>
|
||||
{word}
|
||||
</button>
|
||||
) : (
|
||||
<Input className="MnemonicWord-word-input" value={word} readOnly={true} />
|
||||
)}
|
||||
</label>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
private handleChange = (ev: React.FormEvent<HTMLInputElement>) => {
|
||||
this.props.onChange(this.props.index, ev.currentTarget.value);
|
||||
private handleClick = (value: string) => {
|
||||
const { isNext, index, onClick } = this.props;
|
||||
|
||||
if (!isNext) {
|
||||
this.flashError();
|
||||
}
|
||||
|
||||
onClick(index, value);
|
||||
};
|
||||
|
||||
private toggleShow = () => {
|
||||
this.setState({ isShowingWord: !this.state.isShowingWord });
|
||||
private flashError = () => {
|
||||
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