Merge pull request #283 from MyEtherWallet/develop

Alpha 0.0.3
This commit is contained in:
Daniel Ternyak 2017-10-11 12:42:26 -07:00 committed by GitHub
commit a459042acd
352 changed files with 14323 additions and 10437 deletions

View File

@ -1,25 +0,0 @@
{
"plugins": [
[
"transform-runtime", {
"helpers": false,
"polyfill": false,
"regenerator": true,
"moduleName": "babel-runtime"
}
],
["module-resolver", {
"root": ["./common"],
"alias": {
"underscore": "lodash"
},
"cwd": "babelrc"
}],
"react-hot-loader/babel"],
"presets": ["es2015", "react", "stage-0", "flow"],
"env": {
"production": {
"presets": ["react-optimize"]
}
}
}

View File

@ -1,42 +0,0 @@
{
"extends": ["eslint:recommended", "plugin:react/recommended"],
"parser": "babel-eslint",
"plugins": ["react"],
"parserOptions": {
"ecmaFeatures": {
"jsx": true,
"modules": true
}
},
"env": {
"browser": true,
"amd": true,
"es6": true,
"node": true,
"jest": true
},
"rules": {
"comma-dangle": 1,
"quotes": [1, "single"],
"no-undef": 1,
"global-strict": 0,
"no-extra-semi": 1,
"no-underscore-dangle": 0,
"no-console": 0,
"no-unused-vars": 0,
"no-constant-condition": 0,
"no-trailing-spaces": [1, { "skipBlankLines": true }],
"no-unreachable": 1,
"no-alert": 0,
"react/jsx-uses-react": 1,
"no-unused-vars": [1, { "argsIgnorePattern": "^_" }],
"no-restricted-globals": ["error", "event"]
},
"globals": {
"SyntheticInputEvent": false,
"SyntheticKeyboardEvent": false,
"Generator": false,
"$Keys": false,
"SyntheticMouseEvent": false
}
}

11
.gitignore vendored
View File

@ -45,4 +45,13 @@ jspm_packages
# VScode workspace settings
.vscode/
static/dll.vendor.js
dll
dll
.cache-loader
# SSL cert stuff
webpack_config/server.key
webpack_config/server.crt
webpack_config/server.csr
v8-compile-cache-0/

View File

@ -15,4 +15,5 @@ notifications:
script:
- npm run test
- npm run flow
- npm run tslint
- tsc --noEmit

View File

@ -20,6 +20,16 @@ It generates app in `dist` folder.
npm run test # run tests with Jest
```
#### Dev (HTTPS):
1. Create your own SSL Certificate (Heroku has a [nice guide here](https://devcenter.heroku.com/articles/ssl-certificate-self))
2. Move the `.key` and `.crt` files into `webpack_config/server.*`
3. Run the following command:
```bash
npm run dev:https
```
#### Derivation Check:
##### The derivation checker utility assumes that you have:
1. Docker installed/available
@ -28,7 +38,7 @@ npm run test # run tests with Jest
##### Docker setup instructions:
1. Install docker (on macOS, I suggest [Docker for Mac](https://docs.docker.com/docker-for-mac/))
2. `docker pull dternyak/eth-priv-to-addr`
##### Run Derivation Checker
```bash
npm run derivation-checker
@ -212,3 +222,13 @@ When working on a module that has styling in Less, try to do the following:
* Convert as many element selectors to class name selectors as possible
* Convert as many `<br/>` tags or `&nbsp;`s to margins
* Ensure that there has been little to no deviation from screenshot
## Thanks & Support
<a href="https://browserstack.com/">
<img src="https://i.imgur.com/Rib9y9E.png" align="left" />
</a>
Cross browser testing and debugging provided by the very lovely team at BrowserStack.

View File

@ -1,47 +0,0 @@
// @flow
/*** Change Language ***/
export type ChangeLanguageAction = {
type: 'CONFIG_LANGUAGE_CHANGE',
value: string
};
export function changeLanguage(sign: string): ChangeLanguageAction {
return {
type: 'CONFIG_LANGUAGE_CHANGE',
value: sign
};
}
/*** Change Node ***/
export type ChangeNodeAction = {
type: 'CONFIG_NODE_CHANGE',
// FIXME $keyof?
value: string
};
export function changeNode(value: string): ChangeNodeAction {
return {
type: 'CONFIG_NODE_CHANGE',
value
};
}
/*** Change gas price ***/
export type ChangeGasPriceAction = {
type: 'CONFIG_GAS_PRICE',
value: number
};
export function changeGasPrice(value: number): ChangeGasPriceAction {
return {
type: 'CONFIG_GAS_PRICE',
value
};
}
/*** Union Type ***/
export type ConfigAction =
| ChangeNodeAction
| ChangeLanguageAction
| ChangeGasPriceAction;

View File

@ -0,0 +1,57 @@
import * as interfaces from './actionTypes';
import { TypeKeys } from './constants';
export type TForceOfflineConfig = typeof forceOfflineConfig;
export function forceOfflineConfig(): interfaces.ForceOfflineAction {
return {
type: TypeKeys.CONFIG_FORCE_OFFLINE
};
}
export type TToggleOfflineConfig = typeof toggleOfflineConfig;
export function toggleOfflineConfig(): interfaces.ToggleOfflineAction {
return {
type: TypeKeys.CONFIG_TOGGLE_OFFLINE
};
}
export type TChangeLanguage = typeof changeLanguage;
export function changeLanguage(sign: string): interfaces.ChangeLanguageAction {
return {
type: TypeKeys.CONFIG_LANGUAGE_CHANGE,
payload: sign
};
}
export type TChangeNode = typeof changeNode;
export function changeNode(value: string): interfaces.ChangeNodeAction {
return {
type: TypeKeys.CONFIG_NODE_CHANGE,
payload: value
};
}
export type TChangeGasPrice = typeof changeGasPrice;
export function changeGasPrice(value: number): interfaces.ChangeGasPriceAction {
return {
type: TypeKeys.CONFIG_GAS_PRICE,
payload: value
};
}
export type TPollOfflineStatus = typeof pollOfflineStatus;
export function pollOfflineStatus(): interfaces.PollOfflineStatus {
return {
type: TypeKeys.CONFIG_POLL_OFFLINE_STATUS
};
}
export type TChangeNodeIntent = typeof changeNodeIntent;
export function changeNodeIntent(
payload: string
): interfaces.ChangeNodeIntentAction {
return {
type: TypeKeys.CONFIG_NODE_CHANGE_INTENT,
payload
};
}

View File

@ -0,0 +1,51 @@
import { TypeKeys } from './constants';
/*** Toggle Offline ***/
export interface ToggleOfflineAction {
type: TypeKeys.CONFIG_TOGGLE_OFFLINE;
}
/*** Force Offline ***/
export interface ForceOfflineAction {
type: TypeKeys.CONFIG_FORCE_OFFLINE;
}
/*** Change Language ***/
export interface ChangeLanguageAction {
type: TypeKeys.CONFIG_LANGUAGE_CHANGE;
payload: string;
}
/*** Change Node ***/
export interface ChangeNodeAction {
type: TypeKeys.CONFIG_NODE_CHANGE;
// FIXME $keyof?
payload: string;
}
/*** Change gas price ***/
export interface ChangeGasPriceAction {
type: TypeKeys.CONFIG_GAS_PRICE;
payload: number;
}
/*** Poll offline status ***/
export interface PollOfflineStatus {
type: TypeKeys.CONFIG_POLL_OFFLINE_STATUS;
}
/*** Change Node ***/
export interface ChangeNodeIntentAction {
type: TypeKeys.CONFIG_NODE_CHANGE_INTENT;
payload: string;
}
/*** Union Type ***/
export type ConfigAction =
| ChangeNodeAction
| ChangeLanguageAction
| ChangeGasPriceAction
| ToggleOfflineAction
| PollOfflineStatus
| ForceOfflineAction
| ChangeNodeIntentAction;

View File

@ -0,0 +1,9 @@
export enum TypeKeys {
CONFIG_LANGUAGE_CHANGE = 'CONFIG_LANGUAGE_CHANGE',
CONFIG_NODE_CHANGE = 'CONFIG_NODE_CHANGE',
CONFIG_NODE_CHANGE_INTENT = 'CONFIG_NODE_CHANGE_INTENT',
CONFIG_GAS_PRICE = 'CONFIG_GAS_PRICE',
CONFIG_TOGGLE_OFFLINE = 'CONFIG_TOGGLE_OFFLINE',
CONFIG_FORCE_OFFLINE = 'CONFIG_FORCE_OFFLINE',
CONFIG_POLL_OFFLINE_STATUS = 'CONFIG_POLL_OFFLINE_STATUS'
}

View File

@ -0,0 +1,2 @@
export * from './actionCreators';
export * from './actionTypes';

View File

@ -1,47 +0,0 @@
// @flow
/***** Access Contract *****/
export type AccessContractAction = {
type: 'ACCESS_CONTRACT',
address: string,
abiJson: string
};
export function accessContract(
address: string,
abiJson: string
): AccessContractAction {
return {
type: 'ACCESS_CONTRACT',
address,
abiJson
};
}
/***** Set Interactive Contract *****/
export type ABIFunctionField = {
name: string,
type: string
};
export type ABIFunction = {
name: string,
type: string,
constant: boolean,
inputs: Array<ABIFunctionField>,
outputs: Array<ABIFunctionField>
};
export type SetInteractiveContractAction = {
type: 'SET_INTERACTIVE_CONTRACT',
functions: Array<ABIFunction>
};
export function setInteractiveContract(
functions: Array<ABIFunction>
): SetInteractiveContractAction {
return {
type: 'SET_INTERACTIVE_CONTRACT',
functions
};
}

View File

@ -0,0 +1,22 @@
import * as interfaces from './actionTypes';
import { TypeKeys } from './constants';
export function accessContract(
address: string,
abiJson: string
): interfaces.AccessContractAction {
return {
type: TypeKeys.ACCESS_CONTRACT,
address,
abiJson
};
}
export function setInteractiveContract(
functions: interfaces.ABIFunction[]
): interfaces.SetInteractiveContractAction {
return {
type: TypeKeys.SET_INTERACTIVE_CONTRACT,
functions
};
}

View File

@ -0,0 +1,31 @@
import { TypeKeys } from './constants';
/***** Set Interactive Contract *****/
export interface ABIFunctionField {
name: string;
type: string;
}
export interface ABIFunction {
name: string;
type: string;
constant: boolean;
inputs: ABIFunctionField[];
outputs: ABIFunctionField[];
}
export interface SetInteractiveContractAction {
type: TypeKeys.SET_INTERACTIVE_CONTRACT;
functions: ABIFunction[];
}
/***** Access Contract *****/
export interface AccessContractAction {
type: TypeKeys.ACCESS_CONTRACT;
address: string;
abiJson: string;
}
/*** Union Type ***/
export type ContractsAction =
| SetInteractiveContractAction
| AccessContractAction;

View File

@ -0,0 +1,4 @@
export enum TypeKeys {
ACCESS_CONTRACT = 'ACCESS_CONTRACT',
SET_INTERACTIVE_CONTRACT = 'SET_INTERACTIVE_CONTRACT'
}

View File

@ -0,0 +1,3 @@
export * from './constants';
export * from './actionTypes';
export * from './actionCreators';

View File

@ -1,31 +0,0 @@
// @flow
import type { Token } from 'config/data';
/*** Add custom token ***/
export type AddCustomTokenAction = {
type: 'CUSTOM_TOKEN_ADD',
payload: Token
};
export function addCustomToken(payload: Token): AddCustomTokenAction {
return {
type: 'CUSTOM_TOKEN_ADD',
payload
};
}
/*** Remove Custom Token ***/
export type RemoveCustomTokenAction = {
type: 'CUSTOM_TOKEN_REMOVE',
payload: string
};
export function removeCustomToken(payload: string): RemoveCustomTokenAction {
return {
type: 'CUSTOM_TOKEN_REMOVE',
payload
};
}
/*** Union Type ***/
export type CustomTokenAction = AddCustomTokenAction | RemoveCustomTokenAction;

View File

@ -0,0 +1,24 @@
import { Token } from 'config/data';
import * as interfaces from './actionTypes';
import { TypeKeys } from './constants';
export type TAddCustomToken = typeof addCustomToken;
export function addCustomToken(
payload: Token
): interfaces.AddCustomTokenAction {
return {
type: TypeKeys.CUSTOM_TOKEN_ADD,
payload
};
}
export type TRemoveCustomToken = typeof removeCustomToken;
export function removeCustomToken(
payload: string
): interfaces.RemoveCustomTokenAction {
return {
type: TypeKeys.CUSTOM_TOKEN_REMOVE,
payload
};
}

View File

@ -0,0 +1,15 @@
import { Token } from 'config/data';
import { TypeKeys } from './constants';
/*** Add custom token ***/
export interface AddCustomTokenAction {
type: TypeKeys.CUSTOM_TOKEN_ADD;
payload: Token;
}
/*** Remove Custom Token ***/
export interface RemoveCustomTokenAction {
type: TypeKeys.CUSTOM_TOKEN_REMOVE;
payload: string;
}
export type CustomTokenAction = AddCustomTokenAction | RemoveCustomTokenAction;

View File

@ -0,0 +1,4 @@
export enum TypeKeys {
CUSTOM_TOKEN_ADD = 'CUSTOM_TOKEN_ADD',
CUSTOM_TOKEN_REMOVE = 'CUSTOM_TOKEN_REMOVE'
}

View File

@ -0,0 +1,2 @@
export * from './actionCreators';
export * from './actionTypes';

View File

@ -1,105 +0,0 @@
// @flow
import type Big from 'bignumber.js';
export type TokenValues = { [string]: ?Big };
export type DeterministicWalletData = {
index: number,
address: string,
value?: Big,
tokenValues: TokenValues
};
/*** Get determinstic wallets ***/
export type GetDeterministicWalletsAction = {
type: 'DW_GET_WALLETS',
payload: {
seed: ?string,
dPath: string,
publicKey: ?string,
chainCode: ?string,
limit: number,
offset: number
}
};
export type GetDeterministicWalletsArgs = {
seed: ?string,
dPath: string,
publicKey: ?string,
chainCode: ?string,
limit?: number,
offset?: number
};
export function getDeterministicWallets(
args: GetDeterministicWalletsArgs
): GetDeterministicWalletsAction {
const { seed, dPath, publicKey, chainCode, limit, offset } = args;
return {
type: 'DW_GET_WALLETS',
payload: {
seed,
dPath,
publicKey,
chainCode,
limit: limit || 5,
offset: offset || 0
}
};
}
/*** Set deterministic wallets ***/
export type SetDeterministicWalletsAction = {
type: 'DW_SET_WALLETS',
payload: DeterministicWalletData[]
};
export function setDeterministicWallets(
wallets: DeterministicWalletData[]
): SetDeterministicWalletsAction {
return {
type: 'DW_SET_WALLETS',
payload: wallets
};
}
/*** Set desired token ***/
export type SetDesiredTokenAction = {
type: 'DW_SET_DESIRED_TOKEN',
payload: ?string
};
export function setDesiredToken(token: ?string): SetDesiredTokenAction {
return {
type: 'DW_SET_DESIRED_TOKEN',
payload: token
};
}
/*** Set wallet values ***/
export type UpdateDeterministicWalletArgs = {
address: string,
value: ?Big,
tokenValues: ?TokenValues
};
export type UpdateDeterministicWalletAction = {
type: 'DW_UPDATE_WALLET',
payload: UpdateDeterministicWalletArgs
};
export function updateDeterministicWallet(
args: UpdateDeterministicWalletArgs
): UpdateDeterministicWalletAction {
return {
type: 'DW_UPDATE_WALLET',
payload: args
};
}
/*** Union Type ***/
export type DeterministicWalletAction =
| GetDeterministicWalletsAction
| UpdateDeterministicWalletAction
| SetDesiredTokenAction;

View File

@ -0,0 +1,45 @@
import * as interfaces from './actionTypes';
import { TypeKeys } from './constants';
export function getDeterministicWallets(
args: interfaces.GetDeterministicWalletsArgs
): interfaces.GetDeterministicWalletsAction {
const { seed, dPath, publicKey, chainCode, limit, offset } = args;
return {
type: TypeKeys.DW_GET_WALLETS,
payload: {
seed,
dPath,
publicKey,
chainCode,
limit: limit || 5,
offset: offset || 0
}
};
}
export function setDeterministicWallets(
wallets: interfaces.DeterministicWalletData[]
): interfaces.SetDeterministicWalletsAction {
return {
type: TypeKeys.DW_SET_WALLETS,
payload: wallets
};
}
export function setDesiredToken(
token: string | undefined
): interfaces.SetDesiredTokenAction {
return {
type: TypeKeys.DW_SET_DESIRED_TOKEN,
payload: token
};
}
export function updateDeterministicWallet(
args: interfaces.UpdateDeterministicWalletArgs
): interfaces.UpdateDeterministicWalletAction {
return {
type: TypeKeys.DW_UPDATE_WALLET,
payload: args
};
}

View File

@ -0,0 +1,66 @@
import { BigNumber } from 'bignumber.js';
export interface TokenValues {
[key: string]: BigNumber;
}
export interface DeterministicWalletData {
index: number;
address: string;
value?: BigNumber;
tokenValues: TokenValues;
}
/*** Get determinstic wallets ***/
export interface GetDeterministicWalletsAction {
type: 'DW_GET_WALLETS';
payload: {
seed?: string;
dPath: string;
publicKey?: string;
chainCode?: string;
limit: number;
offset: number;
};
}
/*** Set deterministic wallets ***/
export interface SetDeterministicWalletsAction {
type: 'DW_SET_WALLETS';
payload: DeterministicWalletData[];
}
/*** Set desired token ***/
export interface SetDesiredTokenAction {
type: 'DW_SET_DESIRED_TOKEN';
payload: string | undefined;
}
/*** Set wallet values ***/
export interface UpdateDeterministicWalletArgs {
address: string;
value?: BigNumber;
tokenValues?: TokenValues;
index?: any;
}
export interface UpdateDeterministicWalletAction {
type: 'DW_UPDATE_WALLET';
payload: UpdateDeterministicWalletArgs;
}
export interface GetDeterministicWalletsArgs {
seed?: string;
dPath: string;
publicKey?: string;
chainCode?: string;
limit?: number;
offset?: number;
}
/*** Union Type ***/
export type DeterministicWalletAction =
| SetDeterministicWalletsAction
| GetDeterministicWalletsAction
| UpdateDeterministicWalletAction
| SetDesiredTokenAction;

View File

