Migrate to Typescript (#224)

* Refactor babel/types

* Refactor entry point

* Refactor actions

* Refactor api

* Full project refactor -- Broad type fixing sweep

* - completely fix merge conflicts
- handle various type errors

* Add tslint to package.json

* Dependency cleanup

* Fix module resolution

* Work on type definitions for untyped libs

* progress commit

* Add more definition typing

* various type additions

* Add unit types

* Fix sagaiterator  + unit types

* various types added

* additional type additions

* Fix typing on Sagas

* remove flowfixmes; swap translate for translateRaw

* Get rid of contracts - awaiting Henry's contract PR

* Remove contracts from routing

* Fix most of actions/reducers

* refactor actions directory structure

* fix reducer action type imports

* Fix most of type errors pre-actions refactor

* fix action creator imports in containers

* Refactor more

* Refactor index of actions

* fix action imports; use module level index export

* package-lock.json updated

* Use action types in props

* Type up action creators

* Fix most of connect errors

* Typefixing progress

* More types

* Fix run-time errors

* Caching improvements for webpack

* Remove path resolve from webpack

* Update non-breaking packages to latest version

* Fix token typing

* Remove unused color code

* Fix wallet decrypt dispatch

* Set redux-form related props/functions to ANY, since we're stripping it out later on

* Revert BigNumber.js package changes

* Extend window to custom object for Perf

* Format Navigation

* Typecase keystore errors as any (since we shouldnt touch this)

* Push wallet context fix

* - find/replace value->payload in swap
- properly type swap state properties
- extract inline reducer into reducer function

* - type local storage retrieved items as generic

* - bind all RPCClient methods with fat arrow

* - reformat

* Change to enums for constants

* Change state into any

* Fix swap errors

* ensure that seconds are passed into state as integers

* Fix rest of errors

* use parseInt explicitly instead of type coercion

* Fix derivation-checker, remove flow command, add tslint command, add tslint-react, tell travis to use tslint instead of flow.

* Whoops, remove those tests.

* Remove unsupported (yet) config option.

* Fix precommit to target ts and tsx files.

* Fix some errors, ignore some silly rules.

* Revert jest to v19, use ts-jest and make all tests typescript. Fixes all but one.

* Get rid of saga tests

* Fix tslint errors
This commit is contained in:
HenryNguyen5 2017-09-24 19:06:28 -07:00 committed by Daniel Ternyak
parent 5e66cccac5
commit 5d4b36d453
276 changed files with 8671 additions and 8312 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
}
}

1
.gitignore vendored
View File

@ -46,6 +46,7 @@ jspm_packages
.vscode/
static/dll.vendor.js
dll
.cache-loader
# SSL cert stuff
webpack_config/server.key

View File

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

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,26 @@
import * as interfaces from './actionTypes';
import { TypeKeys } from './constants';
export type TChangeLanguage = typeof changeLanguage;
export function changeLanguage(sign: string): interfaces.ChangeLanguageAction {
return {
type: TypeKeys.CONFIG_LANGUAGE_CHANGE,
value: sign
};
}
export type TChangeNode = typeof changeNode;
export function changeNode(value: string): interfaces.ChangeNodeAction {
return {
type: TypeKeys.CONFIG_NODE_CHANGE,
value
};
}
export type TChangeGasPrice = typeof changeGasPrice;
export function changeGasPrice(value: number): interfaces.ChangeGasPriceAction {
return {
type: TypeKeys.CONFIG_GAS_PRICE,
value
};
}

View File

@ -0,0 +1,25 @@
import { TypeKeys } from './constants';
/*** Change Language ***/
export interface ChangeLanguageAction {
type: TypeKeys.CONFIG_LANGUAGE_CHANGE;
value: string;
}
/*** Change Node ***/
export interface ChangeNodeAction {
type: TypeKeys.CONFIG_NODE_CHANGE;
// FIXME $keyof?
value: string;
}
/*** Change gas price ***/
export interface ChangeGasPriceAction {
type: TypeKeys.CONFIG_GAS_PRICE;
value: number;
}
/*** Union Type ***/
export type ConfigAction =
| ChangeNodeAction
| ChangeLanguageAction
| ChangeGasPriceAction;

View File

@ -0,0 +1,5 @@
export enum TypeKeys {
CONFIG_LANGUAGE_CHANGE = 'CONFIG_LANGUAGE_CHANGE',
CONFIG_NODE_CHANGE = 'CONFIG_NODE_CHANGE',
CONFIG_GAS_PRICE = 'CONFIG_GAS_PRICE'
}

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,19 @@
import * as interfaces from './actionTypes';
import { TypeKeys } from './constants';
export type TFiatRequestedRates = typeof fiatRequestedRates;
export function fiatRequestedRates(): interfaces.FiatRequestedRatesAction {
return {
type: TypeKeys.RATES_FIAT_REQUESTED
};
}
export type TFiatSucceededRates = typeof fiatSucceededRates;
export function fiatSucceededRates(payload: {
[key: string]: number;
}): interfaces.FiatSucceededRatesAction {
return {
type: TypeKeys.RATES_FIAT_SUCCEEDED,
payload
};
}

View File

@ -0,0 +1,13 @@
import { TypeKeys } from './constants';
export interface FiatRequestedRatesAction {
type: TypeKeys.RATES_FIAT_REQUESTED;
}
/*** Set rates ***/
export interface FiatSucceededRatesAction {
type: TypeKeys.RATES_FIAT_SUCCEEDED;
payload: { [key: string]: number };
}
/*** Union Type ***/
export type RatesAction = FiatSucceededRatesAction | FiatRequestedRatesAction;

View File

@ -0,0 +1,4 @@
export enum TypeKeys {
RATES_FIAT_REQUESTED = 'RATES_FIAT_REQUESTED',
RATES_FIAT_SUCCEEDED = 'RATES_FIAT_SUCCEEDED'
}

View File

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

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,101 @@
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
}
};
}

View File

