|
@ -14,6 +14,7 @@ before_install:
|
|||
- export DISPLAY=:99.0
|
||||
- sh -e /etc/init.d/xvfb start
|
||||
- docker pull dternyak/eth-priv-to-addr:latest
|
||||
- sudo apt-get install libusb-1.0
|
||||
|
||||
install:
|
||||
- npm install --silent
|
||||
|
|
|
@ -13,8 +13,8 @@ import BroadcastTx from 'containers/Tabs/BroadcastTx';
|
|||
import ErrorScreen from 'components/ErrorScreen';
|
||||
import PageNotFound from 'components/PageNotFound';
|
||||
import LogOutPrompt from 'components/LogOutPrompt';
|
||||
import { Aux } from 'components/ui';
|
||||
import { Store } from 'redux';
|
||||
import { pollOfflineStatus } from 'actions/config';
|
||||
import { AppState } from 'reducers';
|
||||
|
||||
interface Props {
|
||||
|
@ -30,6 +30,10 @@ export default class Root extends Component<Props, State> {
|
|||
error: null
|
||||
};
|
||||
|
||||
public componentDidMount() {
|
||||
this.props.store.dispatch(pollOfflineStatus());
|
||||
}
|
||||
|
||||
public componentDidCatch(error: Error) {
|
||||
this.setState({ error });
|
||||
}
|
||||
|
@ -70,11 +74,11 @@ export default class Root extends Component<Props, State> {
|
|||
return (
|
||||
<Provider store={store} key={Math.random()}>
|
||||
<Router key={Math.random()}>
|
||||
<Aux>
|
||||
<React.Fragment>
|
||||
{routes}
|
||||
<LegacyRoutes />
|
||||
<LogOutPrompt />
|
||||
</Aux>
|
||||
</React.Fragment>
|
||||
</Router>
|
||||
</Provider>
|
||||
);
|
||||
|
|
|
@ -1,13 +1,6 @@
|
|||
import * as interfaces from './actionTypes';
|
||||
import { TypeKeys } from './constants';
|
||||
import { NodeConfig, CustomNodeConfig, CustomNetworkConfig } from 'config/data';
|
||||
|
||||
export type TForceOfflineConfig = typeof forceOfflineConfig;
|
||||
export function forceOfflineConfig(): interfaces.ForceOfflineAction {
|
||||
return {
|
||||
type: TypeKeys.CONFIG_FORCE_OFFLINE
|
||||
};
|
||||
}
|
||||
import { NodeConfig, CustomNodeConfig, NetworkConfig, CustomNetworkConfig } from 'config/data';
|
||||
|
||||
export type TToggleOfflineConfig = typeof toggleOfflineConfig;
|
||||
export function toggleOfflineConfig(): interfaces.ToggleOfflineAction {
|
||||
|
@ -16,6 +9,13 @@ export function toggleOfflineConfig(): interfaces.ToggleOfflineAction {
|
|||
};
|
||||
}
|
||||
|
||||
export type TToggleAutoGasLimit = typeof toggleAutoGasLimit;
|
||||
export function toggleAutoGasLimit(): interfaces.ToggleAutoGasLimitAction {
|
||||
return {
|
||||
type: TypeKeys.CONFIG_TOGGLE_AUTO_GAS_LIMIT
|
||||
};
|
||||
}
|
||||
|
||||
export type TChangeLanguage = typeof changeLanguage;
|
||||
export function changeLanguage(sign: string): interfaces.ChangeLanguageAction {
|
||||
return {
|
||||
|
@ -25,10 +25,14 @@ export function changeLanguage(sign: string): interfaces.ChangeLanguageAction {
|
|||
}
|
||||
|
||||
export type TChangeNode = typeof changeNode;
|
||||
export function changeNode(nodeSelection: string, node: NodeConfig): interfaces.ChangeNodeAction {
|
||||
export function changeNode(
|
||||
nodeSelection: string,
|
||||
node: NodeConfig,
|
||||
network: NetworkConfig
|
||||
): interfaces.ChangeNodeAction {
|
||||
return {
|
||||
type: TypeKeys.CONFIG_NODE_CHANGE,
|
||||
payload: { nodeSelection, node }
|
||||
payload: { nodeSelection, node, network }
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -1,14 +1,13 @@
|
|||
import { TypeKeys } from './constants';
|
||||
import { NodeConfig, CustomNodeConfig, CustomNetworkConfig } from 'config/data';
|
||||
import { NodeConfig, CustomNodeConfig, NetworkConfig, CustomNetworkConfig } from 'config/data';
|
||||
|
||||
/*** Toggle Offline ***/
|
||||
export interface ToggleOfflineAction {
|
||||
type: TypeKeys.CONFIG_TOGGLE_OFFLINE;
|
||||
}
|
||||
|
||||
/*** Force Offline ***/
|
||||
export interface ForceOfflineAction {
|
||||
type: TypeKeys.CONFIG_FORCE_OFFLINE;
|
||||
export interface ToggleAutoGasLimitAction {
|
||||
type: TypeKeys.CONFIG_TOGGLE_AUTO_GAS_LIMIT;
|
||||
}
|
||||
|
||||
/*** Change Language ***/
|
||||
|
@ -24,6 +23,7 @@ export interface ChangeNodeAction {
|
|||
payload: {
|
||||
nodeSelection: string;
|
||||
node: NodeConfig;
|
||||
network: NetworkConfig;
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -78,8 +78,8 @@ export type ConfigAction =
|
|||
| ChangeNodeAction
|
||||
| ChangeLanguageAction
|
||||
| ToggleOfflineAction
|
||||
| ToggleAutoGasLimitAction
|
||||
| PollOfflineStatus
|
||||
| ForceOfflineAction
|
||||
| ChangeNodeIntentAction
|
||||
| AddCustomNodeAction
|
||||
| RemoveCustomNodeAction
|
||||
|
|
|
@ -3,7 +3,7 @@ export enum TypeKeys {
|
|||
CONFIG_NODE_CHANGE = 'CONFIG_NODE_CHANGE',
|
||||
CONFIG_NODE_CHANGE_INTENT = 'CONFIG_NODE_CHANGE_INTENT',
|
||||
CONFIG_TOGGLE_OFFLINE = 'CONFIG_TOGGLE_OFFLINE',
|
||||
CONFIG_FORCE_OFFLINE = 'CONFIG_FORCE_OFFLINE',
|
||||
CONFIG_TOGGLE_AUTO_GAS_LIMIT = 'CONFIG_TOGGLE_AUTO_GAS_LIMIT',
|
||||
CONFIG_POLL_OFFLINE_STATUS = 'CONFIG_POLL_OFFLINE_STATUS',
|
||||
CONFIG_ADD_CUSTOM_NODE = 'CONFIG_ADD_CUSTOM_NODE',
|
||||
CONFIG_REMOVE_CUSTOM_NODE = 'CONFIG_REMOVE_CUSTOM_NODE',
|
||||
|
|
|
@ -1,22 +0,0 @@
|
|||
import * as interfaces from './actionTypes';
|
||||
import * as constants from './constants';
|
||||
|
||||
export function resolveEnsName(name: string): interfaces.ResolveEnsNameAction {
|
||||
return {
|
||||
type: constants.ENS_RESOLVE,
|
||||
payload: name
|
||||
};
|
||||
}
|
||||
|
||||
export function cacheEnsAddress(
|
||||
ensName: string,
|
||||
address: string
|
||||
): interfaces.CacheEnsAddressAction {
|
||||
return {
|
||||
type: constants.ENS_CACHE,
|
||||
payload: {
|
||||
ensName,
|
||||
address
|
||||
}
|
||||
};
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
export * from './resolveDomain';
|
|
@ -0,0 +1,35 @@
|
|||
import * as ActionTypes from '../actionTypes';
|
||||
import { TypeKeys } from '../constants';
|
||||
import { DomainRequest } from 'libs/ens';
|
||||
import { ResolveDomainCached } from 'actions/ens';
|
||||
|
||||
export type TResolveDomainRequested = typeof resolveDomainRequested;
|
||||
export const resolveDomainRequested = (domain: string): ActionTypes.ResolveDomainRequested => ({
|
||||
type: TypeKeys.ENS_RESOLVE_DOMAIN_REQUESTED,
|
||||
payload: { domain }
|
||||
});
|
||||
|
||||
export const resolveDomainCached = (
|
||||
payload: ResolveDomainCached['payload']
|
||||
): ResolveDomainCached => ({
|
||||
type: TypeKeys.ENS_RESOLVE_DOMAIN_CACHED,
|
||||
payload
|
||||
});
|
||||
|
||||
export type TResolveDomainSucceeded = typeof resolveDomainSucceeded;
|
||||
export const resolveDomainSucceeded = (
|
||||
domain: string,
|
||||
domainData: DomainRequest
|
||||
): ActionTypes.ResolveDomainSucceeded => ({
|
||||
type: TypeKeys.ENS_RESOLVE_DOMAIN_SUCCEEDED,
|
||||
payload: { domain, domainData }
|
||||
});
|
||||
|
||||
export type TResolveDomainFailed = typeof resolveDomainFailed;
|
||||
export const resolveDomainFailed = (
|
||||
domain: string,
|
||||
error: Error
|
||||
): ActionTypes.ResolveDomainFailed => ({
|
||||
type: TypeKeys.ENS_RESOLVE_DOMAIN_FAILED,
|
||||
payload: { domain, error }
|
||||
});
|
|
@ -1,17 +0,0 @@
|
|||
/*** Resolve ENS name ***/
|
||||
export interface ResolveEnsNameAction {
|
||||
type: 'ENS_RESOLVE';
|
||||
payload: string;
|
||||
}
|
||||
|
||||
/*** Cache ENS address ***/
|
||||
export interface CacheEnsAddressAction {
|
||||
type: 'ENS_CACHE';
|
||||
payload: {
|
||||
ensName: string;
|
||||
address: string;
|
||||
};
|
||||
}
|
||||
|
||||
/*** Union Type ***/
|
||||
export type EnsAction = ResolveEnsNameAction | CacheEnsAddressAction;
|
|
@ -0,0 +1,5 @@
|
|||
import { ResolveDomainAction } from './resolveDomain';
|
||||
|
||||
export * from './resolveDomain';
|
||||
|
||||
export type EnsAction = ResolveDomainAction;
|
|
@ -0,0 +1 @@
|
|||
export * from './actionTypes';
|
|
@ -0,0 +1,28 @@
|
|||
import { TypeKeys } from '../constants';
|
||||
import { DomainRequest } from 'libs/ens';
|
||||
|
||||
export interface ResolveDomainRequested {
|
||||
type: TypeKeys.ENS_RESOLVE_DOMAIN_REQUESTED;
|
||||
payload: { domain: string };
|
||||
}
|
||||
|
||||
export interface ResolveDomainSucceeded {
|
||||
type: TypeKeys.ENS_RESOLVE_DOMAIN_SUCCEEDED;
|
||||
payload: { domain: string; domainData: DomainRequest };
|
||||
}
|
||||
|
||||
export interface ResolveDomainCached {
|
||||
type: TypeKeys.ENS_RESOLVE_DOMAIN_CACHED;
|
||||
payload: { domain: string };
|
||||
}
|
||||
|
||||
export interface ResolveDomainFailed {
|
||||
type: TypeKeys.ENS_RESOLVE_DOMAIN_FAILED;
|
||||
payload: { domain: string; error: Error };
|
||||
}
|
||||
|
||||
export type ResolveDomainAction =
|
||||
| ResolveDomainRequested
|
||||
| ResolveDomainSucceeded
|
||||
| ResolveDomainFailed
|
||||
| ResolveDomainCached;
|
|
@ -1,2 +1,6 @@
|
|||
export const ENS_RESOLVE = 'ENS_RESOLVE';
|
||||
export const ENS_CACHE = 'ENS_CACHE';
|
||||
export enum TypeKeys {
|
||||
ENS_RESOLVE_DOMAIN_REQUESTED = 'ENS_RESOLVE_DOMAIN_REQUESTED',
|
||||
ENS_RESOLVE_DOMAIN_SUCCEEDED = 'ENS_RESOLVE_DOMAIN_SUCCEEDED',
|
||||
ENS_RESOLVE_DOMAIN_CACHED = 'ENS_RESOLVE_DOMAIN_CACHED',
|
||||
ENS_RESOLVE_DOMAIN_FAILED = 'ENS_RESOLVE_DOMAIN_FAILED'
|
||||
}
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
import * as interfaces from './actionTypes';
|
||||
import { TypeKeys } from './constants';
|
||||
|
||||
export type TStartOnboardSession = typeof startOnboardSession;
|
||||
export function startOnboardSession(): interfaces.StartOnboardSessionAction {
|
||||
return {
|
||||
type: TypeKeys.START_ONBOARD_SESSION
|
||||
};
|
||||
}
|
||||
|
||||
export type TResumeSlide = typeof resumeSlide;
|
||||
export function resumeSlide(slideNumber: number): interfaces.ResumeSlideAction {
|
||||
return {
|
||||
type: TypeKeys.RESUME_SLIDE,
|
||||
slideNumber
|
||||
};
|
||||
}
|
||||
|
||||
export type TDecrementSlide = typeof decrementSlide;
|
||||
export function decrementSlide(): interfaces.DecrementSlideAction {
|
||||
return {
|
||||
type: TypeKeys.DECREMENT_SLIDE
|
||||
};
|
||||
}
|
||||
|
||||
export type TIncrementSlide = typeof incrementSlide;
|
||||
export function incrementSlide(): interfaces.IncrementSlideAction {
|
||||
return {
|
||||
type: TypeKeys.INCREMENT_SLIDE
|
||||
};
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
import { TypeKeys } from './constants';
|
||||
|
||||
export interface StartOnboardSessionAction {
|
||||
type: TypeKeys.START_ONBOARD_SESSION;
|
||||
}
|
||||
|
||||
export interface ResumeSlideAction {
|
||||
type: TypeKeys.RESUME_SLIDE;
|
||||
slideNumber: number;
|
||||
}
|
||||
|
||||
export interface DecrementSlideAction {
|
||||
type: TypeKeys.DECREMENT_SLIDE;
|
||||
}
|
||||
|
||||
export interface IncrementSlideAction {
|
||||
type: TypeKeys.INCREMENT_SLIDE;
|
||||
}
|
||||
|
||||
export type OnboardStatusAction =
|
||||
| StartOnboardSessionAction
|
||||
| ResumeSlideAction
|
||||
| DecrementSlideAction
|
||||
| IncrementSlideAction;
|
|
@ -0,0 +1,6 @@
|
|||
export enum TypeKeys {
|
||||
START_ONBOARD_SESSION = 'START_ONBOARD_SESSION',
|
||||
RESUME_SLIDE = 'RESUME_SLIDE',
|
||||
DECREMENT_SLIDE = 'DECREMENT_SLIDE',
|
||||
INCREMENT_SLIDE = 'INCREMENT_SLIDE'
|
||||
}
|
|
@ -0,0 +1,2 @@
|
|||
export * from './actionTypes';
|
||||
export * from './actionCreators';
|
|
@ -1,7 +1,8 @@
|
|||
import {
|
||||
TypeKeys,
|
||||
EstimateGasFailedAction,
|
||||
EstimateGasRequestedAction,
|
||||
TypeKeys,
|
||||
EstimateGasTimeoutAction,
|
||||
EstimateGasSucceededAction,
|
||||
GetFromRequestedAction,
|
||||
GetFromSucceededAction,
|
||||
|
@ -29,6 +30,11 @@ const estimateGasFailed = (): EstimateGasFailedAction => ({
|
|||
type: TypeKeys.ESTIMATE_GAS_FAILED
|
||||
});
|
||||
|
||||
type TEstimateGasTimedout = typeof estimateGasTimedout;
|
||||
const estimateGasTimedout = (): EstimateGasTimeoutAction => ({
|
||||
type: TypeKeys.ESTIMATE_GAS_TIMEDOUT
|
||||
});
|
||||
|
||||
type TGetFromRequested = typeof getFromRequested;
|
||||
const getFromRequested = (): GetFromRequestedAction => ({
|
||||
type: TypeKeys.GET_FROM_REQUESTED
|
||||
|
@ -63,6 +69,7 @@ const getNonceFailed = (): GetNonceFailedAction => ({
|
|||
export {
|
||||
estimateGasRequested,
|
||||
estimateGasFailed,
|
||||
estimateGasTimedout,
|
||||
estimateGasSucceeded,
|
||||
getFromRequested,
|
||||
getFromSucceeded,
|
||||
|
@ -73,6 +80,7 @@ export {
|
|||
TEstimateGasRequested,
|
||||
TEstimateGasFailed,
|
||||
TEstimateGasSucceeded,
|
||||
TEstimateGasTimedout,
|
||||
TGetFromRequested,
|
||||
TGetFromSucceeded,
|
||||
TGetNonceRequested,
|
||||
|
|
|
@ -52,7 +52,6 @@ interface SetToFieldAction {
|
|||
payload: {
|
||||
raw: string;
|
||||
value: Address | null;
|
||||
error?: string | null;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -7,7 +7,6 @@ interface SetTokenToMetaAction {
|
|||
payload: {
|
||||
raw: string;
|
||||
value: Address | null;
|
||||
error?: string | null;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -11,6 +11,9 @@ interface EstimateGasSucceededAction {
|
|||
interface EstimateGasFailedAction {
|
||||
type: TypeKeys.ESTIMATE_GAS_FAILED;
|
||||
}
|
||||
interface EstimateGasTimeoutAction {
|
||||
type: TypeKeys.ESTIMATE_GAS_TIMEDOUT;
|
||||
}
|
||||
interface GetFromRequestedAction {
|
||||
type: TypeKeys.GET_FROM_REQUESTED;
|
||||
}
|
||||
|
@ -36,6 +39,7 @@ type NetworkAction =
|
|||
| EstimateGasFailedAction
|
||||
| EstimateGasRequestedAction
|
||||
| EstimateGasSucceededAction
|
||||
| EstimateGasTimeoutAction
|
||||
| GetFromRequestedAction
|
||||
| GetFromSucceededAction
|
||||
| GetFromFailedAction
|
||||
|
@ -47,6 +51,7 @@ export {
|
|||
EstimateGasRequestedAction,
|
||||
EstimateGasSucceededAction,
|
||||
EstimateGasFailedAction,
|
||||
EstimateGasTimeoutAction,
|
||||
GetFromRequestedAction,
|
||||
GetFromSucceededAction,
|
||||
GetFromFailedAction,
|
||||
|
|
|
@ -2,6 +2,7 @@ export enum TypeKeys {
|
|||
ESTIMATE_GAS_REQUESTED = 'ESTIMATE_GAS_REQUESTED',
|
||||
ESTIMATE_GAS_SUCCEEDED = 'ESTIMATE_GAS_SUCCEEDED',
|
||||
ESTIMATE_GAS_FAILED = 'ESTIMATE_GAS_FAILED',
|
||||
ESTIMATE_GAS_TIMEDOUT = 'ESTIMATE_GAS_TIMEDOUT',
|
||||
|
||||
GET_FROM_REQUESTED = 'GET_FROM_REQUESTED',
|
||||
GET_FROM_SUCCEEDED = 'GET_FROM_SUCCEEDED',
|
||||
|
|
|
@ -43,12 +43,25 @@ export function setWallet(value: IWallet): types.SetWalletAction {
|
|||
};
|
||||
}
|
||||
|
||||
export function setWalletPending(loadingStatus: boolean): types.SetWalletPendingAction {
|
||||
return {
|
||||
type: TypeKeys.WALLET_SET_PENDING,
|
||||
payload: loadingStatus
|
||||
};
|
||||
}
|
||||
|
||||
export function setBalancePending(): types.SetBalancePendingAction {
|
||||
return {
|
||||
type: TypeKeys.WALLET_SET_BALANCE_PENDING
|
||||
};
|
||||
}
|
||||
|
||||
export function setPasswordPrompt(): types.SetPasswordPendingAction {
|
||||
return {
|
||||
type: TypeKeys.WALLET_SET_PASSWORD_PENDING
|
||||
};
|
||||
}
|
||||
|
||||
export type TSetBalance = typeof setBalanceFullfilled;
|
||||
export function setBalanceFullfilled(value: Wei): types.SetBalanceFullfilledAction {
|
||||
return {
|
||||
|
|
|
@ -32,6 +32,11 @@ export interface ResetWalletAction {
|
|||
type: TypeKeys.WALLET_RESET;
|
||||
}
|
||||
|
||||
export interface SetWalletPendingAction {
|
||||
type: TypeKeys.WALLET_SET_PENDING;
|
||||
payload: boolean;
|
||||
}
|
||||
|
||||
/*** Set Balance ***/
|
||||
export interface SetBalancePendingAction {
|
||||
type: TypeKeys.WALLET_SET_BALANCE_PENDING;
|
||||
|
@ -116,10 +121,15 @@ export interface SetWalletConfigAction {
|
|||
payload: WalletConfig;
|
||||
}
|
||||
|
||||
export interface SetPasswordPendingAction {
|
||||
type: TypeKeys.WALLET_SET_PASSWORD_PENDING;
|
||||
}
|
||||
|
||||
/*** Union Type ***/
|
||||
export type WalletAction =
|
||||
| UnlockPrivateKeyAction
|
||||
| SetWalletAction
|
||||
| SetWalletPendingAction
|
||||
| ResetWalletAction
|
||||
| SetBalancePendingAction
|
||||
| SetBalanceFullfilledAction
|
||||
|
@ -132,4 +142,5 @@ export type WalletAction =
|
|||
| SetTokenBalanceRejectedAction
|
||||
| ScanWalletForTokensAction
|
||||
| SetWalletTokensAction
|
||||
| SetWalletConfigAction;
|
||||
| SetWalletConfigAction
|
||||
| SetPasswordPendingAction;
|
||||
|
|
|
@ -10,11 +10,14 @@ export enum TypeKeys {
|
|||
WALLET_SET_TOKEN_BALANCES_PENDING = 'WALLET_SET_TOKEN_BALANCES_PENDING',
|
||||
WALLET_SET_TOKEN_BALANCES_FULFILLED = 'WALLET_SET_TOKEN_BALANCES_FULFILLED',
|
||||
WALLET_SET_TOKEN_BALANCES_REJECTED = 'WALLET_SET_TOKEN_BALANCES_REJECTED',
|
||||
WALLET_SET_PENDING = 'WALLET_SET_PENDING',
|
||||
WALLET_SET_NOT_PENDING = 'WALLET_SET_NOT_PENDING',
|
||||
WALLET_SET_TOKEN_BALANCE_PENDING = 'WALLET_SET_TOKEN_BALANCE_PENDING',
|
||||
WALLET_SET_TOKEN_BALANCE_FULFILLED = 'WALLET_SET_TOKEN_BALANCE_FULFILLED',
|
||||
WALLET_SET_TOKEN_BALANCE_REJECTED = 'WALLET_SET_TOKEN_BALANCE_REJECTED',
|
||||
WALLET_SCAN_WALLET_FOR_TOKENS = 'WALLET_SCAN_WALLET_FOR_TOKENS',
|
||||
WALLET_SET_WALLET_TOKENS = 'WALLET_SET_WALLET_TOKENS',
|
||||
WALLET_SET_CONFIG = 'WALLET_SET_CONFIG',
|
||||
WALLET_RESET = 'WALLET_RESET'
|
||||
WALLET_RESET = 'WALLET_RESET',
|
||||
WALLET_SET_PASSWORD_PENDING = 'WALLET_SET_PASSWORD_PENDING'
|
||||
}
|
||||
|
|
|
@ -53,7 +53,15 @@ class ShapeshiftService {
|
|||
headers: new Headers(this.postHeaders)
|
||||
})
|
||||
.then(checkHttpStatus)
|
||||
.then(parseJSON);
|
||||
.then(parseJSON)
|
||||
.catch(err => {
|
||||
// CORS rejection, meaning metamask don't want us
|
||||
if (err.name === 'TypeError') {
|
||||
throw new Error(
|
||||
'Shapeshift has blocked this request, visit shapeshift.io for more information or contact support'
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public getCoins() {
|
||||
|
|
|
@ -1,36 +0,0 @@
|
|||
<?xml version="1.0" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
|
||||
<svg xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<font id="social-media" horiz-adv-x="256">
|
||||
<font-face font-family="social-media"
|
||||
units-per-em="256" ascent="256"
|
||||
descent="0" />
|
||||
<missing-glyph horiz-adv-x="0" />
|
||||
<glyph glyph-name="logo-facebook"
|
||||
unicode=""
|
||||
horiz-adv-x="256" d="M234.6666666666667 256H21.3333333333333C9.5466666666667 256 0 246.4533333333333 0 234.6666666666667V21.3333333333333C0 9.5466666666667 9.5466666666667 0 21.3333333333333 0H138.6666666666667V96H106.6666666666667V138.6666666666667H138.6666666666667V166.2613333333334C138.6666666666667 199.328 158.8586666666667 217.3333333333334 188.3626666666667 217.3333333333334C202.496 217.3333333333334 214.6346666666667 216.2773333333333 218.176 215.808V181.248L197.7173333333333 181.2373333333334C181.6746666666667 181.2373333333334 178.5706666666667 173.6106666666667 178.5706666666667 162.432V138.6666666666667H225.9306666666667L215.264 96H178.5706666666667V0H234.6666666666667C246.4533333333333 0 256 9.5466666666667 256 21.3333333333333V234.6666666666667C256 246.4533333333333 246.4533333333333 256 234.6666666666667 256z" />
|
||||
<glyph glyph-name="logo-reddit"
|
||||
unicode=""
|
||||
horiz-adv-x="256" d="M256 130.1333333333333C256 147.2 242.1333333333334 161.0666666666667 225.0666666666667 161.0666666666667C217.6 161.0666666666667 211.2 158.9333333333333 205.8666666666667 154.6666666666667C186.6666666666667 166.4 163.2 173.8666666666667 137.6 174.9333333333333L150.4 216.5333333333333L186.6666666666667 208C187.7333333333334 195.2 198.4 184.5333333333333 212.2666666666667 184.5333333333333C226.1333333333334 184.5333333333333 237.8666666666667 196.2666666666667 237.8666666666667 210.1333333333333C237.8666666666667 224 226.1333333333334 235.7333333333333 212.2666666666667 235.7333333333333C202.6666666666667 235.7333333333333 194.1333333333334 230.4 189.8666666666667 221.8666666666667L147.2 231.4666666666667C144 232.5333333333333 139.7333333333334 230.4 138.6666666666667 227.2L122.6666666666667 174.9333333333333C96 173.8666666666667 69.3333333333333 167.4666666666667 50.1333333333333 154.6666666666667C44.8 158.9333333333333 38.4 161.0666666666667 30.9333333333333 161.0666666666667C13.8666666666667 161.0666666666667 0 147.2 0 130.1333333333333C0 119.4666666666667 5.3333333333333 109.8666666666667 12.8 104.5333333333333C12.8 102.4 12.8 99.2 12.8 97.0666666666667C12.8 75.7333333333333 25.6 56.5333333333333 48 41.6C69.3333333333333 27.7333333333333 98.1333333333333 20.2666666666667 128 20.2666666666667C157.8666666666667 20.2666666666667 186.6666666666667 27.7333333333333 208 41.6C230.4 56.5333333333333 243.2 75.7333333333333 243.2 97.0666666666667C243.2 99.2 243.2 101.3333333333333 243.2 103.4666666666667C250.6666666666667 109.8666666666667 256 119.4666666666667 256 130.1333333333333zM213.3333333333333 221.8666666666667C219.7333333333333 221.8666666666667 225.0666666666667 216.5333333333333 225.0666666666667 210.1333333333333C225.0666666666667 203.7333333333334 219.7333333333333 198.4 213.3333333333333 198.4S201.6 203.7333333333334 201.6 210.1333333333333C201.6 216.5333333333333 206.9333333333333 221.8666666666667 213.3333333333333 221.8666666666667zM72.5333333333333 110.9333333333333C72.5333333333333 120.5333333333333 81.0666666666667 128 89.6 128C99.2 128 106.6666666666667 119.4666666666667 106.6666666666667 110.9333333333333S99.2 93.8666666666667 89.6 93.8666666666667C81.0666666666667 93.8666666666667 72.5333333333333 101.3333333333333 72.5333333333333 110.9333333333333zM170.6666666666667 60.8C162.1333333333333 52.2666666666667 148.2666666666667 48 129.0666666666667 48C129.0666666666667 48 129.0666666666667 48 129.0666666666667 48C129.0666666666667 48 129.0666666666667 48 129.0666666666667 48C109.8666666666667 48 96 52.2666666666667 87.4666666666667 60.8C84.2666666666667 64 84.2666666666667 68.2666666666667 87.4666666666667 70.4C90.6666666666667 73.6 94.9333333333333 73.6 97.0666666666667 70.4C103.4666666666667 64 114.1333333333333 60.8 129.0666666666667 60.8C129.0666666666667 60.8 129.0666666666667 60.8 129.0666666666667 60.8C129.0666666666667 60.8 129.0666666666667 60.8 129.0666666666667 60.8C144 60.8 154.6666666666667 64 161.0666666666667 70.4C164.2666666666667 73.6 168.5333333333333 73.6 170.6666666666667 70.4C172.8 67.2 172.8 62.9333333333333 170.6666666666667 60.8zM166.4 93.8666666666667C156.8 93.8666666666667 148.2666666666667 101.3333333333333 148.2666666666667 110.9333333333333S156.8 128 166.4 128C176 128 183.4666666666667 119.4666666666667 183.4666666666667 110.9333333333333S176 93.8666666666667 166.4 93.8666666666667z" />
|
||||
<glyph glyph-name="logo-github"
|
||||
unicode=""
|
||||
horiz-adv-x="256" d="M128 252.8C57.6 252.8 0 195.2 0 124.8C0 68.2666666666667 36.2666666666667 20.2666666666667 87.4666666666667 3.2C93.8666666666667 2.1333333333333 96 6.4 96 9.6C96 12.8 96 20.2666666666667 96 30.9333333333333C60.8 23.4666666666667 53.3333333333333 48 53.3333333333333 48C48 62.9333333333333 39.4666666666667 67.2 39.4666666666667 67.2C26.6666666666667 74.6666666666667 39.4666666666667 74.6666666666667 39.4666666666667 74.6666666666667C52.2666666666667 73.6 58.6666666666667 61.8666666666667 58.6666666666667 61.8666666666667C70.4 42.6666666666667 88.5333333333333 48 96 51.2C97.0666666666667 59.7333333333334 100.2666666666667 65.0666666666667 104.5333333333333 68.2666666666667C75.7333333333333 71.4666666666667 45.8666666666667 82.1333333333334 45.8666666666667 131.2C45.8666666666667 145.0666666666667 51.2 156.8 58.6666666666667 165.3333333333334C58.6666666666667 169.6 53.3333333333333 182.4 60.8 199.4666666666667C60.8 199.4666666666667 71.4666666666667 202.6666666666667 96 186.6666666666667C106.6666666666667 189.8666666666667 117.3333333333333 190.9333333333333 128 190.9333333333333C138.6666666666667 190.9333333333333 149.3333333333334 189.8666666666667 160 186.6666666666667C184.5333333333333 203.7333333333334 195.2 199.4666666666667 195.2 199.4666666666667C202.6666666666667 181.3333333333334 197.3333333333333 168.5333333333333 196.2666666666667 165.3333333333334C204.8 156.8 209.0666666666667 145.0666666666667 209.0666666666667 131.2C209.0666666666667 82.1333333333334 179.2 71.4666666666667 150.4 68.2666666666667C154.6666666666667 64 158.9333333333333 56.5333333333334 158.9333333333333 44.8C158.9333333333333 27.7333333333334 158.9333333333333 13.8666666666667 158.9333333333333 9.6C158.9333333333333 6.4 161.0666666666667 2.1333333333334 167.4666666666667 3.2C218.6666666666667 20.2666666666667 254.9333333333334 68.2666666666667 254.9333333333334 124.8C256 195.2 198.4 252.8 128 252.8z" />
|
||||
<glyph glyph-name="logo-twitter"
|
||||
unicode=""
|
||||
horiz-adv-x="256" d="M256 206.9333333333333C246.4 202.6666666666667 236.8 199.4666666666667 226.1333333333334 198.4C236.8 204.8 245.3333333333333 215.4666666666667 249.6 227.2C238.9333333333334 220.8 228.2666666666667 216.5333333333333 216.5333333333333 214.4C206.9333333333333 225.0666666666667 193.0666666666667 231.4666666666667 178.1333333333333 231.4666666666667C149.3333333333333 231.4666666666667 125.8666666666667 208 125.8666666666667 179.2C125.8666666666667 174.9333333333333 125.8666666666667 170.6666666666667 126.9333333333333 167.4666666666667C82.1333333333333 169.6 43.7333333333333 190.9333333333333 18.1333333333333 222.9333333333333C12.8 214.4 10.6666666666667 205.8666666666667 10.6666666666667 196.2666666666667C10.6666666666667 178.1333333333333 20.2666666666667 162.1333333333333 34.1333333333333 152.5333333333334C25.6 152.5333333333334 17.0666666666667 154.6666666666667 10.6666666666667 158.9333333333333C10.6666666666667 158.9333333333333 10.6666666666667 158.9333333333333 10.6666666666667 157.8666666666667C10.6666666666667 132.2666666666667 28.8 110.9333333333333 52.2666666666667 106.6666666666667C48 105.6 43.7333333333333 104.5333333333334 38.4 104.5333333333334C35.2 104.5333333333334 32 104.5333333333334 28.8 105.6C35.2 84.2666666666667 54.4 69.3333333333334 77.8666666666667 69.3333333333334C59.7333333333333 55.4666666666667 37.3333333333333 46.9333333333334 12.8 46.9333333333334C8.5333333333333 46.9333333333334 4.2666666666667 46.9333333333334 0 48C23.4666666666667 33.0666666666667 51.2 24.5333333333334 80 24.5333333333334C177.0666666666667 24.5333333333334 229.3333333333333 104.5333333333334 229.3333333333333 173.8666666666667C229.3333333333333 176 229.3333333333333 178.1333333333333 229.3333333333333 180.2666666666667C240 187.7333333333334 248.5333333333334 197.3333333333334 256 206.9333333333333z" />
|
||||
<glyph glyph-name="logo-linkedin"
|
||||
unicode=""
|
||||
horiz-adv-x="256" d="M245.3333333333333 256H10.6666666666667C4.2666666666667 256 0 251.7333333333333 0 245.3333333333334V10.6666666666667C0 4.2666666666667 4.2666666666667 0 10.6666666666667 0H245.3333333333333C251.7333333333333 0 256 4.2666666666667 256 10.6666666666667V245.3333333333334C256 251.7333333333333 251.7333333333334 256 245.3333333333333 256zM75.7333333333333 37.3333333333333H38.4V160H76.8V37.3333333333333zM56.5333333333333 177.0666666666667C44.8 177.0666666666667 34.1333333333333 186.6666666666667 34.1333333333333 199.4666666666667C34.1333333333333 211.2 43.7333333333333 221.8666666666667 56.5333333333333 221.8666666666667C68.2666666666667 221.8666666666667 78.9333333333333 212.2666666666667 78.9333333333333 199.4666666666667C78.9333333333333 186.6666666666667 69.3333333333333 177.0666666666667 56.5333333333333 177.0666666666667zM218.6666666666667 37.3333333333333H180.2666666666667V97.0666666666667C180.2666666666667 110.9333333333333 180.2666666666667 129.0666666666667 161.0666666666667 129.0666666666667C140.8 129.0666666666667 138.6666666666667 114.1333333333333 138.6666666666667 98.1333333333333V37.3333333333333H100.2666666666667V160H136.5333333333333V142.9333333333333H136.5333333333333C141.8666666666667 152.5333333333333 153.6 162.1333333333333 172.8 162.1333333333333C211.2 162.1333333333333 218.6666666666667 136.5333333333334 218.6666666666667 103.4666666666667V37.3333333333333z" />
|
||||
<glyph glyph-name="logo-slack"
|
||||
unicode=""
|
||||
horiz-adv-x="256" d="M116.2231466666667 106.05504L105.0436266666667 139.43168L139.43168 150.9499733333333L150.6112 117.5733333333334L116.2231466666667 106.05504zM116.2231466666667 106.05504L105.0436266666667 139.43168L139.43168 150.9499733333333L150.6112 117.5733333333334L116.2231466666667 106.05504zM245.3333333333333 163.2C218.6666666666667 250.6666666666667 180.2666666666667 272 92.8 245.3333333333334C5.3333333333333 218.6666666666667 -16 180.2666666666667 10.6666666666667 92.8C37.3333333333333 5.3333333333334 74.6666666666667 -16 163.2 10.6666666666667C250.6666666666667 37.3333333333333 272 75.7333333333334 245.3333333333333 163.2zM200.5333333333333 105.6L183.4666666666667 100.2666666666667L188.8 83.2C190.9333333333333 75.7333333333334 187.7333333333334 68.2666666666667 180.2666666666667 66.1333333333334C179.2 66.1333333333334 177.0666666666667 65.0666666666667 176 65.0666666666667C170.6666666666667 65.0666666666667 165.3333333333334 68.2666666666667 164.2666666666667 73.6L158.9333333333333 90.6666666666667L124.8 78.9333333333333L130.1333333333333 61.8666666666667C132.2666666666667 54.4 129.0666666666667 46.9333333333333 121.6 44.8C120.5333333333333 44.8 118.4 43.7333333333334 117.3333333333333 43.7333333333334C112 43.7333333333334 106.6666666666667 46.9333333333333 105.6 52.2666666666667L100.2666666666667 69.3333333333333L83.2 64C82.1333333333333 64 80 62.9333333333333 78.9333333333333 62.9333333333333C73.6 62.9333333333333 68.2666666666667 66.1333333333333 67.2 71.4666666666667C64 82.1333333333333 67.2 89.6 74.6666666666667 91.7333333333334L91.7333333333333 97.0666666666667L81.0666666666667 130.1333333333334L64 124.8C62.9333333333333 124.8 60.8 123.7333333333334 59.7333333333333 123.7333333333334C54.4 123.7333333333334 49.0666666666667 126.9333333333333 48 132.2666666666667C45.8666666666667 139.7333333333334 49.0666666666667 147.2 56.5333333333333 149.3333333333334L73.6 154.6666666666667L66.1333333333333 172.8C64 179.2 67.2 186.6666666666667 74.6666666666667 189.8666666666667C81.0666666666667 192 88.5333333333333 187.7333333333334 91.7333333333333 181.3333333333334L97.0666666666667 164.2666666666667L131.2 176L125.8666666666667 192C123.7333333333333 199.4666666666667 126.9333333333333 206.9333333333333 134.4 209.0666666666667C141.8666666666667 211.2 149.3333333333334 208 151.4666666666667 200.5333333333333L156.8 183.4666666666667L173.8666666666667 188.8C180.2666666666667 192 187.7333333333334 187.7333333333334 189.8666666666667 181.3333333333334C192 173.8666666666667 188.8 166.4 181.3333333333333 164.2666666666667L164.2666666666667 158.9333333333333L174.9333333333333 125.8666666666667L192 131.2C199.4666666666667 133.3333333333334 206.9333333333333 130.1333333333333 209.0666666666666 122.6666666666667C211.2 116.2666666666667 208 108.8 200.5333333333333 105.6z" />
|
||||
<glyph glyph-name="logo-medium"
|
||||
unicode=""
|
||||
horiz-adv-x="256" d="M245.3333333333333 256H10.6666666666667A10.666666666666666 10.666666666666666 0 0 1 0 245.3333333333334V10.6666666666667A10.666666666666666 10.666666666666666 0 0 1 10.6666666666667 0H245.3333333333333A10.666666666666666 10.666666666666666 0 0 1 256 10.6666666666667V245.3333333333334A10.666666666666666 10.666666666666666 0 0 1 245.3333333333333 256zM212.672 195.3493333333333L198.944 182.1866666666667A4.010666666666666 4.010666666666666 0 0 1 197.4186666666667 178.336V81.6106666666667A4.010666666666666 4.010666666666666 0 0 1 198.944 77.76L212.352 64.5973333333334V61.7066666666667H144.9173333333333V64.5973333333334L158.784 78.08C160.1493333333333 79.4453333333333 160.1493333333333 79.8506666666667 160.1493333333333 81.9306666666667V160.1066666666667L121.5253333333333 61.9733333333334H116.3093333333333L71.3706666666667 160.1066666666667V94.368A9.066666666666666 9.066666666666666 0 0 1 73.856 86.8266666666667L91.9253333333333 64.9173333333333V62.0266666666666H40.7253333333333V64.9173333333333L58.7626666666667 86.8266666666667A8.746666666666666 8.746666666666666 0 0 1 61.088 94.368V170.3786666666667A6.666666666666666 6.666666666666666 0 0 1 58.9546666666667 176L42.8693333333333 195.3493333333333V198.24H92.7253333333333L131.264 113.7173333333333L165.1413333333333 198.24H212.672z" />
|
||||
<glyph glyph-name="logo-github-2"
|
||||
unicode=""
|
||||
horiz-adv-x="256" d="M128 252.8C57.6 252.8 0 195.2 0 124.8C0 68.2666666666667 36.2666666666667 20.2666666666667 87.4666666666667 3.2C93.8666666666667 2.1333333333333 96 6.4 96 9.6C96 12.8 96 20.2666666666667 96 30.9333333333333C60.8 23.4666666666667 53.3333333333333 48 53.3333333333333 48C48 62.9333333333333 39.4666666666667 67.2 39.4666666666667 67.2C26.6666666666667 74.6666666666667 39.4666666666667 74.6666666666667 39.4666666666667 74.6666666666667C52.2666666666667 73.6 58.6666666666667 61.8666666666667 58.6666666666667 61.8666666666667C70.4 42.6666666666667 88.5333333333333 48 96 51.2C97.0666666666667 59.7333333333334 100.2666666666667 65.0666666666667 104.5333333333333 68.2666666666667C75.7333333333333 71.4666666666667 45.8666666666667 82.1333333333334 45.8666666666667 131.2C45.8666666666667 145.0666666666667 51.2 156.8 58.6666666666667 165.3333333333334C58.6666666666667 169.6 53.3333333333333 182.4 60.8 199.4666666666667C60.8 199.4666666666667 71.4666666666667 202.6666666666667 96 186.6666666666667C106.6666666666667 189.8666666666667 117.3333333333333 190.9333333333333 128 190.9333333333333C138.6666666666667 190.9333333333333 149.3333333333334 189.8666666666667 160 186.6666666666667C184.5333333333333 203.7333333333334 195.2 199.4666666666667 195.2 199.4666666666667C202.6666666666667 181.3333333333334 197.3333333333333 168.5333333333333 196.2666666666667 165.3333333333334C204.8 156.8 209.0666666666667 145.0666666666667 209.0666666666667 131.2C209.0666666666667 82.1333333333334 179.2 71.4666666666667 150.4 68.2666666666667C154.6666666666667 64 158.9333333333333 56.5333333333334 158.9333333333333 44.8C158.9333333333333 27.7333333333334 158.9333333333333 13.8666666666667 158.9333333333333 9.6C158.9333333333333 6.4 161.0666666666667 2.1333333333334 167.4666666666667 3.2C218.6666666666667 20.2666666666667 254.9333333333334 68.2666666666667 254.9333333333334 124.8C256 195.2 198.4 252.8 128 252.8z" />
|
||||
</font>
|
||||
</defs>
|
||||
</svg>
|
Before Width: | Height: | Size: 16 KiB |
After Width: | Height: | Size: 19 KiB |
After Width: | Height: | Size: 7.1 KiB |
After Width: | Height: | Size: 7.2 KiB |
|
@ -0,0 +1 @@
|
|||
<svg id="Capa_1" data-name="Capa 1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 576.05 491.69"><defs><style>.cls-1{fill:url(#linear-gradient);}.cls-2{fill:#81cc3b;}.cls-3{fill:#ffa21a;}</style><linearGradient id="linear-gradient" x1="287.22" y1="-433.18" x2="287.22" y2="678.5" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#1aa59c"/><stop offset="1" stop-color="#005daf"/></linearGradient></defs><title>onboarding_icons</title><path class="cls-1" d="M47.42,479.51V12.17H527V479.52Zm437.13-35.58V380H310.14v64Zm-218,0V380H86.41v64Zm218-95.76v-64H310.14v64Zm-218,0v-64H86.41v64Zm218-96.88v-64H310.14v64Zm-218,0v-64H86.41v64Zm216.87-99.14v-64H309v64Zm-218,0v-64H85.29v64Z"/><path d="M521,18.17V473.52H53.42V18.17H521m-218,140H489.42v-76H303v76m-223.73,0H271.42v-76H79.29v76M304.14,257.3H490.55v-76H304.14v76m-223.73,0H272.55v-76H80.41v76m223.73,96.88H490.55v-76H304.14v76m-223.73,0H272.55v-76H80.41v76m223.73,95.76H490.55V374H304.14v76m-223.73,0H272.55V374H80.41v76M533,6.17H41.42V485.51H533V6.17Zm-218,88H477.42v52H315v-52Zm-223.73,0H259.42v52H91.29v-52Zm224.86,99.14H478.55v52H316.14v-52Zm-223.73,0H260.55v52H92.41v-52Zm223.73,96.88H478.55v52H316.14v-52Zm-223.73,0H260.55v52H92.41v-52ZM316.14,386H478.55v52H316.14V386ZM92.41,386H260.55v52H92.41V386Z"/><path class="cls-2" d="M31.63,284V149.42H542.81V284Zm462.71-34.58V188H79v61.46Z"/><path d="M536.81,155.42V278H37.63V155.42H536.81M73,255.41H500.34V182H73v73.46m475.84-112H25.63V290H548.81V143.42ZM85,194H488.34v49.46H85V194Z"/><polygon class="cls-3" points="530.97 219.5 561.79 188.68 561.79 250.32 530.97 219.5"/><polygon class="cls-3" points="45.08 219.5 14.26 250.32 14.26 188.68 45.08 219.5"/></svg>
|
After Width: | Height: | Size: 1.7 KiB |
After Width: | Height: | Size: 6.3 KiB |
|
@ -0,0 +1 @@
|
|||
<svg id="Capa_1" data-name="Capa 1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 527.44 445.03"><defs><style>.cls-1{fill:#dfdfe3;}.cls-2{fill:#ffa21a;}.cls-3{fill:#a1abb8;}.cls-4{fill:#7d8694;}.cls-5,.cls-8{fill:#fff;}.cls-6{fill:url(#linear-gradient);}.cls-7{fill:#ffce00;}.cls-8{stroke:#000;stroke-linejoin:round;stroke-width:2px;}</style><linearGradient id="linear-gradient" x1="343.48" y1="91.29" x2="132.55" y2="342.07" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#1aa59c"/><stop offset="1" stop-color="#005daf"/></linearGradient></defs><title>onboarding_icons</title><rect class="cls-1" x="15.72" y="14.52" width="448" height="400"/><path class="cls-2" d="M495.72,270.52h16v-32h-16v-32h-144v32h16v32h-16v16a72,72,0,1,0,16,142.19v1.81h144v-32h-16v-32h16v-32h-16v-64Z"/><path class="cls-3" d="M273.34,374.52H55.72v-48h24v-80h-24v-64h24v-80h-24v-48h368v144h-32v-64h16v-16h-32v80H358.59c-7.7-58.8-58-104.38-118.86-104.38a120,120,0,1,0,38.14,233.68,79.84,79.84,0,0,0-4.53,46.7Z"/><path class="cls-4" d="M295.21,301.95A104.51,104.51,0,1,1,310.63,290"/><rect x="71.72" y="70.52" width="16" height="16"/><rect x="391.72" y="70.52" width="16" height="16"/><rect x="71.72" y="342.52" width="16" height="16"/><rect x="231.72" y="278.52" width="16" height="24"/><rect x="231.72" y="126.52" width="16" height="24"/><rect x="151.72" y="206.52" width="24" height="16"/><rect x="303.72" y="206.52" width="24" height="16"/><rect x="285.43" y="256.26" width="15.99" height="24" transform="translate(-103.74 286.17) rotate(-45.02)"/><rect x="177.96" y="148.76" width="15.99" height="24" transform="translate(-59.2 178.64) rotate(-45.02)"/><rect x="173.99" y="260.27" width="24" height="15.99" transform="translate(-135.21 210.04) rotate(-44.99)"/><rect x="281.45" y="152.78" width="24" height="15.99" transform="translate(-27.75 254.54) rotate(-44.99)"/><path d="M287.07,207.88l14.78-15.32-21.12-2.63a48,48,0,1,0,6.34,18Zm-47.34,43.8a37.16,37.16,0,1,1,37.16-37.16A37.16,37.16,0,0,1,239.72,251.68Z"/><path class="cls-5" d="M351.72,310.52a48,48,0,1,0,48,48A48,48,0,0,0,351.72,310.52Zm0,90.19a42.19,42.19,0,1,1,42.19-42.19A42.23,42.23,0,0,1,351.72,400.71Z"/><path d="M503.72,294.52v-16h16v-48h-16v-32h-32V6.52H7.72v416h72v16h72v-16H303.93a79.28,79.28,0,0,0,55.79,15.59v.41h160v-48h-16v-16h16v-48h-16v-32Zm-16-80v16h-128v-16Zm-57.62,160h57.62v16H425A78.74,78.74,0,0,0,430.11,374.52Zm-14.54-64h72.15v16H425A79.93,79.93,0,0,0,415.57,310.52Zm-7.85-16h-8.21a79.43,79.43,0,0,0-39.79-15.59v-.41h128v16Zm-112.51,7.43A104.51,104.51,0,1,1,310.63,290,80.34,80.34,0,0,0,295.21,301.95Zm-21.87,72.57H55.72v-48h24v-80h-24v-64h24v-80h-24v-48h368v144h-32v-64h16v-16h-32v80H358.59c-7.7-58.8-58-104.38-118.86-104.38a120,120,0,1,0,38.14,233.68,79.84,79.84,0,0,0-4.53,46.7Zm-209.62-112v48h-16v-48Zm0-144v48h-16v-48Zm296,128v16H349.45a119.82,119.82,0,0,0,5.71-16Zm-16,27.24v5.17c-1.17.12-2.3.34-3.46.51C341.47,277.58,342.62,275.68,343.72,273.76Zm-264,132.76h-56v-384h432v176h-16v-160h-400v64h-8v80h8v64h-8v80h8v64H278.45a79.93,79.93,0,0,0,9.42,16H79.72Zm208-48a63.7,63.7,0,0,1,17.74-44.1,120.92,120.92,0,0,0,14.78-11.55,63.63,63.63,0,0,1,71.48,5.77v1.88h2.19a64,64,0,1,1-106.19,48Zm216,64H399.51a80.46,80.46,0,0,0,16-16h88.16Zm0-64h-72a79.87,79.87,0,0,0-1.62-16h73.62Zm-128-96v-16h128v16Z"/><circle class="cls-6" cx="239.77" cy="214.59" r="37.5"/><rect class="cls-7" x="47.72" y="118.52" width="16" height="48"/><rect class="cls-7" x="47.72" y="262.52" width="16" height="48"/><path class="cls-8" d="M243.46,226.43a11.58,11.58,0,0,1-2.67.31c-6.38,0-11.81-4.86-12.2-12.07a22.71,22.71,0,0,1,.66-4.09l-9.87-4.2a25.49,25.49,0,0,0-1.56,8.8c0,12.37,10.6,22.39,23,22.39a22.33,22.33,0,0,0,8.3-1.59Z"/><path class="cls-8" d="M240.79,191.6a22.31,22.31,0,0,0-9.32,2l5.56,9.44a11.54,11.54,0,0,1,3.76-.63c5.65,0,11.64,4.74,12.21,10.57l-16.07.07,25,10.61a22.9,22.9,0,0,0,1.74-9.51C263.72,201.82,253.15,191.6,240.79,191.6Z"/></svg>
|
After Width: | Height: | Size: 3.8 KiB |
After Width: | Height: | Size: 5.6 KiB |
After Width: | Height: | Size: 10 KiB |
|
@ -0,0 +1 @@
|
|||
<svg id="Capa_1" data-name="Capa 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 534.28 493.58"><defs><style>.cls-1{fill:#ff2434;}.cls-2{fill:#c32434;}.cls-3{fill:#fff;}.cls-4{fill:#e7e7e7;}.cls-5{fill:#1a1a1a;}</style></defs><title>onboarding_icons</title><g id="Symbols"><g id="icon_scam--warning" data-name="icon scam--warning"><g id="malware"><rect id="Rectangle-path" class="cls-1" x="14.92" y="14.37" width="504.44" height="392.23"/><path id="Shape" class="cls-2" d="M370.09,222.32A102.95,102.95,0,1,0,173.89,266a46.35,46.35,0,0,0,16,69.89v32.63H344.35V335.88a46.35,46.35,0,0,0,16-69.89A102.25,102.25,0,0,0,370.09,222.32ZM235.74,254.24a18,18,0,0,1,0-36l18,18A18,18,0,0,1,235.74,254.24Zm62.8,0a18,18,0,0,1-18-18l18-18a18,18,0,0,1,0,36Z"/><g id="Group"><path id="Shape-2" data-name="Shape" class="cls-3" d="M333.79,270.68a82.36,82.36,0,1,0-133.3,0,25.74,25.74,0,0,0,10,49.45v27.8H323.76v-27.8a25.74,25.74,0,0,0,10-49.45Zm-98-16.44a18,18,0,0,1,0-36l18,18A18,18,0,0,1,235.74,254.24Zm62.8,0a18,18,0,0,1-18-18l18-18a18,18,0,0,1,0,36Z"/><rect id="Rectangle-path-2" data-name="Rectangle-path" class="cls-4" x="14.92" y="14.37" width="504.44" height="61.77"/></g><path id="Shape-3" data-name="Shape" class="cls-3" d="M54.55,182.17H39.11V166.73H54.55Zm0-30.88H39.11V105H54.55Z"/><rect id="Rectangle-path-3" data-name="Rectangle-path" class="cls-2" x="14.92" y="76.14" width="504.44" height="20.59"/><rect id="Rectangle-path-4" data-name="Rectangle-path" class="cls-5" x="38.6" y="38.56" width="18.57" height="18.57"/><rect id="Rectangle-path-5" data-name="Rectangle-path" class="cls-5" x="69.48" y="38.56" width="18.57" height="18.57"/><rect id="Rectangle-path-6" data-name="Rectangle-path" class="cls-5" x="100.37" y="38.56" width="18.57" height="18.57"/><rect id="Rectangle-path-7" data-name="Rectangle-path" class="cls-5" x="131.25" y="38.56" width="364.43" height="18.57"/><path id="Shape-4" data-name="Shape" class="cls-5" d="M344.64,268.23a90.08,90.08,0,1,0-155,0,33.47,33.47,0,0,0,13.16,58.71v28.7H331.48v-28.7a33.47,33.47,0,0,0,13.16-58.71Zm-13.86,9.56a18,18,0,0,1-7,34.62H316v27.8H303.17V320.12H287.73V340.2H274.86V320.12H259.42V340.2H246.55V320.12H231.11V340.2H218.24V312.4h-7.72a18,18,0,0,1-7-34.62l8.95-3.79-5.71-7.86a74.64,74.64,0,1,1,120.81,0L321.83,274Z"/><path id="Shape-5" data-name="Shape" class="cls-5" d="M298.54,210.48h-3.2L272.8,233v3.2a25.74,25.74,0,1,0,25.74-25.74Zm0,36a10.31,10.31,0,0,1-9.91-7.49l12.71-12.71a10.29,10.29,0,0,1-2.81,20.2Z"/><path id="Shape-6" data-name="Shape" class="cls-5" d="M235.74,210.48a25.74,25.74,0,1,0,25.74,25.74V233l-22.54-22.54h-3.2Zm0,36a10.3,10.3,0,0,1-2.81-20.2L245.64,239A10.31,10.31,0,0,1,235.74,246.52Z"/><rect id="Rectangle-path-8" data-name="Rectangle-path" class="cls-5" x="242.43" y="279.46" width="18.57" height="18.57"/><rect id="Rectangle-path-9" data-name="Rectangle-path" class="cls-5" x="276.4" y="279.46" width="18.57" height="18.57"/><rect id="Rectangle-path-10" data-name="Rectangle-path" class="cls-5" x="322.22" y="437.48" width="18.57" height="18.57"/><rect id="Rectangle-path-11" data-name="Rectangle-path" class="cls-5" x="322.22" y="468.37" width="18.57" height="18.57"/><rect id="Rectangle-path-12" data-name="Rectangle-path" class="cls-5" x="322.22" y="406.6" width="18.57" height="18.57"/><rect id="Rectangle-path-13" data-name="Rectangle-path" class="cls-5" x="259.93" y="437.48" width="18.57" height="18.57"/><rect id="Rectangle-path-14" data-name="Rectangle-path" class="cls-5" x="259.93" y="406.6" width="18.57" height="18.57"/><rect id="Rectangle-path-15" data-name="Rectangle-path" class="cls-5" x="259.93" y="375.71" width="18.57" height="18.57"/><path id="Shape-7" data-name="Shape" class="cls-5" d="M7.2,6.65V414.32H178.61V398.88h-156v-315h489v315h-156v15.44H527.08V6.65ZM22.64,68.42V22.09h489V68.42h-489Z"/><rect id="Rectangle-path-16" data-name="Rectangle-path" class="cls-5" x="196.62" y="468.37" width="18.57" height="18.57"/><rect id="Rectangle-path-17" data-name="Rectangle-path" class="cls-5" x="196.62" y="437.48" width="18.57" height="18.57"/><rect id="Rectangle-path-18" data-name="Rectangle-path" class="cls-5" x="196.62" y="406.6" width="18.57" height="18.57"/></g></g></g></svg>
|
After Width: | Height: | Size: 4.1 KiB |
After Width: | Height: | Size: 5.1 KiB |
|
@ -1,24 +1,23 @@
|
|||
@font-face {
|
||||
font-family: 'Lato';
|
||||
src: url('../fonts/Lato-Light.woff') format('woff'),
|
||||
url('../fonts/Lato-Light.ttf') format('truetype');
|
||||
src: url('../fonts/Lato-Light.woff2') format('woff2'),
|
||||
url('../fonts/Lato-Light.woff') format('woff');
|
||||
font-style: normal;
|
||||
font-weight: 300;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Lato';
|
||||
src: url('../fonts/Lato-Regular.woff') format('woff'),
|
||||
url('../fonts/Lato-Regular.ttf') format('truetype');
|
||||
src: url('../fonts/Lato-Regular.woff2') format('woff2'),
|
||||
url('../fonts/Lato-Regular.woff') format('woff');
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Lato';
|
||||
src: url('../fonts/Lato-Bold.woff') format('woff'),
|
||||
url('../fonts/Lato-Bold.ttf') format('truetype');
|
||||
|
||||
src: url('../fonts/Lato-Bold.woff2') format('woff2'),
|
||||
url('../fonts/Lato-Bold.woff') format('woff');
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
}
|
|
@ -1,7 +1,6 @@
|
|||
import React from 'react';
|
||||
import { AddressFieldFactory } from './AddressFieldFactory';
|
||||
import { donationAddressMap } from 'config/data';
|
||||
import { Aux } from 'components/ui';
|
||||
|
||||
interface Props {
|
||||
isReadOnly?: boolean;
|
||||
|
@ -9,8 +8,8 @@ interface Props {
|
|||
|
||||
export const AddressField: React.SFC<Props> = ({ isReadOnly }) => (
|
||||
<AddressFieldFactory
|
||||
withProps={({ currentTo, isValid, onChange, readOnly, errorMsg }) => (
|
||||
<Aux>
|
||||
withProps={({ currentTo, isValid, onChange, readOnly }) => (
|
||||
<React.Fragment>
|
||||
<input
|
||||
className={`form-control ${isValid ? 'is-valid' : 'is-invalid'}`}
|
||||
type="text"
|
||||
|
@ -19,12 +18,7 @@ export const AddressField: React.SFC<Props> = ({ isReadOnly }) => (
|
|||
readOnly={!!(isReadOnly || readOnly)}
|
||||
onChange={onChange}
|
||||
/>
|
||||
{errorMsg && (
|
||||
<div className="has-error">
|
||||
<span className="help-block">{errorMsg}</span>
|
||||
</div>
|
||||
)}
|
||||
</Aux>
|
||||
</React.Fragment>
|
||||
)}
|
||||
/>
|
||||
);
|
||||
|
|
|
@ -18,14 +18,12 @@ export interface CallbackProps {
|
|||
isValid: boolean;
|
||||
readOnly: boolean;
|
||||
currentTo: ICurrentTo;
|
||||
errorMsg?: string | null;
|
||||
onChange(ev: React.FormEvent<HTMLInputElement>): void;
|
||||
}
|
||||
|
||||
type Props = DispatchProps & DispatchProps & OwnProps;
|
||||
type Props = DispatchProps & OwnProps;
|
||||
|
||||
//TODO: add ens resolving
|
||||
class AddressFieldFactoryClass extends React.Component<Props, {}> {
|
||||
class AddressFieldFactoryClass extends React.Component<Props> {
|
||||
public componentDidMount() {
|
||||
// this 'to' parameter can be either token or actual field related
|
||||
const { to } = this.props;
|
||||
|
@ -44,14 +42,17 @@ class AddressFieldFactoryClass extends React.Component<Props, {}> {
|
|||
};
|
||||
}
|
||||
|
||||
const AddressField = connect(null, { setCurrentTo })(AddressFieldFactoryClass);
|
||||
const AddressFieldFactory = connect(null, { setCurrentTo })(AddressFieldFactoryClass);
|
||||
|
||||
interface DefaultAddressFieldProps {
|
||||
withProps(props: CallbackProps): React.ReactElement<any> | null;
|
||||
}
|
||||
|
||||
const DefaultAddressField: React.SFC<DefaultAddressFieldProps> = ({ withProps }) => (
|
||||
<Query params={['to']} withQuery={({ to }) => <AddressField to={to} withProps={withProps} />} />
|
||||
<Query
|
||||
params={['to']}
|
||||
withQuery={({ to }) => <AddressFieldFactory to={to} withProps={withProps} />}
|
||||
/>
|
||||
);
|
||||
|
||||
export { DefaultAddressField as AddressFieldFactory };
|
||||
|
|
|
@ -1,29 +1,51 @@
|
|||
import React, { Component } from 'react';
|
||||
import { Identicon } from 'components/ui';
|
||||
import { Identicon, Spinner } from 'components/ui';
|
||||
import translate from 'translations';
|
||||
//import { EnsAddress } from './components';
|
||||
import { Query } from 'components/renderCbs';
|
||||
import { ICurrentTo, getCurrentTo, isValidCurrentTo } from 'selectors/transaction';
|
||||
import { connect } from 'react-redux';
|
||||
import { AppState } from 'reducers';
|
||||
import { CallbackProps } from 'components/AddressFieldFactory';
|
||||
import { addHexPrefix } from 'ethereumjs-util';
|
||||
import { getResolvingDomain } from 'selectors/ens';
|
||||
import { isValidENSAddress } from 'libs/validators';
|
||||
|
||||
interface StateProps {
|
||||
currentTo: ICurrentTo;
|
||||
isValid: boolean;
|
||||
isResolving: boolean;
|
||||
}
|
||||
|
||||
interface OwnProps {
|
||||
onChange(ev: React.FormEvent<HTMLInputElement>): void;
|
||||
withProps(props: CallbackProps): React.ReactElement<any> | null;
|
||||
}
|
||||
|
||||
const ENSStatus: React.SFC<{ isLoading: boolean; ensAddress: string; rawAddress: string }> = ({
|
||||
isLoading,
|
||||
ensAddress,
|
||||
rawAddress
|
||||
}) => {
|
||||
const isENS = isValidENSAddress(ensAddress);
|
||||
const text = 'Loading ENS address...';
|
||||
if (isLoading) {
|
||||
return (
|
||||
<>
|
||||
<Spinner /> {text}
|
||||
</>
|
||||
);
|
||||
} else {
|
||||
return isENS ? <>{`Resolved Address: ${rawAddress}`}</> : null;
|
||||
}
|
||||
};
|
||||
|
||||
type Props = OwnProps & StateProps;
|
||||
|
||||
//TODO: ENS handling
|
||||
class AddressInputFactoryClass extends Component<Props> {
|
||||
public render() {
|
||||
const { currentTo, onChange, isValid, withProps } = this.props;
|
||||
const { raw } = currentTo;
|
||||
const { currentTo, onChange, isValid, withProps, isResolving } = this.props;
|
||||
const { value } = currentTo;
|
||||
const addr = addHexPrefix(value ? value.toString('hex') : '0');
|
||||
return (
|
||||
<div className="row form-group">
|
||||
<div className="col-xs-11">
|
||||
|
@ -35,15 +57,14 @@ class AddressInputFactoryClass extends Component<Props> {
|
|||
currentTo,
|
||||
isValid,
|
||||
onChange,
|
||||
readOnly: !!readOnly,
|
||||
errorMsg: currentTo.error
|
||||
readOnly: !!readOnly || this.props.isResolving
|
||||
})
|
||||
}
|
||||
/>
|
||||
{/*<EnsAddress ensAddress={ensAddress} />*/}
|
||||
<ENSStatus ensAddress={currentTo.raw} isLoading={isResolving} rawAddress={addr} />
|
||||
</div>
|
||||
<div className="col-xs-1" style={{ padding: 0 }}>
|
||||
<Identicon address={/*ensAddress ||*/ raw} />
|
||||
<Identicon address={addr} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
@ -52,5 +73,6 @@ class AddressInputFactoryClass extends Component<Props> {
|
|||
|
||||
export const AddressInputFactory = connect((state: AppState) => ({
|
||||
currentTo: getCurrentTo(state),
|
||||
isResolving: getResolvingDomain(state),
|
||||
isValid: isValidCurrentTo(state)
|
||||
}))(AddressInputFactoryClass);
|
||||
|
|
|
@ -1,36 +0,0 @@
|
|||
import React from 'react';
|
||||
/*
|
||||
|
||||
public onChange = (e: React.SyntheticEvent<HTMLInputElement>) => {
|
||||
const newValue = (e.target as HTMLInputElement).value;
|
||||
const { onChange } = this.props;
|
||||
if (!onChange) {
|
||||
return;
|
||||
}
|
||||
// FIXME debounce?
|
||||
if (isValidENSAddress(newValue)) {
|
||||
this.props.resolveEnsName(newValue);
|
||||
}
|
||||
onChange(newValue);
|
||||
};
|
||||
}
|
||||
function mapStateToProps(state: AppState, props: PublicProps) {
|
||||
return {
|
||||
ensAddress: getEnsAddress(state, props.value)
|
||||
};
|
||||
}
|
||||
export default connect(mapStateToProps, { resolveEnsName })(AddressField);
|
||||
*/
|
||||
|
||||
interface EnsAddressProps {
|
||||
ensAddress: string | null;
|
||||
}
|
||||
|
||||
export const EnsAddress: React.SFC<EnsAddressProps> = ({ ensAddress }) =>
|
||||
(!!ensAddress && (
|
||||
<p className="ens-response">
|
||||
↳
|
||||
<span className="mono">{ensAddress}</span>
|
||||
</p>
|
||||
)) ||
|
||||
null;
|
|
@ -1 +0,0 @@
|
|||
export * from './EnsAddress';
|
|
@ -1,6 +1,5 @@
|
|||
import React from 'react';
|
||||
import { AmountFieldFactory } from './AmountFieldFactory';
|
||||
import { Aux } from 'components/ui';
|
||||
import { UnitDropDown } from 'components';
|
||||
import translate, { translateRaw } from 'translations';
|
||||
|
||||
|
@ -17,7 +16,7 @@ export const AmountField: React.SFC<Props> = ({
|
|||
}) => (
|
||||
<AmountFieldFactory
|
||||
withProps={({ currentValue: { raw }, isValid, onChange, readOnly }) => (
|
||||
<Aux>
|
||||
<React.Fragment>
|
||||
<label>{translate('SEND_amount')}</label>
|
||||
<div className="input-group">
|
||||
<input
|
||||
|
@ -32,7 +31,7 @@ export const AmountField: React.SFC<Props> = ({
|
|||
/>
|
||||
{hasUnitDropdown && <UnitDropDown showAllTokens={showAllTokens} />}
|
||||
</div>
|
||||
</Aux>
|
||||
</React.Fragment>
|
||||
)}
|
||||
/>
|
||||
);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
@import "common/sass/variables";
|
||||
@import "common/sass/mixins";
|
||||
@import 'common/sass/variables';
|
||||
@import 'common/sass/mixins';
|
||||
|
||||
.AccountInfo {
|
||||
&-section {
|
||||
|
@ -22,18 +22,35 @@
|
|||
&-address {
|
||||
@include clearfix;
|
||||
|
||||
&-section {
|
||||
display: flex;
|
||||
max-width: 100%;
|
||||
flex-direction: row;
|
||||
flex-wrap: nowrap;
|
||||
}
|
||||
|
||||
&-wrapper {
|
||||
max-width: calc(100% - 44px - 24px);
|
||||
}
|
||||
|
||||
&-icon {
|
||||
float: left;
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
margin-right: $space-md;
|
||||
}
|
||||
|
||||
&-addr {
|
||||
width: 100%;
|
||||
word-wrap: break-word;
|
||||
@include mono;
|
||||
}
|
||||
|
||||
&-confirm {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
.Spinner {
|
||||
margin-right: 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&-list {
|
||||
|
@ -53,10 +70,8 @@
|
|||
.account-info {
|
||||
padding-left: 1em;
|
||||
margin: 0;
|
||||
li {
|
||||
}
|
||||
table {
|
||||
font-weight: 200;
|
||||
font-weight: 300;
|
||||
border-bottom: 0;
|
||||
min-width: 200px;
|
||||
td {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { Identicon, UnitDisplay } from 'components/ui';
|
||||
import { NetworkConfig } from 'config/data';
|
||||
import { IWallet, Balance } from 'libs/wallet';
|
||||
import { IWallet, Balance, TrezorWallet, LedgerWallet } from 'libs/wallet';
|
||||
import React from 'react';
|
||||
import translate from 'translations';
|
||||
import './AccountInfo.scss';
|
||||
|
@ -15,11 +15,13 @@ interface Props {
|
|||
interface State {
|
||||
showLongBalance: boolean;
|
||||
address: string;
|
||||
confirmAddr: boolean;
|
||||
}
|
||||
export default class AccountInfo extends React.Component<Props, State> {
|
||||
public state = {
|
||||
showLongBalance: false,
|
||||
address: ''
|
||||
address: '',
|
||||
confirmAddr: false
|
||||
};
|
||||
|
||||
public async setAddressFromWallet() {
|
||||
|
@ -37,6 +39,12 @@ export default class AccountInfo extends React.Component<Props, State> {
|
|||
this.setAddressFromWallet();
|
||||
}
|
||||
|
||||
public toggleConfirmAddr = () => {
|
||||
this.setState(state => {
|
||||
return { confirmAddr: !state.confirmAddr };
|
||||
});
|
||||
};
|
||||
|
||||
public toggleShowLongBalance = (e: React.FormEvent<HTMLSpanElement>) => {
|
||||
e.preventDefault();
|
||||
this.setState(state => {
|
||||
|
@ -48,21 +56,46 @@ export default class AccountInfo extends React.Component<Props, State> {
|
|||
|
||||
public render() {
|
||||
const { network, balance } = this.props;
|
||||
const { address, showLongBalance, confirmAddr } = this.state;
|
||||
const { blockExplorer, tokenExplorer } = network;
|
||||
const { address, showLongBalance } = this.state;
|
||||
|
||||
const wallet = this.props.wallet as LedgerWallet | TrezorWallet;
|
||||
return (
|
||||
<div className="AccountInfo">
|
||||
<div className="AccountInfo-section">
|
||||
<h5 className="AccountInfo-section-header">{translate('sidebar_AccountAddr')}</h5>
|
||||
<div className="AccountInfo-address">
|
||||
<div className="AccountInfo-section AccountInfo-address-section">
|
||||
<div className="AccountInfo-address-icon">
|
||||
<Identicon address={address} size="100%" />
|
||||
</div>
|
||||
<div className="AccountInfo-address-wrapper">
|
||||
<div className="AccountInfo-address-addr">{address}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{typeof wallet.displayAddress === 'function' && (
|
||||
<div className="AccountInfo-section">
|
||||
<a
|
||||
className="AccountInfo-address-hw-addr"
|
||||
onClick={() => {
|
||||
this.toggleConfirmAddr();
|
||||
wallet
|
||||
.displayAddress()
|
||||
.then(() => this.toggleConfirmAddr())
|
||||
.catch(e => {
|
||||
this.toggleConfirmAddr();
|
||||
throw new Error(e);
|
||||
});
|
||||
}}
|
||||
>
|
||||
{confirmAddr ? null : 'Display address on ' + wallet.getWalletType()}
|
||||
</a>
|
||||
{confirmAddr ? (
|
||||
<span className="AccountInfo-address-confirm">
|
||||
<Spinner /> Confirm address on {wallet.getWalletType()}
|
||||
</span>
|
||||
) : null}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="AccountInfo-section">
|
||||
<h5 className="AccountInfo-section-header">{translate('sidebar_AccountBal')}</h5>
|
||||
<ul className="AccountInfo-list">
|
||||
|
@ -93,14 +126,22 @@ export default class AccountInfo extends React.Component<Props, State> {
|
|||
<ul className="AccountInfo-list">
|
||||
{!!blockExplorer && (
|
||||
<li className="AccountInfo-list-item">
|
||||
<a href={blockExplorer.address(address)} target="_blank">
|
||||
<a
|
||||
href={blockExplorer.address(address)}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
{`${network.name} (${blockExplorer.name})`}
|
||||
</a>
|
||||
</li>
|
||||
)}
|
||||
{!!tokenExplorer && (
|
||||
<li className="AccountInfo-list-item">
|
||||
<a href={tokenExplorer.address(address)} target="_blank">
|
||||
<a
|
||||
href={tokenExplorer.address(address)}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
{`Tokens (${tokenExplorer.name})`}
|
||||
</a>
|
||||
</li>
|
||||
|
|
|
@ -40,4 +40,9 @@
|
|||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
&-offline {
|
||||
margin-bottom: 0;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ import { State } from 'reducers/rates';
|
|||
import { rateSymbols, TFetchCCRates } from 'actions/rates';
|
||||
import { TokenBalance } from 'selectors/wallet';
|
||||
import { Balance } from 'libs/wallet';
|
||||
import { NetworkConfig } from 'config/data';
|
||||
import { ETH_DECIMAL, convertTokenBase } from 'libs/units';
|
||||
import Spinner from 'components/ui/Spinner';
|
||||
import UnitDisplay from 'components/ui/UnitDisplay';
|
||||
|
@ -18,6 +19,8 @@ interface Props {
|
|||
rates: State['rates'];
|
||||
ratesError?: State['ratesError'];
|
||||
fetchCCRates: TFetchCCRates;
|
||||
network: NetworkConfig;
|
||||
isOffline: boolean;
|
||||
}
|
||||
|
||||
interface CmpState {
|
||||
|
@ -42,24 +45,30 @@ export default class EquivalentValues extends React.Component<Props, CmpState> {
|
|||
}
|
||||
|
||||
public componentWillReceiveProps(nextProps: Props) {
|
||||
const { balance, tokenBalances } = this.props;
|
||||
if (nextProps.balance !== balance || nextProps.tokenBalances !== tokenBalances) {
|
||||
const { balance, tokenBalances, isOffline } = this.props;
|
||||
if (
|
||||
nextProps.balance !== balance ||
|
||||
nextProps.tokenBalances !== tokenBalances ||
|
||||
nextProps.isOffline !== isOffline
|
||||
) {
|
||||
this.makeBalanceLookup(nextProps);
|
||||
this.fetchRates(nextProps);
|
||||
}
|
||||
}
|
||||
|
||||
public render() {
|
||||
const { balance, tokenBalances, rates, ratesError } = this.props;
|
||||
const { balance, tokenBalances, rates, ratesError, isOffline, network } = this.props;
|
||||
const { currency } = this.state;
|
||||
|
||||
// There are a bunch of reasons why the incorrect balances might be rendered
|
||||
// while we have incomplete data that's being fetched.
|
||||
const isFetching =
|
||||
!balance || balance.isPending || !tokenBalances || Object.keys(rates).length === 0;
|
||||
// Currency exists in rates or the all option is selected
|
||||
const rateExistsOrAll = rates[currency] || currency === ALL_OPTION;
|
||||
|
||||
let valuesEl;
|
||||
if (!isFetching && (rates[currency] || currency === ALL_OPTION)) {
|
||||
if (!isFetching && rateExistsOrAll && !network.isTestnet) {
|
||||
const values = this.getEquivalentValues(currency);
|
||||
valuesEl = rateSymbols.map(key => {
|
||||
if (!values[key] || key === currency) {
|
||||
|
@ -80,6 +89,14 @@ export default class EquivalentValues extends React.Component<Props, CmpState> {
|
|||
</li>
|
||||
);
|
||||
});
|
||||
} else if (network.isTestnet) {
|
||||
valuesEl = (
|
||||
<div className="text-center">
|
||||
<h5 style={{ color: 'red' }}>
|
||||
On test network, equivalent values will not be displayed.
|
||||
</h5>
|
||||
</div>
|
||||
);
|
||||
} else if (ratesError) {
|
||||
valuesEl = <h5>{ratesError}</h5>;
|
||||
} else if (tokenBalances && tokenBalances.length === 0) {
|
||||
|
@ -118,7 +135,13 @@ export default class EquivalentValues extends React.Component<Props, CmpState> {
|
|||
</select>
|
||||
</h5>
|
||||
|
||||
{isOffline ? (
|
||||
<div className="EquivalentValues-offline well well-sm">
|
||||
Equivalent values are unavailable offline
|
||||
</div>
|
||||
) : (
|
||||
<ul className="EquivalentValues-values">{valuesEl}</ul>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -142,8 +165,8 @@ export default class EquivalentValues extends React.Component<Props, CmpState> {
|
|||
}
|
||||
|
||||
private fetchRates(props: Props) {
|
||||
// Duck out if we haven't gotten balances yet
|
||||
if (!props.balance || !props.tokenBalances) {
|
||||
// Duck out if we haven't gotten balances yet, or we're not going to
|
||||
if (!props.balance || !props.tokenBalances || props.isOffline) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -58,6 +58,7 @@ export default class Promos extends React.Component<{}, State> {
|
|||
className="Promos-promo"
|
||||
key={promo.href}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
href={promo.href}
|
||||
style={{ backgroundColor: promo.color }}
|
||||
>
|
||||
|
|
|
@ -41,4 +41,9 @@
|
|||
color: $gray;
|
||||
}
|
||||
}
|
||||
|
||||
&-offline {
|
||||
margin-bottom: 0;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,6 +29,7 @@ interface StateProps {
|
|||
tokensError: AppState['wallet']['tokensError'];
|
||||
isTokensLoading: AppState['wallet']['isTokensLoading'];
|
||||
hasSavedWalletTokens: AppState['wallet']['hasSavedWalletTokens'];
|
||||
isOffline: AppState['config']['offline'];
|
||||
}
|
||||
interface ActionProps {
|
||||
addCustomToken: TAddCustomToken;
|
||||
|
@ -46,13 +47,20 @@ class TokenBalances extends React.Component<Props> {
|
|||
tokenBalances,
|
||||
hasSavedWalletTokens,
|
||||
isTokensLoading,
|
||||
tokensError
|
||||
tokensError,
|
||||
isOffline
|
||||
} = this.props;
|
||||
|
||||
const walletTokens = walletConfig ? walletConfig.tokens : [];
|
||||
|
||||
let content;
|
||||
if (tokensError) {
|
||||
if (isOffline) {
|
||||
content = (
|
||||
<div className="TokenBalances-offline well well-sm">
|
||||
Token balances are unavailable offline
|
||||
</div>
|
||||
);
|
||||
} else if (tokensError) {
|
||||
content = <h5>{tokensError}</h5>;
|
||||
} else if (isTokensLoading) {
|
||||
content = (
|
||||
|
@ -109,7 +117,8 @@ function mapStateToProps(state: AppState): StateProps {
|
|||
tokenBalances: getTokenBalances(state),
|
||||
tokensError: state.wallet.tokensError,
|
||||
isTokensLoading: state.wallet.isTokensLoading,
|
||||
hasSavedWalletTokens: state.wallet.hasSavedWalletTokens
|
||||
hasSavedWalletTokens: state.wallet.hasSavedWalletTokens,
|
||||
isOffline: state.config.offline
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@ interface Props {
|
|||
rates: AppState['rates']['rates'];
|
||||
ratesError: AppState['rates']['ratesError'];
|
||||
fetchCCRates: TFetchCCRates;
|
||||
isOffline: AppState['config']['offline'];
|
||||
}
|
||||
|
||||
interface Block {
|
||||
|
@ -29,7 +30,7 @@ interface Block {
|
|||
|
||||
export class BalanceSidebar extends React.Component<Props, {}> {
|
||||
public render() {
|
||||
const { wallet, balance, network, tokenBalances, rates, ratesError } = this.props;
|
||||
const { wallet, balance, network, tokenBalances, rates, ratesError, isOffline } = this.props;
|
||||
|
||||
if (!wallet) {
|
||||
return null;
|
||||
|
@ -53,11 +54,13 @@ export class BalanceSidebar extends React.Component<Props, {}> {
|
|||
name: 'Equivalent Values',
|
||||
content: (
|
||||
<EquivalentValues
|
||||
network={network}
|
||||
balance={balance}
|
||||
tokenBalances={tokenBalances}
|
||||
rates={rates}
|
||||
ratesError={ratesError}
|
||||
fetchCCRates={this.props.fetchCCRates}
|
||||
isOffline={isOffline}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
@ -82,7 +85,8 @@ function mapStateToProps(state: AppState) {
|
|||
tokenBalances: getShownTokenBalances(state, true),
|
||||
network: getNetworkConfig(state),
|
||||
rates: state.rates.rates,
|
||||
ratesError: state.rates.ratesError
|
||||
ratesError: state.rates.ratesError,
|
||||
isOffline: state.config.offline
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -7,10 +7,12 @@ import React, { Component } from 'react';
|
|||
import { connect } from 'react-redux';
|
||||
import { AppState } from 'reducers';
|
||||
import { getDecimal, getUnit } from 'selectors/transaction';
|
||||
import { getNetworkConfig } from 'selectors/config';
|
||||
|
||||
interface StateProps {
|
||||
unit: string;
|
||||
decimal: number;
|
||||
network: AppState['config']['network'];
|
||||
}
|
||||
|
||||
class AmountClass extends Component<StateProps> {
|
||||
|
@ -20,14 +22,16 @@ class AmountClass extends Component<StateProps> {
|
|||
withSerializedTransaction={serializedTransaction => {
|
||||
const transactionInstance = makeTransaction(serializedTransaction);
|
||||
const { value, data } = getTransactionFields(transactionInstance);
|
||||
const { decimal, unit } = this.props;
|
||||
const { decimal, unit, network } = this.props;
|
||||
const isToken = unit !== 'ether';
|
||||
const handledValue = isToken
|
||||
? TokenValue(ERC20.transfer.decodeInput(data)._value)
|
||||
: Wei(value);
|
||||
return (
|
||||
<UnitDisplay
|
||||
decimal={decimal}
|
||||
value={
|
||||
unit === 'ether' ? Wei(value) : TokenValue(ERC20.transfer.decodeInput(data)._value)
|
||||
}
|
||||
symbol={unit}
|
||||
value={handledValue}
|
||||
symbol={isToken ? unit : network.unit}
|
||||
checkOffline={false}
|
||||
/>
|
||||
);
|
||||
|
@ -39,5 +43,6 @@ class AmountClass extends Component<StateProps> {
|
|||
|
||||
export const Amount = connect((state: AppState) => ({
|
||||
decimal: getDecimal(state),
|
||||
unit: getUnit(state)
|
||||
unit: getUnit(state),
|
||||
network: getNetworkConfig(state)
|
||||
}))(AmountClass);
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import { getTransactionFields, makeTransaction } from 'libs/transaction';
|
||||
import { SerializedTransaction } from 'components/renderCbs';
|
||||
import { Aux } from 'components/ui';
|
||||
import ERC20 from 'libs/erc20';
|
||||
import { From } from '../From';
|
||||
import React, { Component } from 'react';
|
||||
|
@ -21,7 +20,7 @@ class AddressesClass extends Component<StateProps> {
|
|||
const { to, data } = getTransactionFields(transactionInstance);
|
||||
|
||||
return (
|
||||
<Aux>
|
||||
<React.Fragment>
|
||||
<li className="ConfModal-details-detail">
|
||||
You are sending from <From withFrom={from => <code>{from}</code>} />
|
||||
</li>
|
||||
|
@ -32,7 +31,7 @@ class AddressesClass extends Component<StateProps> {
|
|||
{this.props.unit === 'ether' ? to : ERC20.transfer.decodeInput(data)._to}
|
||||
</code>
|
||||
</li>
|
||||
</Aux>
|
||||
</React.Fragment>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
|
|
|
@ -1,22 +1,13 @@
|
|||
import { DataFieldFactory } from './DataFieldFactory';
|
||||
import React from 'react';
|
||||
import { Expandable, ExpandHandler } from 'components/ui';
|
||||
import translate from 'translations';
|
||||
import { donationAddressMap } from 'config/data';
|
||||
|
||||
const expander = (expandHandler: ExpandHandler) => (
|
||||
<a onClick={expandHandler}>
|
||||
<p className="strong">{translate('TRANS_advanced')}</p>
|
||||
</a>
|
||||
);
|
||||
|
||||
export const DataField: React.SFC<{}> = () => (
|
||||
<DataFieldFactory
|
||||
withProps={({ data: { raw }, dataExists, onChange, readOnly }) => (
|
||||
<Expandable expandLabel={expander}>
|
||||
<div className="form-group">
|
||||
<label>{translate('TRANS_data')}</label>
|
||||
|
||||
<>
|
||||
<label>{translate('OFFLINE_Step2_Label_6')}</label>
|
||||
<input
|
||||
className={`form-control ${dataExists ? 'is-valid' : 'is-invalid'}`}
|
||||
type="text"
|
||||
|
@ -25,8 +16,7 @@ export const DataField: React.SFC<{}> = () => (
|
|||
readOnly={!!readOnly}
|
||||
onChange={onChange}
|
||||
/>
|
||||
</div>
|
||||
</Expandable>
|
||||
</>
|
||||
)}
|
||||
/>
|
||||
);
|
||||
|
|
|
@ -19,7 +19,7 @@ const ErrorScreen: React.SFC<Props> = ({ error }) => {
|
|||
Please contact{' '}
|
||||
<a
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
rel="noopener noreferrer"
|
||||
href={`mailto:support@myetherwallet.com?Subject=${SUBJECT}&body=${DESCRIPTION}`}
|
||||
>
|
||||
support@myetherwallet.com
|
||||
|
|
|
@ -14,7 +14,12 @@ const TransactionSucceeded = ({ txHash, blockExplorer }: TransactionSucceededPro
|
|||
return (
|
||||
<div>
|
||||
<p>{translateRaw('SUCCESS_3') + txHash}</p>
|
||||
<a className="btn btn-xs btn-info string" href={txHashLink} target="_blank" rel="noopener">
|
||||
<a
|
||||
className="btn btn-xs btn-info string"
|
||||
href={txHashLink}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
Verify Transaction
|
||||
</a>
|
||||
</div>
|
||||
|
|
|
@ -142,13 +142,6 @@
|
|||
font-size: $font-size-small;
|
||||
margin: $space-sm 0;
|
||||
}
|
||||
|
||||
@media screen and (max-width: $grid-float-breakpoint) {
|
||||
.row {
|
||||
margin-left: -0.5rem;
|
||||
margin-right: -0.5rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.Modal {
|
||||
|
|
|
@ -14,6 +14,7 @@ import './index.scss';
|
|||
import PreFooter from './PreFooter';
|
||||
import Modal, { IButton } from 'components/ui/Modal';
|
||||
import { NewTabLink } from 'components/ui';
|
||||
import OnboardModal from 'containers/OnboardModal';
|
||||
|
||||
const AffiliateTag = ({ link, text }: Link) => {
|
||||
return (
|
||||
|
@ -125,6 +126,7 @@ export default class Footer extends React.Component<Props, State> {
|
|||
const buttons: IButton[] = [{ text: 'Okay', type: 'default', onClick: this.closeModal }];
|
||||
return (
|
||||
<div>
|
||||
<OnboardModal />
|
||||
<PreFooter />
|
||||
<footer className="Footer" role="contentinfo" aria-label="footer">
|
||||
<div className="Footer-about">
|
||||
|
|
|
@ -1,21 +1,45 @@
|
|||
import React from 'react';
|
||||
import { GasLimitFieldFactory } from './GasLimitFieldFactory';
|
||||
import translate from 'translations';
|
||||
import { Aux } from 'components/ui';
|
||||
import { CSSTransition } from 'react-transition-group';
|
||||
import { Spinner } from 'components/ui';
|
||||
|
||||
interface Props {
|
||||
includeLabel: boolean;
|
||||
onlyIncludeLoader: boolean;
|
||||
}
|
||||
|
||||
export const GaslimitLoading: React.SFC<{ gasEstimationPending: boolean }> = ({
|
||||
gasEstimationPending
|
||||
}) => (
|
||||
<CSSTransition in={gasEstimationPending} timeout={300} classNames="fade">
|
||||
<div className={`SimpleGas-estimating small ${gasEstimationPending ? 'active' : ''}`}>
|
||||
Calculating gas limit
|
||||
<Spinner />
|
||||
</div>
|
||||
</CSSTransition>
|
||||
);
|
||||
|
||||
export const GasLimitField: React.SFC<Props> = ({ includeLabel, onlyIncludeLoader }) => (
|
||||
<React.Fragment>
|
||||
{includeLabel ? <label>{translate('TRANS_gas')} </label> : null}
|
||||
|
||||
export const GasLimitField: React.SFC<{}> = () => (
|
||||
<Aux>
|
||||
<label>{translate('TRANS_gas')} </label>
|
||||
<GasLimitFieldFactory
|
||||
withProps={({ gasLimit: { raw, value }, onChange, readOnly }) => (
|
||||
withProps={({ gasLimit: { raw, value }, onChange, readOnly, gasEstimationPending }) => (
|
||||
<>
|
||||
<GaslimitLoading gasEstimationPending={gasEstimationPending} />
|
||||
{onlyIncludeLoader ? null : (
|
||||
<input
|
||||
className={`form-control ${!!value ? 'is-valid' : 'is-invalid'}`}
|
||||
type="text"
|
||||
type="number"
|
||||
placeholder="e.g. 21000"
|
||||
readOnly={!!readOnly}
|
||||
value={raw}
|
||||
onChange={onChange}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
/>
|
||||
</Aux>
|
||||
</React.Fragment>
|
||||
);
|
||||
|
|
|
@ -10,6 +10,7 @@ const defaultGasLimit = '21000';
|
|||
export interface CallBackProps {
|
||||
readOnly: boolean;
|
||||
gasLimit: AppState['transaction']['fields']['gasLimit'];
|
||||
gasEstimationPending: boolean;
|
||||
onChange(value: React.FormEvent<HTMLInputElement>): void;
|
||||
}
|
||||
|
||||
|
|
|
@ -2,11 +2,14 @@ import React, { Component } from 'react';
|
|||
import { Query } from 'components/renderCbs';
|
||||
import { connect } from 'react-redux';
|
||||
import { AppState } from 'reducers';
|
||||
import { getGasLimit } from 'selectors/transaction';
|
||||
import { getGasLimit, getGasEstimationPending } from 'selectors/transaction';
|
||||
import { CallBackProps } from 'components/GasLimitFieldFactory';
|
||||
import { getAutoGasLimitEnabled } from 'selectors/config';
|
||||
|
||||
interface StateProps {
|
||||
gasLimit: AppState['transaction']['fields']['gasLimit'];
|
||||
gasEstimationPending: boolean;
|
||||
autoGasLimitEnabled: boolean;
|
||||
}
|
||||
|
||||
interface OwnProps {
|
||||
|
@ -17,18 +20,24 @@ interface OwnProps {
|
|||
type Props = StateProps & OwnProps;
|
||||
class GasLimitInputClass extends Component<Props> {
|
||||
public render() {
|
||||
const { gasLimit, onChange } = this.props;
|
||||
const { gasLimit, onChange, gasEstimationPending, autoGasLimitEnabled } = this.props;
|
||||
return (
|
||||
<Query
|
||||
params={['readOnly']}
|
||||
withQuery={({ readOnly }) =>
|
||||
this.props.withProps({ gasLimit, onChange, readOnly: !!readOnly })
|
||||
this.props.withProps({
|
||||
gasLimit,
|
||||
onChange,
|
||||
readOnly: !!(readOnly || autoGasLimitEnabled),
|
||||
gasEstimationPending
|
||||
})
|
||||
}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export const GasLimitInput = connect((state: AppState) => ({ gasLimit: getGasLimit(state) }))(
|
||||
GasLimitInputClass
|
||||
);
|
||||
export const GasLimitInput = connect((state: AppState) => ({
|
||||
gasLimit: getGasLimit(state),
|
||||
gasEstimationPending: getGasEstimationPending(state),
|
||||
autoGasLimitEnabled: getAutoGasLimitEnabled(state)
|
||||
}))(GasLimitInputClass);
|
||||
|
|
|
@ -1,36 +1,32 @@
|
|||
import React from 'react';
|
||||
import { translateRaw } from 'translations';
|
||||
import { connect } from 'react-redux';
|
||||
import {
|
||||
inputGasPrice,
|
||||
TInputGasPrice,
|
||||
inputGasLimit,
|
||||
TInputGasLimit,
|
||||
inputNonce,
|
||||
TInputNonce
|
||||
} from 'actions/transaction';
|
||||
import { inputGasPrice, TInputGasPrice } from 'actions/transaction';
|
||||
import { fetchCCRates, TFetchCCRates } from 'actions/rates';
|
||||
import { getNetworkConfig } from 'selectors/config';
|
||||
import { getNetworkConfig, getOffline } from 'selectors/config';
|
||||
import { AppState } from 'reducers';
|
||||
import SimpleGas from './components/SimpleGas';
|
||||
import AdvancedGas from './components/AdvancedGas';
|
||||
import './GasSlider.scss';
|
||||
import { getGasPrice } from 'selectors/transaction';
|
||||
|
||||
interface Props {
|
||||
// Component configuration
|
||||
disableAdvanced?: boolean;
|
||||
// Data
|
||||
interface StateProps {
|
||||
gasPrice: AppState['transaction']['fields']['gasPrice'];
|
||||
gasLimit: AppState['transaction']['fields']['gasLimit'];
|
||||
offline: AppState['config']['offline'];
|
||||
network: AppState['config']['network'];
|
||||
// Actions
|
||||
}
|
||||
|
||||
interface DispatchProps {
|
||||
inputGasPrice: TInputGasPrice;
|
||||
inputGasLimit: TInputGasLimit;
|
||||
inputNonce: TInputNonce;
|
||||
fetchCCRates: TFetchCCRates;
|
||||
}
|
||||
|
||||
interface OwnProps {
|
||||
disableAdvanced?: boolean;
|
||||
}
|
||||
|
||||
type Props = DispatchProps & OwnProps & StateProps;
|
||||
|
||||
interface State {
|
||||
showAdvanced: boolean;
|
||||
}
|
||||
|
@ -41,24 +37,27 @@ class GasSlider extends React.Component<Props, State> {
|
|||
};
|
||||
|
||||
public componentDidMount() {
|
||||
if (!this.props.offline) {
|
||||
this.props.fetchCCRates([this.props.network.unit]);
|
||||
}
|
||||
}
|
||||
|
||||
public componentWillReceiveProps(nextProps: Props) {
|
||||
if (this.props.offline && !nextProps.offline) {
|
||||
this.props.fetchCCRates([this.props.network.unit]);
|
||||
}
|
||||
}
|
||||
|
||||
public render() {
|
||||
const { gasPrice, gasLimit, offline, disableAdvanced } = this.props;
|
||||
const { offline, disableAdvanced, gasPrice } = this.props;
|
||||
const showAdvanced = (this.state.showAdvanced || offline) && !disableAdvanced;
|
||||
|
||||
return (
|
||||
<div className="GasSlider">
|
||||
{showAdvanced ? (
|
||||
<AdvancedGas
|
||||
gasPrice={gasPrice.raw}
|
||||
gasLimit={gasLimit.raw}
|
||||
changeGasPrice={this.props.inputGasPrice}
|
||||
changeGasLimit={this.props.inputGasLimit}
|
||||
/>
|
||||
<AdvancedGas gasPrice={gasPrice} inputGasPrice={this.props.inputGasPrice} />
|
||||
) : (
|
||||
<SimpleGas gasPrice={gasPrice.raw} changeGasPrice={this.props.inputGasPrice} />
|
||||
<SimpleGas gasPrice={gasPrice} inputGasPrice={this.props.inputGasPrice} />
|
||||
)}
|
||||
|
||||
{!offline &&
|
||||
|
@ -68,7 +67,7 @@ class GasSlider extends React.Component<Props, State> {
|
|||
<strong>
|
||||
{showAdvanced
|
||||
? `- ${translateRaw('Back to simple')}`
|
||||
: `+ ${translateRaw('Advanced: Data, Gas Price, Gas Limit')}`}
|
||||
: `+ ${translateRaw('Advanced Settings')}`}
|
||||
</strong>
|
||||
</a>
|
||||
</div>
|
||||
|
@ -82,18 +81,15 @@ class GasSlider extends React.Component<Props, State> {
|
|||
};
|
||||
}
|
||||
|
||||
function mapStateToProps(state: AppState) {
|
||||
function mapStateToProps(state: AppState): StateProps {
|
||||
return {
|
||||
gasPrice: state.transaction.fields.gasPrice,
|
||||
gasLimit: state.transaction.fields.gasLimit,
|
||||
offline: state.config.offline,
|
||||
gasPrice: getGasPrice(state),
|
||||
offline: getOffline(state),
|
||||
network: getNetworkConfig(state)
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps, {
|
||||
inputGasPrice,
|
||||
inputGasLimit,
|
||||
inputNonce,
|
||||
fetchCCRates
|
||||
})(GasSlider);
|
||||
|
|
|
@ -1,4 +1,31 @@
|
|||
.AdvancedGas {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
.checkbox {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: fit-content;
|
||||
input[type='checkbox'] {
|
||||
position: initial;
|
||||
margin: 0;
|
||||
margin-right: 8px;
|
||||
}
|
||||
span {
|
||||
font-size: 1rem;
|
||||
font-weight: 400;
|
||||
}
|
||||
}
|
||||
|
||||
&-gasLimit {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: baseline;
|
||||
.flex-spacer {
|
||||
flex-grow: 2;
|
||||
}
|
||||
input {
|
||||
width: 100%;
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,52 +1,69 @@
|
|||
import React from 'react';
|
||||
import classnames from 'classnames';
|
||||
import translate from 'translations';
|
||||
import { DataFieldFactory } from 'components/DataFieldFactory';
|
||||
import FeeSummary from './FeeSummary';
|
||||
import './AdvancedGas.scss';
|
||||
import { TToggleAutoGasLimit, toggleAutoGasLimit } from 'actions/config';
|
||||
import { AppState } from 'reducers';
|
||||
import { TInputGasPrice } from 'actions/transaction';
|
||||
import { NonceField, GasLimitField, DataField } from 'components';
|
||||
import { connect } from 'react-redux';
|
||||
import { getAutoGasLimitEnabled } from 'selectors/config';
|
||||
|
||||
interface Props {
|
||||
gasPrice: string;
|
||||
gasLimit: string;
|
||||
changeGasPrice(gwei: string): void;
|
||||
changeGasLimit(wei: string): void;
|
||||
interface OwnProps {
|
||||
inputGasPrice: TInputGasPrice;
|
||||
gasPrice: AppState['transaction']['fields']['gasPrice'];
|
||||
}
|
||||
|
||||
export default class AdvancedGas extends React.Component<Props> {
|
||||
interface StateProps {
|
||||
autoGasLimitEnabled: AppState['config']['autoGasLimit'];
|
||||
}
|
||||
|
||||
interface DispatchProps {
|
||||
toggleAutoGasLimit: TToggleAutoGasLimit;
|
||||
}
|
||||
|
||||
type Props = OwnProps & StateProps & DispatchProps;
|
||||
|
||||
class AdvancedGas extends React.Component<Props> {
|
||||
public render() {
|
||||
const { autoGasLimitEnabled, gasPrice } = this.props;
|
||||
return (
|
||||
<div className="AdvancedGas row form-group">
|
||||
<div className="col-md-3 col-sm-6 col-xs-12">
|
||||
<div className="col-md-12">
|
||||
<label className="checkbox">
|
||||
<input
|
||||
type="checkbox"
|
||||
defaultChecked={autoGasLimitEnabled}
|
||||
onChange={this.handleToggleAutoGasLimit}
|
||||
/>
|
||||
<span>Automatically Calculate Gas Limit</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div className="col-md-4 col-sm-6 col-xs-12">
|
||||
<label>{translate('OFFLINE_Step2_Label_3')} (gwei)</label>
|
||||
<input
|
||||
className="form-control"
|
||||
className={classnames('form-control', { 'is-invalid': !gasPrice.value })}
|
||||
type="number"
|
||||
value={this.props.gasPrice}
|
||||
placeholder="e.g. 40"
|
||||
value={gasPrice.raw}
|
||||
onChange={this.handleGasPriceChange}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="col-md-3 col-sm-6 col-xs-12">
|
||||
<div className="col-md-4 col-sm-6 col-xs-12 AdvancedGas-gasLimit">
|
||||
<label>{translate('OFFLINE_Step2_Label_4')}</label>
|
||||
<input
|
||||
className="form-control"
|
||||
type="number"
|
||||
value={this.props.gasLimit}
|
||||
onChange={this.handleGasLimitChange}
|
||||
/>
|
||||
<div className="SimpleGas-flex-spacer" />
|
||||
<GasLimitField includeLabel={false} onlyIncludeLoader={false} />
|
||||
</div>
|
||||
|
||||
<div className="col-md-6 col-sm-12">
|
||||
<label>{translate('OFFLINE_Step2_Label_6')}</label>
|
||||
<DataFieldFactory
|
||||
withProps={({ data, onChange }) => (
|
||||
<input
|
||||
className="form-control"
|
||||
value={data.raw}
|
||||
onChange={onChange}
|
||||
placeholder="0x7cB57B5A..."
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
<div className="col-md-4 col-sm-12">
|
||||
<NonceField alwaysDisplay={true} />
|
||||
</div>
|
||||
|
||||
<div className="col-md-12">
|
||||
<DataField />
|
||||
</div>
|
||||
|
||||
<div className="col-sm-12">
|
||||
|
@ -63,10 +80,15 @@ export default class AdvancedGas extends React.Component<Props> {
|
|||
}
|
||||
|
||||
private handleGasPriceChange = (ev: React.FormEvent<HTMLInputElement>) => {
|
||||
this.props.changeGasPrice(ev.currentTarget.value);
|
||||
this.props.inputGasPrice(ev.currentTarget.value);
|
||||
};
|
||||
|
||||
private handleGasLimitChange = (ev: React.FormEvent<HTMLInputElement>) => {
|
||||
this.props.changeGasLimit(ev.currentTarget.value);
|
||||
private handleToggleAutoGasLimit = (_: React.FormEvent<HTMLInputElement>) => {
|
||||
this.props.toggleAutoGasLimit();
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(
|
||||
(state: AppState) => ({ autoGasLimitEnabled: getAutoGasLimitEnabled(state) }),
|
||||
{ toggleAutoGasLimit }
|
||||
)(AdvancedGas);
|
||||
|
|
|
@ -20,13 +20,14 @@ interface Props {
|
|||
gasLimit: AppState['transaction']['fields']['gasLimit'];
|
||||
rates: AppState['rates']['rates'];
|
||||
network: AppState['config']['network'];
|
||||
isOffline: AppState['config']['offline'];
|
||||
// Component props
|
||||
render(data: RenderData): React.ReactElement<string> | string;
|
||||
}
|
||||
|
||||
class FeeSummary extends React.Component<Props> {
|
||||
public render() {
|
||||
const { gasPrice, gasLimit, rates, network } = this.props;
|
||||
const { gasPrice, gasLimit, rates, network, isOffline } = this.props;
|
||||
|
||||
const feeBig = gasPrice.value && gasLimit.value && gasPrice.value.mul(gasLimit.value);
|
||||
const fee = (
|
||||
|
@ -42,7 +43,7 @@ class FeeSummary extends React.Component<Props> {
|
|||
const usdBig = network.isTestnet
|
||||
? new BN(0)
|
||||
: feeBig && rates[network.unit] && feeBig.muln(rates[network.unit].USD);
|
||||
const usd = (
|
||||
const usd = isOffline ? null : (
|
||||
<UnitDisplay
|
||||
value={usdBig}
|
||||
unit="ether"
|
||||
|
@ -71,7 +72,8 @@ function mapStateToProps(state: AppState) {
|
|||
gasPrice: state.transaction.fields.gasPrice,
|
||||
gasLimit: state.transaction.fields.gasLimit,
|
||||
rates: state.rates.rates,
|
||||
network: getNetworkConfig(state)
|
||||
network: getNetworkConfig(state),
|
||||
isOffline: state.config.offline
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -4,8 +4,24 @@
|
|||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
|
||||
&-label {
|
||||
display: block;
|
||||
&-flex-spacer {
|
||||
flex-grow: 2;
|
||||
}
|
||||
&-title {
|
||||
display: flex;
|
||||
}
|
||||
&-estimating {
|
||||
color: rgba(51, 51, 51, 0.7);
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
font-weight: 400;
|
||||
opacity: 0;
|
||||
&.active {
|
||||
opacity: 1;
|
||||
}
|
||||
.Spinner {
|
||||
margin-left: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
&-slider {
|
||||
|
@ -34,3 +50,26 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
.fade {
|
||||
&-enter,
|
||||
&-exit {
|
||||
transition: opacity 300ms;
|
||||
}
|
||||
|
||||
&-enter {
|
||||
opacity: 0;
|
||||
|
||||
&-active {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
&-exit {
|
||||
opacity: 1;
|
||||
|
||||
&-active {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,30 +3,55 @@ import Slider from 'rc-slider';
|
|||
import translate from 'translations';
|
||||
import { gasPriceDefaults } from 'config/data';
|
||||
import FeeSummary from './FeeSummary';
|
||||
import { TInputGasPrice } from 'actions/transaction';
|
||||
import './SimpleGas.scss';
|
||||
import { AppState } from 'reducers';
|
||||
import { getGasLimitEstimationTimedOut } from 'selectors/transaction';
|
||||
import { connect } from 'react-redux';
|
||||
import { GasLimitField } from 'components/GasLimitField';
|
||||
import { getIsWeb3Node } from 'selectors/config';
|
||||
|
||||
interface Props {
|
||||
gasPrice: string;
|
||||
changeGasPrice(gwei: string): void;
|
||||
interface OwnProps {
|
||||
gasPrice: AppState['transaction']['fields']['gasPrice'];
|
||||
inputGasPrice: TInputGasPrice;
|
||||
}
|
||||
|
||||
export default class SimpleGas extends React.Component<Props> {
|
||||
interface StateProps {
|
||||
isWeb3Node: boolean;
|
||||
gasLimitEstimationTimedOut: boolean;
|
||||
}
|
||||
|
||||
type Props = OwnProps & StateProps;
|
||||
|
||||
class SimpleGas extends React.Component<Props> {
|
||||
public render() {
|
||||
const { gasPrice } = this.props;
|
||||
const { gasPrice, gasLimitEstimationTimedOut, isWeb3Node } = this.props;
|
||||
|
||||
return (
|
||||
<div className="SimpleGas row form-group">
|
||||
<div className="col-md-12">
|
||||
<div className="col-md-12 SimpleGas-title">
|
||||
<label className="SimpleGas-label">{translate('Transaction Fee')}</label>
|
||||
<div className="SimpleGas-flex-spacer" />
|
||||
<GasLimitField includeLabel={false} onlyIncludeLoader={true} />
|
||||
</div>
|
||||
|
||||
{gasLimitEstimationTimedOut && (
|
||||
<div className="col-md-12 prompt-toggle-gas-limit">
|
||||
<p className="small">
|
||||
{isWeb3Node
|
||||
? "Couldn't calculate gas limit, if you know what your doing, try setting manually in Advanced settings"
|
||||
: "Couldn't calculate gas limit, try switching nodes"}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="col-md-8 col-sm-12">
|
||||
<div className="SimpleGas-slider">
|
||||
<Slider
|
||||
onChange={this.handleSlider}
|
||||
min={gasPriceDefaults.gasPriceMinGwei}
|
||||
max={gasPriceDefaults.gasPriceMaxGwei}
|
||||
value={parseFloat(gasPrice)}
|
||||
value={parseFloat(gasPrice.raw)}
|
||||
/>
|
||||
<div className="SimpleGas-slider-labels">
|
||||
<span>{translate('Cheap')}</span>
|
||||
|
@ -49,6 +74,10 @@ export default class SimpleGas extends React.Component<Props> {
|
|||
}
|
||||
|
||||
private handleSlider = (gasGwei: number) => {
|
||||
this.props.changeGasPrice(gasGwei.toString());
|
||||
this.props.inputGasPrice(gasGwei.toString());
|
||||
};
|
||||
}
|
||||
export default connect((state: AppState) => ({
|
||||
gasLimitEstimationTimedOut: getGasLimitEstimationTimedOut(state),
|
||||
isWeb3Node: getIsWeb3Node(state)
|
||||
}))(SimpleGas);
|
||||
|
|
|
@ -4,7 +4,7 @@ import EthTx from 'ethereumjs-tx';
|
|||
import React, { Component } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { AppState } from 'reducers';
|
||||
import { getTransaction, isNetworkRequestPending } from 'selectors/transaction';
|
||||
import { getTransaction, isNetworkRequestPending, isValidAmount } from 'selectors/transaction';
|
||||
import { getWalletType } from 'selectors/wallet';
|
||||
|
||||
interface StateProps {
|
||||
|
@ -12,17 +12,24 @@ interface StateProps {
|
|||
networkRequestPending: boolean;
|
||||
isFullTransaction: boolean;
|
||||
isWeb3Wallet: boolean;
|
||||
validAmount: boolean;
|
||||
}
|
||||
|
||||
class GenerateTransactionClass extends Component<StateProps> {
|
||||
public render() {
|
||||
const { isFullTransaction, isWeb3Wallet, transaction, networkRequestPending } = this.props;
|
||||
const {
|
||||
isFullTransaction,
|
||||
isWeb3Wallet,
|
||||
transaction,
|
||||
networkRequestPending,
|
||||
validAmount
|
||||
} = this.props;
|
||||
return (
|
||||
<WithSigner
|
||||
isWeb3={isWeb3Wallet}
|
||||
withSigner={signer => (
|
||||
<button
|
||||
disabled={!isFullTransaction || networkRequestPending}
|
||||
disabled={!isFullTransaction || networkRequestPending || !validAmount}
|
||||
className="btn btn-info btn-block"
|
||||
onClick={signer(transaction)}
|
||||
>
|
||||
|
@ -37,5 +44,6 @@ class GenerateTransactionClass extends Component<StateProps> {
|
|||
export const GenerateTransaction = connect((state: AppState) => ({
|
||||
...getTransaction(state),
|
||||
networkRequestPending: isNetworkRequestPending(state),
|
||||
isWeb3Wallet: getWalletType(state).isWeb3Wallet
|
||||
isWeb3Wallet: getWalletType(state).isWeb3Wallet,
|
||||
validAmount: isValidAmount(state)
|
||||
}))(GenerateTransactionClass);
|
||||
|
|
|
@ -61,7 +61,11 @@ export default class GasPriceDropdown extends Component<Props> {
|
|||
<code>21 GWEI</code>.
|
||||
</p>
|
||||
<p>
|
||||
<a href={`${knowledgeBaseURL}/gas/what-is-gas-ethereum`} target="_blank">
|
||||
<a
|
||||
href={`${knowledgeBaseURL}/gas/what-is-gas-ethereum`}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
Read more
|
||||
</a>
|
||||
</p>
|
||||
|
|
|
@ -35,7 +35,13 @@ class NavigationLink extends React.Component<Props, {}> {
|
|||
|
||||
const linkEl =
|
||||
link.external || !link.to ? (
|
||||
<a className={linkClasses} href={link.to} aria-label={linkLabel} target="_blank">
|
||||
<a
|
||||
className={linkClasses}
|
||||
href={link.to}
|
||||
aria-label={linkLabel}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
{translate(link.name)}
|
||||
</a>
|
||||
) : (
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
@import 'common/sass/variables';
|
||||
@import 'common/sass/mixins';
|
||||
|
||||
@keyframes online-pulse {
|
||||
0%, 100% {
|
||||
opacity: 0.8;
|
||||
}
|
||||
50% {
|
||||
opacity: 0.7;
|
||||
}
|
||||
}
|
||||
|
||||
.OnlineStatus {
|
||||
position: relative;
|
||||
top: -2px;
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
text-align: center;
|
||||
transition: $transition;
|
||||
border-radius: 100%;
|
||||
transition: background-color 300ms ease;
|
||||
@include show-tooltip-on-hover;
|
||||
|
||||
&.is-online {
|
||||
background: lighten($brand-success, 10%);
|
||||
}
|
||||
|
||||
&.is-offline {
|
||||
background: lighten($brand-danger, 5%);
|
||||
}
|
||||
|
||||
&.is-connecting {
|
||||
background: #FFF;
|
||||
animation: online-pulse 800ms ease infinite;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
import React from 'react';
|
||||
import { Tooltip } from 'components/ui';
|
||||
import './OnlineStatus.scss';
|
||||
|
||||
interface Props {
|
||||
isOffline: boolean;
|
||||
}
|
||||
|
||||
const OnlineStatus: React.SFC<Props> = ({ isOffline }) => (
|
||||
<div className={`OnlineStatus fa-stack ${isOffline ? 'is-offline' : 'is-online'}`}>
|
||||
<Tooltip>{isOffline ? 'Offline' : 'Online'}</Tooltip>
|
||||
</div>
|
||||
);
|
||||
|
||||
export default OnlineStatus;
|
|
@ -1,5 +1,5 @@
|
|||
@import "common/sass/variables";
|
||||
@import "common/sass/mixins";
|
||||
@import 'common/sass/variables';
|
||||
@import 'common/sass/mixins';
|
||||
|
||||
$small-size: 900px;
|
||||
|
||||
|
@ -16,7 +16,8 @@ $small-size: 900px;
|
|||
}
|
||||
|
||||
@keyframes dropdown-is-flashing {
|
||||
0%, 100% {
|
||||
0%,
|
||||
100% {
|
||||
opacity: 0.8;
|
||||
}
|
||||
50% {
|
||||
|
@ -109,7 +110,7 @@ $small-size: 900px;
|
|||
|
||||
&-right {
|
||||
font-size: 18px;
|
||||
font-weight: 200;
|
||||
font-weight: 300;
|
||||
color: white;
|
||||
flex: 1 auto;
|
||||
text-align: right;
|
||||
|
@ -130,12 +131,16 @@ $small-size: 900px;
|
|||
margin-right: 10px;
|
||||
}
|
||||
|
||||
&-online {
|
||||
margin-right: 6px;
|
||||
}
|
||||
|
||||
&-dropdown {
|
||||
margin-left: 6px;
|
||||
|
||||
@media screen and (max-width: $screen-xs) {
|
||||
.btn {
|
||||
padding: .3rem .5rem;
|
||||
padding: 0.3rem 0.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -24,6 +24,7 @@ import {
|
|||
import GasPriceDropdown from './components/GasPriceDropdown';
|
||||
import Navigation from './components/Navigation';
|
||||
import CustomNodeModal from './components/CustomNodeModal';
|
||||
import OnlineStatus from './components/OnlineStatus';
|
||||
import { getKeyByValue } from 'utils/helpers';
|
||||
import { makeCustomNodeId } from 'utils/node';
|
||||
import { getNetworkConfigFromId } from 'utils/network';
|
||||
|
@ -35,6 +36,7 @@ interface Props {
|
|||
node: NodeConfig;
|
||||
nodeSelection: string;
|
||||
isChangingNode: boolean;
|
||||
isOffline: boolean;
|
||||
gasPrice: AppState['transaction']['fields']['gasPrice'];
|
||||
customNodes: CustomNodeConfig[];
|
||||
customNetworks: CustomNetworkConfig[];
|
||||
|
@ -62,6 +64,7 @@ export default class Header extends Component<Props, State> {
|
|||
node,
|
||||
nodeSelection,
|
||||
isChangingNode,
|
||||
isOffline,
|
||||
customNodes,
|
||||
customNetworks
|
||||
} = this.props;
|
||||
|
@ -127,6 +130,10 @@ export default class Header extends Component<Props, State> {
|
|||
<div className="Header-branding-right">
|
||||
<span className="Header-branding-right-version hidden-xs">v{VERSION}</span>
|
||||
|
||||
<div className="Header-branding-right-online">
|
||||
<OnlineStatus isOffline={isOffline} />
|
||||
</div>
|
||||
|
||||
<div className="Header-branding-right-dropdown">
|
||||
<GasPriceDropdown
|
||||
value={this.props.gasPrice.raw}
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
import React from 'react';
|
||||
import { NonceFieldFactory } from 'components/NonceFieldFactory';
|
||||
import Help from 'components/ui/Help';
|
||||
|
||||
interface Props {
|
||||
alwaysDisplay: boolean;
|
||||
}
|
||||
|
||||
const nonceHelp = (
|
||||
<Help
|
||||
size={'x1'}
|
||||
link={'https://myetherwallet.github.io/knowledge-base/transactions/what-is-nonce.html'}
|
||||
/>
|
||||
);
|
||||
|
||||
export const NonceField: React.SFC<Props> = ({ alwaysDisplay }) => (
|
||||
<NonceFieldFactory
|
||||
withProps={({ nonce: { raw, value }, onChange, readOnly, shouldDisplay }) => {
|
||||
const content = (
|
||||
<>
|
||||
<label>Nonce</label>
|
||||
{nonceHelp}
|
||||
|
||||
<input
|
||||
className={`form-control ${!!value ? 'is-valid' : 'is-invalid'}`}
|
||||
type="number"
|
||||
placeholder="e.g. 7"
|
||||
value={raw}
|
||||
readOnly={readOnly}
|
||||
onChange={onChange}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
|
||||
return alwaysDisplay || shouldDisplay ? content : null;
|
||||
}}
|
||||
/>
|
||||
);
|
|
@ -1,23 +0,0 @@
|
|||
import { NonceInput } from './NonceInput';
|
||||
import { inputNonce, TInputNonce } from 'actions/transaction';
|
||||
import React, { Component } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
interface DispatchProps {
|
||||
inputNonce: TInputNonce;
|
||||
}
|
||||
|
||||
class NonceFieldClass extends Component<DispatchProps> {
|
||||
public render() {
|
||||
return <NonceInput onChange={this.setNonce} />;
|
||||
}
|
||||
|
||||
private setNonce = (ev: React.FormEvent<HTMLInputElement>) => {
|
||||
const { value } = ev.currentTarget;
|
||||
this.props.inputNonce(value);
|
||||
};
|
||||
}
|
||||
|
||||
export const NonceField = connect(null, {
|
||||
inputNonce
|
||||
})(NonceFieldClass);
|
|
@ -1,55 +0,0 @@
|
|||
import React, { Component } from 'react';
|
||||
import { Aux } from 'components/ui';
|
||||
import { Query } from 'components/renderCbs';
|
||||
import Help from 'components/ui/Help';
|
||||
import { getNonce, nonceRequestFailed } from 'selectors/transaction';
|
||||
import { isAnyOffline } from 'selectors/config';
|
||||
import { AppState } from 'reducers';
|
||||
import { connect } from 'react-redux';
|
||||
const nonceHelp = (
|
||||
<Help
|
||||
size={'x1'}
|
||||
link={'https://myetherwallet.github.io/knowledge-base/transactions/what-is-nonce.html'}
|
||||
/>
|
||||
);
|
||||
|
||||
interface OwnProps {
|
||||
onChange(ev: React.FormEvent<HTMLInputElement>): void;
|
||||
}
|
||||
interface StateProps {
|
||||
shouldDisplay: boolean;
|
||||
nonce: AppState['transaction']['fields']['nonce'];
|
||||
}
|
||||
type Props = OwnProps & StateProps;
|
||||
|
||||
class NonceInputClass extends Component<Props> {
|
||||
public render() {
|
||||
const { nonce: { raw, value }, onChange, shouldDisplay } = this.props;
|
||||
const content = (
|
||||
<Aux>
|
||||
<label>Nonce</label>
|
||||
{nonceHelp}
|
||||
|
||||
<Query
|
||||
params={['readOnly']}
|
||||
withQuery={({ readOnly }) => (
|
||||
<input
|
||||
className={`form-control ${!!value ? 'is-valid' : 'is-invalid'}`}
|
||||
type="text"
|
||||
value={raw}
|
||||
readOnly={!!readOnly}
|
||||
onChange={onChange}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</Aux>
|
||||
);
|
||||
|
||||
return shouldDisplay ? content : null;
|
||||
}
|
||||
}
|
||||
|
||||
export const NonceInput = connect((state: AppState) => ({
|
||||
shouldDisplay: isAnyOffline(state) || nonceRequestFailed(state),
|
||||
nonce: getNonce(state)
|
||||
}))(NonceInputClass);
|
|
@ -1 +0,0 @@
|
|||
export * from './NonceField';
|
|
@ -0,0 +1,37 @@
|
|||
import { NonceInputFactory } from './NonceInputFactory';
|
||||
import { inputNonce, TInputNonce } from 'actions/transaction';
|
||||
import React, { Component } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { AppState } from 'reducers';
|
||||
|
||||
export interface CallbackProps {
|
||||
nonce: AppState['transaction']['fields']['nonce'];
|
||||
readOnly: boolean;
|
||||
shouldDisplay: boolean;
|
||||
onChange(ev: React.FormEvent<HTMLInputElement>): void;
|
||||
}
|
||||
|
||||
interface DispatchProps {
|
||||
inputNonce: TInputNonce;
|
||||
}
|
||||
|
||||
interface OwnProps {
|
||||
withProps(props: CallbackProps): React.ReactElement<any> | null;
|
||||
}
|
||||
|
||||
type Props = OwnProps & DispatchProps;
|
||||
|
||||
class NonceFieldClass extends Component<Props> {
|
||||
public render() {
|
||||
return <NonceInputFactory onChange={this.setNonce} withProps={this.props.withProps} />;
|
||||
}
|
||||
|
||||
private setNonce = (ev: React.FormEvent<HTMLInputElement>) => {
|
||||
const { value } = ev.currentTarget;
|
||||
this.props.inputNonce(value);
|
||||
};
|
||||
}
|
||||
|
||||
export const NonceFieldFactory = connect(null, {
|
||||
inputNonce
|
||||
})(NonceFieldClass);
|
|
@ -0,0 +1,39 @@
|
|||
import React, { Component } from 'react';
|
||||
import { Query } from 'components/renderCbs';
|
||||
import { getNonce, nonceRequestFailed } from 'selectors/transaction';
|
||||
import { getOffline } from 'selectors/config';
|
||||
import { AppState } from 'reducers';
|
||||
import { connect } from 'react-redux';
|
||||
import { CallbackProps } from 'components/NonceFieldFactory';
|
||||
|
||||
interface OwnProps {
|
||||
onChange(ev: React.FormEvent<HTMLInputElement>): void;
|
||||
withProps(props: CallbackProps): React.ReactElement<any> | null;
|
||||
}
|
||||
|
||||
interface StateProps {
|
||||
shouldDisplay: boolean;
|
||||
nonce: AppState['transaction']['fields']['nonce'];
|
||||
}
|
||||
|
||||
type Props = OwnProps & StateProps;
|
||||
|
||||
class NonceInputFactoryClass extends Component<Props> {
|
||||
public render() {
|
||||
const { nonce, onChange, shouldDisplay, withProps } = this.props;
|
||||
|
||||
return (
|
||||
<Query
|
||||
params={['readOnly']}
|
||||
withQuery={({ readOnly }) =>
|
||||
withProps({ nonce, onChange, readOnly: !!readOnly, shouldDisplay })
|
||||
}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export const NonceInputFactory = connect((state: AppState) => ({
|
||||
shouldDisplay: getOffline(state) || nonceRequestFailed(state),
|
||||
nonce: getNonce(state)
|
||||
}))(NonceInputFactoryClass);
|
|
@ -0,0 +1 @@
|
|||
export * from './NonceFieldFactory';
|
|
@ -1,36 +0,0 @@
|
|||
import { UnlockHeader } from 'components/ui';
|
||||
import React, { Component } from 'react';
|
||||
import translate from 'translations';
|
||||
import { isAnyOffline } from 'selectors/config';
|
||||
import { connect } from 'react-redux';
|
||||
import { AppState } from 'reducers';
|
||||
|
||||
interface Props {
|
||||
disabledWallets?: string[];
|
||||
}
|
||||
export const OfflineAwareUnlockHeader: React.SFC<Props> = ({ disabledWallets }) => (
|
||||
<UnlockHeader title={<Title />} disabledWallets={disabledWallets} />
|
||||
);
|
||||
|
||||
interface StateProps {
|
||||
shouldDisplayOffline: boolean;
|
||||
}
|
||||
|
||||
class TitleClass extends Component<StateProps> {
|
||||
public render() {
|
||||
const { shouldDisplayOffline } = this.props;
|
||||
const offlineTitle = shouldDisplayOffline ? (
|
||||
<span style={{ color: 'red' }}> (Offline)</span>
|
||||
) : null;
|
||||
return (
|
||||
<div>
|
||||
{translate('Account')}
|
||||
{offlineTitle}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const Title = connect((state: AppState) => ({
|
||||
shouldDisplayOffline: isAnyOffline(state)
|
||||
}))(TitleClass);
|
|
@ -1,5 +1,4 @@
|
|||
import React, { Component } from 'react';
|
||||
import { Aux } from 'components/ui';
|
||||
import { ConfirmationModal } from 'components/ConfirmationModal';
|
||||
import { getOffline } from 'selectors/config';
|
||||
import { AppState } from 'reducers';
|
||||
|
@ -40,10 +39,10 @@ class OnlineSendClass extends Component<Props, State> {
|
|||
) : null;
|
||||
|
||||
return !this.props.offline ? (
|
||||
<Aux>
|
||||
<React.Fragment>
|
||||
{this.props.withProps({ onClick: this.openModal })}
|
||||
{displayModal}
|
||||
</Aux>
|
||||
</React.Fragment>
|
||||
) : null;
|
||||
}
|
||||
private openModal = () => {
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import translate from 'translations';
|
||||
import { Aux } from 'components/ui';
|
||||
import { getTransactionFields, makeTransaction } from 'libs/transaction';
|
||||
import { OfflineBroadcast } from './OfflineBroadcast';
|
||||
import { SerializedTransaction } from 'components/renderCbs';
|
||||
|
@ -33,7 +32,7 @@ class SendButtonFactoryClass extends Component<Props> {
|
|||
return (
|
||||
<SerializedTransaction
|
||||
withSerializedTransaction={serializedTransaction => (
|
||||
<Aux>
|
||||
<React.Fragment>
|
||||
<div className={`col-sm-${columnSize}`}>
|
||||
<label>
|
||||
{this.props.walletType.isWeb3Wallet
|
||||
|
@ -64,7 +63,7 @@ class SendButtonFactoryClass extends Component<Props> {
|
|||
)}
|
||||
<OfflineBroadcast />
|
||||
<OnlineSend withProps={this.props.withProps} />
|
||||
</Aux>
|
||||
</React.Fragment>
|
||||
)}
|
||||
/>
|
||||
);
|
||||
|
|
|
@ -7,6 +7,7 @@ import { Query } from 'components/renderCbs';
|
|||
import { connect } from 'react-redux';
|
||||
import { AppState } from 'reducers';
|
||||
import { getUnit } from 'selectors/transaction';
|
||||
import { getNetworkConfig } from 'selectors/config';
|
||||
|
||||
interface DispatchProps {
|
||||
setUnitMeta: TSetUnitMeta;
|
||||
|
@ -17,6 +18,7 @@ interface StateProps {
|
|||
tokens: TokenBalance[];
|
||||
allTokens: MergedToken[];
|
||||
showAllTokens?: boolean;
|
||||
network: AppState['config']['network'];
|
||||
}
|
||||
|
||||
const StringDropdown = Dropdown as new () => Dropdown<string>;
|
||||
|
@ -24,7 +26,7 @@ const ConditionalStringDropDown = withConditional(StringDropdown);
|
|||
|
||||
class UnitDropdownClass extends Component<DispatchProps & StateProps> {
|
||||
public render() {
|
||||
const { tokens, allTokens, showAllTokens, unit } = this.props;
|
||||
const { tokens, allTokens, showAllTokens, unit, network } = this.props;
|
||||
const focusedTokens = showAllTokens ? allTokens : tokens;
|
||||
return (
|
||||
<div className="input-group-btn">
|
||||
|
@ -32,8 +34,8 @@ class UnitDropdownClass extends Component<DispatchProps & StateProps> {
|
|||
params={['readOnly']}
|
||||
withQuery={({ readOnly }) => (
|
||||
<ConditionalStringDropDown
|
||||
options={['ether', ...getTokenSymbols(focusedTokens)]}
|
||||
value={unit}
|
||||
options={[network.unit, ...getTokenSymbols(focusedTokens)]}
|
||||
value={unit === 'ether' ? network.unit : unit}
|
||||
condition={!readOnly}
|
||||
conditionalProps={{
|
||||
onChange: this.handleOnChange
|
||||
|
@ -55,7 +57,8 @@ function mapStateToProps(state: AppState) {
|
|||
return {
|
||||
tokens: getShownTokenBalances(state, true),
|
||||
allTokens: getTokens(state),
|
||||
unit: getUnit(state)
|
||||
unit: getUnit(state),
|
||||
network: getNetworkConfig(state)
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -33,13 +33,15 @@ import {
|
|||
import { AppState } from 'reducers';
|
||||
import { knowledgeBaseURL, isWeb3NodeAvailable } from 'config/data';
|
||||
import { IWallet } from 'libs/wallet';
|
||||
import DISABLES from './disables.json';
|
||||
import { showNotification, TShowNotification } from 'actions/notifications';
|
||||
|
||||
import DigitalBitboxIcon from 'assets/images/wallets/digital-bitbox.svg';
|
||||
import LedgerIcon from 'assets/images/wallets/ledger.svg';
|
||||
import MetamaskIcon from 'assets/images/wallets/metamask.svg';
|
||||
import MistIcon from 'assets/images/wallets/mist.svg';
|
||||
import TrezorIcon from 'assets/images/wallets/trezor.svg';
|
||||
import './WalletDecrypt.scss';
|
||||
type UnlockParams = {} | PrivateKeyValue;
|
||||
|
||||
interface Props {
|
||||
resetTransactionState: TReset;
|
||||
|
@ -49,12 +51,16 @@ interface Props {
|
|||
setWallet: TSetWallet;
|
||||
unlockWeb3: TUnlockWeb3;
|
||||
resetWallet: TResetWallet;
|
||||
showNotification: TShowNotification;
|
||||
wallet: IWallet;
|
||||
hidden?: boolean;
|
||||
offline: boolean;
|
||||
disabledWallets?: string[];
|
||||
isWalletPending: AppState['wallet']['isWalletPending'];
|
||||
isPasswordPending: AppState['wallet']['isPasswordPending'];
|
||||
}
|
||||
|
||||
type UnlockParams = {} | PrivateKeyValue;
|
||||
interface State {
|
||||
selectedWalletKey: string | null;
|
||||
value: UnlockParams | null;
|
||||
|
@ -92,7 +98,7 @@ const WEB3_TYPES = {
|
|||
const WEB3_TYPE: string | false =
|
||||
(window as any).web3 && (window as any).web3.currentProvider.constructor.name;
|
||||
|
||||
const SECURE_WALLETS = ['web3', 'ledger-nano-s', 'trezor', 'digital-bitbox'];
|
||||
const SECURE_WALLETS = ['web3', 'ledger-nano-s', 'trezor'];
|
||||
const INSECURE_WALLETS = ['private-key', 'keystore-file', 'mnemonic-phrase'];
|
||||
|
||||
export class WalletDecrypt extends Component<Props, State> {
|
||||
|
@ -210,15 +216,19 @@ export class WalletDecrypt extends Component<Props, State> {
|
|||
value={this.state.value}
|
||||
onChange={this.onChange}
|
||||
onUnlock={this.onUnlock}
|
||||
showNotification={this.props.showNotification}
|
||||
isWalletPending={
|
||||
this.state.selectedWalletKey === 'keystore-file' ? this.props.isWalletPending : undefined
|
||||
}
|
||||
isPasswordPending={
|
||||
this.state.selectedWalletKey === 'keystore-file'
|
||||
? this.props.isPasswordPending
|
||||
: undefined
|
||||
}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
public isOnlineRequiredWalletAndOffline(selectedWalletKey) {
|
||||
const onlineRequiredWallets = ['trezor', 'ledger-nano-s'];
|
||||
return this.props.offline && onlineRequiredWallets.includes(selectedWalletKey);
|
||||
}
|
||||
|
||||
public buildWalletOptions() {
|
||||
const viewOnly = this.WALLETS['view-only'] as InsecureWalletInfo;
|
||||
|
||||
|
@ -366,6 +376,10 @@ export class WalletDecrypt extends Component<Props, State> {
|
|||
};
|
||||
|
||||
private isWalletDisabled = (walletKey: string) => {
|
||||
if (this.props.offline && DISABLES.ONLINE_ONLY.includes(walletKey)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!this.props.disabledWallets) {
|
||||
return false;
|
||||
}
|
||||
|
@ -376,7 +390,9 @@ export class WalletDecrypt extends Component<Props, State> {
|
|||
function mapStateToProps(state: AppState) {
|
||||
return {
|
||||
offline: state.config.offline,
|
||||
wallet: state.wallet.inst
|
||||
wallet: state.wallet.inst,
|
||||
isWalletPending: state.wallet.isWalletPending,
|
||||
isPasswordPending: state.wallet.isPasswordPending
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -387,5 +403,6 @@ export default connect(mapStateToProps, {
|
|||
unlockWeb3,
|
||||
setWallet,
|
||||
resetWallet,
|
||||
resetTransactionState: reset
|
||||
resetTransactionState: reset,
|
||||
showNotification
|
||||
})(WalletDecrypt);
|
||||
|
|
|
@ -292,7 +292,11 @@ class DeterministicWalletsModalClass extends React.Component<Props, State> {
|
|||
)}
|
||||
</td>
|
||||
<td>
|
||||
<a target="_blank" href={`https://ethplorer.io/address/${wallet.address}`}>
|
||||
<a
|
||||
target="_blank"
|
||||
href={`https://ethplorer.io/address/${wallet.address}`}
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<i className="DWModal-addresses-table-more" />
|
||||
</a>
|
||||
</td>
|
||||
|
@ -310,7 +314,9 @@ function mapStateToProps(state: AppState) {
|
|||
};
|
||||
}
|
||||
|
||||
export const DeterministicWalletsModal = connect(mapStateToProps, {
|
||||
const DeterministicWalletsModal = connect(mapStateToProps, {
|
||||
getDeterministicWallets,
|
||||
setDesiredToken
|
||||
})(DeterministicWalletsModalClass);
|
||||
|
||||
export default DeterministicWalletsModal;
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
import { isKeystorePassRequired } from 'libs/wallet';
|
||||
import React, { Component } from 'react';
|
||||
import translate, { translateRaw } from 'translations';
|
||||
import Spinner from 'components/ui/Spinner';
|
||||
import { TShowNotification } from 'actions/notifications';
|
||||
|
||||
export interface KeystoreValue {
|
||||
file: string;
|
||||
|
@ -18,15 +20,23 @@ function isPassRequired(file: string): boolean {
|
|||
return passReq;
|
||||
}
|
||||
|
||||
function isValidFile(rawFile: File): boolean {
|
||||
const fileType = rawFile.type;
|
||||
return fileType === '' || fileType === 'application/json';
|
||||
}
|
||||
|
||||
export class KeystoreDecrypt extends Component {
|
||||
public props: {
|
||||
value: KeystoreValue;
|
||||
isWalletPending: boolean;
|
||||
isPasswordPending: boolean;
|
||||
onChange(value: KeystoreValue): void;
|
||||
onUnlock(): void;
|
||||
showNotification(level: string, message: string): TShowNotification;
|
||||
};
|
||||
|
||||
public render() {
|
||||
const { file, password } = this.props.value;
|
||||
const { isWalletPending, isPasswordPending, value: { file, password } } = this.props;
|
||||
const passReq = isPassRequired(file);
|
||||
const unlockDisabled = !file || (passReq && !password);
|
||||
|
||||
|
@ -44,7 +54,8 @@ export class KeystoreDecrypt extends Component {
|
|||
{translate('ADD_Radio_2_short')}
|
||||
</a>
|
||||
</label>
|
||||
<div className={file.length && passReq ? '' : 'hidden'}>
|
||||
{isWalletPending ? <Spinner /> : ''}
|
||||
<div className={file.length && isPasswordPending ? '' : 'hidden'}>
|
||||
<p>{translate('ADD_Label_3')}</p>
|
||||
<input
|
||||
className={`form-control ${password.length > 0 ? 'is-valid' : 'is-invalid'}`}
|
||||
|
@ -97,10 +108,15 @@ export class KeystoreDecrypt extends Component {
|
|||
this.props.onChange({
|
||||
...this.props.value,
|
||||
file: keystore,
|
||||
valid: keystore.length && !passReq
|
||||
valid: keystore.length && !passReq,
|
||||
password: ''
|
||||
});
|
||||
this.props.onUnlock();
|
||||
};
|
||||
|
||||
if (isValidFile(inputFile)) {
|
||||
fileReader.readAsText(inputFile, 'utf-8');
|
||||
} else {
|
||||
this.props.showNotification('danger', translateRaw('ERROR_3'));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|