@ -0,0 +1,6 @@
export enum TypeKeys {
DW_GET_WALLETS = 'DW_GET_WALLETS',
DW_SET_WALLETS = 'DW_SET_WALLETS',
DW_SET_DESIRED_TOKEN = 'DW_SET_DESIRED_TOKEN',
DW_UPDATE_WALLET = 'DW_UPDATE_WALLET'
}

View File

@ -0,0 +1,2 @@
export * from './actionTypes';
export * from './actionCreators';

View File

@ -1,39 +0,0 @@
// @flow
/*** Resolve ENS name ***/
export type ResolveEnsNameAction = {
type: 'ENS_RESOLVE',
payload: string
};
export function resolveEnsName(name: string): ResolveEnsNameAction {
return {
type: 'ENS_RESOLVE',
payload: name
};
}
/*** Cache ENS address ***/
export type CacheEnsAddressAction = {
type: 'ENS_CACHE',
payload: {
ensName: string,
address: string
}
};
export function cacheEnsAddress(
ensName: string,
address: string
): CacheEnsAddressAction {
return {
type: 'ENS_CACHE',
payload: {
ensName,
address
}
};
}
/*** Union Type ***/
export type EnsAction = ResolveEnsNameAction | CacheEnsAddressAction;

View File

@ -0,0 +1,22 @@
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
}
};
}

View File

@ -0,0 +1,19 @@
import * as constants from './constants';
/*** 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;

View File

@ -0,0 +1,2 @@
export const ENS_RESOLVE = 'ENS_RESOLVE';
export const ENS_CACHE = 'ENS_CACHE';

View File

@ -0,0 +1,3 @@
export * from './constants';
export * from './actionTypes';
export * from './actionCreators';

View File

@ -1,38 +0,0 @@
// @flow
import { PrivKeyWallet } from 'libs/wallet';
/*** Generate Wallet File ***/
export type GenerateNewWalletAction = {
type: 'GENERATE_WALLET_GENERATE_WALLET',
wallet: PrivKeyWallet,
password: string
};
export function generateNewWallet(password: string): GenerateNewWalletAction {
return {
type: 'GENERATE_WALLET_GENERATE_WALLET',
wallet: PrivKeyWallet.generate(),
password
};
}
/*** Confirm Continue To Paper ***/
export type ContinueToPaperAction = {
type: 'GENERATE_WALLET_CONTINUE_TO_PAPER'
};
export function continueToPaper(): ContinueToPaperAction {
return { type: 'GENERATE_WALLET_CONTINUE_TO_PAPER' };
}
/*** Reset Generate Wallet ***/
export type ResetGenerateWalletAction = {
type: 'GENERATE_WALLET_RESET'
};
export function resetGenerateWallet(): ResetGenerateWalletAction {
return { type: 'GENERATE_WALLET_RESET' };
}
/*** Action Union ***/
export type GenerateWalletAction = GenerateWalletAction;

View File

@ -0,0 +1,24 @@
import { PrivKeyWallet } from 'libs/wallet';
import * as interfaces from './actionTypes';
import { TypeKeys } from './constants';
export type TGenerateNewWallet = typeof generateNewWallet;
export function generateNewWallet(
password: string
): interfaces.GenerateNewWalletAction {
return {
type: TypeKeys.GENERATE_WALLET_GENERATE_WALLET,
wallet: PrivKeyWallet.generate(),
password
};
}
export type TContinueToPaper = typeof continueToPaper;
export function continueToPaper(): interfaces.ContinueToPaperAction {
return { type: TypeKeys.GENERATE_WALLET_CONTINUE_TO_PAPER };
}
export type TResetGenerateWallet = typeof resetGenerateWallet;
export function resetGenerateWallet(): interfaces.ResetGenerateWalletAction {
return { type: TypeKeys.GENERATE_WALLET_RESET };
}

View File

@ -0,0 +1,25 @@
import { PrivKeyWallet } from 'libs/wallet';
import { TypeKeys } from './constants';
/*** Generate Wallet File ***/
export interface GenerateNewWalletAction {
type: TypeKeys.GENERATE_WALLET_GENERATE_WALLET;
wallet: PrivKeyWallet;
password: string;
}
/*** Reset Generate Wallet ***/
export interface ResetGenerateWalletAction {
type: TypeKeys.GENERATE_WALLET_RESET;
}
/*** Confirm Continue To Paper ***/
export interface ContinueToPaperAction {
type: TypeKeys.GENERATE_WALLET_CONTINUE_TO_PAPER;
}
/*** Action Union ***/
export type GenerateWalletAction =
| GenerateNewWalletAction
| ContinueToPaperAction
| ResetGenerateWalletAction;

View File

@ -0,0 +1,5 @@
export enum TypeKeys {
GENERATE_WALLET_GENERATE_WALLET = 'GENERATE_WALLET_GENERATE_WALLET',
GENERATE_WALLET_CONTINUE_TO_PAPER = 'GENERATE_WALLET_CONTINUE_TO_PAPER',
GENERATE_WALLET_RESET = 'GENERATE_WALLET_RESET'
}

View File

@ -0,0 +1,3 @@
export * from './constants';
export * from './actionTypes';
export * from './actionCreators';

View File

@ -1,53 +0,0 @@
// @flow
import type { Element } from 'react';
/*** Shared types ***/
export type NOTIFICATION_LEVEL = 'danger' | 'warning' | 'success' | 'info';
export type INFINITY = 'infinity';
export type Notification = {
level: NOTIFICATION_LEVEL,
msg: Element<*> | string,
duration?: number | INFINITY
};
/*** Show Notification ***/
export type ShowNotificationAction = {
type: 'SHOW_NOTIFICATION',
payload: Notification
};
export function showNotification(
level: NOTIFICATION_LEVEL = 'info',
msg: Element<*> | string,
duration?: number | INFINITY
): ShowNotificationAction {
return {
type: 'SHOW_NOTIFICATION',
payload: {
level,
msg,
duration
}
};
}
/*** Close notification ***/
export type CloseNotificationAction = {
type: 'CLOSE_NOTIFICATION',
payload: Notification
};
export function closeNotification(
notification: Notification
): CloseNotificationAction {
return {
type: 'CLOSE_NOTIFICATION',
payload: notification
};
}
/*** Union Type ***/
export type NotificationsAction =
| ShowNotificationAction
| CloseNotificationAction;

View File

@ -0,0 +1,29 @@
import { ReactElement } from 'react';
import * as types from './actionTypes';
import { TypeKeys } from './constants';
export type TShowNotification = typeof showNotification;
export function showNotification(
level: types.NOTIFICATION_LEVEL = 'info',
msg: ReactElement<any> | string,
duration?: number | types.INFINITY
): types.ShowNotificationAction {
return {
type: TypeKeys.SHOW_NOTIFICATION,
payload: {
level,
msg,
duration
}
};
}
export type TCloseNotification = typeof closeNotification;
export function closeNotification(
notification: types.Notification
): types.CloseNotificationAction {
return {
type: TypeKeys.CLOSE_NOTIFICATION,
payload: notification
};
}

View File

@ -0,0 +1,28 @@
import { ReactElement } from 'react';
import { TypeKeys } from './constants';
/*** Shared types ***/
export type NOTIFICATION_LEVEL = 'danger' | 'warning' | 'success' | 'info';
export type INFINITY = 'infinity';
export interface Notification {
level: NOTIFICATION_LEVEL;
msg: ReactElement<any> | string;
duration?: number | INFINITY;
}
/*** Close notification ***/
export interface CloseNotificationAction {
type: TypeKeys.CLOSE_NOTIFICATION;
payload: Notification;
}
/*** Show Notification ***/
export interface ShowNotificationAction {
type: TypeKeys.SHOW_NOTIFICATION;
payload: Notification;
}
/*** Union Type ***/
export type NotificationsAction =
| ShowNotificationAction
| CloseNotificationAction;

View File

@ -0,0 +1,4 @@
export enum TypeKeys {
SHOW_NOTIFICATION = 'SHOW_NOTIFICATION',
CLOSE_NOTIFICATION = 'CLOSE_NOTIFICATION'
}

View File

@ -0,0 +1,2 @@
export * from './actionTypes';
export * from './actionCreators';

View File

@ -1,29 +0,0 @@
// @flow
export type FiatRequestedRatesAction = {
type: 'RATES_FIAT_REQUESTED'
};
export function fiatRequestedRates() {
return {
type: 'RATES_FIAT_REQUESTED'
};
}
/*** Set rates ***/
export type FiatSucceededRatesAction = {
type: 'RATES_FIAT_SUCCEEDED',
payload: { [string]: number }
};
export function fiatSucceededRates(payload: {
[string]: number
}): FiatSucceededRatesAction {
return {
type: 'RATES_FIAT_SUCCEEDED',
payload
};
}
/*** Union Type ***/
export type RatesAction = FiatSucceededRatesAction | FiatRequestedRatesAction;

View File

@ -0,0 +1,11 @@
import * as interfaces from './actionTypes';
import { TypeKeys } from './constants';
import { fetchRates, CCResponse } from './actionPayloads';
export type TFetchCCRates = typeof fetchCCRates;
export function fetchCCRates(): interfaces.FetchCCRates {
return {
type: TypeKeys.RATES_FETCH_CC,
payload: fetchRates()
};
}

View File

@ -0,0 +1,22 @@
import { handleJSONResponse } from 'api/utils';
export const symbols = ['USD', 'EUR', 'GBP', 'BTC', 'CHF', 'REP'];
const symbolsURL = symbols.join(',');
// TODO - internationalize
const ERROR_MESSAGE = 'Could not fetch rate data.';
const CCApi = 'https://min-api.cryptocompare.com';
const CCRates = CCSymbols => `${CCApi}/data/price?fsym=ETH&tsyms=${CCSymbols}`;
export interface CCResponse {
BTC: number;
EUR: number;
GBP: number;
CHF: number;
REP: number;
}
export const fetchRates = (): Promise<CCResponse> =>
fetch(CCRates(symbolsURL)).then(response =>
handleJSONResponse(response, ERROR_MESSAGE)
);

View File

@ -0,0 +1,23 @@
import { TypeKeys } from './constants';
import { CCResponse } from './actionPayloads';
export interface FetchCCRates {
type: TypeKeys.RATES_FETCH_CC;
payload: Promise<CCResponse>;
}
/*** Set rates ***/
export interface FetchCCRatesSucceeded {
type: TypeKeys.RATES_FETCH_CC_SUCCEEDED;
payload: CCResponse;
}
export interface FetchCCRatesFailed {
type: TypeKeys.RATES_FETCH_CC_FAILED;
}
/*** Union Type ***/
export type RatesAction =
| FetchCCRatesSucceeded
| FetchCCRates
| FetchCCRatesFailed;

View File

@ -0,0 +1,5 @@
export enum TypeKeys {
RATES_FETCH_CC = 'RATES_FETCH_CC',
RATES_FETCH_CC_FAILED = 'RATES_FETCH_CC_FAILED',
RATES_FETCH_CC_SUCCEEDED = 'RATES_FETCH_CC_SUCCEEDED'
}

View File

@ -0,0 +1,3 @@
export * from './actionCreators';
export * from './actionTypes';
export * from './actionPayloads';

View File

@ -1,169 +0,0 @@
// @flow
import type {
OriginKindSwapAction,
DestinationKindSwapAction,
OriginAmountSwapAction,
DestinationAmountSwapAction,
LoadBityRatesSucceededSwapAction,
DestinationAddressSwapAction,
BityOrderCreateSucceededSwapAction,
BityOrderCreateRequestedSwapAction,
OrderStatusSucceededSwapAction,
ChangeStepSwapAction,
Pairs,
RestartSwapAction,
LoadBityRatesRequestedSwapAction,
StopLoadBityRatesSwapAction,
BityOrderResponse,
BityOrderPostResponse,
OrderStatusRequestedSwapAction,
StopOrderTimerSwapAction,
StartOrderTimerSwapAction,
StartPollBityOrderStatusAction,
StopPollBityOrderStatusAction
} from './swapTypes';
export function changeStepSwap(value: number): ChangeStepSwapAction {
return {
type: 'SWAP_STEP',
value
};
}
export function originKindSwap(value: string): OriginKindSwapAction {
return {
type: 'SWAP_ORIGIN_KIND',
value
};
}
export function destinationKindSwap(value: string): DestinationKindSwapAction {
return {
type: 'SWAP_DESTINATION_KIND',
value
};
}
export function originAmountSwap(value: ?number): OriginAmountSwapAction {
return {
type: 'SWAP_ORIGIN_AMOUNT',
value
};
}
export function destinationAmountSwap(
value: ?number
): DestinationAmountSwapAction {
return {
type: 'SWAP_DESTINATION_AMOUNT',
value
};
}
export function loadBityRatesSucceededSwap(
value: Pairs
): LoadBityRatesSucceededSwapAction {
return {
type: 'SWAP_LOAD_BITY_RATES_SUCCEEDED',
value
};
}
export function destinationAddressSwap(
value: ?string
): DestinationAddressSwapAction {
return {
type: 'SWAP_DESTINATION_ADDRESS',
value
};
}
export function restartSwap(): RestartSwapAction {
return {
type: 'SWAP_RESTART'
};
}
export function loadBityRatesRequestedSwap(): LoadBityRatesRequestedSwapAction {
return {
type: 'SWAP_LOAD_BITY_RATES_REQUESTED'
};
}
export function stopLoadBityRatesSwap(): StopLoadBityRatesSwapAction {
return {
type: 'SWAP_STOP_LOAD_BITY_RATES'
};
}
export function orderTimeSwap(value: number) {
return {
type: 'SWAP_ORDER_TIME',
value
};
}
export function bityOrderCreateSucceededSwap(
payload: BityOrderPostResponse
): BityOrderCreateSucceededSwapAction {
return {
type: 'SWAP_BITY_ORDER_CREATE_SUCCEEDED',
payload
};
}
export function bityOrderCreateRequestedSwap(
amount: number,
destinationAddress: string,
pair: string,
mode: number = 0
): BityOrderCreateRequestedSwapAction {
return {
type: 'SWAP_ORDER_CREATE_REQUESTED',
payload: {
amount,
destinationAddress,
pair,
mode
}
};
}
export function orderStatusSucceededSwap(
payload: BityOrderResponse
): OrderStatusSucceededSwapAction {
return {
type: 'SWAP_BITY_ORDER_STATUS_SUCCEEDED',
payload
};
}
export function orderStatusRequestedSwap(): OrderStatusRequestedSwapAction {
return {
type: 'SWAP_BITY_ORDER_STATUS_REQUESTED'
};
}
export function startOrderTimerSwap(): StartOrderTimerSwapAction {
return {
type: 'SWAP_ORDER_START_TIMER'
};
}
export function stopOrderTimerSwap(): StopOrderTimerSwapAction {
return {
type: 'SWAP_ORDER_STOP_TIMER'
};
}
export function startPollBityOrderStatus(): StartPollBityOrderStatusAction {
return {
type: 'SWAP_START_POLL_BITY_ORDER_STATUS'
};
}
export function stopPollBityOrderStatus(): StopPollBityOrderStatusAction {
return {
type: 'SWAP_STOP_POLL_BITY_ORDER_STATUS'
};
}

View File

@ -0,0 +1,182 @@
import * as interfaces from './actionTypes';
import { TypeKeys } from './constants';
export type TChangeStepSwap = typeof changeStepSwap;
export function changeStepSwap(
payload: number
): interfaces.ChangeStepSwapAction {
return {
type: TypeKeys.SWAP_STEP,
payload
};
}
export type TOriginKindSwap = typeof originKindSwap;
export function originKindSwap(
payload: string
): interfaces.OriginKindSwapAction {
return {
type: TypeKeys.SWAP_ORIGIN_KIND,
payload
};
}
export type TDestinationKindSwap = typeof destinationKindSwap;
export function destinationKindSwap(
payload: string
): interfaces.DestinationKindSwapAction {
return {
type: TypeKeys.SWAP_DESTINATION_KIND,
payload
};
}
export type TOriginAmountSwap = typeof originAmountSwap;
export function originAmountSwap(
payload?: number | null
): interfaces.OriginAmountSwapAction {
return {
type: TypeKeys.SWAP_ORIGIN_AMOUNT,
payload
};
}
export type TDestinationAmountSwap = typeof destinationAmountSwap;
export function destinationAmountSwap(
payload?: number | null
): interfaces.DestinationAmountSwapAction {
return {
type: TypeKeys.SWAP_DESTINATION_AMOUNT,
payload
};
}
export type TLoadBityRatesSucceededSwap = typeof loadBityRatesSucceededSwap;
export function loadBityRatesSucceededSwap(
payload: interfaces.Pairs
): interfaces.LoadBityRatesSucceededSwapAction {
return {
type: TypeKeys.SWAP_LOAD_BITY_RATES_SUCCEEDED,
payload
};
}
export type TDestinationAddressSwap = typeof destinationAddressSwap;
export function destinationAddressSwap(
payload?: string
): interfaces.DestinationAddressSwapAction {
return {
type: TypeKeys.SWAP_DESTINATION_ADDRESS,
payload
};
}
export type TRestartSwap = typeof restartSwap;
export function restartSwap(): interfaces.RestartSwapAction {
return {
type: TypeKeys.SWAP_RESTART
};
}
export type TLoadBityRatesRequestedSwap = typeof loadBityRatesRequestedSwap;
export function loadBityRatesRequestedSwap(): interfaces.LoadBityRatesRequestedSwapAction {
return {
type: TypeKeys.SWAP_LOAD_BITY_RATES_REQUESTED
};
}
export type TStopLoadBityRatesSwap = typeof stopLoadBityRatesSwap;
export function stopLoadBityRatesSwap(): interfaces.StopLoadBityRatesSwapAction {
return {
type: TypeKeys.SWAP_STOP_LOAD_BITY_RATES
};
}
export type TOrderTimeSwap = typeof orderTimeSwap;
export function orderTimeSwap(
payload: number
): interfaces.OrderSwapTimeSwapAction {
return {
type: TypeKeys.SWAP_ORDER_TIME,
payload
};
}
export type TBityOrderCreateSucceededSwap = typeof bityOrderCreateSucceededSwap;
export function bityOrderCreateSucceededSwap(
payload: interfaces.BityOrderPostResponse
): interfaces.BityOrderCreateSucceededSwapAction {
return {
type: TypeKeys.SWAP_BITY_ORDER_CREATE_SUCCEEDED,
payload
};
}
export type TBityOrderCreateRequestedSwap = typeof bityOrderCreateRequestedSwap;
export function bityOrderCreateRequestedSwap(
amount: number,
destinationAddress: string,
pair: string,
mode: number = 0
): interfaces.BityOrderCreateRequestedSwapAction {
return {
type: TypeKeys.SWAP_ORDER_CREATE_REQUESTED,
payload: {
amount,
destinationAddress,
pair,
mode
}
};
}
export function bityOrderCreateFailedSwap(): interfaces.BityOrderCreateFailedSwapAction {
return {
type: TypeKeys.SWAP_ORDER_CREATE_FAILED
};
}
export type TOrderStatusSucceededSwap = typeof orderStatusSucceededSwap;
export function orderStatusSucceededSwap(
payload: interfaces.BityOrderResponse
): interfaces.OrderStatusSucceededSwapAction {
return {
type: TypeKeys.SWAP_BITY_ORDER_STATUS_SUCCEEDED,
payload
};
}
export type TOrderStatusRequestedSwap = typeof orderStatusRequestedSwap;
export function orderStatusRequestedSwap(): interfaces.OrderStatusRequestedSwapAction {
return {
type: TypeKeys.SWAP_BITY_ORDER_STATUS_REQUESTED
};
}
export type TStartOrderTimerSwap = typeof startOrderTimerSwap;
export function startOrderTimerSwap(): interfaces.StartOrderTimerSwapAction {
return {
type: TypeKeys.SWAP_ORDER_START_TIMER
};
}
export type TStopOrderTimerSwap = typeof stopOrderTimerSwap;
export function stopOrderTimerSwap(): interfaces.StopOrderTimerSwapAction {
return {
type: TypeKeys.SWAP_ORDER_STOP_TIMER
};
}
export type TStartPollBityOrderStatus = typeof startPollBityOrderStatus;
export function startPollBityOrderStatus(): interfaces.StartPollBityOrderStatusAction {
return {
type: TypeKeys.SWAP_START_POLL_BITY_ORDER_STATUS
};
}
export type TStopPollBityOrderStatus = typeof stopPollBityOrderStatus;
export function stopPollBityOrderStatus(): interfaces.StopPollBityOrderStatusAction {
return {
type: TypeKeys.SWAP_STOP_POLL_BITY_ORDER_STATUS
};
}