@ -0,0 +1,91 @@
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;
}
/*** 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
| SetBalanceAction
| SetTokenBalancesAction
| BroadcastTxRequestedAction
| BroadcastTxFailedAction
| BroadcastTxSuccededAction;

View File

@ -0,0 +1,9 @@
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';

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,8 +2,9 @@ export function checkHttpStatus(response) {
if (response.status >= 200 && response.status < 300) {
return response;
} else {
let error = new Error(response.statusText);
error.response = response;
const error = new Error(response.statusText);
// TODO: why assign response?
// error.response = response;
throw error;
}
}

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,37 +1,39 @@
// @flow
import './AccountInfo.scss';
import { FiatRequestedRatesAction } 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 { 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';
import './AccountInfo.scss';
type Props = {
balance: Ether,
wallet: IWallet,
network: NetworkConfig,
fiatRequestedRates: () => FiatRequestedRatesAction
};
interface Props {
balance: Ether;
wallet: IWallet;
network: NetworkConfig;
fiatRequestedRates(): FiatRequestedRatesAction;
}
export default class AccountInfo extends React.Component {
props: Props;
state = {
interface State {
showLongBalance: boolean;
address: string;
}
export default class AccountInfo extends React.Component<Props, State> {
public state = {
showLongBalance: false,
address: ''
};
componentDidMount() {
public componentDidMount() {
this.props.fiatRequestedRates();
this.props.wallet.getAddress().then(addr => {
this.setState({ address: addr });
});
}
toggleShowLongBalance = (e: SyntheticMouseEvent) => {
// TODO: don't use any;
public toggleShowLongBalance = (e: any) => {
e.preventDefault();
this.setState(state => {
return {
@ -40,7 +42,7 @@ export default class AccountInfo extends React.Component {
});
};
render() {
public render() {
const { network, balance } = this.props;
const { blockExplorer, tokenExplorer } = network;
const { address } = this.state;

View File

@ -1,21 +1,18 @@
// @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';
const ratesKeys = ['BTC', 'REP', 'EUR', 'USD', 'GBP', 'CHF'];
type Props = {
balance: ?Ether,
rates: ?{ [string]: number }
};
interface Props {
balance?: Ether;
rates?: { [key: string]: number };
}
export default class EquivalentValues extends React.Component {
props: Props;
render() {
export default class EquivalentValues extends React.Component<Props, {}> {
public render() {
const { balance, rates } = this.props;
return (
@ -27,7 +24,9 @@ export default class EquivalentValues extends React.Component {
<ul className="EquivalentValues-values">
{rates
? ratesKeys.map(key => {
if (!rates[key]) return null;
if (!rates[key]) {
return null;
}
return (
<li className="EquivalentValues-values-currency" key={key}>
<span className="EquivalentValues-values-currency-label">

View File

@ -1,14 +1,14 @@
// @flow
import './Promos.scss';
import React from 'react';
import { Link } from 'react-router';
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,39 +1,51 @@
// @flow
import {
addCustomToken,
removeCustomToken,
TAddCustomToken,
TRemoveCustomToken
} from 'actions/customTokens';
import { showNotification, TShowNotification } from 'actions/notifications';
import {
fiatRequestedRates as dFiatRequestedRates,
TFiatRequestedRates
} from 'actions/rates';
import { NetworkConfig } from 'config/data';
import { Ether } from 'libs/units';
import { IWallet } from 'libs/wallet/IWallet';
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 { AppState } from 'reducers';
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 {
getTokenBalances,
getWalletInst,
TokenBalance
} from 'selectors/wallet';
import AccountInfo from './AccountInfo';
import EquivalentValues from './EquivalentValues';
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
};
interface Props {
wallet: IWallet;
balance: Ether;
network: NetworkConfig;
tokenBalances: TokenBalance[];
rates: { [key: string]: number };
showNotification: TShowNotification;
addCustomToken: TAddCustomToken;
removeCustomToken: TRemoveCustomToken;
fiatRequestedRates: TFiatRequestedRates;
}
export class BalanceSidebar extends React.Component {
props: Props;
interface Block {
name: string;
content: React.ReactElement<any>;
isFullWidth?: boolean;
}
render() {
export class BalanceSidebar extends React.Component<Props, {}> {
public render() {
const {
wallet,
balance,
@ -46,7 +58,7 @@ export class BalanceSidebar extends React.Component {
return null;
}
const blocks = [
const blocks: Block[] = [
{
name: 'Account Info',
content: (
@ -94,7 +106,7 @@ export class BalanceSidebar extends React.Component {
}
}
function mapStateToProps(state: State) {
function mapStateToProps(state: AppState) {
return {
wallet: getWalletInst(state),
balance: state.wallet.balance,
@ -105,7 +117,8 @@ function mapStateToProps(state: State) {
}
export default connect(mapStateToProps, {
...customTokenActions,
addCustomToken,
removeCustomToken,
showNotification,
fiatRequestedRates
fiatRequestedRates: dFiatRequestedRates
})(BalanceSidebar);

View File

@ -1,11 +1,11 @@
import { BlockExplorerConfig } from 'config/data';
import React from 'react';
import type { BlockExplorerConfig } from 'config.data';
import translate from 'translations';
import {translateRaw} from 'translations';
export type TransactionSucceededProps = {
txHash: string,
blockExplorer: BlockExplorerConfig
};
export interface TransactionSucceededProps {
txHash: string;
blockExplorer: BlockExplorerConfig;
}
const TransactionSucceeded = ({
txHash,
@ -17,7 +17,7 @@ const TransactionSucceeded = ({
return (
<div>
<p>
{translate('SUCCESS_3', true) + txHash}
{translateRaw('SUCCESS_3') + txHash}
</p>
<a
className="btn btn-xs btn-info string"

View File

@ -1,10 +1,9 @@
import logo from 'assets/images/logo-myetherwallet.svg';
import { bityReferralURL, donationAddressMap } from 'config/data';
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';
import PreFooter from './PreFooter';
const LINKS_LEFT = [
{
@ -93,7 +92,7 @@ const LINKS_SOCIAL = [
];
export default class Footer extends Component {
render() {
public render() {
return (
<div>
<PreFooter />

View File

@ -1,30 +1,24 @@
// @flow
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import throttle from 'lodash/throttle';
import './GasPriceDropdown.scss';
import { gasPriceDefaults } from 'config/data';
import throttle from 'lodash/throttle';
import React, { Component } from 'react';
import './GasPriceDropdown.scss';
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
};
interface Props {
value: number;
onChange(gasPrice: number): void;
}
interface State {
expanded: boolean;
}
export default class GasPriceDropdown extends Component<Props, State> {
public state = { expanded: false };
constructor(props: Props) {
super(props);
this.updateGasPrice = throttle(this.updateGasPrice, 50);
}
render() {
public render() {
const { expanded } = this.state;
return (
<span className={`dropdown ${expanded ? 'open' : ''}`}>
@ -78,7 +72,7 @@ export default class GasPriceDropdown extends Component {
);
}
toggleExpanded = () => {
public toggleExpanded = () => {
this.setState(state => {
return {
expanded: !state.expanded
@ -86,11 +80,11 @@ export default class GasPriceDropdown extends Component {
});
};
updateGasPrice = (value: string) => {
public updateGasPrice = (value: string) => {
this.props.onChange(parseInt(value, 10));
};
handleGasPriceChange = (e: SyntheticInputEvent) => {
this.updateGasPrice(e.target.value);
public handleGasPriceChange = (e: React.SyntheticEvent<HTMLInputElement>) => {
this.updateGasPrice((e.target as HTMLInputElement).value);
};
}

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';
@ -20,10 +20,6 @@ const tabs = [
{
name: 'NAV_Offline'
},
{
name: 'NAV_Contracts',
to: 'contracts'
},
{
name: 'NAV_ViewWallet'
// to: 'view-wallet'
@ -39,27 +35,35 @@ const tabs = [
}
];
export default class Navigation extends Component {
constructor(props) {
super(props);
this.state = {
showLeftArrow: false,
showRightArrow: false
};
}
interface Props {
location: object;
color?: string;
}
static propTypes = {
location: PropTypes.object,
color: PropTypes.string
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() {}
render() {
public render() {
const { location, color } = this.props;
const borderStyle = {};
const borderStyle: BorderStyle = {};
if (color) {
borderStyle.borderTopColor = color;
@ -72,14 +76,15 @@ export default class Navigation extends Component {
className="Navigation"
style={borderStyle}
>
{this.state.showLeftArrow &&
{this.state.showLeftArrow && (
<a
aria-hidden="true"
className="Navigation-arrow Navigation-arrow--left"
onClick={() => this.scrollLeft(100)}
// onClick={() => this.scrollLeft()}
>
&#171;
</a>}
</a>
)}
<div className="Navigation-scroll container">
<ul className="Navigation-links">
@ -95,14 +100,15 @@ export default class Navigation extends Component {
</ul>
</div>
{this.state.showRightArrow &&
{this.state.showRightArrow && (
<a
aria-hidden="true"
className="Navigation-arrow Navigation-arrow-right"
onClick={() => this.scrollRight(100)}
// onClick={() => this.scrollRight()}
>
&#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,49 @@
import classnames from 'classnames';
import React from 'react';
import { Link } from 'react-router';
import translate, { translateRaw } from 'translations';
import './NavigationLink.scss';
interface Props {
link: {
name: string;
to?: string;
external?: boolean;
};
location: any;
}
export default class NavigationLink extends React.Component<Props, {}> {
public 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
});
const linkLabel = `nav item: ${translateRaw(link.name)}`;
const linkEl = link.external ? (
<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>;
}
}

View File

@ -1,49 +1,53 @@
// @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,
NETWORKS,
VERSION,
ANNOUNCEMENT_TYPE,
ANNOUNCEMENT_MESSAGE
} from '../../config/data';
import { TChangeGasPrice, TChangeLanguage, TChangeNode } from 'actions/config';
import logo from 'assets/images/logo-myetherwallet.svg';
import { Dropdown } from 'components/ui';
import React, { Component } from 'react';
import { Link } from 'react-router';
import {
ANNOUNCEMENT_MESSAGE,
ANNOUNCEMENT_TYPE,
languages,
NETWORKS,
NODES,
VERSION
} from '../../config/data';
import GasPriceDropdown from './components/GasPriceDropdown';
import Navigation from './components/Navigation';
import './index.scss';
export default class Header extends Component {
props: {
location: {},
languageSelection: string,
nodeSelection: string,
gasPriceGwei: number,
interface Props {
location: {};
languageSelection: string;
nodeSelection: string;
gasPriceGwei: number;
changeLanguage: (sign: string) => any,
changeNode: (key: string) => any,
changeGasPrice: (price: number) => any
};
changeLanguage: TChangeLanguage;
changeNode: TChangeNode;
changeGasPrice: TChangeGasPrice;
}
render() {
export default class Header extends Component<Props, {}> {
public render() {
const { languageSelection, changeNode, nodeSelection } = this.props;
const selectedLanguage =
languages.find(l => l.sign === languageSelection) || languages[0];
const selectedNode = NODES[nodeSelection];
const selectedNetwork = NETWORKS[selectedNode.network];
const LanguageDropDown = Dropdown as new () => Dropdown<
typeof selectedLanguage
>;
const NodeDropDown = Dropdown as new () => Dropdown<keyof typeof NODES>;
return (
<div className="Header">
{ANNOUNCEMENT_MESSAGE &&
{ANNOUNCEMENT_MESSAGE && (
<div
className={`Header-announcement is-${ANNOUNCEMENT_TYPE}`}
dangerouslySetInnerHTML={{
__html: ANNOUNCEMENT_MESSAGE
}}
/>}
/>
)}
<section className="Header-branding">
<section className="Header-branding-inner container">
@ -71,10 +75,10 @@ export default class Header extends Component {
onChange={this.props.changeGasPrice}
/>
<Dropdown
<LanguageDropDown
ariaLabel={`change language. current language ${selectedLanguage.name}`}
options={languages}
formatTitle={o => o.name}
formatTitle={this.extractName}
value={selectedLanguage}
extra={[
<li key={'separator'} role="separator" className="divider" />,
@ -87,20 +91,14 @@ export default class Header extends Component {
onChange={this.changeLanguage}
/>
<Dropdown
<NodeDropDown
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>
]}
formatTitle={this.nodeNetworkAndService}
value={nodeSelection}
extra={
<li>
<a onClick={() => {}}>Add Custom Node</a>
<a>Add Custom Node</a>
</li>
}
onChange={changeNode}
@ -111,13 +109,23 @@ export default class Header extends Component {
<Navigation
location={this.props.location}
color={selectedNetwork.color}
// color={selectedNetwork.color}
/>
</div>
);
}
changeLanguage = (value: { sign: string }) => {
public changeLanguage = (value: { sign: string }) => {
this.props.changeLanguage(value.sign);
};
private extractName(): (option: { sign: string; name: string }) => string {
return name;
}
private nodeNetworkAndService = (option: string) => [
NODES[option].network,
' ',
<small key="service">({NODES[option].service}) </small>
];
}

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;
public componentDidMount() {
if (!this.props.wallet) {
return;
}
this.props.wallet.getAddress().then(addr => {
this.setState({ address: addr });
});
}
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,16 +1,16 @@
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
};
// TODO: fix this
interface Props {
store: any;
history: any;
routes(): null;
}
render() {
export default class Root extends Component<Props, {}> {
public render() {
const { store, history, routes } = this.props;
// key={Math.random()} = hack for HMR from https://github.com/webpack/webpack-dev-server/issues/395
return (

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

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

View File

@ -1,29 +1,30 @@
import { mnemonicToSeed, validateMnemonic } from 'bip39';
import DPATHS from 'config/dpaths';
import React, { Component } from 'react';
import translate, { translateRaw } from 'translations';
import { validateMnemonic, mnemonicToSeed } from 'bip39';
import DeterministicWalletsModal from './DeterministicWalletsModal';
import DPATHS from 'config/dpaths.js';
const DEFAULT_PATH = DPATHS.MNEMONIC[0].value;
type State = {
phrase: string,
pass: string,
seed: string,
dPath: string
};
interface Props {
onUnlock(param: any): void;
}
interface State {
phrase: string;
pass: string;
seed: string;
dPath: string;
}
export default class MnemonicDecrypt extends Component {
props: { onUnlock: any => void };
state: State = {
export default class MnemonicDecrypt extends Component<Props, State> {
public state: State = {
phrase: '',
pass: '',
seed: '',
dPath: DEFAULT_PATH
};
render() {
public render() {
const { phrase, seed, dPath, pass } = this.state;
const isValidMnemonic = validateMnemonic(phrase);
@ -42,7 +43,7 @@ export default class MnemonicDecrypt extends Component {
value={phrase}
onChange={this.onMnemonicChange}
placeholder={translateRaw('x_Mnemonic')}
rows="4"
rows={4}
/>
</div>
<div className="form-group">
@ -72,45 +73,47 @@ export default class MnemonicDecrypt extends Component {
seed={seed}
dPath={dPath}
dPaths={DPATHS.MNEMONIC}
onCancel={this._handleCancel}
onConfirmAddress={this._handleUnlock}
onPathChange={this._handlePathChange}
onCancel={this.handleCancel}
onConfirmAddress={this.handleUnlock}
onPathChange={this.handlePathChange}
walletType={translateRaw('x_Mnemonic')}
/>
</section>
);
}
onPasswordChange = (e: SyntheticInputEvent) => {
this.setState({ pass: e.target.value });
public onPasswordChange = (e: React.SyntheticEvent<HTMLInputElement>) => {
this.setState({ pass: (e.target as HTMLInputElement).value });
};
onMnemonicChange = (e: SyntheticInputEvent) => {
this.setState({ phrase: e.target.value });
public onMnemonicChange = (e: React.SyntheticEvent<HTMLTextAreaElement>) => {
this.setState({ phrase: (e.target as HTMLTextAreaElement).value });
};
onDWModalOpen = (e: SyntheticInputEvent) => {
public onDWModalOpen = (e: React.SyntheticEvent<HTMLButtonElement>) => {
const { phrase, pass } = this.state;
if (!validateMnemonic(phrase)) return;
if (!validateMnemonic(phrase)) {
return;
}
try {
let seed = mnemonicToSeed(phrase.trim(), pass).toString('hex');
const seed = mnemonicToSeed(phrase.trim(), pass).toString('hex');
this.setState({ seed });
} catch (err) {
console.log(err);
}
};
_handleCancel = () => {
private handleCancel = () => {
this.setState({ seed: '' });
};
_handlePathChange = (dPath: string) => {
private handlePathChange = (dPath: string) => {
this.setState({ dPath });
};
_handleUnlock = (address, index) => {
private handleUnlock = (address, index) => {
const { phrase, pass, dPath } = this.state;
this.props.onUnlock({

View File

@ -1,13 +1,12 @@
// @flow
import { isValidEncryptedPrivKey, isValidPrivKey } from 'libs/validators';
import React, { Component } from 'react';
import translate, { translateRaw } from 'translations';
import { isValidPrivKey, isValidEncryptedPrivKey } from 'libs/validators';
export type PrivateKeyValue = {
key: string,
password: string,
valid: boolean
};
export interface PrivateKeyValue {
key: string;
password: string;
valid: boolean;
}
function fixPkey(key) {
if (key.indexOf('0x') === 0) {
@ -16,14 +15,14 @@ function fixPkey(key) {
return key;
}
type validated = {
fixedPkey: string,
isValidPkey: boolean,
isPassRequired: boolean,
valid: boolean
};
interface Validated {
fixedPkey: string;
isValidPkey: boolean;
isPassRequired: boolean;
valid: boolean;
}
function validatePkeyAndPass(pkey: string, pass: string): validated {
function validatePkeyAndPass(pkey: string, pass: string): Validated {
const fixedPkey = fixPkey(pkey);
const validPkey = isValidPrivKey(fixedPkey);
const validEncPkey = isValidEncryptedPrivKey(fixedPkey);
@ -46,13 +45,13 @@ function validatePkeyAndPass(pkey: string, pass: string): validated {
}
export default class PrivateKeyDecrypt extends Component {
props: {
value: PrivateKeyValue,
onChange: (value: PrivateKeyValue) => void,
onUnlock: () => void
public props: {
value: PrivateKeyValue;
onChange(value: PrivateKeyValue): void;
onUnlock(): void;
};
render() {
public render() {
const { key, password } = this.props.value;
const { isValidPkey, isPassRequired } = validatePkeyAndPass(key, password);
@ -72,7 +71,7 @@ export default class PrivateKeyDecrypt extends Component {
onChange={this.onPkeyChange}
onKeyDown={this.onKeyDown}
placeholder={translateRaw('x_PrivKey2')}
rows="4"
rows={4}
/>
</div>
{isValidPkey &&
@ -97,17 +96,17 @@ export default class PrivateKeyDecrypt extends Component {
);
}
onPkeyChange = (e: SyntheticInputEvent) => {
const pkey = e.target.value;
public onPkeyChange = (e: React.SyntheticEvent<HTMLTextAreaElement>) => {
const pkey = (e.target as HTMLInputElement).value;
const pass = this.props.value.password;
const { fixedPkey, valid } = validatePkeyAndPass(pkey, pass);
this.props.onChange({ ...this.props.value, key: fixedPkey, valid });
};
onPasswordChange = (e: SyntheticInputEvent) => {
public onPasswordChange = (e: React.SyntheticEvent<HTMLInputElement>) => {
const pkey = this.props.value.key;
const pass = e.target.value;
const pass = (e.target as HTMLInputElement).value;
const { valid } = validatePkeyAndPass(pkey, pass);
this.props.onChange({
@ -117,7 +116,7 @@ export default class PrivateKeyDecrypt extends Component {
});
};
onKeyDown = (e: SyntheticKeyboardEvent) => {
public onKeyDown = (e: any) => {
if (e.keyCode === 13) {
e.preventDefault();
e.stopPropagation();

View File

@ -1,24 +1,25 @@
// @flow
import './Trezor.scss';
import DPATHS from 'config/dpaths';
import TrezorWallet from 'libs/wallet/trezor';
import React, { Component } from 'react';
import translate from 'translations';
import translate, { translateRaw } from 'translations';
import TrezorConnect from 'vendor/trezor-connect';
import DeterministicWalletsModal from './DeterministicWalletsModal';
import TrezorWallet from 'libs/wallet/trezor';
import DPATHS from 'config/dpaths.js';
import './Trezor.scss';
const DEFAULT_PATH = DPATHS.TREZOR[0].value;
type State = {
publicKey: string,
chainCode: string,
dPath: string,
error: ?string,
isLoading: boolean
};
interface Props {
onUnlock(param: any): void;
}
interface State {
publicKey: string;
chainCode: string;
dPath: string;
error: string | null;
isLoading: boolean;
}
export default class TrezorDecrypt extends Component {
props: { onUnlock: any => void };
state: State = {
export default class TrezorDecrypt extends Component<Props, State> {
public state: State = {
publicKey: '',
chainCode: '',
dPath: DEFAULT_PATH,
@ -26,50 +27,7 @@ export default class TrezorDecrypt extends Component {
isLoading: false
};
_handlePathChange = (dPath: string) => {
this._handleConnect(dPath);
};
_handleConnect = (dPath: string = this.state.dPath) => {
this.setState({
isLoading: true,
error: null
});
TrezorConnect.getXPubKey(
dPath,
res => {
if (res.success) {
this.setState({
dPath,
publicKey: res.publicKey,
chainCode: res.chainCode,
isLoading: false
});
} else {
this.setState({
error: res.error,
isLoading: false
});
}
},
'1.5.2'
);
};
_handleCancel = () => {
this.setState({
publicKey: '',
chainCode: '',
dPath: DEFAULT_PATH
});
};
_handleUnlock = (address: string, index: number) => {
this.props.onUnlock(new TrezorWallet(address, this.state.dPath, index));
};
render() {
public render() {
const { dPath, publicKey, chainCode, error, isLoading } = this.state;
const showErr = error ? 'is-showing' : '';
@ -77,7 +35,7 @@ export default class TrezorDecrypt extends Component {
<section className="TrezorDecrypt col-md-4 col-sm-6">
<button
className="TrezorDecrypt-decrypt btn btn-primary btn-lg"
onClick={() => this._handleConnect()}
onClick={this.handleNullConnect}
disabled={isLoading}
>
{isLoading ? 'Unlocking...' : translate('ADD_Trezor_scan')}
@ -113,12 +71,60 @@ export default class TrezorDecrypt extends Component {
chainCode={chainCode}
dPath={dPath}
dPaths={DPATHS.TREZOR}
onCancel={this._handleCancel}
onConfirmAddress={this._handleUnlock}
onPathChange={this._handlePathChange}
walletType={translate('x_Trezor', true)}
onCancel={this.handleCancel}
onConfirmAddress={this.handleUnlock}
onPathChange={this.handlePathChange}
walletType={translateRaw('x_Trezor')}
/>
</section>
);
}
private handlePathChange = (dPath: string) => {
this.handleConnect(dPath);
};
private handleConnect = (dPath: string = this.state.dPath): void => {
this.setState({
isLoading: true,
error: null
});
// TODO: type vendor file
(TrezorConnect as any).getXPubKey(
dPath,
res => {
if (res.success) {
this.setState({
dPath,
publicKey: res.publicKey,
chainCode: res.chainCode,
isLoading: false
});
} else {
this.setState({
error: res.error,
isLoading: false
});
}
},
'1.5.2'
);
};
private handleCancel = () => {
this.setState({
publicKey: '',
chainCode: '',
dPath: DEFAULT_PATH
});
};
private handleUnlock = (address: string, index: number) => {
this.props.onUnlock(new TrezorWallet(address, this.state.dPath, index));
};
private handleNullConnect(): void {
return this.handleConnect();
}
}

View File

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

View File

@ -1,22 +1,24 @@
// @flow
import {
setWallet,
unlockKeystore,
UnlockKeystoreAction,
unlockMnemonic,
UnlockMnemonicAction,
unlockPrivateKey,
UnlockPrivateKeyAction
} from 'actions/wallet';
import isEmpty from 'lodash/isEmpty';
import map from 'lodash/map';
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { Dispatch } from 'redux';
import translate from 'translations';
import KeystoreDecrypt from './Keystore';
import PrivateKeyDecrypt from './PrivateKey';
import type { PrivateKeyValue } from './PrivateKey';
import MnemonicDecrypt from './Mnemonic';
import LedgerNanoSDecrypt from './LedgerNano';
import MnemonicDecrypt from './Mnemonic';
import PrivateKeyDecrypt, { PrivateKeyValue } from './PrivateKey';
import TrezorDecrypt from './Trezor';
import ViewOnlyDecrypt from './ViewOnly';
import map from 'lodash/map';
import {
unlockPrivateKey,
unlockKeystore,
unlockMnemonic,
setWallet
} from 'actions/wallet';
import { connect } from 'react-redux';
import isEmpty from 'lodash/isEmpty';
const WALLETS = {
'keystore-file': {
@ -26,7 +28,8 @@ const WALLETS = {
file: '',
password: ''
},
unlock: unlockKeystore
unlock: unlockKeystore,
disabled: false
},
'private-key': {
lid: 'x_PrivKey2',
@ -35,13 +38,15 @@ const WALLETS = {
key: '',
password: ''
},
unlock: unlockPrivateKey
unlock: unlockPrivateKey,
disabled: false
},
'mnemonic-phrase': {
lid: 'x_Mnemonic',
component: MnemonicDecrypt,
initialParams: {},
unlock: unlockMnemonic
unlock: unlockMnemonic,
disabled: false
},
'ledger-nano-s': {
lid: 'x_Ledger',
@ -52,7 +57,8 @@ const WALLETS = {
lid: 'x_Trezor',
component: TrezorDecrypt,
initialParams: {},
unlock: setWallet
unlock: setWallet,
disabled: false
},
'view-only': {
lid: 'View with Address Only',
@ -63,22 +69,25 @@ const WALLETS = {
type UnlockParams = {} | PrivateKeyValue;
type State = {
selectedWalletKey: string,
value: UnlockParams
};
interface Props {
// FIXME
dispatch: Dispatch<
UnlockKeystoreAction | UnlockMnemonicAction | UnlockPrivateKeyAction
>;
}
export class WalletDecrypt extends Component {
props: {
// FIXME
dispatch: (action: any) => void
};
state: State = {
interface State {
selectedWalletKey: string;
value: UnlockParams;
}
export class WalletDecrypt extends Component<Props, State> {
public state: State = {
selectedWalletKey: 'keystore-file',
value: WALLETS['keystore-file'].initialParams
};
getDecryptionComponent() {
public getDecryptionComponent() {
const { selectedWalletKey, value } = this.state;
const selectedWallet = WALLETS[selectedWalletKey];
@ -95,7 +104,7 @@ export class WalletDecrypt extends Component {
);
}
buildWalletOptions() {
public buildWalletOptions() {
return map(WALLETS, (wallet, key) => {
const isSelected = this.state.selectedWalletKey === key;
@ -119,20 +128,22 @@ export class WalletDecrypt extends Component {
});
}
handleDecryptionChoiceChange = (event: SyntheticInputEvent) => {
const wallet = WALLETS[event.target.value];
public handleDecryptionChoiceChange = (
event: React.SyntheticEvent<HTMLInputElement>
) => {
const wallet = WALLETS[(event.target as HTMLInputElement).value];
if (!wallet) {
return;
}
this.setState({
selectedWalletKey: event.target.value,
selectedWalletKey: (event.target as HTMLInputElement).value,
value: wallet.initialParams
});
};
render() {
public render() {
const decryptionComponent = this.getDecryptionComponent();
return (
@ -146,14 +157,14 @@ export class WalletDecrypt extends Component {
</section>
{decryptionComponent}
{!!this.state.value.valid &&
{!!(this.state.value as PrivateKeyValue).valid &&
<section className="col-md-4 col-sm-6">
<h4 id="uploadbtntxt-wallet">
{translate('ADD_Label_6')}
</h4>
<div className="form-group">
<a
tabIndex="0"
tabIndex={0}
role="button"
className="btn btn-primary btn-block"
onClick={this.onUnlock}
@ -166,11 +177,11 @@ export class WalletDecrypt extends Component {
);
}
onChange = (value: UnlockParams) => {
public onChange = (value: UnlockParams) => {
this.setState({ value });
};
onUnlock = (payload: any) => {
public onUnlock = (payload: any) => {
// some components (TrezorDecrypt) don't take an onChange prop, and thus this.state.value will remain unpopulated.
// in this case, we can expect the payload to contain the unlocked wallet info.
const unlockValue =

View File

@ -1,5 +1,3 @@
// @flow
export { default as Header } from './Header';
export { default as Footer } from './Footer';
export { default as Root } from './Root';

View File

@ -1,38 +1,31 @@
// @flow
import React, { Component } from 'react';
type Props<T> = {
value: T,
options: T[],
ariaLabel: string,
formatTitle: (option: T) => any,
extra?: any,
onChange: (value: T) => void
};
interface Props<T> {
value: T;
options: T[];
ariaLabel: string;
extra?: any;
formatTitle(option: T): any;
onChange(value: T): void;
}
type State = {
expanded: boolean
};
interface State {
expanded: boolean;
}
export default class DropdownComponent<T: *> extends Component<
void,
Props<T>,
State
> {
props: Props<T>;
state = {
export default class DropdownComponent<T> extends Component<Props<T>, State> {
public state = {
expanded: false
};
render() {
public render() {
const { options, value, ariaLabel, extra } = this.props;
const { expanded } = this.state;
return (
<span className={`dropdown ${expanded ? 'open' : ''}`}>
<a
tabIndex="0"
tabIndex={0}
aria-haspopup="true"
aria-expanded="false"
aria-label={ariaLabel}
@ -62,11 +55,11 @@ export default class DropdownComponent<T: *> extends Component<
);
}
formatTitle(option: any) {
public formatTitle(option: any) {
return this.props.formatTitle(option);
}
toggleExpanded = () => {
public toggleExpanded = () => {
this.setState(state => {
return {
expanded: !state.expanded
@ -74,7 +67,7 @@ export default class DropdownComponent<T: *> extends Component<
});
};
onChange = (value: any) => {
public onChange = (value: any) => {
this.props.onChange(value);
this.setState({ expanded: false });
};

View File

@ -1,13 +1,11 @@
// @flow
import React from 'react';
import { toDataUrl } from 'ethereum-blockies';
import { isValidETHAddress } from 'libs/validators';
import React from 'react';
type Props = {
address: string,
size?: string
};
interface Props {
address: string;
size?: string;
}
export default function Identicon(props: Props) {
const size = props.size || '4rem';

View File

@ -1,61 +1,83 @@
// @flow
import React, { Component } from 'react';
import closeIcon from 'assets/images/icon-x.svg';
import React, { Component } from 'react';
import './Modal.scss';
type Props = {
isOpen?: boolean,
title: string,
buttons: {
text: string,
type?:
| 'default'
| 'primary'
| 'success'
| 'info'
| 'warning'
| 'danger'
| 'link',
disabled?: boolean,
onClick?: () => void
}[],
handleClose: () => void,
disableButtons?: boolean,
children: any
};
export interface IButton {
text: string;
type?:
| 'default'
| 'primary'
| 'success'
| 'info'
| 'warning'
| 'danger'
| 'link';
disabled?: boolean;
onClick?(): void;
}
interface Props {
isOpen?: boolean;
title: string;
disableButtons?: boolean;
children: any;
buttons: IButton[];
handleClose(): void;
}
export default class Modal extends Component {
props: Props;
componentDidMount() {
export default class Modal extends Component<Props, {}> {
public componentDidMount() {
this.updateBodyClass();
document.addEventListener('keydown', this._escapeListner);
document.addEventListener('keydown', this.escapeListner);
}
componentDidUpdate() {
public componentDidUpdate() {
this.updateBodyClass();
}
updateBodyClass() {
// $FlowFixMe
public updateBodyClass() {
document.body.classList.toggle('no-scroll', !!this.props.isOpen);
}
componentWillUnmount() {
document.removeEventListener('keydown', this._escapeListner);
// $FlowFixMe
public componentWillUnmount() {
document.removeEventListener('keydown', this.escapeListner);
document.body.classList.remove('no-scroll');
}
_escapeListner = (ev: KeyboardEvent) => {
public render() {
const { isOpen, title, children, buttons, handleClose } = this.props;
const hasButtons = buttons && buttons.length;
return (
<div>
<div className={`Modalshade ${isOpen ? 'is-open' : ''}`} />
<div className={`Modal ${isOpen ? 'is-open' : ''}`}>
<div className="Modal-header">
<h2 className="Modal-header-title">
{title}
</h2>
<button className="Modal-header-close" onClick={handleClose}>
<img className="Modal-header-close-icon" src={closeIcon} />
</button>
</div>
<div className="Modal-content">
{isOpen && children}
</div>
{hasButtons &&
<div className="Modal-footer">
{this.renderButtons()}
</div>}
</div>
</div>
);
}
private escapeListner = (ev: KeyboardEvent) => {
// Don't trigger if they hit escape while on an input
if (ev.target) {
if (
ev.target.tagName === 'INPUT' ||
ev.target.tagName === 'SELECT' ||
ev.target.tagName === 'TEXTAREA' ||
ev.target.isContentEditable
(ev.target as HTMLElement).tagName === 'INPUT' ||
(ev.target as HTMLElement).tagName === 'SELECT' ||
(ev.target as HTMLElement).tagName === 'TEXTAREA' ||
(ev.target as HTMLElement).isContentEditable
) {
return;
}
@ -66,7 +88,7 @@ export default class Modal extends Component {
}
};
_renderButtons = () => {
private renderButtons = () => {
const { disableButtons, buttons } = this.props;
return buttons.map((btn, idx) => {
@ -88,32 +110,4 @@ export default class Modal extends Component {
);
});
};
render() {
const { isOpen, title, children, buttons, handleClose } = this.props;
const hasButtons = buttons && buttons.length;
return (
<div>
<div className={`Modalshade ${isOpen ? 'is-open' : ''}`} />
<div className={`Modal ${isOpen ? 'is-open' : ''}`}>
<div className="Modal-header">
<h2 className="Modal-header-title">
{title}
</h2>
<button className="Modal-header-close" onClick={handleClose}>
<img className="Modal-header-close-icon" src={closeIcon} />
</button>
</div>
<div className="Modal-content">
{isOpen && children}
</div>
{hasButtons &&
<div className="Modal-footer">
{this._renderButtons()}
</div>}
</div>
</div>
);
}
}

View File

@ -1,48 +0,0 @@
// @flow
import * as React from 'react';
type AAttributes = {
charset?: string,
coords?: string,
download?: string,
href: string,
hreflang?: string,
media?: string,
name?: string,
rel?:
| 'alternate'
| 'author'
| 'bookmark'
| 'external'
| 'help'
| 'license'
| 'next'
| 'nofollow'
| 'noreferrer'
| 'noopener'
| 'prev'
| 'search'
| 'tag',
rev?: string,
shape?: 'default' | 'rect' | 'circle' | 'poly',
target?: '_blank' | '_parent' | '_self' | '_top',
type?: string
};
type NewTabLinkProps = {
content?: React.Element<any> | string,
children?: React.Element<any> | string,
rest?: AAttributes
};
const NewTabLink = ({
/* eslint-disable */
content, // ESLINT: prop-types validation error, to be fixed in #122
children /* eslint-enable */,
...rest
}: NewTabLinkProps) =>
<a target="_blank" rel="noopener" {...rest}>
{content || children} {/* Keep content for short-hand text insertion */}
</a>;
export default NewTabLink;