View File

@ -0,0 +1,152 @@
import { TypeKeys } from './constants';
export interface Pairs {
ETHBTC: number;
ETHREP: number;
BTCETH: number;
BTCREP: number;
}
export interface OriginKindSwapAction {
type: TypeKeys.SWAP_ORIGIN_KIND;
payload: string;
}
export interface DestinationKindSwapAction {
type: TypeKeys.SWAP_DESTINATION_KIND;
payload: string;
}
export interface OriginAmountSwapAction {
type: TypeKeys.SWAP_ORIGIN_AMOUNT;
payload?: number | null;
}
export interface DestinationAmountSwapAction {
type: TypeKeys.SWAP_DESTINATION_AMOUNT;
payload?: number | null;
}
export interface LoadBityRatesSucceededSwapAction {
type: TypeKeys.SWAP_LOAD_BITY_RATES_SUCCEEDED;
payload: Pairs;
}
export interface DestinationAddressSwapAction {
type: TypeKeys.SWAP_DESTINATION_ADDRESS;
payload?: string;
}
export interface RestartSwapAction {
type: TypeKeys.SWAP_RESTART;
}
export interface LoadBityRatesRequestedSwapAction {
type: TypeKeys.SWAP_LOAD_BITY_RATES_REQUESTED;
payload?: null;
}
export interface ChangeStepSwapAction {
type: TypeKeys.SWAP_STEP;
payload: number;
}
export interface StopLoadBityRatesSwapAction {
type: TypeKeys.SWAP_STOP_LOAD_BITY_RATES;
}
export interface OrderSwapTimeSwapAction {
type: TypeKeys.SWAP_ORDER_TIME;
payload: number;
}
export interface BityOrderCreateRequestedSwapAction {
type: TypeKeys.SWAP_ORDER_CREATE_REQUESTED;
payload: {
amount: number;
destinationAddress: string;
pair: string;
mode: number;
};
}
interface BityOrderInput {
amount: string;
currency: string;
reference: string;
status: string;
}
interface BityOrderOutput {
amount: string;
currency: string;
reference: string;
status: string;
}
export interface BityOrderResponse {
input: BityOrderInput;
output: BityOrderOutput;
status: string;
}
export type BityOrderPostResponse = BityOrderResponse & {
payment_address: string;
status: string;
input: BityOrderInput;
output: BityOrderOutput;
timestamp_created: string;
validFor: number;
id: string;
};
export interface BityOrderCreateSucceededSwapAction {
type: TypeKeys.SWAP_BITY_ORDER_CREATE_SUCCEEDED;
payload: BityOrderPostResponse;
}
export interface BityOrderCreateFailedSwapAction {
type: TypeKeys.SWAP_ORDER_CREATE_FAILED;
}
export interface OrderStatusRequestedSwapAction {
type: TypeKeys.SWAP_BITY_ORDER_STATUS_REQUESTED;
}
export interface OrderStatusSucceededSwapAction {
type: TypeKeys.SWAP_BITY_ORDER_STATUS_SUCCEEDED;
payload: BityOrderResponse;
}
export interface StartOrderTimerSwapAction {
type: TypeKeys.SWAP_ORDER_START_TIMER;
}
export interface StopOrderTimerSwapAction {
type: TypeKeys.SWAP_ORDER_STOP_TIMER;
}
export interface StartPollBityOrderStatusAction {
type: TypeKeys.SWAP_START_POLL_BITY_ORDER_STATUS;
}
export interface StopPollBityOrderStatusAction {
type: TypeKeys.SWAP_STOP_POLL_BITY_ORDER_STATUS;
}
/*** Action Type Union ***/
export type SwapAction =
| ChangeStepSwapAction
| OriginKindSwapAction
| DestinationKindSwapAction
| OriginAmountSwapAction
| DestinationAmountSwapAction
| LoadBityRatesSucceededSwapAction
| DestinationAddressSwapAction
| RestartSwapAction
| LoadBityRatesRequestedSwapAction
| StopLoadBityRatesSwapAction
| BityOrderCreateRequestedSwapAction
| BityOrderCreateSucceededSwapAction
| OrderStatusSucceededSwapAction
| StartPollBityOrderStatusAction
| BityOrderCreateFailedSwapAction
| OrderSwapTimeSwapAction;

View File

@ -0,0 +1,22 @@
export enum TypeKeys {
SWAP_STEP = 'SWAP_STEP',
SWAP_ORIGIN_KIND = 'SWAP_ORIGIN_KIND',
SWAP_DESTINATION_KIND = 'SWAP_DESTINATION_KIND',
SWAP_ORIGIN_AMOUNT = 'SWAP_ORIGIN_AMOUNT',
SWAP_DESTINATION_AMOUNT = 'SWAP_DESTINATION_AMOUNT',
SWAP_LOAD_BITY_RATES_SUCCEEDED = 'SWAP_LOAD_BITY_RATES_SUCCEEDED',
SWAP_DESTINATION_ADDRESS = 'SWAP_DESTINATION_ADDRESS',
SWAP_RESTART = 'SWAP_RESTART',
SWAP_LOAD_BITY_RATES_REQUESTED = 'SWAP_LOAD_BITY_RATES_REQUESTED',
SWAP_STOP_LOAD_BITY_RATES = 'SWAP_STOP_LOAD_BITY_RATES',
SWAP_ORDER_TIME = 'SWAP_ORDER_TIME',
SWAP_BITY_ORDER_CREATE_SUCCEEDED = 'SWAP_BITY_ORDER_CREATE_SUCCEEDED',
SWAP_BITY_ORDER_STATUS_SUCCEEDED = 'SWAP_BITY_ORDER_STATUS_SUCCEEDED',
SWAP_BITY_ORDER_STATUS_REQUESTED = 'SWAP_BITY_ORDER_STATUS_REQUESTED',
SWAP_ORDER_START_TIMER = 'SWAP_ORDER_START_TIMER',
SWAP_ORDER_STOP_TIMER = 'SWAP_ORDER_STOP_TIMER',
SWAP_START_POLL_BITY_ORDER_STATUS = 'SWAP_START_POLL_BITY_ORDER_STATUS',
SWAP_STOP_POLL_BITY_ORDER_STATUS = 'SWAP_STOP_POLL_BITY_ORDER_STATUS',
SWAP_ORDER_CREATE_REQUESTED = 'SWAP_ORDER_CREATE_REQUESTED',
SWAP_ORDER_CREATE_FAILED = 'SWAP_ORDER_CREATE_FAILED'
}

View File

@ -0,0 +1,2 @@
export * from './actionTypes';
export * from './actionCreators';

View File

@ -1,128 +0,0 @@
export type Pairs = {
ETHBTC: number,
ETHREP: number,
BTCETH: number,
BTCREP: number
};
export type OriginKindSwapAction = {
type: 'SWAP_ORIGIN_KIND',
value: string
};
export type DestinationKindSwapAction = {
type: 'SWAP_DESTINATION_KIND',
value: string
};
export type OriginAmountSwapAction = {
type: 'SWAP_ORIGIN_AMOUNT',
value: ?number
};
export type DestinationAmountSwapAction = {
type: 'SWAP_DESTINATION_AMOUNT',
value: ?number
};
export type LoadBityRatesSucceededSwapAction = {
type: 'SWAP_LOAD_BITY_RATES_SUCCEEDED',
value: Pairs
};
export type DestinationAddressSwapAction = {
type: 'SWAP_DESTINATION_ADDRESS',
value: ?number
};
export type RestartSwapAction = {
type: 'SWAP_RESTART'
};
export type LoadBityRatesRequestedSwapAction = {
type: 'SWAP_LOAD_BITY_RATES_REQUESTED'
};
export type ChangeStepSwapAction = {
type: 'SWAP_STEP',
value: number
};
export type StopLoadBityRatesSwapAction = {
type: 'SWAP_STOP_LOAD_BITY_RATES'
};
export type BityOrderCreateRequestedSwapAction = {
type: 'SWAP_ORDER_CREATE_REQUESTED',
payload: {
amount: number,
destinationAddress: string,
pair: string,
mode: number
}
};
type BityOrderInput = {
amount: string
};
type BityOrderOutput = {
amount: string
};
export type BityOrderResponse = {
status: string
};
export type BityOrderPostResponse = BityOrderResponse & {
payment_address: string,
status: string,
input: BityOrderInput,
output: BityOrderOutput,
timestamp_created: string,
validFor: number
};
export type BityOrderCreateSucceededSwapAction = {
type: 'SWAP_BITY_ORDER_CREATE_SUCCEEDED',
payload: BityOrderPostResponse
};
export type OrderStatusRequestedSwapAction = {
type: 'SWAP_BITY_ORDER_STATUS_REQUESTED',
payload: BityOrderResponse
};
export type OrderStatusSucceededSwapAction = {
type: 'SWAP_BITY_ORDER_STATUS_SUCCEEDED',
payload: BityOrderResponse
};
export type StartOrderTimerSwapAction = {
type: 'SWAP_ORDER_START_TIMER'
};
export type StopOrderTimerSwapAction = {
type: 'SWAP_ORDER_STOP_TIMER'
};
export type StartPollBityOrderStatusAction = {
type: 'SWAP_START_POLL_BITY_ORDER_STATUS'
};
export type StopPollBityOrderStatusAction = {
type: 'SWAP_STOP_POLL_BITY_ORDER_STATUS'
};
/*** Action Type Union ***/
export type SwapAction =
| ChangeStepSwapAction
| OriginKindSwapAction
| DestinationKindSwapAction
| OriginAmountSwapAction
| DestinationAmountSwapAction
| LoadBityRatesSucceededSwapAction
| DestinationAddressSwapAction
| RestartSwapAction
| LoadBityRatesRequestedSwapAction
| StopLoadBityRatesSwapAction
| BityOrderCreateRequestedSwapAction
| BityOrderCreateSucceededSwapAction
| BityOrderResponse
| OrderStatusSucceededSwapAction
| StartPollBityOrderStatusAction;

View File

@ -1,134 +0,0 @@
// @flow
import type { IWallet } from 'libs/wallet/IWallet';
import Big from 'bignumber.js';
import { Wei } from 'libs/units';
/*** Unlock Private Key ***/
export type PrivateKeyUnlockParams = {
key: string,
password: string
};
export type UnlockPrivateKeyAction = {
type: 'WALLET_UNLOCK_PRIVATE_KEY',
payload: PrivateKeyUnlockParams
};
export function unlockPrivateKey(
value: PrivateKeyUnlockParams
): UnlockPrivateKeyAction {
return {
type: 'WALLET_UNLOCK_PRIVATE_KEY',
payload: value
};
}
/*** Unlock Keystore File ***/
export type KeystoreUnlockParams = {
file: string,
password: string
};
export type UnlockKeystoreAction = {
type: 'WALLET_UNLOCK_KEYSTORE',
payload: KeystoreUnlockParams
};
export function unlockKeystore(
value: KeystoreUnlockParams
): UnlockKeystoreAction {
return {
type: 'WALLET_UNLOCK_KEYSTORE',
payload: value
};
}
/*** Unlock Mnemonic ***/
export type MnemonicUnlockParams = {
phrase: string,
pass: string,
path: string,
address: string
};
export type UnlockMnemonicAction = {
type: 'WALLET_UNLOCK_MNEMONIC',
payload: MnemonicUnlockParams
};
export function unlockMnemonic(
value: MnemonicUnlockParams
): UnlockMnemonicAction {
return {
type: 'WALLET_UNLOCK_MNEMONIC',
payload: value
};
}
/*** Set Wallet ***/
export type SetWalletAction = {
type: 'WALLET_SET',
payload: IWallet
};
export function setWallet(value: IWallet): SetWalletAction {
return {
type: 'WALLET_SET',
payload: value
};
}
/*** Set Balance ***/
export type SetBalanceAction = {
type: 'WALLET_SET_BALANCE',
payload: Wei
};
export function setBalance(value: Wei): SetBalanceAction {
return {
type: 'WALLET_SET_BALANCE',
payload: value
};
}
/*** Set Token Balance ***/
export type SetTokenBalancesAction = {
type: 'WALLET_SET_TOKEN_BALANCES',
payload: {
[string]: Big
}
};
export function setTokenBalances(payload: {
[string]: Big
}): SetTokenBalancesAction {
return {
type: 'WALLET_SET_TOKEN_BALANCES',
payload
};
}
/*** Broadcast Tx ***/
export type BroadcastTxRequestedAction = {
type: 'WALLET_BROADCAST_TX_REQUESTED',
payload: {
signedTx: string
}
};
export function broadcastTx(signedTx: string): BroadcastTxRequestedAction {
return {
type: 'WALLET_BROADCAST_TX_REQUESTED',
payload: {
signedTx
}
};
}
/*** Union Type ***/
export type WalletAction =
| UnlockPrivateKeyAction
| SetWalletAction
| SetBalanceAction
| SetTokenBalancesAction
| BroadcastTxRequestedAction;

View File

@ -0,0 +1,108 @@
import { BigNumber } from 'bignumber.js';
import { Wei } from 'libs/units';
import { IWallet } from 'libs/wallet/IWallet';
import * as types from './actionTypes';
import * as constants from './constants';
export type TUnlockPrivateKey = typeof unlockPrivateKey;
export function unlockPrivateKey(
value: types.PrivateKeyUnlockParams
): types.UnlockPrivateKeyAction {
return {
type: constants.WALLET_UNLOCK_PRIVATE_KEY,
payload: value
};
}
export type TUnlockKeystore = typeof unlockKeystore;
export function unlockKeystore(
value: types.KeystoreUnlockParams
): types.UnlockKeystoreAction {
return {
type: constants.WALLET_UNLOCK_KEYSTORE,
payload: value
};
}
export type TUnlockMnemonic = typeof unlockMnemonic;
export function unlockMnemonic(
value: types.MnemonicUnlockParams
): types.UnlockMnemonicAction {
return {
type: constants.WALLET_UNLOCK_MNEMONIC,
payload: value
};
}
export type TSetWallet = typeof setWallet;
export function setWallet(value: IWallet): types.SetWalletAction {
return {
type: constants.WALLET_SET,
payload: value
};
}
export type TSetBalance = typeof setBalance;
export function setBalance(value: Wei): types.SetBalanceAction {
return {
type: constants.WALLET_SET_BALANCE,
payload: value
};
}
export type TSetTokenBalances = typeof setTokenBalances;
export function setTokenBalances(payload: {
[key: string]: BigNumber;
}): types.SetTokenBalancesAction {
return {
type: constants.WALLET_SET_TOKEN_BALANCES,
payload
};
}
export type TBroadcastTx = typeof broadcastTx;
export function broadcastTx(
signedTx: string
): types.BroadcastTxRequestedAction {
return {
type: constants.WALLET_BROADCAST_TX_REQUESTED,
payload: {
signedTx
}
};
}
export type TBroadcastTxSucceded = typeof broadcastTxSucceded;
export function broadcastTxSucceded(
txHash: string,
signedTx: string
): types.BroadcastTxSuccededAction {
return {
type: constants.WALLET_BROADCAST_TX_SUCCEEDED,
payload: {
txHash,
signedTx
}
};
}
export type TBroadCastTxFailed = typeof broadCastTxFailed;
export function broadCastTxFailed(
signedTx: string,
errorMsg: string
): types.BroadcastTxFailedAction {
return {
type: constants.WALLET_BROADCAST_TX_FAILED,
payload: {
signedTx,
error: errorMsg
}
};
}
export type TResetWallet = typeof resetWallet;
export function resetWallet() {
return {
type: constants.WALLET_RESET
};
}

View File

@ -0,0 +1,97 @@
import { BigNumber } from 'bignumber.js';
import { Wei } from 'libs/units';
import { IWallet } from 'libs/wallet/IWallet';
/*** Unlock Private Key ***/
export interface PrivateKeyUnlockParams {
key: string;
password: string;
}
export interface UnlockPrivateKeyAction {
type: 'WALLET_UNLOCK_PRIVATE_KEY';
payload: PrivateKeyUnlockParams;
}
export interface UnlockMnemonicAction {
type: 'WALLET_UNLOCK_MNEMONIC';
payload: MnemonicUnlockParams;
}
/*** Set Wallet ***/
export interface SetWalletAction {
type: 'WALLET_SET';
payload: IWallet;
}
/*** Reset Wallet ***/
export interface ResetWalletAction {
type: 'WALLET_RESET';
}
/*** Set Balance ***/
export interface SetBalanceAction {
type: 'WALLET_SET_BALANCE';
payload: Wei;
}
/*** Set Token Balance ***/
export interface SetTokenBalancesAction {
type: 'WALLET_SET_TOKEN_BALANCES';
payload: {
[key: string]: BigNumber;
};
}
/*** Broadcast Tx ***/
export interface BroadcastTxRequestedAction {
type: 'WALLET_BROADCAST_TX_REQUESTED';
payload: {
signedTx: string;
};
}
/*** Unlock Mnemonic ***/
export interface MnemonicUnlockParams {
phrase: string;
pass: string;
path: string;
address: string;
}
/*** Unlock Keystore File ***/
export interface KeystoreUnlockParams {
file: string;
password: string;
}
export interface UnlockKeystoreAction {
type: 'WALLET_UNLOCK_KEYSTORE';
payload: KeystoreUnlockParams;
}
export interface BroadcastTxSuccededAction {
type: 'WALLET_BROADCAST_TX_SUCCEEDED';
payload: {
txHash: string;
signedTx: string;
};
}
export interface BroadcastTxFailedAction {
type: 'WALLET_BROADCAST_TX_FAILED';
payload: {
signedTx: string;
error: string;
};
}
/*** Union Type ***/
export type WalletAction =
| UnlockPrivateKeyAction
| SetWalletAction
| ResetWalletAction
| SetBalanceAction
| SetTokenBalancesAction
| BroadcastTxRequestedAction
| BroadcastTxFailedAction
| BroadcastTxSuccededAction;

View File

@ -0,0 +1,10 @@
export const WALLET_UNLOCK_PRIVATE_KEY = 'WALLET_UNLOCK_PRIVATE_KEY';
export const WALLET_UNLOCK_KEYSTORE = 'WALLET_UNLOCK_KEYSTORE';
export const WALLET_UNLOCK_MNEMONIC = 'WALLET_UNLOCK_MNEMONIC';
export const WALLET_SET = 'WALLET_SET';
export const WALLET_SET_BALANCE = 'WALLET_SET_BALANCE';
export const WALLET_SET_TOKEN_BALANCES = 'WALLET_SET_TOKEN_BALANCES';
export const WALLET_BROADCAST_TX_REQUESTED = 'WALLET_BROADCAST_TX_REQUESTED';
export const WALLET_BROADCAST_TX_FAILED = 'WALLET_BROADCAST_TX_FAILED';
export const WALLET_BROADCAST_TX_SUCCEEDED = 'WALLET_BROADCAST_TX_SUCCEEDED';
export const WALLET_RESET = 'WALLET_RESET';

View File

@ -0,0 +1,3 @@
export * from './constants';
export * from './actionTypes';
export * from './actionCreators';

View File

@ -1,22 +1,10 @@
// @flow
import bityConfig from 'config/bity';
import { checkHttpStatus, parseJSON } from './utils';
// import { combineAndUpper } from 'utils/formatters';
// function findRateFromBityRateList(rateObjects, pairName: string) {
// return rateObjects.find(x => x.pair === pairName);
// }
// function _getRate(bityRates, originKind: string, destinationKind: string) {
// const pairName = combineAndUpper(originKind, destinationKind);
// const rateObjects = bityRates.objects;
// return findRateFromBityRateList(rateObjects, pairName);
// }
export function getAllRates() {
const mappedRates = {};
return _getAllRates().then(bityRates => {
bityRates.objects.forEach(each => {
return _getAllRates().then((bityRates) => {
bityRates.objects.forEach((each) => {
const pairName = each.pair;
mappedRates[pairName] = parseFloat(each.rate_we_sell);
});
@ -44,11 +32,11 @@ export function postOrder(
.then(parseJSON);
}
export function getOrderStatus(orderid: string) {
export function getOrderStatus(orderId: string) {
return fetch(`${bityConfig.serverURL}/status`, {
method: 'POST',
body: JSON.stringify({
orderid
orderid: orderId
}),
headers: bityConfig.postConfig.headers
})
@ -60,6 +48,4 @@ function _getAllRates() {
return fetch(`${bityConfig.bityURL}/v1/rate2/`)
.then(checkHttpStatus)
.then(parseJSON);
}
// function requestOrderStatus() {}
}

View File

@ -2,9 +2,7 @@ export function checkHttpStatus(response) {
if (response.status >= 200 && response.status < 300) {
return response;
} else {
let error = new Error(response.statusText);
error.response = response;
throw error;
return new Error(response.statusText);
}
}
@ -14,8 +12,7 @@ export function parseJSON(response) {
export async function handleJSONResponse(response, errorMessage) {
if (response.ok) {
const json = await response.json();
return json;
return await response.json();
}
if (errorMessage) {
throw new Error(errorMessage);

View File

@ -0,0 +1,33 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="15.5 15.5 224.5 224.5">
<defs>
<radialGradient cy="0" cx="0.5" id="r">
<stop stop-color="#f06b59"/>
<stop offset="1" stop-color="#df2227"/>
</radialGradient>
<radialGradient r="0.76" cy="0.3" cx="0.65" id="g">
<stop offset="0.65" stop-color="#4cb749"/>
<stop offset="1" stop-color="#388b41"/>
</radialGradient>
<radialGradient r="0.8" cy="0.25" cx="0.36" id="y">
<stop offset="0.6" stop-color="#FCD209"/>
<stop offset="0.7" stop-color="#f7c616"/>
<stop offset="1" stop-color="#bc821e"/>
</radialGradient>
<radialGradient r="1" cy="0" cx="0.5" spreadMethod="pad" id="cf">
<stop offset="0.1" stop-color="#7FB3DF"/>
<stop offset="0.9" stop-color="#0F5B94"/>
</radialGradient>
<radialGradient id="cb" r="1" cy="0" cx="0.5">
<stop offset="0" stop-color="#F6F0EE"/>
<stop offset="1" stop-color="#ddd"/>
</radialGradient>
</defs>
<path d="m198,148a70,70 0 0 0 -140,0l20,0a50,50 0 0 1 100,0" fill-opacity="0.1"/>
<circle r="45" cx="127.5" cy="127.6" fill="url(#cf)" stroke="url(#cb)" stroke-width="9" />
<path d="m228,78a112,112 0 0 0 -193,-13l45,78a50,50 0 0 1 47,-65" fill="url(#r)"/>
<path d="m35,65a112,112 0 0 0 84,174l47,-80a50,50 0 0 1 -86,-16" fill="url(#g)"/>
<path d="m119,239a112,112 0 0 0 109,-161l-101,0a50,50 0 0 1 39,81" fill="url(#y)"/>
<path d="m35,65l45,78a50,50 0 0 1 2,-34l-45,-47" opacity="0.075"/>
<path d="m119,239l47,-80a50,50 0 0 1 -29,17l-20,63" opacity="0.05"/>
<path d="m228,78l-101,0a50,50 0 0 1 39,19l64,-16" opacity="0.05"/>
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 140 KiB

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="9.75 9.75 100.5 100.5"><linearGradient id="a" gradientTransform="matrix(0 97.396584 -97.396584 0 51.620814 11.389501)" gradientUnits="userSpaceOnUse" spreadMethod="pad" x1="0" x2="1" y1="0" y2="0"><stop offset="0" stop-color="#ff1b2d"/><stop offset=".3" stop-color="#ff1b2d"/><stop offset=".61" stop-color="#ff1b2d"/><stop offset="1" stop-color="#a70014"/></linearGradient><linearGradient id="b" gradientTransform="matrix(0 86.142511 -86.142511 0 76.85435 17.130095)" gradientUnits="userSpaceOnUse" spreadMethod="pad" x1="0" x2="1" y1="0" y2="0"><stop offset="0" stop-color="#9c0000"/><stop offset=".7" stop-color="#ff4b4b"/><stop offset="1" stop-color="#ff4b4b"/></linearGradient><path d="m60 9.75c-27.75 0-50.25 22.5-50.25 50.25l0 0c0 26.95 21.22 48.94 47.86 50.19l0 0c .8.04 1.59.06 2.39.06l0 0c12.87 0 24.6-4.84 33.49-12.79l0 0c-5.89 3.91-12.78 6.15-20.14 6.15l0 0c-11.97 0-22.68-5.94-29.89-15.3l0 0c-5.56-6.56-9.15-16.25-9.4-27.13l0 0c0-.03 0-2.34 0-2.37l0 0c .25-10.88 3.84-20.58 9.4-27.13l0 0c7.21-9.36 17.93-15.3 29.89-15.3l0 0c7.36 0 14.25 2.25 20.14 6.16l0 0c-8.84-7.91-20.51-12.74-33.3-12.79l0 0c-.06 0-.13 0-.19 0z" fill="url(#a)"/><path d="m43.46 31.68c4.61-5.44 10.57-8.73 17.07-8.73l0 0c14.63 0 26.49 16.59 26.49 37.04l0 0c0 20.46-11.86 37.04-26.49 37.04l0 0c-6.51 0-12.46-3.28-17.07-8.72l0 0c7.21 9.36 17.92 15.3 29.89 15.3l0 0c7.36 0 14.25-2.25 20.14-6.15l0 0c10.29-9.2 16.76-22.57 16.76-37.46l0 0c0-14.88-6.47-28.26-16.76-37.46l0 0c-5.89-3.91-12.78-6.15-20.14-6.15l0 0c-11.97 0-22.68 5.94-29.89 15.3" fill="url(#b)"/></svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@ -0,0 +1 @@
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 496.16 496.16"><defs><style>.cls-1{fill:#04778d}.cls-2{fill:#fff}.cls-3{fill:#f34002}</style></defs><title>Offline Icon</title><path class="cls-1" d="M496.16 248.09C496.16 111.06 385.09 0 248.08 0S0 111.06 0 248.09s111.07 248.07 248.08 248.07 248.08-111.07 248.08-248.07z"/><circle class="cls-2" cx="248.08" cy="332.81" r="49.15"/><path class="cls-2" d="M172.13 289.13c20.29-23.3 47.26-36.13 76-36.13s55.66 12.83 76 36.13l16-18.34c-24.55-28.2-57.2-43.73-91.93-43.73s-67.37 15.53-91.92 43.73l16 18.34z"/><path class="cls-2" d="M138.56 248.41c29.26-33.6 68.15-52.1 109.52-52.1s80.27 18.5 109.52 52.1l16-18.34c-33.52-38.49-78.09-59.7-125.49-59.7s-92 21.2-125.49 59.7l16 18.34z"/><path class="cls-2" d="M105.31 208.06c38.14-43.8 88.84-67.92 142.77-67.92s104.64 24.12 142.77 67.92l16-18.34C364.42 141 308 114.21 248.08 114.21S131.74 141 89.34 189.72l16 18.34zM248.08 376.37h.01v.01h-.01z"/><circle class="cls-3" cx="340.29" cy="343.74" r="63.78"/><path class="cls-2" d="M315 365.51l16.82-21.59-14.12-19.21a36.92 36.92 0 0 1-3-4.8 8.64 8.64 0 0 1-1-3.86 4.31 4.31 0 0 1 1.92-3.4 7.36 7.36 0 0 1 4.69-1.51 7 7 0 0 1 4.95 1.65 42.15 42.15 0 0 1 4.9 6.11l11.28 16 12.05-16 2.54-3.47a18.66 18.66 0 0 1 2-2.39 6.53 6.53 0 0 1 2.18-1.42 7.61 7.61 0 0 1 2.79-.47 7.11 7.11 0 0 1 4.69 1.51 4.53 4.53 0 0 1 1.82 3.58q0 3-3.95 8.21l-14.82 19.48 16 21.59a35.31 35.31 0 0 1 3.13 4.71 7.7 7.7 0 0 1 1 3.54 5.09 5.09 0 0 1-.87 2.89 6 6 0 0 1-2.46 2.07 8.17 8.17 0 0 1-3.59.77 7.7 7.7 0 0 1-3.64-.79 7.44 7.44 0 0 1-2.41-2q-.92-1.17-3.44-4.55l-13.23-18.3-14.05 18.84q-1.64 2.26-2.33 3.16a12.43 12.43 0 0 1-1.67 1.76 7.32 7.32 0 0 1-2.31 1.35 9 9 0 0 1-3.13.5 7 7 0 0 1-4.59-1.49 5.27 5.27 0 0 1-1.82-4.33q-.04-3.32 3.67-8.14z"/></svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@ -0,0 +1 @@
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 496.16 496.16"><defs><style>.cls-1{fill:#04778d}.cls-2{fill:#fff}.cls-3{fill:#00e65f}</style></defs><title>Online Icon</title><path class="cls-1" d="M496.16 248.09C496.16 111.06 385.09 0 248.08 0S0 111.06 0 248.09s111.07 248.07 248.08 248.07 248.08-111.07 248.08-248.07z"/><circle class="cls-2" cx="248.08" cy="332.81" r="49.15"/><path class="cls-2" d="M172.13 289.13c20.29-23.3 47.26-36.13 76-36.13s55.66 12.83 76 36.13l16-18.34c-24.55-28.2-57.2-43.73-91.93-43.73s-67.37 15.53-91.92 43.73l16 18.34z"/><path class="cls-2" d="M138.56 248.41c29.26-33.6 68.15-52.1 109.52-52.1s80.27 18.5 109.52 52.1l16-18.34c-33.52-38.49-78.09-59.7-125.49-59.7s-92 21.2-125.49 59.7l16 18.34z"/><path class="cls-2" d="M105.31 208.06c38.14-43.8 88.84-67.92 142.77-67.92s104.64 24.12 142.77 67.92l16-18.34C364.42 141 308 114.21 248.08 114.21S131.74 141 89.34 189.72l16 18.34zM248.08 376.37h.01v.01h-.01z"/><circle class="cls-3" cx="340.29" cy="343.74" r="63.78"/><path class="cls-2" d="M383.53 317.43c-1.85-4.77-5.61-4-9.7-3.21-2.44.51-13.28 3.68-30.44 21.77a144.85 144.85 0 0 0-14.91 18.06c-1.89-2.32-4.05-4.8-6.33-7.08a91.5 91.5 0 0 0-15.11-12 6.95 6.95 0 0 0-7.26 11.85 78.31 78.31 0 0 1 12.54 10 107.89 107.89 0 0 1 11.29 14 6.95 6.95 0 0 0 12.52-2.17c0-.06 2.77-7.68 17.32-23 11.72-12.36 19.54-16.29 22.25-17.38h.08l.25-.12a7.69 7.69 0 0 1 .73-.24h-.2l3.65-1.58c3.55-1.54 4.7-5.33 3.32-8.9z"/></svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@ -1,33 +1,11 @@
@import "common/sass/variables";
@import "common/sass/mixins";
.AlphaAgreement {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
@include cover-message;
background: $brand-warning;
overflow: auto;
z-index: 10000;
&-content {
max-width: 520px;
padding: 20px;
margin: 0 auto;
color: #fff;
text-shadow: 1px 1px 1px rgba(#000, 0.12);
overflow: auto;
h2 {
font-size: 42px;
margin-bottom: 20px;
}
p {
font-size: 20px;
margin-bottom: 15px;
}
&-buttons {
padding-top: 20px;

View File

@ -3,35 +3,17 @@ import './index.scss';
const LS_KEY = 'acknowledged-alpha';
export default class AlphaAgreement extends React.Component {
state = {
isFading: false,
hasAcknowledged: false
interface State {
isFading: boolean;
hasAcknowledged: boolean;
}
export default class AlphaAgreement extends React.Component<{}, State> {
public state = {
hasAcknowledged: !!localStorage.getItem(LS_KEY),
isFading: false
};
constructor(props) {
super(props);
this.state = {
hasAcknowledged: localStorage.getItem(LS_KEY),
isFading: false
};
}
_continue = () => {
localStorage.setItem(LS_KEY, true);
this.setState({ isFading: true });
setTimeout(() => {
this.setState({ hasAcknowledged: true });
}, 1000);
};
_reject = () => {
window.location = 'https://myetherwallet.com';
};
render() {
public render() {
if (this.state.hasAcknowledged) {
return null;
}
@ -58,13 +40,13 @@ export default class AlphaAgreement extends React.Component {
<div className="AlphaAgreement-content-buttons">
<button
className="AlphaAgreement-content-buttons-btn is-reject"
onClick={this._reject}
onClick={this.reject}
>
No, Take Me to v3
</button>
<button
className="AlphaAgreement-content-buttons-btn is-continue"
onClick={this._continue}
onClick={this.doContinue}
>
Yes, Continue to v4
</button>
@ -73,4 +55,17 @@ export default class AlphaAgreement extends React.Component {
</div>
);
}
private doContinue = () => {
localStorage.setItem(LS_KEY, 'true');
this.setState({ isFading: true });
setTimeout(() => {
this.setState({ hasAcknowledged: true });
}, 1000);
};
private reject = () => {
window.location.assign('https://myetherwallet.com');
};
}

View File

@ -1,106 +0,0 @@
// @flow
import './AccountInfo.scss';
import React from 'react';
import translate from 'translations';
import { Identicon } from 'components/ui';
import { formatNumber } from 'utils/formatters';
import type { IWallet } from 'libs/wallet';
import type { NetworkConfig } from 'config/data';
import { Ether } from 'libs/units';
import type { FiatRequestedRatesAction } from 'actions/rates';
type Props = {
balance: Ether,
wallet: IWallet,
network: NetworkConfig,
fiatRequestedRates: () => FiatRequestedRatesAction
};
export default class AccountInfo extends React.Component {
props: Props;
state = {
showLongBalance: false,
address: ''
};
componentDidMount() {
this.props.fiatRequestedRates();
this.props.wallet.getAddress().then(addr => {
this.setState({ address: addr });
});
}
toggleShowLongBalance = (e: SyntheticMouseEvent) => {
e.preventDefault();
this.setState(state => {
return {
showLongBalance: !state.showLongBalance
};
});
};
render() {
const { network, balance } = this.props;
const { blockExplorer, tokenExplorer } = network;
const { address } = this.state;
return (
<div className="AccountInfo">
<div className="AccountInfo-section">
<h5 className="AccountInfo-section-header">
{translate('sidebar_AccountAddr')}
</h5>
<div className="AccountInfo-address">
<div className="AccountInfo-address-icon">
<Identicon address={address} size="100%" />
</div>
<div className="AccountInfo-address-addr">
{address}
</div>
</div>
</div>
<div className="AccountInfo-section">
<h5 className="AccountInfo-section-header">
{translate('sidebar_AccountBal')}
</h5>
<ul className="AccountInfo-list">
<li className="AccountInfo-list-item">
<span
className="AccountInfo-list-item-clickable mono wrap"
onClick={this.toggleShowLongBalance}
>
{this.state.showLongBalance
? balance ? balance.toString() : '???'
: balance ? formatNumber(balance.amount) : '???'}
</span>
{` ${network.name}`}
</li>
</ul>
</div>
{(!!blockExplorer || !!tokenExplorer) &&
<div className="AccountInfo-section">
<h5 className="AccountInfo-section-header">
{translate('sidebar_TransHistory')}
</h5>
<ul className="AccountInfo-list">
{!!blockExplorer &&
<li className="AccountInfo-list-item">
<a href={blockExplorer.address(address)} target="_blank">
{`${network.name} (${blockExplorer.name})`}
</a>
</li>}
{!!tokenExplorer &&
<li className="AccountInfo-list-item">
<a href={tokenExplorer.address(address)} target="_blank">
{`Tokens (${tokenExplorer.name})`}
</a>
</li>}
</ul>
</div>}
</div>
);
}
}

View File

@ -0,0 +1,118 @@
import { TFetchCCRates } from 'actions/rates';
import { Identicon } from 'components/ui';
import { NetworkConfig } from 'config/data';
import { Ether } from 'libs/units';
import { IWallet } from 'libs/wallet';
import React from 'react';
import translate from 'translations';
import { formatNumber } from 'utils/formatters';
import './AccountInfo.scss';
interface Props {
balance: Ether;
wallet: IWallet;
network: NetworkConfig;
fetchCCRates: TFetchCCRates;
}
interface State {
showLongBalance: boolean;
address: string;
}
export default class AccountInfo extends React.Component<Props, State> {
public state = {
showLongBalance: false,
address: ''
};
public async setAddressFromWallet() {
const address = await this.props.wallet.getAddress();
if (address !== this.state.address) {
this.setState({ address });
}
}
public componentDidMount() {
this.props.fetchCCRates();
this.setAddressFromWallet();
}
public componentDidUpdate() {
this.setAddressFromWallet();
}
// TODO: don't use any;
public toggleShowLongBalance = (e: any) => {
e.preventDefault();
this.setState(state => {
return {
showLongBalance: !state.showLongBalance
};
});
};
public render() {
const { network, balance } = this.props;
const { blockExplorer, tokenExplorer } = network;
const { address } = this.state;
return (
<div className="AccountInfo">
<div className="AccountInfo-section">
<h5 className="AccountInfo-section-header">
{translate('sidebar_AccountAddr')}
</h5>
<div className="AccountInfo-address">
<div className="AccountInfo-address-icon">
<Identicon address={address} size="100%" />
</div>
<div className="AccountInfo-address-addr">{address}</div>
</div>
</div>
<div className="AccountInfo-section">
<h5 className="AccountInfo-section-header">
{translate('sidebar_AccountBal')}
</h5>
<ul className="AccountInfo-list">
<li className="AccountInfo-list-item">
<span
className="AccountInfo-list-item-clickable mono wrap"
onClick={this.toggleShowLongBalance}
>
{this.state.showLongBalance
? balance ? balance.toString() : '???'
: balance ? formatNumber(balance.amount) : '???'}
</span>
{` ${network.name}`}
</li>
</ul>
</div>
{(!!blockExplorer || !!tokenExplorer) && (
<div className="AccountInfo-section">
<h5 className="AccountInfo-section-header">
{translate('sidebar_TransHistory')}
</h5>
<ul className="AccountInfo-list">
{!!blockExplorer && (
<li className="AccountInfo-list-item">
<a href={blockExplorer.address(address)} target="_blank">
{`${network.name} (${blockExplorer.name})`}
</a>
</li>
)}
{!!tokenExplorer && (
<li className="AccountInfo-list-item">
<a href={tokenExplorer.address(address)} target="_blank">
{`Tokens (${tokenExplorer.name})`}
</a>
</li>
)}
</ul>
</div>
)}
</div>
);
}
}

View File

@ -1,47 +1,46 @@
// @flow
import './EquivalentValues.scss';
import { Ether } from 'libs/units';
import React from 'react';
import translate from 'translations';
import { formatNumber } from 'utils/formatters';
import { Ether } from 'libs/units';
import './EquivalentValues.scss';
import { State } from 'reducers/rates';
import { symbols } from 'actions/rates';
const ratesKeys = ['BTC', 'REP', 'EUR', 'USD', 'GBP', 'CHF'];
interface Props {
balance?: Ether;
rates?: State['rates'];
ratesError?: State['ratesError'];
}
type Props = {
balance: ?Ether,
rates: ?{ [string]: number }
};
export default class EquivalentValues extends React.Component {
props: Props;
render() {
const { balance, rates } = this.props;
export default class EquivalentValues extends React.Component<Props, {}> {
public render() {
const { balance, rates, ratesError } = this.props;
return (
<div className="EquivalentValues">
<h5 className="EquivalentValues-title">
{translate('sidebar_Equiv')}
</h5>
<h5 className="EquivalentValues-title">{translate('sidebar_Equiv')}</h5>
<ul className="EquivalentValues-values">
{rates
? ratesKeys.map(key => {
if (!rates[key]) return null;
? symbols.map(key => {
if (!rates[key]) {
return null;
}
return (
<li className="EquivalentValues-values-currency" key={key}>
<span className="EquivalentValues-values-currency-label">
{key}:
</span>
<span className="EquivalentValues-values-currency-value">
{' '}{balance
{' '}
{balance
? formatNumber(balance.amount.times(rates[key]))
: '???'}
</span>
</li>
);
})
: <h5>No rates were loaded.</h5>}
: ratesError && <h5>{ratesError}</h5>}
</ul>
</div>
);

View File

@ -0,0 +1,55 @@
import React from 'react';
import {
forceOfflineConfig as dForceOfflineConfig,
TForceOfflineConfig
} from 'actions/config';
import OfflineSymbol from 'components/ui/OfflineSymbol';
import { connect } from 'react-redux';
import { AppState } from 'reducers';
type sizeType = 'small' | 'medium' | 'large';
interface OfflineToggleProps {
offline: boolean;
forceOffline: boolean;
forceOfflineConfig: TForceOfflineConfig;
size?: sizeType;
}
class OfflineToggle extends React.Component<OfflineToggleProps, {}> {
public render() {
const { forceOfflineConfig, offline, forceOffline, size } = this.props;
return (
<div>
{!offline ? (
<div className="row text-center">
<div className="col-md-3">
<OfflineSymbol offline={offline || forceOffline} size={size} />
</div>
<div className="col-md-6">
<button className="btn-xs btn-info" onClick={forceOfflineConfig}>
{forceOffline ? 'Go Online' : 'Go Offline'}
</button>
</div>
</div>
) : (
<div className="text-center">
<h5>You are currently offline.</h5>
</div>
)}
</div>
);
}
}
function mapStateToProps(state: AppState) {
return {
offline: state.config.offline,
forceOffline: state.config.forceOffline
};
}
export default connect(mapStateToProps, {
forceOfflineConfig: dForceOfflineConfig
})(OfflineToggle);

View File

@ -1,14 +1,14 @@
// @flow
import './Promos.scss';
import React from 'react';
import { Link } from 'react-router';
import { Link } from 'react-router-dom';
import './Promos.scss';
const promos = [
{
isExternal: true,
color: '#6e9a3e',
href:
'https://myetherwallet.groovehq.com/knowledge_base/topics/protecting-yourself-and-your-funds',
isExternal: true,
texts: [<h6 key="1">Learn more about protecting your funds.</h6>],
images: [
require('assets/images/logo-ledger.svg'),
@ -16,10 +16,10 @@ const promos = [
]
},
{
isExternal: true,
color: '#2b71b1',
href:
'https://buy.coinbase.com?code=a6e1bd98-6464-5552-84dd-b27f0388ac7d&address=0xA7DeFf12461661212734dB35AdE9aE7d987D648c&crypto_currency=ETH&currency=USD',
isExternal: true,
texts: [
<p key="1">Its now easier to get more ETH</p>,
<h5 key="2">Buy ETH with USD</h5>
@ -27,6 +27,7 @@ const promos = [
images: [require('assets/images/logo-coinbase.svg')]
},
{
isExternal: false,
color: '#006e79',
href: '/swap',
texts: [
@ -37,64 +38,60 @@ const promos = [
}
];
export default class Promos extends React.Component {
state: { activePromo: number };
interface State {
activePromo: number;
}
state = {
activePromo: parseInt(Math.random() * promos.length)
export default class Promos extends React.Component<{}, State> {
public state = {
activePromo: parseInt(String(Math.random() * promos.length), 10)
};
_navigateToPromo = (idx: number) => {
this.setState({ activePromo: Math.max(0, Math.min(promos.length, idx)) });
};
render() {
public render() {
const { activePromo } = this.state;
const promo = promos[activePromo];
const promoContent = (
<div className="Promos-promo-inner">
<div className="Promos-promo-text">
{promo.texts}
</div>
<div className="Promos-promo-text">{promo.texts}</div>
<div className="Promos-promo-images">
{promo.images.map((img, idx) => <img src={img} key={idx} />)}
</div>
</div>
);
const promoEl = promo.isExternal
? <a
className="Promos-promo"
key={promo.href}
target="_blank"
href={promo.href}
style={{ backgroundColor: promo.color }}
>
{promoContent}
</a>
: <Link
className="Promos-promo"
key={promo.href}
to={promo.href}
style={{ backgroundColor: promo.color }}
>
<div className="Promos-promo-inner">
{promoContent}
</div>
</Link>;
const promoEl = promo.isExternal ? (
<a
className="Promos-promo"
key={promo.href}
target="_blank"
href={promo.href}
style={{ backgroundColor: promo.color }}
>
{promoContent}
</a>
) : (
<Link
className="Promos-promo"
key={promo.href}
to={promo.href}
style={{ backgroundColor: promo.color }}
>
<div className="Promos-promo-inner">{promoContent}</div>
</Link>
);
return (
<div className="Promos">
{promoEl}
<div className="Promos-nav">
{promos.map((promo, idx) => {
{promos.map((_, index) => {
return (
<button
className={`Promos-nav-btn ${idx === activePromo
className={`Promos-nav-btn ${index === activePromo
? 'is-active'
: ''}`}
key={idx}
onClick={() => this._navigateToPromo(idx)}
key={index}
onClick={this.navigateToPromo(index)}
/>
);
})}
@ -102,4 +99,8 @@ export default class Promos extends React.Component {
</div>
);
}
private navigateToPromo = (idx: number) => () => {
this.setState({ activePromo: Math.max(0, Math.min(promos.length, idx)) });
};
}

View File

@ -1,20 +1,27 @@
// @flow
import React from 'react';
import classnames from 'classnames';
import { isValidETHAddress, isPositiveIntegerOrZero } from 'libs/validators';
import { Token } from 'config/data';
import { isPositiveIntegerOrZero, isValidETHAddress } from 'libs/validators';
import React from 'react';
import translate from 'translations';
export default class AddCustomTokenForm extends React.Component {
props: {
onSave: ({ address: string, symbol: string, decimal: number }) => void
};
state = {
interface Props {
onSave(params: Token): void;
}
interface State {
address: string;
symbol: string;
decimal: string;
}
export default class AddCustomTokenForm extends React.Component<Props, State> {
public state = {
address: '',
symbol: '',
decimal: ''
};
render() {
public render() {
const { address, symbol, decimal } = this.state;
const inputClasses = 'AddCustom-field-input form-control input-sm';
const errors = this.getErrors();
@ -42,9 +49,7 @@ export default class AddCustomTokenForm extends React.Component {
{fields.map(field => {
return (
<label className="AddCustom-field form-group" key={field.name}>
<span className="AddCustom-field-label">
{field.label}
</span>
<span className="AddCustom-field-label">{field.label}</span>
<input
className={classnames(
inputClasses,
@ -69,9 +74,13 @@ export default class AddCustomTokenForm extends React.Component {
);
}
getErrors() {
public getErrors() {
const { address, symbol, decimal } = this.state;
const errors = {};
const errors = {
decimal: false,
address: false,
symbol: false
};
if (!isPositiveIntegerOrZero(parseInt(decimal, 10))) {
errors.decimal = true;
@ -86,17 +95,18 @@ export default class AddCustomTokenForm extends React.Component {
return errors;
}
isValid() {
public isValid() {
return !Object.keys(this.getErrors()).length;
}
onFieldChange = (e: SyntheticInputEvent) => {
var name = e.target.name;
var value = e.target.value;
public onFieldChange = (e: React.SyntheticEvent<HTMLInputElement>) => {
// TODO: typescript bug: https://github.com/Microsoft/TypeScript/issues/13948
const name: any = (e.target as HTMLInputElement).name;
const value = (e.target as HTMLInputElement).value;
this.setState({ [name]: value });
};
onSave = (ev: SyntheticInputEvent) => {
public onSave = (ev: React.SyntheticEvent<HTMLFormElement>) => {
ev.preventDefault();
if (!this.isValid()) {
return;

View File

@ -1,22 +1,24 @@
// @flow
import './TokenRow.scss';
import React from 'react';
import Big from 'bignumber.js';
import { formatNumber } from 'utils/formatters';
import removeIcon from 'assets/images/icon-remove.svg';
import { BigNumber } from 'bignumber.js';
import React from 'react';
import { formatNumber } from 'utils/formatters';
import './TokenRow.scss';
export default class TokenRow extends React.Component {
props: {
balance: Big,
symbol: string,
custom?: boolean,
onRemove: (symbol: string) => void
};
interface Props {
balance: BigNumber;
symbol: string;
custom?: boolean;
onRemove(symbol: string): void;
}
interface State {
showLongBalance: boolean;
}
state = {
export default class TokenRow extends React.Component<Props, State> {
public state = {
showLongBalance: false
};
render() {
public render() {
const { balance, symbol, custom } = this.props;
const { showLongBalance } = this.state;
return (
@ -32,7 +34,7 @@ export default class TokenRow extends React.Component {
className="TokenRow-balance-remove"
title="Remove Token"
onClick={this.onRemove}
tabIndex="0"
tabIndex={0}
/>}
<span>
{showLongBalance ? balance.toString() : formatNumber(balance)}
@ -45,7 +47,10 @@ export default class TokenRow extends React.Component {
);
}
toggleShowLongBalance = (e: SyntheticInputEvent) => {
public toggleShowLongBalance = (
// TODO: don't use any
e: any
) => {
e.preventDefault();
this.setState(state => {
return {
@ -54,7 +59,7 @@ export default class TokenRow extends React.Component {
});
};
onRemove = () => {
public onRemove = () => {
this.props.onRemove(this.props.symbol);
};
}

View File

@ -1,26 +1,28 @@
// @flow
import './index.scss';
import { Token } from 'config/data';
import React from 'react';
import { TokenBalance } from 'selectors/wallet';
import translate from 'translations';
import TokenRow from './TokenRow';
import AddCustomTokenForm from './AddCustomTokenForm';
import type { TokenBalance } from 'selectors/wallet';
import type { Token } from 'config/data';
import './index.scss';
import TokenRow from './TokenRow';
type Props = {
tokens: TokenBalance[],
onAddCustomToken: (token: Token) => any,
onRemoveCustomToken: (symbol: string) => any
};
interface Props {
tokens: TokenBalance[];
onAddCustomToken(token: Token): any;
onRemoveCustomToken(symbol: string): any;
}
export default class TokenBalances extends React.Component {
props: Props;
state = {
interface State {
showAllTokens: boolean;
showCustomTokenForm: boolean;
}
export default class TokenBalances extends React.Component<Props, State> {
public state = {
showAllTokens: false,
showCustomTokenForm: false
};
render() {
public render() {
const { tokens } = this.props;
const shownTokens = tokens.filter(
token => !token.balance.eq(0) || token.custom || this.state.showAllTokens
@ -70,7 +72,7 @@ export default class TokenBalances extends React.Component {
);
}
toggleShowAllTokens = () => {
public toggleShowAllTokens = () => {
this.setState(state => {
return {
showAllTokens: !state.showAllTokens
@ -78,7 +80,7 @@ export default class TokenBalances extends React.Component {
});
};
toggleShowCustomTokenForm = () => {
public toggleShowCustomTokenForm = () => {
this.setState(state => {
return {
showCustomTokenForm: !state.showCustomTokenForm
@ -86,7 +88,7 @@ export default class TokenBalances extends React.Component {
});
};
addCustomToken = (token: Token) => {
public addCustomToken = (token: Token) => {
this.props.onAddCustomToken(token);
this.setState({ showCustomTokenForm: false });
};

View File

@ -1,111 +0,0 @@
// @flow
import React from 'react';
import { IWallet } from 'libs/wallet';
import type { NetworkConfig } from 'config/data';
import type { State } from 'reducers';
import { connect } from 'react-redux';
import { getWalletInst, getTokenBalances } from 'selectors/wallet';
import type { TokenBalance } from 'selectors/wallet';
import { getNetworkConfig } from 'selectors/config';
import * as customTokenActions from 'actions/customTokens';
import { showNotification } from 'actions/notifications';
import { fiatRequestedRates } from 'actions/rates';
import type { FiatRequestedRatesAction } from 'actions/rates';
import AccountInfo from './AccountInfo';
import Promos from './Promos';
import TokenBalances from './TokenBalances';
import EquivalentValues from './EquivalentValues';
import { Ether } from 'libs/units';
type Props = {
wallet: IWallet,
balance: Ether,
network: NetworkConfig,
tokenBalances: TokenBalance[],
rates: { [string]: number },
showNotification: Function,
addCustomToken: typeof customTokenActions.addCustomToken,
removeCustomToken: typeof customTokenActions.removeCustomToken,
fiatRequestedRates: () => FiatRequestedRatesAction
};
export class BalanceSidebar extends React.Component {
props: Props;
render() {
const {
wallet,
balance,
network,
tokenBalances,
rates,
fiatRequestedRates
} = this.props;
if (!wallet) {
return null;
}
const blocks = [
{
name: 'Account Info',
content: (
<AccountInfo
wallet={wallet}
balance={balance}
network={network}
fiatRequestedRates={fiatRequestedRates}
/>
)
},
{
name: 'Promos',
isFullWidth: true,
content: <Promos />
},
{
name: 'Token Balances',
content: (
<TokenBalances
tokens={tokenBalances}
onAddCustomToken={this.props.addCustomToken}
onRemoveCustomToken={this.props.removeCustomToken}
/>
)
},
{
name: 'Equivalent Values',
content: <EquivalentValues balance={balance} rates={rates} />
}
];
return (
<aside>
{blocks.map(block =>
<section
className={`Block ${block.isFullWidth ? 'is-full-width' : ''}`}
key={block.name}
>
{block.content}
</section>
)}
</aside>
);
}
}
function mapStateToProps(state: State) {
return {
wallet: getWalletInst(state),
balance: state.wallet.balance,
tokenBalances: getTokenBalances(state),
network: getNetworkConfig(state),
rates: state.rates
};
}
export default connect(mapStateToProps, {
...customTokenActions,
showNotification,
fiatRequestedRates
})(BalanceSidebar);

View File

@ -0,0 +1,136 @@
import {
addCustomToken,
removeCustomToken,
TAddCustomToken,
TRemoveCustomToken
} from 'actions/customTokens';
import { showNotification, TShowNotification } from 'actions/notifications';
import { fetchCCRates as dFetchCCRates, TFetchCCRates } from 'actions/rates';
import { NetworkConfig } from 'config/data';
import { Ether } from 'libs/units';
import { IWallet } from 'libs/wallet/IWallet';
import React from 'react';
import { connect } from 'react-redux';
import { AppState } from 'reducers';
import { getNetworkConfig } from 'selectors/config';
import {
getTokenBalances,
getWalletInst,
TokenBalance
} from 'selectors/wallet';
import AccountInfo from './AccountInfo';
import EquivalentValues from './EquivalentValues';
import Promos from './Promos';
import TokenBalances from './TokenBalances';
import { State } from 'reducers/rates';
import OfflineToggle from './OfflineToggle';
interface Props {
wallet: IWallet;
balance: Ether;
network: NetworkConfig;
tokenBalances: TokenBalance[];
rates: State['rates'];
ratesError: State['ratesError'];
showNotification: TShowNotification;
addCustomToken: TAddCustomToken;
removeCustomToken: TRemoveCustomToken;
fetchCCRates: TFetchCCRates;
}
interface Block {
name: string;
content: React.ReactElement<any>;
isFullWidth?: boolean;
}
export class BalanceSidebar extends React.Component<Props, {}> {
public render() {
const {
wallet,
balance,
network,
tokenBalances,
rates,
ratesError,
fetchCCRates
} = this.props;
if (!wallet) {
return null;
}
const blocks: Block[] = [
{
name: 'Go Offline',
content: <OfflineToggle />
},
{
name: 'Account Info',
content: (
<AccountInfo
wallet={wallet}
balance={balance}
network={network}
fetchCCRates={fetchCCRates}
/>
)
},
{
name: 'Promos',
isFullWidth: true,
content: <Promos />
},
{
name: 'Token Balances',
content: (
<TokenBalances
tokens={tokenBalances}
onAddCustomToken={this.props.addCustomToken}
onRemoveCustomToken={this.props.removeCustomToken}
/>
)
},
{
name: 'Equivalent Values',
content: (
<EquivalentValues
balance={balance}
rates={rates}
ratesError={ratesError}
/>
)
}
];
return (
<aside>
{blocks.map(block => (
<section
className={`Block ${block.isFullWidth ? 'is-full-width' : ''}`}
key={block.name}
>
{block.content}
</section>
))}
</aside>
);
}
}
function mapStateToProps(state: AppState) {
return {
wallet: getWalletInst(state),
balance: state.wallet.balance,
tokenBalances: getTokenBalances(state),
network: getNetworkConfig(state),
rates: state.rates.rates,
ratesError: state.rates.ratesError
};
}
export default connect(mapStateToProps, {
addCustomToken,
removeCustomToken,
showNotification,
fetchCCRates: dFetchCCRates
})(BalanceSidebar);

View File

@ -1,18 +1,23 @@
import { BlockExplorerConfig } from 'config/data';
import React from 'react';
import { ETHTxExplorer } from 'config/data';
import translate from 'translations';
export type TransactionSucceededProps = {
txHash: string
};
import {translateRaw} from 'translations';
const TransactionSucceeded = ({ txHash }: TransactionSucceededProps) => {
export interface TransactionSucceededProps {
txHash: string;
blockExplorer: BlockExplorerConfig;
}
const TransactionSucceeded = ({
txHash,
blockExplorer
}: TransactionSucceededProps) => {
// const checkTxLink = `https://www.myetherwallet.com?txHash=${txHash}/#check-tx-status`;
const txHashLink = ETHTxExplorer(txHash);
const txHashLink = blockExplorer.tx(txHash);
return (
<div>
<p>
{translate('SUCCESS_3', true) + txHash}
{translateRaw('SUCCESS_3') + txHash}
</p>
<a
className="btn btn-xs btn-info string"

View File

@ -1,199 +0,0 @@
import React, { Component } from 'react';
import translate from 'translations';
import { donationAddressMap } from 'config/data';
import logo from 'assets/images/logo-myetherwallet.svg';
import { bityReferralURL } from 'config/data';
import PreFooter from './PreFooter';
import './index.scss';
const LINKS_LEFT = [
{
text: 'Knowledge Base',
href: 'https://myetherwallet.groovehq.com/help_center'
},
{
text: 'Helpers & ENS Debugging',
href: 'https://www.myetherwallet.com/helpers.html'
},
{
text: 'Sign Message',
href: 'https://www.myetherwallet.com/signmsg.html'
}
];
const LINKS_SUPPORT = [
{
href: bityReferralURL,
text: 'Swap ETH/BTC/EUR/CHF via Bity.com'
},
{
href: 'https://www.ledgerwallet.com/r/fa4b?path=/products/',
text: 'Buy a Ledger Nano S'
},
{
href: 'https://trezor.io/?a=myetherwallet.com',
text: 'Buy a TREZOR'
},
{
href: 'https://digitalbitbox.com/?ref=mew',
text: 'Buy a Digital Bitbox'
}
];
const LINKS_RIGHT = [
{
href: 'https://www.MyEtherWallet.com',
text: 'MyEtherWallet.com'
},
{
href: 'https://github.com/MyEtherWallet/MyEtherWallet',
text: 'Github: Current Site'
},
{
href: 'https://github.com/MyEtherWallet',
text: 'Github: MEW Org'
},
{
href: 'https://github.com/MyEtherWallet/MyEtherWallet/releases/latest',
text: 'Github: Latest Release'
},
{
href:
'https://chrome.google.com/webstore/detail/myetherwallet-cx/nlbmnnijcnlegkjjpcfjclmcfggfefdm?hl=en',
text: 'MyEtherWallet CX'
},
{
href:
'https://chrome.google.com/webstore/detail/etheraddresslookup/pdknmigbbbhmllnmgdfalmedcmcefdfn',
text: 'Anti-Phishing CX'
}
];
const LINKS_SOCIAL = [
{
href: 'https://myetherwallet.herokuapp.com/',
text: 'Slack'
},
{
href: 'https://www.reddit.com/r/MyEtherWallet/',
text: 'Reddit'
},
{
href: 'https://twitter.com/myetherwallet',
text: 'Twitter'
},
{
href: 'https://www.facebook.com/MyEtherWallet/',
text: 'Facebook'
},
{
href: 'https://medium.com/@myetherwallet',
text: 'Medium'
}
];
export default class Footer extends Component {
render() {
return (
<div>
<PreFooter />
<footer className="Footer" role="contentinfo" aria-label="footer">
<div className="Footer-column Footer-about">
<p aria-hidden="true">
<a href="/">
<img
className="Footer-about-logo"
src={logo}
height="55px"
width="auto"
alt="MyEtherWallet"
/>
</a>
</p>
<p className="Footer-about-text">
<span>
{translate('FOOTER_1')}
</span>
<span>
{translate('FOOTER_1b')}
</span>
</p>
{LINKS_LEFT.map(link => {
return (
<p key={link.href}>
<a href={link.href} target="_blank" rel="noopener">
{link.text}
</a>
</p>
);
})}
<p>&copy; 2017 MyEtherWallet, LLC</p>
</div>
<div className="Footer-column Footer-info">
<h5>
<i aria-hidden="true">👫</i>
You can support us by supporting our blockchain-family.
</h5>
<p>Consider using our affiliate links to...</p>
<ul>
{LINKS_SUPPORT.map(link => {
return (
<li key={link.href}>
<a href={link.href} target="_blank">
{link.text}
</a>
</li>
);
})}
</ul>
<h5>
<i aria-hidden="true">💝</i>
{translate('FOOTER_2')}
</h5>
<ul>
<li>
{' '}ETH:{' '}
<span className="mono wrap">{donationAddressMap.ETH}</span>
</li>
<li>
{' '}BTC:{' '}
<span className="mono wrap">{donationAddressMap.BTC}</span>
</li>
</ul>
</div>
<div className="Footer-column Footer-links">
{LINKS_RIGHT.map(link => {
return (
<p key={link.href}>
<a href={link.href} target="_blank">
{link.text}
</a>
</p>
);
})}
<p>
{LINKS_SOCIAL.map((link, i) => {
return (
<span key={link.href}>
<a key={link.href} href={link.href} target="_blank">
{link.text}
</a>
{i !== LINKS_SOCIAL.length - 1 && ' · '}
</span>
);
})}
</p>
{/* TODO: Fix me */}
<p>Latest Block#: ?????</p>
</div>
</footer>
</div>
);
}
}

View File

@ -1,4 +1,4 @@
@import "common/sass/variables";
@import 'common/sass/variables';
// footer
.Footer {
@ -40,6 +40,20 @@
max-width: 28rem;
}
&-modal-button {
color: #4ac8ed;
background-color: rgba(0, 0, 0, 0);
border: none;
text-align: left;
padding: 0;
margin: 0;
font-weight: 300;
transition: 500ms all ease-in-out;
&:hover {
color: #0e97c0;
}
}
p {
margin: $space-xs 0 $space-sm;
}
@ -57,7 +71,7 @@
margin: $font-size-small 0 0;
i {
margin-right: .25em;
margin-right: 0.25em;
@media (min-width: $screen-sm-min) {
margin-left: -1.5em;
@ -79,8 +93,12 @@
@media screen and (max-width: $grid-float-breakpoint) {
.row {
margin-left: -.5rem;
margin-right: -.5rem;
margin-left: -0.5rem;
margin-right: -0.5rem;
}
}
}
.Modal {
color: #000;
}

View File

@ -0,0 +1,287 @@
import logo from 'assets/images/logo-myetherwallet.svg';
import { bityReferralURL, donationAddressMap } from 'config/data';
import React, { Component } from 'react';
import translate from 'translations';
import './index.scss';
import PreFooter from './PreFooter';
import Modal, { IButton } from 'components/ui/Modal';
const LINKS_LEFT = [
{
text: 'Knowledge Base',
href: 'https://myetherwallet.groovehq.com/help_center'
},
{
text: 'Helpers & ENS Debugging',
href: 'https://www.myetherwallet.com/helpers.html'
},
{
text: 'Sign Message',
href: 'https://www.myetherwallet.com/signmsg.html'
}
];
const LINKS_SUPPORT = [
{
href: bityReferralURL,
text: 'Swap ETH/BTC/EUR/CHF via Bity.com'
},
{
href: 'https://www.ledgerwallet.com/r/fa4b?path=/products/',
text: 'Buy a Ledger Nano S'
},
{
href: 'https://trezor.io/?a=myetherwallet.com',
text: 'Buy a TREZOR'
},
{
href: 'https://digitalbitbox.com/?ref=mew',
text: 'Buy a Digital Bitbox'
}
];
const LINKS_RIGHT = [
{
href: 'https://www.MyEtherWallet.com',
text: 'MyEtherWallet.com'
},
{
href: 'https://github.com/MyEtherWallet/MyEtherWallet',
text: 'Github: Current Site'
},
{
href: 'https://github.com/MyEtherWallet',
text: 'Github: MEW Org'
},
{
href: 'https://github.com/MyEtherWallet/MyEtherWallet/releases/latest',
text: 'Github: Latest Release'
},
{
href:
'https://chrome.google.com/webstore/detail/myetherwallet-cx/nlbmnnijcnlegkjjpcfjclmcfggfefdm?hl=en',
text: 'MyEtherWallet CX'
},
{
href:
'https://chrome.google.com/webstore/detail/etheraddresslookup/pdknmigbbbhmllnmgdfalmedcmcefdfn',
text: 'Anti-Phishing CX'
}
];
const LINKS_SOCIAL = [
{
href: 'https://myetherwallet.herokuapp.com/',
text: 'Slack'
},
{
href: 'https://www.reddit.com/r/MyEtherWallet/',
text: 'Reddit'
},
{
href: 'https://twitter.com/myetherwallet',
text: 'Twitter'
},
{
href: 'https://www.facebook.com/MyEtherWallet/',
text: 'Facebook'
},
{
href: 'https://medium.com/@myetherwallet',
text: 'Medium'
}
];
interface ComponentState {
isOpen: boolean;
}
export default class Footer extends React.Component<{}, ComponentState> {
constructor(props) {
super(props);
this.state = { isOpen: false };
}
public openModal = () => {
this.setState({ isOpen: true });
};
public closeModal = () => {
this.setState({ isOpen: false });
};
public render() {
const buttons: IButton[] = [
{ text: 'Okay', type: 'default', onClick: this.closeModal }
];
return (
<div>
<PreFooter />
<footer className="Footer" role="contentinfo" aria-label="footer">
<div className="Footer-column Footer-about">
<p aria-hidden="true">
<a href="/">
<img
className="Footer-about-logo"
src={logo}
height="55px"
width="auto"
alt="MyEtherWallet"
/>
</a>
</p>
<p className="Footer-about-text">
<span>{translate('FOOTER_1')}</span>
<span>{translate('FOOTER_1b')}</span>
</p>
{LINKS_LEFT.map(link => {
return (
<p key={link.href}>
<a href={link.href} target="_blank" rel="noopener">
{link.text}
</a>
</p>
);
})}
<button className="Footer-modal-button" onClick={this.openModal}>
Disclaimer
</button>
<Modal
isOpen={this.state.isOpen}
title="Disclaimer"
buttons={buttons}
handleClose={this.closeModal}
>
<p>
<b>Be safe & secure: </b>
<a href="https://myetherwallet.groovehq.com/knowledge_base/topics/protecting-yourself-and-your-funds">
We highly recommend that you read our guide on How to Prevent
Loss & Theft for some recommendations on how to be proactive
about your security.
</a>
</p>
<p>
<b>Always backup your keys: </b>
MyEtherWallet.com & MyEtherWallet CX are not "web wallets". You
do not create an account or give us your funds to hold onto. No
data leaves your computer / your browser. We make it easy for
you to create, save, and access your information and interact
with the blockchain.
</p>
<p>
<b>We are not responsible for any loss: </b>
Ethereum, MyEtherWallet.com & MyEtherWallet CX, and some of the
underlying Javascript libraries we use are under active
development. While we have thoroughly tested & tens of thousands
of wallets have been successfully created by people all over the
globe, there is always the possibility something unexpected
happens that causes your funds to be lost. Please do not invest
more than you are willing to lose, and please be careful.
</p>
<p>
<b>Translations of MyEtherWallet: </b>
The community has done an amazing job translating MyEtherWallet
into a variety of languages. However, MyEtherWallet can only
verify the validity and accuracy of the information provided in
English and, because of this, the English version of our website
is the official text.
</p>
<p>
<b>MIT License</b> Copyright © 2015-2017 MyEtherWallet LLC
</p>
<p>
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or
sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following
conditions:
</p>
<p>
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
</p>
<b>
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
</b>
</Modal>
<p>&copy; 2017 MyEtherWallet, LLC</p>
</div>
<div className="Footer-column Footer-info">
<h5>
<i aria-hidden="true">👫</i>
You can support us by supporting our blockchain-family.
</h5>
<p>Consider using our affiliate links to...</p>
<ul>
{LINKS_SUPPORT.map(link => {
return (
<li key={link.href}>
<a href={link.href} target="_blank">
{link.text}
</a>
</li>
);
})}
</ul>
<h5>
<i aria-hidden="true">💝</i>
{translate('FOOTER_2')}
</h5>
<ul>
<li>
{' '}
ETH: <span className="mono wrap">{donationAddressMap.ETH}</span>
</li>
<li>
{' '}
BTC: <span className="mono wrap">{donationAddressMap.BTC}</span>
</li>
</ul>
</div>
<div className="Footer-column Footer-links">
{LINKS_RIGHT.map(link => {
return (
<p key={link.href}>
<a href={link.href} target="_blank">
{link.text}
</a>
</p>
);
})}
<p>
{LINKS_SOCIAL.map((link, i) => {
return (
<span key={link.href}>
<a key={link.href} href={link.href} target="_blank">
{link.text}
</a>
{i !== LINKS_SOCIAL.length - 1 && ' · '}
</span>
);
})}
</p>
{/* TODO: Fix me */}
<p>Latest Block#: ?????</p>
</div>
</footer>
</div>
);
}
}

View File

@ -1,96 +0,0 @@
// @flow
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import throttle from 'lodash/throttle';
import './GasPriceDropdown.scss';
import { gasPriceDefaults } from 'config/data';
type Props = {
value: number,
onChange: (gasPrice: number) => void
};
export default class GasPriceDropdown extends Component {
state = { expanded: false };
static propTypes = {
value: PropTypes.number,
onChange: PropTypes.func.isRequired
};
constructor(props: Props) {
super(props);
this.updateGasPrice = throttle(this.updateGasPrice, 50);
}
render() {
const { expanded } = this.state;
return (
<span className={`dropdown ${expanded ? 'open' : ''}`}>
<a
aria-haspopup="true"
aria-label="adjust gas price"
className="dropdown-toggle"
onClick={this.toggleExpanded}
>
<span>Gas Price</span>: {this.props.value} Gwei
<i className="caret" />
</a>
{expanded &&
<ul className="dropdown-menu GasPrice-dropdown-menu">
<div className="GasPrice-header">
<span>Gas Price</span>: {this.props.value} Gwei
<input
type="range"
value={this.props.value}
min={gasPriceDefaults.gasPriceMinGwei}
max={gasPriceDefaults.gasPriceMaxGwei}
onChange={this.handleGasPriceChange}
/>
<p className="small col-xs-4 text-left GasPrice-padding-reset">
Not So Fast
</p>
<p className="small col-xs-4 text-center GasPrice-padding-reset">
Fast
</p>
<p className="small col-xs-4 text-right GasPrice-padding-reset">
Fast AF
</p>
<p className="small GasPrice-description">
Gas Price is the amount you pay per unit of gas.{' '}
<code>TX fee = gas price * gas limit</code> & is paid to miners
for including your TX in a block. Higher the gas price = faster
transaction, but more expensive. Default is <code>21 GWEI</code>.
</p>
<p>
{/* TODO: maybe not hardcode a link? :) */}
<a
href="https://myetherwallet.groovehq.com/knowledge_base/topics/what-is-gas"
target="_blank"
>
Read more
</a>
</p>
</div>
</ul>}
</span>
);
}
toggleExpanded = () => {
this.setState(state => {
return {
expanded: !state.expanded
};
});
};
updateGasPrice = (value: string) => {
this.props.onChange(parseInt(value, 10));
};
handleGasPriceChange = (e: SyntheticInputEvent) => {
this.updateGasPrice(e.target.value);
};
}

View File

@ -0,0 +1,86 @@
import { gasPriceDefaults } from 'config/data';
import throttle from 'lodash/throttle';
import React, { Component } from 'react';
import DropdownShell from 'components/ui/DropdownShell';
import './GasPriceDropdown.scss';
interface Props {
value: number;
onChange(gasPrice: number): void;
}
export default class GasPriceDropdown extends Component<Props, {}> {
constructor(props: Props) {
super(props);
this.updateGasPrice = throttle(this.updateGasPrice, 50);
}
public render() {
const { value } = this.props;
return (
<DropdownShell
color="white"
size="smr"
ariaLabel={`adjust gas price. current price is ${value} gwei`}
renderLabel={this.renderLabel}
renderOptions={this.renderOptions}
/>
);
}
private renderLabel = () => {
return `Gas Price: ${this.props.value} Gwei`;
};
private renderOptions = () => {
const { value } = this.props;
return (
<div className="GasPrice-dropdown-menu dropdown-menu dropdown-menu-right">
<div className="GasPrice-header">
<span>Gas Price</span>: {value} Gwei
<input
type="range"
value={value}
min={gasPriceDefaults.gasPriceMinGwei}
max={gasPriceDefaults.gasPriceMaxGwei}
onChange={this.handleGasPriceChange}
/>
<p className="small col-xs-4 text-left GasPrice-padding-reset">
Not So Fast
</p>
<p className="small col-xs-4 text-center GasPrice-padding-reset">
Fast
</p>
<p className="small col-xs-4 text-right GasPrice-padding-reset">
Fast AF
</p>
<p className="small GasPrice-description">
Gas Price is the amount you pay per unit of gas.{' '}
<code>TX fee = gas price * gas limit</code> & is paid to miners for
including your TX in a block. Higher the gas price = faster
transaction, but more expensive. Default is <code>21 GWEI</code>.
</p>
<p>
{/* TODO: maybe not hardcode a link? :) */}
<a
href="https://myetherwallet.groovehq.com/knowledge_base/topics/what-is-gas"
target="_blank"
>
Read more
</a>
</p>
</div>
</div>
);
};
private updateGasPrice = (value: string) => {
this.props.onChange(parseInt(value, 10));
};
private handleGasPriceChange = (
e: React.SyntheticEvent<HTMLInputElement>
) => {
this.updateGasPrice((e.target as HTMLInputElement).value);
};
}

View File

@ -5,6 +5,7 @@
position: relative;
overflow-y: hidden;
border-top: .25rem solid $brand-primary;
transition: border 300ms ease;
&-scroll {
-ms-overflow-style: -ms-autohiding-scrollbar;

View File

@ -1,5 +1,5 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import NavigationLink from './NavigationLink';
import './Navigation.scss';
@ -17,13 +17,6 @@ const tabs = [
name: 'NAV_Swap',
to: 'swap'
},
{
name: 'NAV_Offline'
},
{
name: 'NAV_Contracts',
to: 'contracts'
},
{
name: 'NAV_ViewWallet'
// to: 'view-wallet'
@ -39,62 +32,71 @@ const tabs = [
}
];
export default class TabsOptions extends Component {
constructor(props) {
super(props);
this.state = {
showLeftArrow: false,
showRightArrow: false
};
}
interface Props {
color?: string;
}
static propTypes = {
location: PropTypes.object
interface State {
showLeftArrow: boolean;
showRightArrow: boolean;
}
interface BorderStyle {
borderTopColor?: string;
}
export default class Navigation extends Component<Props, State> {
public state = {
showLeftArrow: false,
showRightArrow: false
};
scrollLeft() {}
/*
* public scrollLeft() {}
public scrollRight() {}
*
*/
scrollRight() {}
public render() {
const { color } = this.props;
const borderStyle: BorderStyle = {};
if (color) {
borderStyle.borderTopColor = color;
}
render() {
const { location } = this.props;
return (
<nav
role="navigation"
aria-label="main navigation"
className="Navigation"
style={borderStyle}
>
{this.state.showLeftArrow &&
{this.state.showLeftArrow && (
<a
aria-hidden="true"
className="Navigation-arrow Navigation-arrow--left"
onClick={() => this.scrollLeft(100)}
>
&#171;
</a>}
</a>
)}
<div className="Navigation-scroll container">
<ul className="Navigation-links">
{tabs.map(link => {
return (
<NavigationLink
key={link.name}
link={link}
location={location}
/>
);
return <NavigationLink key={link.name} link={link} />;
})}
</ul>
</div>
{this.state.showRightArrow &&
{this.state.showRightArrow && (
<a
aria-hidden="true"
className="Navigation-arrow Navigation-arrow-right"
onClick={() => this.scrollRight(100)}
>
&#187;
</a>}
</a>
)}
</nav>
);
}

View File

@ -1,51 +0,0 @@
// @flow
import React from 'react';
import classnames from 'classnames';
import translate from 'translations';
import { Link } from 'react-router';
import './NavigationLink.scss';
type Props = {
link: {
name: string,
to?: string,
external?: boolean
},
location: Object
};
export default class NavigationLink extends React.Component {
props: Props;
render() {
const { link, location } = this.props;
const linkClasses = classnames({
'NavigationLink-link': true,
'is-disabled': !link.to,
'is-active':
location.pathname === link.to ||
location.pathname.substring(1) === link.to
});
// $FlowFixMe flow is wrong, this isn't an element
const linkLabel = `nav item: ${translate(link.name, true)}`;
const linkEl = link.external
? <a
className={linkClasses}
href={link.to}
aria-label={linkLabel}
target="_blank"
>
{translate(link.name)}
</a>
: <Link className={linkClasses} to={link.to} aria-label={linkLabel}>
{translate(link.name)}
</Link>;
return (
<li className="NavigationLink">
{linkEl}
</li>
);
}
}

View File

@ -0,0 +1,60 @@
import classnames from 'classnames';
import React from 'react';
import { Link, withRouter } from 'react-router-dom';
import translate, { translateRaw } from 'translations';
import './NavigationLink.scss';
interface Props {
link: {
name: string;
to?: string;
external?: boolean;
};
}
interface InjectedLocation extends Props {
location: { pathname: string };
}
class NavigationLink extends React.Component<Props, {}> {
get injected() {
return this.props as InjectedLocation;
}
public render() {
const { link } = this.props;
const { location } = this.injected;
const linkClasses = classnames({
'NavigationLink-link': true,
'is-disabled': !link.to,
'is-active':
location.pathname === link.to ||
location.pathname.substring(1) === link.to
});
const linkLabel = `nav item: ${translateRaw(link.name)}`;
const linkEl =
link.external || !link.to ? (
<a
className={linkClasses}
href={link.to}
aria-label={linkLabel}
target="_blank"
>
{translate(link.name)}
</a>
) : (
<Link
className={linkClasses}
to={(link as any).to}
aria-label={linkLabel}
>
{translate(link.name)}
</Link>
);
return <li className="NavigationLink">{linkEl}</li>;
}
}
// withRouter is a HOC which provides NavigationLink with a react-router location prop
export default withRouter(NavigationLink);

View File

@ -1,118 +0,0 @@
// @flow
import React, { Component } from 'react';
import Navigation from './components/Navigation';
import GasPriceDropdown from './components/GasPriceDropdown';
import { Link } from 'react-router';
import { Dropdown } from 'components/ui';
import {
languages,
NODES,
VERSION,
ANNOUNCEMENT_TYPE,
ANNOUNCEMENT_MESSAGE
} from '../../config/data';
import logo from 'assets/images/logo-myetherwallet.svg';
import './index.scss';
export default class Header extends Component {
props: {
location: {},
languageSelection: string,
nodeSelection: string,
gasPriceGwei: number,
changeLanguage: (sign: string) => any,
changeNode: (key: string) => any,
changeGasPrice: (price: number) => any
};
render() {
const { languageSelection, changeNode, nodeSelection } = this.props;
const selectedLanguage =
languages.find(l => l.sign === languageSelection) || languages[0];
const selectedNode = NODES[nodeSelection];
return (
<div className="Header">
{ANNOUNCEMENT_MESSAGE &&
<div
className={`Header-announcement is-${ANNOUNCEMENT_TYPE}`}
dangerouslySetInnerHTML={{
__html: ANNOUNCEMENT_MESSAGE
}}
/>}
<section className="Header-branding">
<section className="Header-branding-inner container">
<Link
to={'/'}
className="Header-branding-title"
aria-label="Go to homepage"
>
{/* TODO - don't hardcode image path*/}
<img
className="Header-branding-title-logo"
src={logo}
height="64px"
width="245px"
alt="MyEtherWallet"
/>
</Link>
<div className="Header-branding-title-tagline">
<span className="Header-branding-title-tagline-version">
v{VERSION}
</span>
<GasPriceDropdown
value={this.props.gasPriceGwei}
onChange={this.props.changeGasPrice}
/>
<Dropdown
ariaLabel={`change language. current language ${selectedLanguage.name}`}
options={languages}
formatTitle={o => o.name}
value={selectedLanguage}
extra={[
<li key={'separator'} role="separator" className="divider" />,
<li key={'disclaimer'}>
<a data-toggle="modal" data-target="#disclaimerModal">
Disclaimer
</a>
</li>
]}
onChange={this.changeLanguage}
/>
<Dropdown
ariaLabel={`change node. current node ${selectedNode.network} node by ${selectedNode.service}`}
options={Object.keys(NODES)}
formatTitle={o => [
NODES[o].network,
' ',
<small key="service">
({NODES[o].service})
</small>
]}
value={nodeSelection}
extra={
<li>
<a onClick={() => {}}>Add Custom Node</a>
</li>
}
onChange={changeNode}
/>
</div>
</section>
</section>
<Navigation location={this.props.location} />
</div>
);
}
changeLanguage = (value: { sign: string }) => {
this.props.changeLanguage(value.sign);
};
}

View File

@ -92,66 +92,37 @@ $small-size: 900px;
padding: 5px 0;
min-width: 220px;
}
&-tagline {
font-size: 18px;
font-weight: 200;
color: white;
flex: 1 auto;
text-align: right;
padding: 5px 0;
@include small-query {
text-align: center;
}
> * {
display: inline;
vertical-align: middle;
}
&-version {
max-width: 395px;
}
}
}
a {
&-right {
font-size: 18px;
font-weight: 200;
color: white;
cursor: pointer;
font-weight: 400;
transition: 250ms all ease;
&:hover,
&:active {
opacity: .8;
color: white;
text-decoration: none;
transition: 250ms all ease;
}
}
// TODO - Move to dropdown component?
.dropdown {
margin-left: 15px;
padding: 0;
flex: 1 auto;
text-align: right;
white-space: nowrap;
padding: 0 0 5px;
@include small-query {
text-align: center;
}
.dropdown-menu {
right: -10px;
left: auto;
min-width: auto;
left: auto;
> * {
display: inline-block;
vertical-align: middle;
margin-top: 5px;
}
& > li > a {
font-size: 15px;
padding: 5px 30px 5px 15px;
position: relative;
&-version {
max-width: 395px;
margin-right: 10px;
}
&.active {
text-decoration: none;
color: $brand-primary;
background-color: $gray-lightest;
}
&-dropdown {
margin-left: 6px;
&-add {
text-align: center;
padding-top: $space-sm !important;
padding-bottom: $space-sm !important;
}
}
}

View File

@ -0,0 +1,141 @@
import {
TChangeGasPrice,
TChangeLanguage,
TChangeNodeIntent
} from 'actions/config';
import logo from 'assets/images/logo-myetherwallet.svg';
import { Dropdown, ColorDropdown } from 'components/ui';
import React, { Component } from 'react';
import { Link } from 'react-router-dom';
import {
ANNOUNCEMENT_MESSAGE,
ANNOUNCEMENT_TYPE,
languages,
NETWORKS,
NODES,
VERSION
} from '../../config/data';
import GasPriceDropdown from './components/GasPriceDropdown';
import Navigation from './components/Navigation';
import { getKeyByValue } from 'utils/helpers';
import './index.scss';
interface Props {
languageSelection: string;
nodeSelection: string;
gasPriceGwei: number;
changeLanguage: TChangeLanguage;
changeNodeIntent: TChangeNodeIntent;
changeGasPrice: TChangeGasPrice;
}
export default class Header extends Component<Props, {}> {
public render() {
const { languageSelection, changeNodeIntent, nodeSelection } = this.props;
const selectedLanguage = languageSelection;
const selectedNode = NODES[nodeSelection];
const selectedNetwork = NETWORKS[selectedNode.network];
const LanguageDropDown = Dropdown as new () => Dropdown<
typeof selectedLanguage
>;
const nodeOptions = Object.keys(NODES).map(key => {
return {
value: key,
name: (
<span>
{NODES[key].network} <small>({NODES[key].service})</small>
</span>
),
color: NETWORKS[NODES[key].network].color
};
});
return (
<div className="Header">
{ANNOUNCEMENT_MESSAGE && (
<div
className={`Header-announcement is-${ANNOUNCEMENT_TYPE}`}
dangerouslySetInnerHTML={{
__html: ANNOUNCEMENT_MESSAGE
}}
/>
)}
<section className="Header-branding">
<section className="Header-branding-inner container">
<Link
to={'/'}
className="Header-branding-title"
aria-label="Go to homepage"
>
{/* TODO - don't hardcode image path*/}
<img
className="Header-branding-title-logo"
src={logo}
height="64px"
width="245px"
alt="MyEtherWallet"
/>
</Link>
<div className="Header-branding-right">
<span className="Header-branding-right-version">v{VERSION}</span>
<div className="Header-branding-right-dropdown">
<GasPriceDropdown
value={this.props.gasPriceGwei}
onChange={this.props.changeGasPrice}
/>
</div>
<div className="Header-branding-right-dropdown">
<LanguageDropDown
ariaLabel={`change language. current language ${languages[
selectedLanguage
]}`}
options={Object.values(languages)}
value={languages[selectedLanguage]}
extra={
<li key="disclaimer">
<a data-toggle="modal" data-target="#disclaimerModal">
Disclaimer
</a>
</li>
}
onChange={this.changeLanguage}
size="smr"
color="white"
/>
</div>
<div className="Header-branding-right-dropdown">
<ColorDropdown
ariaLabel={`change node. current node ${selectedNode.network} node by ${selectedNode.service}`}
options={nodeOptions}
value={nodeSelection}
extra={
<li>
<a>Add Custom Node</a>
</li>
}
onChange={changeNodeIntent}
size="smr"
color="white"
/>
</div>
</div>
</section>
</section>
<Navigation color={selectedNetwork.color} />
</div>
);
}
public changeLanguage = (value: string) => {
const key = getKeyByValue(languages, value);
if (key) {
this.props.changeLanguage(key);
}
};
}

View File

@ -1,16 +1,15 @@
// @flow
import { Identicon, QRCode } from 'components/ui';
import PrivKeyWallet from 'libs/wallet/privkey';
import React from 'react';
import { QRCode, Identicon } from 'components/ui';
import type PrivKeyWallet from 'libs/wallet/privkey';
import ethLogo from 'assets/images/logo-ethereum-1.png';
import sidebarImg from 'assets/images/print-sidebar.png';
import notesBg from 'assets/images/notes-bg.png';
import sidebarImg from 'assets/images/print-sidebar.png';
const walletWidth = 680;
const walletHeight = 280;
const styles = {
const styles: any = {
container: {
position: 'relative',
margin: '0 auto',
@ -91,22 +90,26 @@ const styles = {
}
};
type Props = {
wallet: PrivKeyWallet
};
interface Props {
wallet: PrivKeyWallet;
}
export default class PaperWallet extends React.Component {
props: Props;
state = { address: '' };
interface State {
address: string;
}
export default class PaperWallet extends React.Component<Props, State> {
public state = { address: '' };
componentDidMount() {
if (!this.props.wallet) return;
this.props.wallet.getAddress().then(addr => {
this.setState({ address: addr });
public componentDidMount() {
if (!this.props.wallet) {
return;
}
this.props.wallet.getAddress().then(address => {
this.setState({ address });
});
}
render() {
public render() {
const privateKey = this.props.wallet.getPrivateKey();
return (

View File

@ -1,22 +1,15 @@
// @flow
import { PaperWallet } from 'components';
import PrivKeyWallet from 'libs/wallet/privkey';
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import translate from 'translations';
import printElement from 'utils/printElement';
import { PaperWallet } from 'components';
import type PrivKeyWallet from 'libs/wallet/privkey';
type Props = {
wallet: PrivKeyWallet
};
interface Props {
wallet: PrivKeyWallet;
}
export default class PrintableWallet extends Component {
props: Props;
static propTypes = {
wallet: PropTypes.object.isRequired
};
print = () => {
export default class PrintableWallet extends Component<Props, {}> {
public print = () => {
printElement(<PaperWallet wallet={this.props.wallet} />, {
popupFeatures: {
scrollbars: 'no'
@ -36,7 +29,7 @@ export default class PrintableWallet extends Component {
});
};
render() {
public render() {
return (
<div>
<PaperWallet wallet={this.props.wallet} />

View File

@ -1,24 +0,0 @@
import React, { Component } from 'react';
import { Provider } from 'react-redux';
import { Router } from 'react-router';
import PropTypes from 'prop-types';
export default class Root extends Component {
static propTypes = {
store: PropTypes.object,
history: PropTypes.object,
routes: PropTypes.func
};
render() {
const { store, history, routes } = this.props;
// key={Math.random()} = hack for HMR from https://github.com/webpack/webpack-dev-server/issues/395
return (
<Provider store={store} key={Math.random()}>
<Router history={history} key={Math.random()}>
{routes()}
</Router>
</Provider>
);
}
}

View File

@ -0,0 +1,37 @@
import React, { Component } from 'react';
import { Provider } from 'react-redux';
import { Router, Route } from 'react-router-dom';
// Components
import ENS from 'containers/Tabs/ENS';
import GenerateWallet from 'containers/Tabs/GenerateWallet';
import Help from 'containers/Tabs/Help';
import SendTransaction from 'containers/Tabs/SendTransaction';
import Swap from 'containers/Tabs/Swap';
import ViewWallet from 'containers/Tabs/ViewWallet';
// TODO: fix this
interface Props {
store: any;
history: any;
}
export default class Root extends Component<Props, {}> {
public render() {
const { store, history } = this.props;
// key={Math.random()} = hack for HMR from https://github.com/webpack/webpack-dev-server/issues/395
return (
<Provider store={store} key={Math.random()}>
<Router history={history} key={Math.random()}>
<div>
<Route exact={true} path="/" component={GenerateWallet} />
<Route path="/view-wallet" component={ViewWallet} />
<Route path="/help" component={Help} />
<Route path="/swap" component={Swap} />
<Route path="/send-transaction" component={SendTransaction} />
<Route path="/ens" component={ENS} />
</div>
</Router>
</Provider>
);
}
}

View File

@ -1,11 +1,10 @@
// @flow
import React from 'react';
import Markdown from 'react-markdown';
import { translateRaw } from 'translations';
type Props = {
translationKey: string
};
interface Props {
translationKey: string;
}
const Translate = ({ translationKey }: Props) => {
const source = translateRaw(translationKey);

View File

@ -1,60 +1,59 @@
// @flow
import './DeterministicWalletsModal.scss';
import React from 'react';
import { connect } from 'react-redux';
import Modal from 'components/ui/Modal';
import {
getDeterministicWallets,
setDesiredToken
} from 'actions/deterministicWallets';
import { getNetworkConfig } from 'selectors/config';
import { getTokens } from 'selectors/wallet';
import { isValidPath } from 'libs/validators';
import type {
DeterministicWalletData,
GetDeterministicWalletsArgs,
getDeterministicWallets,
GetDeterministicWalletsAction,
GetDeterministicWalletsArgs,
setDesiredToken,
SetDesiredTokenAction
} from 'actions/deterministicWallets';
import type { NetworkConfig, Token } from 'config/data';
import Modal, { IButton } from 'components/ui/Modal';
import { NetworkConfig, Token } from 'config/data';
import { isValidPath } from 'libs/validators';
import React from 'react';
import { connect } from 'react-redux';
import { getNetworkConfig } from 'selectors/config';
import { getTokens, MergedToken } from 'selectors/wallet';
import './DeterministicWalletsModal.scss';
const WALLETS_PER_PAGE = 5;
type Props = {
interface Props {
// Passed props
isOpen?: boolean;
walletType?: string;
dPath: string;
dPaths: { label: string; value: string }[];
publicKey?: string;
chainCode?: string;
seed?: string;
// Redux state
wallets: DeterministicWalletData[],
desiredToken: string,
network: NetworkConfig,
tokens: Token[],
wallets: DeterministicWalletData[];
desiredToken: string;
network: NetworkConfig;
tokens: MergedToken[];
// Redux actions
getDeterministicWallets: GetDeterministicWalletsArgs => GetDeterministicWalletsAction,
setDesiredToken: (tkn: ?string) => SetDesiredTokenAction,
getDeterministicWallets(
args: GetDeterministicWalletsArgs
): GetDeterministicWalletsAction;
setDesiredToken(tkn: string | undefined): SetDesiredTokenAction;
// Passed props
isOpen?: boolean,
walletType: ?string,
dPath: string,
dPaths: { label: string, value: string }[],
publicKey: ?string,
chainCode: ?string,
seed: ?string,
onCancel: () => void,
onConfirmAddress: (string, number) => void,
onPathChange: string => void
};
onCancel(): void;
onConfirmAddress(address: string, addressIndex: number): void;
onPathChange(path: string): void;
}
type State = {
selectedAddress: string,
selectedAddrIndex: number,
isCustomPath: boolean,
customPath: string,
page: number
};
interface State {
selectedAddress: string;
selectedAddrIndex: number;
isCustomPath: boolean;
customPath: string;
page: number;
}
class DeterministicWalletsModal extends React.Component {
props: Props;
state: State = {
class DeterministicWalletsModal extends React.Component<Props, State> {
public state = {
selectedAddress: '',
selectedAddrIndex: 0,
isCustomPath: false,
@ -62,11 +61,11 @@ class DeterministicWalletsModal extends React.Component {
page: 0
};
componentDidMount() {
this._getAddresses();
public componentDidMount() {
this.getAddresses();
}
componentWillReceiveProps(nextProps) {
public componentWillReceiveProps(nextProps) {
const { publicKey, chainCode, seed, dPath } = this.props;
if (
nextProps.publicKey !== publicKey ||
@ -74,11 +73,125 @@ class DeterministicWalletsModal extends React.Component {
nextProps.dPath !== dPath ||
nextProps.seed !== seed
) {
this._getAddresses(nextProps);
this.getAddresses(nextProps);
}
}
_getAddresses(props: Props = this.props) {
public render() {
const {
wallets,
desiredToken,
network,
tokens,
dPath,
dPaths,
onCancel,
walletType
} = this.props;
const { selectedAddress, isCustomPath, customPath, page } = this.state;
const validPathClass = isValidPath(customPath) ? 'is-valid' : 'is-invalid';
const buttons: IButton[] = [
{
text: 'Unlock this Address',
type: 'primary',
onClick: this.handleConfirmAddress,
disabled: !selectedAddress
},
{
text: 'Cancel',
type: 'default',
onClick: onCancel
}
];
return (
<Modal
title={`Unlock your ${walletType || ''} Wallet`}
isOpen={this.props.isOpen}
buttons={buttons}
handleClose={onCancel}
>
<div className="DWModal">
<form
className="DWModal-path form-group-sm"
onSubmit={this.handleSubmitCustomPath}
>
<span className="DWModal-path-label">Addresses for</span>
<select
className="form-control"
onChange={this.handleChangePath}
value={isCustomPath ? 'custom' : dPath}
>
{dPaths.map(dp =>
<option key={dp.value} value={dp.value}>
{dp.label}
</option>
)}
<option value="custom">Custom path...</option>
</select>
{isCustomPath &&
<input
className={`form-control ${validPathClass}`}
value={customPath}
placeholder="m/44'/60'/0'/0"
onChange={this.handleChangeCustomPath}
/>}
</form>
<div className="DWModal-addresses">
<table className="DWModal-addresses-table table table-striped table-hover">
<thead>
<tr>
<td>#</td>
<td>Address</td>
<td>
{network.unit}
</td>
<td>
<select
className="DWModal-addresses-table-token"
value={desiredToken}
onChange={this.handleChangeToken}
>
<option value="">-Token-</option>
{tokens.map(t =>
<option key={t.symbol} value={t.symbol}>
{t.symbol}
</option>
)}
</select>
</td>
<td>More</td>
</tr>
</thead>
<tbody>
{wallets.map(wallet => this.renderWalletRow(wallet))}
</tbody>
</table>
<div className="DWModal-addresses-nav">
<button
className="DWModal-addresses-nav-btn btn btn-sm btn-default"
disabled={page === 0}
onClick={this.prevPage}
>
Back
</button>
<button
className="DWModal-addresses-nav-btn btn btn-sm btn-default"
onClick={this.nextPage}
>
More
</button>
</div>
</div>
</div>
</Modal>
);
}
private getAddresses(props: Props = this.props) {
const { dPath, publicKey, chainCode, seed } = props;
if (dPath && ((publicKey && chainCode) || seed) && isValidPath(dPath)) {
@ -93,8 +206,8 @@ class DeterministicWalletsModal extends React.Component {
}
}
_handleChangePath = (ev: SyntheticInputEvent) => {
const { value } = ev.target;
private handleChangePath = (ev: React.SyntheticEvent<HTMLSelectElement>) => {
const { value } = ev.target as HTMLSelectElement;
if (value === 'custom') {
this.setState({ isCustomPath: true });
@ -106,21 +219,29 @@ class DeterministicWalletsModal extends React.Component {
}
};
_handleChangeCustomPath = (ev: SyntheticInputEvent) => {
this.setState({ customPath: ev.target.value });
private handleChangeCustomPath = (
ev: React.SyntheticEvent<HTMLInputElement>
) => {
this.setState({ customPath: (ev.target as HTMLInputElement).value });
};
_handleSubmitCustomPath = (ev: SyntheticInputEvent) => {
private handleSubmitCustomPath = (
ev: React.SyntheticEvent<HTMLFormElement>
) => {
ev.preventDefault();
if (!isValidPath(this.state.customPath)) return;
if (!isValidPath(this.state.customPath)) {
return;
}
this.props.onPathChange(this.state.customPath);
};
_handleChangeToken = (ev: SyntheticInputEvent) => {
this.props.setDesiredToken(ev.target.value || null);
private handleChangeToken = (ev: React.SyntheticEvent<HTMLSelectElement>) => {
this.props.setDesiredToken(
(ev.target as HTMLSelectElement).value || undefined
);
};
_handleConfirmAddress = () => {
private handleConfirmAddress = () => {
if (this.state.selectedAddress) {
this.props.onConfirmAddress(
this.state.selectedAddress,
@ -129,22 +250,22 @@ class DeterministicWalletsModal extends React.Component {
}
};
_selectAddress(selectedAddress, selectedAddrIndex) {
private selectAddress(selectedAddress, selectedAddrIndex) {
this.setState({ selectedAddress, selectedAddrIndex });
}
_nextPage = () => {
this.setState({ page: this.state.page + 1 }, this._getAddresses);
private nextPage = () => {
this.setState({ page: this.state.page + 1 }, this.getAddresses);
};
_prevPage = () => {
private prevPage = () => {
this.setState(
{ page: Math.max(this.state.page - 1, 0) },
this._getAddresses
this.getAddresses
);
};
_renderWalletRow(wallet) {
private renderWalletRow(wallet) {
const { desiredToken, network } = this.props;
const { selectedAddress } = this.state;
@ -157,7 +278,7 @@ class DeterministicWalletsModal extends React.Component {
return (
<tr
key={wallet.address}
onClick={this._selectAddress.bind(this, wallet.address, wallet.index)}
onClick={this.selectAddress.bind(this, wallet.address, wallet.index)}
>
<td>
{wallet.index + 1}
@ -188,120 +309,6 @@ class DeterministicWalletsModal extends React.Component {
</tr>
);
}
render() {
const {
wallets,
desiredToken,
network,
tokens,
dPath,
dPaths,
onCancel,
walletType
} = this.props;
const { selectedAddress, isCustomPath, customPath, page } = this.state;
const validPathClass = isValidPath(customPath) ? 'is-valid' : 'is-invalid';
const buttons = [
{
text: 'Unlock this Address',
type: 'primary',
onClick: this._handleConfirmAddress,
disabled: !selectedAddress
},
{
text: 'Cancel',
type: 'default',
onClick: onCancel
}
];
return (
<Modal
title={`Unlock your ${walletType || ''} Wallet`}
isOpen={this.props.isOpen}
buttons={buttons}
handleClose={onCancel}
>
<div className="DWModal">
<form
className="DWModal-path form-group-sm"
onSubmit={this._handleSubmitCustomPath}
>
<span className="DWModal-path-label">Addresses for</span>
<select
className="form-control"
onChange={this._handleChangePath}
value={isCustomPath ? 'custom' : dPath}
>
{dPaths.map(dp =>
<option key={dp.value} value={dp.value}>
{dp.label}
</option>
)}
<option value="custom">Custom path...</option>
</select>
{isCustomPath &&
<input
className={`form-control ${validPathClass}`}
value={customPath}
placeholder="m/44'/60'/0'/0"
onChange={this._handleChangeCustomPath}
/>}
</form>
<div className="DWModal-addresses">
<table className="DWModal-addresses-table table table-striped table-hover">
<thead>
<tr>
<td>#</td>
<td>Address</td>
<td>
{network.unit}
</td>
<td>
<select
className="DWModal-addresses-table-token"
value={desiredToken}
onChange={this._handleChangeToken}
>
<option value="">-Token-</option>
{tokens.map(t =>
<option key={t.symbol} value={t.symbol}>
{t.symbol}
</option>
)}
</select>
</td>
<td>More</td>
</tr>
</thead>
<tbody>
{wallets.map(wallet => this._renderWalletRow(wallet))}
</tbody>
</table>
<div className="DWModal-addresses-nav">
<button
className="DWModal-addresses-nav-btn btn btn-sm btn-default"
disabled={page === 0}
onClick={this._prevPage}
>
Back
</button>
<button
className="DWModal-addresses-nav-btn btn btn-sm btn-default"
onClick={this._nextPage}
>
More
</button>
</div>
</div>
</div>
</Modal>
);
}
}
function mapStateToProps(state) {

View File

@ -1,33 +1,33 @@
import { isKeystorePassRequired } from 'libs/keystore';
import React, { Component } from 'react';
import translate, { translateRaw } from 'translations';
import { isKeystorePassRequired } from 'libs/keystore';
export type KeystoreValue = {
file: string,
password: string,
valid: boolean
};
export interface KeystoreValue {
file: string;
password: string;
valid: boolean;
}
function isPassRequired(file: string): boolean {
let passReq = false;
try {
passReq = isKeystorePassRequired(file);
} catch (e) {
//TODO: communicate invalid file to user
// TODO: communicate invalid file to user
}
return passReq;
}
export default class KeystoreDecrypt extends Component {
props: {
value: KeystoreValue,
onChange: (value: KeystoreValue) => void,
onUnlock: () => void
public props: {
value: KeystoreValue;
onChange(value: KeystoreValue): void;
onUnlock(): void;
};
render() {
public render() {
const { file, password } = this.props.value;
let passReq = isPassRequired(file);
const passReq = isPassRequired(file);
return (
<section className="col-md-4 col-sm-6">
@ -47,7 +47,7 @@ export default class KeystoreDecrypt extends Component {
<a
className="btn btn-default btn-block"
id="aria1"
tabIndex="0"
tabIndex={0}
role="button"
>
{translate('ADD_Radio_2_short')}
@ -74,7 +74,7 @@ export default class KeystoreDecrypt extends Component {
);
}
onKeyDown = (e: SyntheticKeyboardEvent) => {
public onKeyDown = (e: any) => {
if (e.keyCode === 13) {
e.preventDefault();
e.stopPropagation();
@ -82,7 +82,7 @@ export default class KeystoreDecrypt extends Component {
}
};
onPasswordChange = (e: SyntheticInputEvent) => {
public onPasswordChange = (e: any) => {
const valid = this.props.value.file.length && e.target.value.length;
this.props.onChange({
...this.props.value,
@ -91,13 +91,14 @@ export default class KeystoreDecrypt extends Component {
});
};
handleFileSelection = (e: SyntheticInputEvent) => {
public handleFileSelection = (e: any) => {
const fileReader = new FileReader();
const inputFile = e.target.files[0];
const target = e.target;
const inputFile = target.files[0];
fileReader.onload = () => {
const keystore = fileReader.result;
let passReq = isPassRequired(keystore);
const passReq = isPassRequired(keystore);
this.props.onChange({
...this.props.value,

View File

@ -1,27 +0,0 @@
import React, { Component } from 'react';
import translate from 'translations';
export default class LedgerNanoSDecrypt extends Component {
render() {
return (
<section className="col-md-4 col-sm-6">
<div id="selectedUploadKey">
<h4>{translate('ADD_Radio_2_alt')}</h4>
<div className="form-group">
<input type="file" id="fselector" />
<a
className="btn-file marg-v-sm"
id="aria1"
tabIndex="0"
role="button"
>
{translate('ADD_Radio_2_short')}
</a>
</div>
</div>
</section>
);
}
}

View File

@ -0,0 +1,26 @@
.LedgerDecrypt {
text-align: center;
padding-top: 30px;
&-decrypt {
width: 100%;
}
&-help {
margin-top: 10px;
font-size: 13px;
}
&-error {
opacity: 0;
transition: none;
&.is-showing {
opacity: 1;
}
}
&-buy {
margin-top: 10px;
}
}

View File

@ -0,0 +1,150 @@
import './LedgerNano.scss';
import React, { Component } from 'react';
import translate, { translateRaw } from 'translations';
import DeterministicWalletsModal from './DeterministicWalletsModal';
import LedgerWallet from 'libs/wallet/ledger';
import Ledger3 from 'vendor/ledger3';
import LedgerEth from 'vendor/ledger-eth';
import DPATHS from 'config/dpaths';
const DEFAULT_PATH = DPATHS.LEDGER[0].value;
interface Props {
onUnlock(param: any): void;
}
interface State {
publicKey: string;
chainCode: string;
dPath: string;
error: string | null;
isLoading: boolean;
}
export default class LedgerNanoSDecrypt extends Component<Props, State> {
public state: State = {
publicKey: '',
chainCode: '',
dPath: DEFAULT_PATH,
error: null,
isLoading: false
};
public render() {
const { dPath, publicKey, chainCode, error, isLoading } = this.state;
const showErr = error ? 'is-showing' : '';
return (
<section className="LedgerDecrypt col-md-4 col-sm-6">
<button
className="LedgerDecrypt-decrypt btn btn-primary btn-lg"
onClick={this.handleNullConnect}
disabled={isLoading}
>
{isLoading ? 'Unlocking...' : translate('ADD_Ledger_scan')}
</button>
<div className="LedgerDecrypt-help">
Guides:
<div>
<a
href="http://support.ledgerwallet.com/knowledge_base/topics/how-to-use-myetherwallet-with-ledger"
target="_blank"
rel="noopener"
>
How to use MyEtherWallet with your Nano S
</a>
</div>
<div>
<a
href="https://ledger.groovehq.com/knowledge_base/topics/how-to-secure-your-eth-tokens-augur-rep-dot-dot-dot-with-your-nano-s"
target="_blank"
rel="noopener"
>
How to secure your tokens with your Nano S
</a>
</div>
</div>
<div className={`LedgerDecrypt-error alert alert-danger ${showErr}`}>
{error || '-'}
</div>
<a
className="LedgerDecrypt-buy btn btn-sm btn-default"
href="https://www.ledgerwallet.com/r/fa4b?path=/products/"
target="_blank"
rel="noopener"
>
{translate('Dont have a Ledger? Order one now!')}
</a>
<DeterministicWalletsModal
isOpen={!!publicKey && !!chainCode}
publicKey={publicKey}
chainCode={chainCode}
dPath={dPath}
dPaths={DPATHS.LEDGER}
onCancel={this.handleCancel}
onConfirmAddress={this.handleUnlock}
onPathChange={this.handlePathChange}
walletType={translateRaw('x_Ledger')}
/>
</section>
);
}
private handlePathChange = (dPath: string) => {
this.handleConnect(dPath);
};
private handleConnect = (dPath: string = this.state.dPath) => {
this.setState({
isLoading: true,
error: null
});
const ledger = new Ledger3('w0w');
const ethApp = new LedgerEth(ledger);
ethApp.getAddress(
dPath,
(res, err) => {
if (err) {
err = ethApp.getError(err);
}
if (res) {
this.setState({
publicKey: res.publicKey,
chainCode: res.chainCode,
isLoading: false
});
} else {
this.setState({
error: err,
isLoading: false
});
}
},
false,
true
);
};
private handleCancel = () => {
this.setState({
publicKey: '',
chainCode: '',
dPath: DEFAULT_PATH
});
};
private handleUnlock = (address: string, index: number) => {
this.props.onUnlock(new LedgerWallet(address, this.state.dPath, index));
};
private handleNullConnect = (): void => {
return this.handleConnect();
};
}

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