View File

@ -0,0 +1,41 @@
import React from 'react';
interface AAttributes {
charset?: string;
coords?: string;
download?: string;
href: string;
hreflang?: string;
media?: string;
name?: string;
rel?:
| 'alternate'
| 'author'
| 'bookmark'
| 'external'
| 'help'
| 'license'
| 'next'
| 'nofollow'
| 'noreferrer'
| 'noopener'
| 'prev'
| 'search'
| 'tag';
rev?: string;
shape?: 'default' | 'rect' | 'circle' | 'poly';
target?: '_blank' | '_parent' | '_self' | '_top';
type?: string;
}
interface NewTabLinkProps extends AAttributes {
content?: React.ReactElement<any> | string;
children?: React.ReactElement<any> | string;
}
const NewTabLink = ({ content, children, ...rest }: NewTabLinkProps) =>
<a target="_blank" rel="noopener" {...rest}>
{content || children} {/* Keep content for short-hand text insertion */}
</a>;
export default NewTabLink;

View File

@ -1,59 +1,36 @@
// @flow
import React from 'react';
import QRCodeLib from 'qrcode';
import React from 'react';
// FIXME should store limited amount if history
// data -> qr cache
const cache: { [key: string]: string } = {};
type Props = {
data: string
};
interface Props {
data: string;
}
type State = {
qr?: string
};
interface State {
qr?: string;
}
export default class QRCode extends React.Component {
props: Props;
state: State = {};
componentWillMount() {
export default class QRCode extends React.Component<Props, State> {
public state: State = {}
public componentWillMount() {
console.error(this.props.data)
// Start generating QR codes immediately
this._generateQrCode(this.props.data);
this.generateQrCode(this.props.data);
}
componentWillReceiveProps(nextProps: Props) {
public componentWillReceiveProps(nextProps: Props) {
console.error(this.props.data)
// Regenerate QR codes if props change
if (nextProps.data !== this.props.data) {
this._generateQrCode(nextProps.data);
this.generateQrCode(nextProps.data);
}
}
_generateQrCode(value: string) {
if (cache[value]) {
this.setState({ qr: cache[value] });
return;
}
QRCodeLib.toDataURL(
value,
{
color: {
dark: '#000',
light: '#fff'
},
margin: 0,
errorCorrectionLevel: 'H'
},
(err, qr) => {
if (err) return;
cache[value] = qr;
this.setState({ qr });
}
);
}
render() {
public render() {
const { qr } = this.state;
if (!qr) {
return null;
@ -68,4 +45,30 @@ export default class QRCode extends React.Component {
/>
);
}
private generateQrCode(value: string) {
if (cache[value]) {
this.setState({ qr: cache[value] });
return;
}
console.error(value, 'Value')
QRCodeLib.toDataURL(
value,
{
color: {
dark: '#000',
light: '#fff'
},
margin: 0,
errorCorrectionLevel: 'H'
},
(err, qr) => {
if (err) {
return;
}
cache[value] = qr;
this.setState({ qr });
}
);
}
}

View File

@ -1,12 +1,9 @@
// @flow
import React, { Component } from 'react';
import type { Element } from 'react';
import Spinner from './Spinner';
const DEFAULT_BUTTON_TYPE = 'primary';
const DEFAULT_BUTTON_SIZE = 'lg';
import Spinner from './Spinner';
type ButtonType =
| 'default'
| 'primary'
@ -16,26 +13,24 @@ type ButtonType =
| 'danger';
type ButtonSize = 'lg' | 'sm' | 'xs';
type Props = {
onClick: () => any,
text: Element<*> | string,
loading?: boolean,
disabled?: boolean,
loadingText?: string,
size?: ButtonSize,
type?: ButtonType
};
interface Props {
text: React.ReactElement<any> | string;
loading?: boolean;
disabled?: boolean;
loadingText?: string;
size?: ButtonSize;
type?: ButtonType;
onClick(): any;
}
export default class SimpleButton extends Component {
props: Props;
computedClass = () => {
export default class SimpleButton extends Component<Props, {}> {
public computedClass = () => {
return `btn btn-${this.props.size || DEFAULT_BUTTON_TYPE} btn-${this.props
.type || DEFAULT_BUTTON_SIZE}`;
};
render() {
let { loading, disabled, loadingText, text, onClick } = this.props;
public render() {
const { loading, disabled, loadingText, text, onClick } = this.props;
return (
<div>

View File

@ -1,33 +1,32 @@
// @flow
import React, { Component } from 'react';
type Props = {
value?: string,
options: string[],
onChange: (value: string) => void
};
interface Props {
value?: string;
options: string[];
onChange(value: string): void;
}
export default class SimpleDropDown extends Component {
props: Props;
state: {
expanded: boolean
} = {
interface State {
expanded: boolean;
}
export default class SimpleDropDown extends Component<Props, State> {
public state = {
expanded: false
};
toggleExpanded = () => {
public toggleExpanded = () => {
this.setState(state => {
return { expanded: !state.expanded };
});
};
onClick = (event: SyntheticInputEvent) => {
const value = event.target.getAttribute('data-value') || '';
public onClick = (event: React.SyntheticEvent<HTMLAnchorElement>) => {
const value = (event.target as HTMLAnchorElement).getAttribute('data-value') || '';
this.props.onChange(value);
this.setState({ expanded: false });
};
render() {
public render() {
const { options, value } = this.props;
const { expanded } = this.state;
return (

View File

@ -1,16 +1,13 @@
// @flow
import React, { Component } from 'react';
type Props = {
value?: string,
options: string[],
onChange: (event: SyntheticInputEvent) => void
};
interface Props {
value?: string;
options: string[];
onChange(event: React.SyntheticEvent<HTMLSpanElement>): void;
}
export default class SimpleSelect extends Component {
props: Props;
render() {
export default class SimpleSelect extends Component<Props, {}> {
public render() {
return (
<select
value={this.props.value || this.props.options[0]}

View File

@ -1,10 +1,10 @@
import React from 'react';
type size = 'lg' | '2x' | '3x' | '4x' | '5x';
type Size = 'lg' | '2x' | '3x' | '4x' | '5x';
type SpinnerProps = {
size?: size
};
interface SpinnerProps {
size?: Size;
}
const Spinner = ({ size = 'fa-' }: SpinnerProps) => {
return <i className={`fa fa-spinner fa-spin fa-${size ? size : 'fw'}`} />;

View File

@ -1,30 +1,23 @@
// @flow
import React from 'react';
import PropTypes from 'prop-types';
import translate from 'translations';
import WalletDecrypt from 'components/WalletDecrypt';
import type { IWallet } from 'libs/wallet/IWallet';
import { IWallet } from 'libs/wallet/IWallet';
import React from 'react';
import { connect } from 'react-redux';
import type { State } from 'reducers';
import { AppState } from 'reducers';
import translate from 'translations';
type Props = {
title: string,
wallet: IWallet
};
export class UnlockHeader extends React.Component {
props: Props;
static propTypes = {
title: PropTypes.string.isRequired
};
state: {
expanded: boolean
} = {
interface Props {
title: string;
wallet: IWallet;
}
interface State {
expanded: boolean;
}
export class UnlockHeader extends React.Component<Props, State> {
public state = {
expanded: !this.props.wallet
};
componentDidUpdate(prevProps: Props) {
public componentDidUpdate(prevProps: Props) {
if (this.props.wallet && this.props.wallet !== prevProps.wallet) {
this.setState({ expanded: false });
}
@ -35,7 +28,7 @@ export class UnlockHeader extends React.Component {
}
}
render() {
public render() {
return (
<article className="collapse-container">
<div onClick={this.toggleExpanded}>
@ -54,14 +47,14 @@ export class UnlockHeader extends React.Component {
);
}
toggleExpanded = () => {
public toggleExpanded = () => {
this.setState(state => {
return { expanded: !state.expanded };
});
};
}
function mapStateToProps(state: State) {
function mapStateToProps(state: AppState) {
return {
wallet: state.wallet.inst
};

View File

@ -1,8 +1,6 @@
// @flow
export { default as Dropdown } from './Dropdown';
export { default as Identicon } from './Identicon';
export { default as Modal } from './Modal';
export { default as UnlockHeader } from './UnlockHeader';
export { default as QRCode } from './QRCode';
export { default as NewTabLink } from './NewTabLink.jsx';
export { default as NewTabLink } from './NewTabLink';

View File

@ -1,4 +1,4 @@
import { ETHTxExplorer, BTCTxExplorer } from './data';
import { BTCTxExplorer, ETHTxExplorer } from './data';
type SupportedDestinationKind = 'ETH' | 'BTC' | 'REP';
@ -16,16 +16,16 @@ const buffers = {
};
// rate must be BTC[KIND]
export function kindMin(
export function generateKindMin(
BTCKINDRate: number,
kind: SupportedDestinationKind
): number {
const kindMin = BTCKINDRate * BTCMin;
return kindMin + kindMin * buffers[kind];
const kindMinVal = BTCKINDRate * BTCMin;
return kindMinVal + kindMinVal * buffers[kind];
}
// rate must be BTC[KIND]
export function kindMax(
export function generateKindMax(
BTCKINDRate: number,
kind: SupportedDestinationKind
): number {

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