diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md new file mode 100644 index 00000000..3b5738a8 --- /dev/null +++ b/.github/CONTRIBUTING.md @@ -0,0 +1 @@ +Please see the [Contributor Guidelines on the wiki](https://github.com/MyCryptoHQ/MyCrypto/wiki/Contributor-Guidelines). diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md new file mode 100644 index 00000000..2a2ddf20 --- /dev/null +++ b/.github/ISSUE_TEMPLATE.md @@ -0,0 +1,37 @@ +If you're having an issue with unlocking your wallet, a transaction, a swap, +or an ENS auction, please email support@mycrypto.com. Github issues are for +development only, and support questions that aren't bugs or feature requests +will not be answered. + +--- + +If you've experienced a bug, please provide the following information: + +### Description of the Issue + +(Your description goes here) + +### Steps to Reproduce + +(Explain how you got this issue) + +### Description of Your Machine + +(Your browser, browser version, operating system, device) + +### Console Logs / Screenshots + +(Console logs or screenshots go here, if applicable) + +--- + +If you're suggesting a feature, make sure someone hasn't already posted it. If +no one has, please provide the following information: + +### Description of the Feature + +(Your description goes here) + +### Example(s) of Feature + +(If another product or app is doing this, please link it) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 00000000..fffe8cfa --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,29 @@ +If you're contributing translations, please follow the contributor guidelines +at https://github.com/MyCryptoHQ/MyCrypto/wiki/Contributor-Guidelines#providing-translations + +--- + +If you're contributing code, please link to the issue it resolves and follow the +contributor guidelines at https://github.com/MyCryptoHQ/MyCrypto/wiki/Contributor-Guidelines#contributing-code and fill out the following template: + +Closes #(issue number goes here) + +### Description + +(Description goes here) + +### Changes + +* High level +* Changes that +* You Made + +### Steps to Test + +1. Steps +2. To +3. Test + +### Screenshots + +(Only if applicable) diff --git a/.nvmrc b/.nvmrc index 45a4fb75..e3f76f34 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -8 +8.9.4 diff --git a/.travis.yml b/.travis.yml index 5ac7cb17..d07dfadf 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,7 +13,8 @@ before_install: - export CHROME_BIN=chromium-browser - export DISPLAY=:99.0 - sh -e /etc/init.d/xvfb start - - docker pull dternyak/eth-priv-to-addr:latest +# uncomment once integration tests are included in CI +# - docker pull dternyak/eth-priv-to-addr:latest - sudo apt-get install libusb-1.0 install: @@ -26,13 +27,7 @@ jobs: - stage: test script: npm run test - stage: test - script: npm run tslint - - stage: test - script: npm run tscheck - - stage: test - script: npm run freezer - - stage: test - script: npm run freezer:validate + script: npm run tslint && npm run tscheck && npm run freezer && npm run freezer:validate notifications: email: diff --git a/LICENSE b/LICENSE index eca703eb..7b1edff2 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,7 @@ MIT License -Copyright (c) 2016 MyEtherWallet +Copyright (c) 2015-2017 MyEtherWallet LLC +Copyright (c) 2018 MyCrypto, Inc. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index 40c02759..09bce8cb 100644 --- a/README.md +++ b/README.md @@ -1,35 +1,63 @@ -# MyEtherWallet V4+ (ALPHA - VISIT [V3](https://github.com/kvhnuke/etherwallet) for the production site) +# MyCrypto Beta (VISIT [MyCryptoHQ/mycrypto.com](https://github.com/MyCryptoHQ/mycrypto.com) for the current site)
Just looking to download? Grab our [latest release](https://github.com/MyCryptoHQ/MyCrypto/releases) -[![Greenkeeper badge](https://badges.greenkeeper.io/MyEtherWallet/MyEtherWallet.svg)](https://greenkeeper.io/) +[![Greenkeeper badge](https://badges.greenkeeper.io/MyCryptoHq/MyCrypto.svg)](https://greenkeeper.io/) -#### Run: +## Running the App + +This codebase targets Node 8.9.4 (LTS). After `npm install`ing all dependencies (You may be required to install additional system dependencies, due to some node modules relying on them) you can run various commands depending on what you want to do: + +#### Development ```bash -npm run dev # run app in dev mode +# run app in dev mode in browser, rebuild on file changes +npm run dev ``` -#### Build: - ```bash -npm run build # build app +# run app in dev mode in electron, rebuild on file changes +npm run dev:electron ``` -It generates app in `dist` folder. +#### Build Releases + +```bash +# builds the production server app +npm run build +``` + +```bash +# builds the downloadable version of the site +npm run build:downloadable +``` + +```bash +# builds the electron apps +npm run build:electron + +# builds only one OS's electron app +npm run build:electron:(osx|linux|windows) +``` + +All of these builds are output to a folder in `dist/`. #### Unit Tests: ```bash -npm run test # run tests with Jest +# run unit tests with Jest +npm run test ``` #### Integration Tests: ```bash -npm run test:int # run tests with Jest +# run integration tests with Jest +npm run test:int ``` #### Dev (HTTPS): +Some parts of the site, such as the Ledger wallet, require an HTTPS environment to work. To develop on HTTPS, do the following: + 1. Create your own SSL Certificate (Heroku has a [nice guide here](https://devcenter.heroku.com/articles/ssl-certificate-self)) 2. Move the `.key` and `.crt` files into `webpack_config/server.*` 3. Run the following command: @@ -69,300 +97,32 @@ npm run test:int ``` │ ├── common -│ ├── actions - application actions +│ ├── actions - Application actions │ ├── api - Services and XHR utils -│ ├── components - components according to "Redux philosophy" -│ ├── config - frontend config depending on REACT_WEBPACK_ENV -│ ├── containers - containers according to "Redux philosophy" -│ ├── reducers - application reducers -│ ├── routing - application routing -│ ├── index.tsx - entry -│ ├── index.html -├── static -├── webpack_config - Webpack configuration -├── jest_config - Jest configuration +│ ├── assets - Images, fonts, etc. +│ ├── components - Components according to "Redux philosophy" +│ ├── config - Various config data and hard-coded json +│ ├── containers - Containers according to "Redux philosophy" +│ ├── libs - Framework-agnostic libraries and business logic +│ ├── reducers - Redux reducers +│ ├── sagas - Redux sagas +│ ├── sass - SCSS styles, variables, mixins +│ ├── selectors - Redux selectors +│ ├── translations - Language JSON dictionaries +│ ├── typescript - Typescript definition files +│ ├── utils - Common use utility functions +│ ├── index.tsx - Entry point for app +│ ├── index.html - Html template file for html-webpack-plugin +│ ├── Root.tsx - Root component for React +│ └── store.ts - Redux reducer combiner and middleware injector +├── electron-app - Code for the native electron app +├── jest_config - Jest testing configuration +├── spec - Jest unit tests, mirror's common's structure +├── static - Files that don't get compiled, just moved to build +└── webpack_config - Webpack configuration ``` -## Style Guides and Philosophies - -The following are guides for developers to follow for writing compliant code. - -### Redux and Actions - -Each reducer has one file in `reducers/[namespace].ts` that contains the reducer -and initial state, one file in `actions/[namespace].ts` that contains the action -creators and their return types, and optionally one file in -`sagas/[namespace].ts` that handles action side effects using -[`redux-saga`](https://github.com/redux-saga/redux-saga). - -The files should be laid out as follows: - -#### Reducer - -* State should be explicitly defined and exported -* Initial state should match state typing, define every key - -```ts -import { NamespaceAction } from "actions/[namespace]"; -import { TypeKeys } from 'actions/[namespace]/constants'; - -export interface State { /* definition for state object */ }; -export const INITIAL_STATE: State = { /* Initial state shape */ }; - -export function [namespace]( - state: State = INITIAL_STATE, - action: NamespaceAction -): State { - switch (action.type) { - case TypeKeys.NAMESPACE_NAME_OF_ACTION: - return { - ...state, - // Alterations to state - }; - default: - return state; - } -} -``` - -#### Actions - -* Define each action creator in `actionCreator.ts` -* Define each action object type in `actionTypes.ts` - * Export a union of all of the action types for use by the reducer -* Define each action type as a string enum in `constants.ts` -* Export `actionCreators` and `actionTypes` from module file `index.ts` - -``` -├── common - ├── actions - application actions - ├── [namespace] - action namespace - ├── actionCreators.ts - action creators - ├── actionTypes.ts - action interfaces / types - ├── constants.ts - string enum - ├── index.ts - exports all action creators and action object types -``` - -##### constants.ts - -```ts -export enum TypeKeys { - NAMESPACE_NAME_OF_ACTION = 'NAMESPACE_NAME_OF_ACTION' -} -``` - -##### actionTypes.ts - -```ts -/*** Name of action ***/ -export interface NameOfActionAction { - type: TypeKeys.NAMESPACE_NAME_OF_ACTION; - /* Rest of the action object shape */ -} - -/*** Action Union ***/ -export type NamespaceAction = ActionOneAction | ActionTwoAction | ActionThreeAction; -``` - -##### actionCreators.ts - -```ts -import * as interfaces from './actionTypes'; -import { TypeKeys } from './constants'; - -export interface TNameOfAction = typeof nameOfAction; -export function nameOfAction(): interfaces.NameOfActionAction { - return { - type: TypeKeys.NAMESPACE_NAME_OF_ACTION, - payload: {} - }; -}; -``` - -##### index.ts - -```ts -export * from './actionCreators'; -export * from './actionTypes'; -``` - -### Typing Redux-Connected Components - -Components that receive props directly from redux as a result of the `connect` -function should use AppState for typing, rather than manually defining types. -This makes refactoring reducers easier by catching mismatches or changes of -types in components, and reduces the chance for inconsistency. It's also less -code overall. - -``` -// Do this -import { AppState } from 'reducers'; - -interface Props { - wallet: AppState['wallet']['inst']; - rates: AppState['rates']['rates']; - // ... -} - -// Not this -import { IWallet } from 'libs/wallet'; -import { Rates } from 'libs/rates'; - -interface Props { - wallet: IWallet; - rates: Rates; - // ... -} -``` - -However, if you have a sub-component that takes in props from a connected -component, it's OK to manually specify the type. Especially if you go from -being type-or-null to guaranteeing the prop will be passed (because of a -conditional render.) - -### Higher Order Components - -#### Typing Injected Props - -Props made available through higher order components can be tricky to type. You can inherit the injected props, and in the case of react router, specialize the generic in `withRouter` so it can omit all of its injected props from the component. - -```ts -import { RouteComponentProps } from 'react-router-dom'; - -interface MyComponentProps extends RouteComponentProps<{}> { - name: string; - countryCode?: string; -} -``` - -```ts -class MyComponent extends React.Component { - - render() { - const { name, countryCode, location } = this.props; // location being the one of the injected props from the withRouter HOC - ... - } -} - -export default withRouter(MyComponent); -``` - -## Event Handlers - -Event handlers such as `onChange` and `onClick`, should be properly typed. For example, if you have an event listener on an input element inside a form: - -```ts -public onValueChange = (e: React.FormEvent) => { - if (this.props.onChange) { - this.props.onChange( - e.currentTarget.value, - this.props.unit - ); - } - }; -``` - -Where you type the event as a `React.FormEvent` of type `HTMLElement`. - -## Class names - -Dynamic class names should use the `classnames` module to simplify how they are created instead of using string template literals with expressions inside. - -### Styling - -Legacy styles are housed under `common/assets/styles` and written with LESS. -However, going forward, each styled component should create a a `.scss` file of -the same name in the same folder, and import it like so: - -```ts -import React from 'react'; - -import './MyComponent.scss'; - -export default class MyComponent extends React.component<{}, {}> { - render() { - return ( -
-
Hello!
-
- ); - } -} -``` - -These style modules adhere to [SuitCSS naming convention](https://github.com/suitcss/suit/blob/master/doc/naming-conventions.md): - -```scss -.MyComponent { - /* Styles */ - - &-child { - /* Styles */ - - &.is-hidden { - display: none; - } - } -} -``` - -All elements inside of a component should extend its parent class namespace, or -create a new namespace (Potentially breaking that out into its own component.) - -Variables and mixins can be imported from the files in `common/styles`: - -```scss -@import 'sass/colors'; - -code { - color: $code-color; -} -``` - -#### Converting Styles - -When working on a module that has styling in Less, try to do the following: - -* Screenshot the component in question -* Create a new SCSS file in the same directory -* Remove styling from LESS file, convert it to the SCSS file (Mostly s/@/$) -* Convert class names to SuitCSS naming convention -* Convert any utility classes from `etherewallet-utilities.less` into mixins -* Convert as many element selectors to class name selectors as possible -* Convert as many `
` tags or ` `s to margins -* Ensure that there has been little to no deviation from screenshot - -#### Adding Icon-fonts - -1. Download chosen icon-font -1. Declare css font-family: - ``` - @font-face { - font-family: 'social-media'; - src: url('../assets/fonts/social-media.eot'); - src: url('../assets/fonts/social-media.eot') format('embedded-opentype'), - url('../assets/fonts/social-media.woff2') format('woff2'), - url('../assets/fonts/social-media.woff') format('woff'), - url('../assets/fonts/social-media.ttf') format('truetype'), - url('../assets/fonts/social-media.svg') format('svg'); - font-weight: normal; - font-style: normal; - } - ``` -1. Create classes for each icon using their unicode character - ``` - .sm-logo-facebook:before { - content: '\ea02'; - } - ``` - * [How to get unicode icon values?](https://stackoverflow.com/questions/27247145/get-the-unicode-icon-value-from-a-custom-font) -1. Write some markup: - ``` - - - Hello World - - ``` +### More information is available on the [Wiki Pages](https://github.com/MyCryptoHQ/MyCrypto/wiki) ## Thanks & Support diff --git a/common/Root.tsx b/common/Root.tsx index bad06b74..0de08d6f 100644 --- a/common/Root.tsx +++ b/common/Root.tsx @@ -5,7 +5,6 @@ import { withRouter, Switch, Redirect, HashRouter, Route, BrowserRouter } from ' import Contracts from 'containers/Tabs/Contracts'; import ENS from 'containers/Tabs/ENS'; import GenerateWallet from 'containers/Tabs/GenerateWallet'; -import Help from 'containers/Tabs/Help'; import SendTransaction from 'containers/Tabs/SendTransaction'; import Swap from 'containers/Tabs/Swap'; import SignAndVerifyMessage from 'containers/Tabs/SignAndVerifyMessage'; @@ -13,9 +12,13 @@ import BroadcastTx from 'containers/Tabs/BroadcastTx'; import ErrorScreen from 'components/ErrorScreen'; import PageNotFound from 'components/PageNotFound'; import LogOutPrompt from 'components/LogOutPrompt'; +import { TitleBar } from 'components/ui'; import { Store } from 'redux'; import { pollOfflineStatus } from 'actions/config'; import { AppState } from 'reducers'; +import { RouteNotFound } from 'components/RouteNotFound'; +import { RedirectWithQuery } from 'components/RedirectWithQuery'; +import 'what-input'; interface Props { store: Store; @@ -46,35 +49,40 @@ export default class Root extends Component { return ; } - // key={Math.random()} = hack for HMR from https://github.com/webpack/webpack-dev-server/issues/395 + const CaptureRouteNotFound = withRouter(({ children, location }) => { + return location && location.state && location.state.error ? ( + + ) : ( + (children as JSX.Element) + ); + }); + const routes = ( - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + ); - const Router = process.env.BUILD_DOWNLOADABLE ? HashRouter : BrowserRouter; + const Router = + process.env.BUILD_DOWNLOADABLE && process.env.NODE_ENV === 'production' + ? HashRouter + : BrowserRouter; return ( + {process.env.BUILD_ELECTRON && } {routes} @@ -87,14 +95,15 @@ export default class Root extends Component { const LegacyRoutes = withRouter(props => { const { history } = props; - const { pathname, hash } = props.location; + const { pathname } = props.location; + let { hash } = props.location; if (pathname === '/') { + hash = hash.split('?')[0]; switch (hash) { case '#send-transaction': case '#offline-transaction': - history.push('/send-transaction'); - break; + return ; case '#generate-wallet': history.push('/'); break; @@ -118,9 +127,9 @@ const LegacyRoutes = withRouter(props => { return ( - - - + + + ); }); diff --git a/common/actions/config/actionCreators.ts b/common/actions/config/actionCreators.ts index f1cb8b2a..0c298680 100644 --- a/common/actions/config/actionCreators.ts +++ b/common/actions/config/actionCreators.ts @@ -1,9 +1,8 @@ import * as interfaces from './actionTypes'; import { TypeKeys } from './constants'; -import { NodeConfig, CustomNodeConfig, NetworkConfig, CustomNetworkConfig } from 'config/data'; -export type TToggleOfflineConfig = typeof toggleOfflineConfig; -export function toggleOfflineConfig(): interfaces.ToggleOfflineAction { +export type TToggleOffline = typeof toggleOffline; +export function toggleOffline(): interfaces.ToggleOfflineAction { return { type: TypeKeys.CONFIG_TOGGLE_OFFLINE }; @@ -26,13 +25,11 @@ export function changeLanguage(sign: string): interfaces.ChangeLanguageAction { export type TChangeNode = typeof changeNode; export function changeNode( - nodeSelection: string, - node: NodeConfig, - network: NetworkConfig + payload: interfaces.ChangeNodeAction['payload'] ): interfaces.ChangeNodeAction { return { type: TypeKeys.CONFIG_NODE_CHANGE, - payload: { nodeSelection, node, network } + payload }; } @@ -52,7 +49,9 @@ export function changeNodeIntent(payload: string): interfaces.ChangeNodeIntentAc } export type TAddCustomNode = typeof addCustomNode; -export function addCustomNode(payload: CustomNodeConfig): interfaces.AddCustomNodeAction { +export function addCustomNode( + payload: interfaces.AddCustomNodeAction['payload'] +): interfaces.AddCustomNodeAction { return { type: TypeKeys.CONFIG_ADD_CUSTOM_NODE, payload @@ -60,7 +59,9 @@ export function addCustomNode(payload: CustomNodeConfig): interfaces.AddCustomNo } export type TRemoveCustomNode = typeof removeCustomNode; -export function removeCustomNode(payload: CustomNodeConfig): interfaces.RemoveCustomNodeAction { +export function removeCustomNode( + payload: interfaces.RemoveCustomNodeAction['payload'] +): interfaces.RemoveCustomNodeAction { return { type: TypeKeys.CONFIG_REMOVE_CUSTOM_NODE, payload @@ -68,7 +69,9 @@ export function removeCustomNode(payload: CustomNodeConfig): interfaces.RemoveCu } export type TAddCustomNetwork = typeof addCustomNetwork; -export function addCustomNetwork(payload: CustomNetworkConfig): interfaces.AddCustomNetworkAction { +export function addCustomNetwork( + payload: interfaces.AddCustomNetworkAction['payload'] +): interfaces.AddCustomNetworkAction { return { type: TypeKeys.CONFIG_ADD_CUSTOM_NETWORK, payload @@ -77,7 +80,7 @@ export function addCustomNetwork(payload: CustomNetworkConfig): interfaces.AddCu export type TRemoveCustomNetwork = typeof removeCustomNetwork; export function removeCustomNetwork( - payload: CustomNetworkConfig + payload: interfaces.RemoveCustomNetworkAction['payload'] ): interfaces.RemoveCustomNetworkAction { return { type: TypeKeys.CONFIG_REMOVE_CUSTOM_NETWORK, @@ -93,6 +96,15 @@ export function setLatestBlock(payload: string): interfaces.SetLatestBlockAction }; } +export function web3SetNode( + payload: interfaces.Web3setNodeAction['payload'] +): interfaces.Web3setNodeAction { + return { + type: TypeKeys.CONFIG_NODE_WEB3_SET, + payload + }; +} + export type TWeb3UnsetNode = typeof web3UnsetNode; export function web3UnsetNode(): interfaces.Web3UnsetNodeAction { return { diff --git a/common/actions/config/actionTypes.ts b/common/actions/config/actionTypes.ts index cd147b74..8758af67 100644 --- a/common/actions/config/actionTypes.ts +++ b/common/actions/config/actionTypes.ts @@ -1,5 +1,6 @@ import { TypeKeys } from './constants'; -import { NodeConfig, CustomNodeConfig, NetworkConfig, CustomNetworkConfig } from 'config/data'; +import { CustomNodeConfig, Web3NodeConfig } from 'types/node'; +import { CustomNetworkConfig } from 'types/network'; /*** Toggle Offline ***/ export interface ToggleOfflineAction { @@ -19,11 +20,9 @@ export interface ChangeLanguageAction { /*** Change Node ***/ export interface ChangeNodeAction { type: TypeKeys.CONFIG_NODE_CHANGE; - // FIXME $keyof? payload: { - nodeSelection: string; - node: NodeConfig; - network: NetworkConfig; + nodeId: string; + networkId: string; }; } @@ -41,25 +40,25 @@ export interface ChangeNodeIntentAction { /*** Add Custom Node ***/ export interface AddCustomNodeAction { type: TypeKeys.CONFIG_ADD_CUSTOM_NODE; - payload: CustomNodeConfig; + payload: { id: string; config: CustomNodeConfig }; } /*** Remove Custom Node ***/ export interface RemoveCustomNodeAction { type: TypeKeys.CONFIG_REMOVE_CUSTOM_NODE; - payload: CustomNodeConfig; + payload: { id: string }; } /*** Add Custom Network ***/ export interface AddCustomNetworkAction { type: TypeKeys.CONFIG_ADD_CUSTOM_NETWORK; - payload: CustomNetworkConfig; + payload: { id: string; config: CustomNetworkConfig }; } /*** Remove Custom Network ***/ export interface RemoveCustomNetworkAction { type: TypeKeys.CONFIG_REMOVE_CUSTOM_NETWORK; - payload: CustomNetworkConfig; + payload: { id: string }; } /*** Set Latest Block ***/ @@ -73,17 +72,28 @@ export interface Web3UnsetNodeAction { type: TypeKeys.CONFIG_NODE_WEB3_UNSET; } -/*** Union Type ***/ -export type ConfigAction = +/*** Set Web3 as a Node ***/ +export interface Web3setNodeAction { + type: TypeKeys.CONFIG_NODE_WEB3_SET; + payload: { id: 'web3'; config: Web3NodeConfig }; +} + +export type CustomNetworkAction = AddCustomNetworkAction | RemoveCustomNetworkAction; + +export type CustomNodeAction = AddCustomNodeAction | RemoveCustomNodeAction; + +export type NodeAction = | ChangeNodeAction + | ChangeNodeIntentAction + | Web3UnsetNodeAction + | Web3setNodeAction; + +export type MetaAction = | ChangeLanguageAction | ToggleOfflineAction | ToggleAutoGasLimitAction | PollOfflineStatus - | ChangeNodeIntentAction - | AddCustomNodeAction - | RemoveCustomNodeAction - | AddCustomNetworkAction - | RemoveCustomNetworkAction - | SetLatestBlockAction - | Web3UnsetNodeAction; + | SetLatestBlockAction; + +/*** Union Type ***/ +export type ConfigAction = CustomNetworkAction | CustomNodeAction | NodeAction | MetaAction; diff --git a/common/actions/config/constants.ts b/common/actions/config/constants.ts index 58fa8e15..a3b58ac1 100644 --- a/common/actions/config/constants.ts +++ b/common/actions/config/constants.ts @@ -1,14 +1,19 @@ export enum TypeKeys { CONFIG_LANGUAGE_CHANGE = 'CONFIG_LANGUAGE_CHANGE', - CONFIG_NODE_CHANGE = 'CONFIG_NODE_CHANGE', - CONFIG_NODE_CHANGE_INTENT = 'CONFIG_NODE_CHANGE_INTENT', + CONFIG_TOGGLE_OFFLINE = 'CONFIG_TOGGLE_OFFLINE', CONFIG_TOGGLE_AUTO_GAS_LIMIT = 'CONFIG_TOGGLE_AUTO_GAS_LIMIT', CONFIG_POLL_OFFLINE_STATUS = 'CONFIG_POLL_OFFLINE_STATUS', + CONFIG_SET_LATEST_BLOCK = 'CONFIG_SET_LATEST_BLOCK', + + CONFIG_NODE_WEB3_SET = 'CONFIG_NODE_WEB3_SET', + CONFIG_NODE_WEB3_UNSET = 'CONFIG_NODE_WEB3_UNSET', + CONFIG_NODE_CHANGE = 'CONFIG_NODE_CHANGE', + CONFIG_NODE_CHANGE_INTENT = 'CONFIG_NODE_CHANGE_INTENT', + CONFIG_ADD_CUSTOM_NODE = 'CONFIG_ADD_CUSTOM_NODE', CONFIG_REMOVE_CUSTOM_NODE = 'CONFIG_REMOVE_CUSTOM_NODE', + CONFIG_ADD_CUSTOM_NETWORK = 'CONFIG_ADD_CUSTOM_NETWORK', - CONFIG_REMOVE_CUSTOM_NETWORK = 'CONFIG_REMOVE_CUSTOM_NETWORK', - CONFIG_SET_LATEST_BLOCK = 'CONFIG_SET_LATEST_BLOCK', - CONFIG_NODE_WEB3_UNSET = 'CONFIG_NODE_WEB3_UNSET' + CONFIG_REMOVE_CUSTOM_NETWORK = 'CONFIG_REMOVE_CUSTOM_NETWORK' } diff --git a/common/actions/customTokens/actionCreators.ts b/common/actions/customTokens/actionCreators.ts index a18e8435..72d5246d 100644 --- a/common/actions/customTokens/actionCreators.ts +++ b/common/actions/customTokens/actionCreators.ts @@ -1,6 +1,6 @@ -import { Token } from 'config/data'; import * as interfaces from './actionTypes'; import { TypeKeys } from './constants'; +import { Token } from 'types/network'; export type TAddCustomToken = typeof addCustomToken; export function addCustomToken(payload: Token): interfaces.AddCustomTokenAction { diff --git a/common/actions/customTokens/actionTypes.ts b/common/actions/customTokens/actionTypes.ts index 2d214915..b1368005 100644 --- a/common/actions/customTokens/actionTypes.ts +++ b/common/actions/customTokens/actionTypes.ts @@ -1,5 +1,6 @@ -import { Token } from 'config/data'; import { TypeKeys } from './constants'; +import { Token } from 'types/network'; + /*** Add custom token ***/ export interface AddCustomTokenAction { type: TypeKeys.CUSTOM_TOKEN_ADD; diff --git a/common/actions/ens/actionTypes/actionTypes.ts b/common/actions/ens/actionTypes/actionTypes.ts index 22b4ce18..e9d035f9 100644 --- a/common/actions/ens/actionTypes/actionTypes.ts +++ b/common/actions/ens/actionTypes/actionTypes.ts @@ -1,5 +1,4 @@ import { ResolveDomainAction } from './resolveDomain'; - export * from './resolveDomain'; export type EnsAction = ResolveDomainAction; diff --git a/common/actions/ens/constants.ts b/common/actions/ens/constants.ts index 6a752c30..fdab3be9 100644 --- a/common/actions/ens/constants.ts +++ b/common/actions/ens/constants.ts @@ -1,6 +1,6 @@ export enum TypeKeys { ENS_RESOLVE_DOMAIN_REQUESTED = 'ENS_RESOLVE_DOMAIN_REQUESTED', ENS_RESOLVE_DOMAIN_SUCCEEDED = 'ENS_RESOLVE_DOMAIN_SUCCEEDED', - ENS_RESOLVE_DOMAIN_CACHED = 'ENS_RESOLVE_DOMAIN_CACHED', - ENS_RESOLVE_DOMAIN_FAILED = 'ENS_RESOLVE_DOMAIN_FAILED' + ENS_RESOLVE_DOMAIN_FAILED = 'ENS_RESOLVE_DOMAIN_FAILED', + ENS_RESOLVE_DOMAIN_CACHED = 'ENS_RESOLVE_DOMAIN_CACHED' } diff --git a/common/actions/rates/actionCreators.ts b/common/actions/rates/actionCreators.ts index 98f1e2e7..22b82167 100644 --- a/common/actions/rates/actionCreators.ts +++ b/common/actions/rates/actionCreators.ts @@ -1,12 +1,12 @@ import * as interfaces from './actionTypes'; import { TypeKeys } from './constants'; -import { fetchRates, CCResponse } from './actionPayloads'; +import { CCResponse } from 'api/rates'; -export type TFetchCCRates = typeof fetchCCRates; -export function fetchCCRates(symbols: string[] = []): interfaces.FetchCCRates { +export type TFetchCCRatesRequested = typeof fetchCCRatesRequested; +export function fetchCCRatesRequested(symbols: string[] = []): interfaces.FetchCCRatesRequested { return { - type: TypeKeys.RATES_FETCH_CC, - payload: fetchRates(symbols) + type: TypeKeys.RATES_FETCH_CC_REQUESTED, + payload: symbols }; } diff --git a/common/actions/rates/actionTypes.ts b/common/actions/rates/actionTypes.ts index f0d3ca4a..abd4d9eb 100644 --- a/common/actions/rates/actionTypes.ts +++ b/common/actions/rates/actionTypes.ts @@ -1,9 +1,9 @@ import { TypeKeys } from './constants'; -import { CCResponse } from './actionPayloads'; +import { CCResponse } from 'api/rates'; -export interface FetchCCRates { - type: TypeKeys.RATES_FETCH_CC; - payload: Promise; +export interface FetchCCRatesRequested { + type: TypeKeys.RATES_FETCH_CC_REQUESTED; + payload: string[]; } /*** Set rates ***/ @@ -17,4 +17,4 @@ export interface FetchCCRatesFailed { } /*** Union Type ***/ -export type RatesAction = FetchCCRates | FetchCCRatesSucceeded | FetchCCRatesFailed; +export type RatesAction = FetchCCRatesRequested | FetchCCRatesSucceeded | FetchCCRatesFailed; diff --git a/common/actions/rates/constants.ts b/common/actions/rates/constants.ts index 74999a17..419a1c30 100644 --- a/common/actions/rates/constants.ts +++ b/common/actions/rates/constants.ts @@ -1,5 +1,5 @@ export enum TypeKeys { - RATES_FETCH_CC = 'RATES_FETCH_CC', + RATES_FETCH_CC_REQUESTED = 'RATES_FETCH_CC_REQUESTED', RATES_FETCH_CC_FAILED = 'RATES_FETCH_CC_FAILED', RATES_FETCH_CC_SUCCEEDED = 'RATES_FETCH_CC_SUCCEEDED' } diff --git a/common/actions/rates/index.ts b/common/actions/rates/index.ts index 0028ed74..113dd69a 100644 --- a/common/actions/rates/index.ts +++ b/common/actions/rates/index.ts @@ -1,3 +1,2 @@ export * from './actionCreators'; export * from './actionTypes'; -export * from './actionPayloads'; diff --git a/common/actions/transaction/actionCreators/fields.ts b/common/actions/transaction/actionCreators/fields.ts index 0ac1a4b5..e64c15aa 100644 --- a/common/actions/transaction/actionCreators/fields.ts +++ b/common/actions/transaction/actionCreators/fields.ts @@ -6,6 +6,7 @@ import { SetValueFieldAction, InputGasLimitAction, InputGasPriceAction, + InputGasPriceIntentAction, InputDataAction, InputNonceAction, ResetAction, @@ -25,6 +26,12 @@ const inputGasPrice = (payload: InputGasPriceAction['payload']) => ({ payload }); +type TInputGasPriceIntent = typeof inputGasPrice; +const inputGasPriceIntent = (payload: InputGasPriceIntentAction['payload']) => ({ + type: TypeKeys.GAS_PRICE_INPUT_INTENT, + payload +}); + type TInputNonce = typeof inputNonce; const inputNonce = (payload: InputNonceAction['payload']) => ({ type: TypeKeys.NONCE_INPUT, @@ -79,6 +86,7 @@ const reset = (): ResetAction => ({ type: TypeKeys.RESET }); export { TInputGasLimit, TInputGasPrice, + TInputGasPriceIntent, TInputNonce, TInputData, TSetGasLimitField, @@ -90,6 +98,7 @@ export { TReset, inputGasLimit, inputGasPrice, + inputGasPriceIntent, inputNonce, inputData, setGasLimitField, diff --git a/common/actions/transaction/actionCreators/sign.ts b/common/actions/transaction/actionCreators/sign.ts index 4de60e82..2ede9c25 100644 --- a/common/actions/transaction/actionCreators/sign.ts +++ b/common/actions/transaction/actionCreators/sign.ts @@ -1,9 +1,8 @@ import { SignTransactionFailedAction, - SignLocalTransactionRequestedAction, - SignWeb3TransactionRequestedAction, SignLocalTransactionSucceededAction, - SignWeb3TransactionSucceededAction + SignWeb3TransactionSucceededAction, + SignTransactionRequestedAction } from '../actionTypes'; import { TypeKeys } from '../constants'; @@ -12,6 +11,12 @@ const signTransactionFailed = (): SignTransactionFailedAction => ({ type: TypeKeys.SIGN_TRANSACTION_FAILED }); +type TSignTransactionRequested = typeof signTransactionRequested; +const signTransactionRequested = (payload: SignTransactionRequestedAction['payload']) => ({ + type: TypeKeys.SIGN_TRANSACTION_REQUESTED, + payload +}); + type TSignLocalTransactionSucceeded = typeof signLocalTransactionSucceeded; const signLocalTransactionSucceeded = ( payload: SignLocalTransactionSucceededAction['payload'] @@ -20,14 +25,6 @@ const signLocalTransactionSucceeded = ( payload }); -type TSignLocalTransactionRequested = typeof signLocalTransactionRequested; -const signLocalTransactionRequested = ( - payload: SignLocalTransactionRequestedAction['payload'] -): SignLocalTransactionRequestedAction => ({ - type: TypeKeys.SIGN_LOCAL_TRANSACTION_REQUESTED, - payload -}); - type TSignWeb3TransactionSucceeded = typeof signWeb3TransactionSucceeded; const signWeb3TransactionSucceeded = ( payload: SignWeb3TransactionSucceededAction['payload'] @@ -36,23 +33,13 @@ const signWeb3TransactionSucceeded = ( payload }); -type TSignWeb3TransactionRequested = typeof signWeb3TransactionRequested; -const signWeb3TransactionRequested = ( - payload: SignWeb3TransactionRequestedAction['payload'] -): SignWeb3TransactionRequestedAction => ({ - type: TypeKeys.SIGN_WEB3_TRANSACTION_REQUESTED, - payload -}); - export { + signTransactionRequested, signTransactionFailed, signLocalTransactionSucceeded, - signLocalTransactionRequested, signWeb3TransactionSucceeded, - signWeb3TransactionRequested, TSignLocalTransactionSucceeded, - TSignLocalTransactionRequested, TSignWeb3TransactionSucceeded, - TSignWeb3TransactionRequested, - TSignTransactionFailed + TSignTransactionFailed, + TSignTransactionRequested }; diff --git a/common/actions/transaction/actionTypes/fields.ts b/common/actions/transaction/actionTypes/fields.ts index c390844b..a4183a94 100644 --- a/common/actions/transaction/actionTypes/fields.ts +++ b/common/actions/transaction/actionTypes/fields.ts @@ -10,6 +10,10 @@ interface InputGasPriceAction { type: TypeKeys.GAS_PRICE_INPUT; payload: string; } +interface InputGasPriceIntentAction { + type: TypeKeys.GAS_PRICE_INPUT_INTENT; + payload: string; +} interface InputDataAction { type: TypeKeys.DATA_FIELD_INPUT; payload: string; @@ -84,6 +88,7 @@ type FieldAction = export { InputGasLimitAction, InputGasPriceAction, + InputGasPriceIntentAction, InputDataAction, InputNonceAction, SetGasLimitFieldAction, diff --git a/common/actions/transaction/actionTypes/sign.ts b/common/actions/transaction/actionTypes/sign.ts index d9c46c3c..f9bf5509 100644 --- a/common/actions/transaction/actionTypes/sign.ts +++ b/common/actions/transaction/actionTypes/sign.ts @@ -8,8 +8,8 @@ import { TypeKeys } from 'actions/transaction/constants'; */ /* Signing / Async actions */ -interface SignLocalTransactionRequestedAction { - type: TypeKeys.SIGN_LOCAL_TRANSACTION_REQUESTED; +interface SignTransactionRequestedAction { + type: TypeKeys.SIGN_TRANSACTION_REQUESTED; payload: EthTx; } interface SignLocalTransactionSucceededAction { @@ -17,10 +17,6 @@ interface SignLocalTransactionSucceededAction { payload: { signedTransaction: Buffer; indexingHash: string; noVerify?: boolean }; // dont verify against fields, for pushTx } -interface SignWeb3TransactionRequestedAction { - type: TypeKeys.SIGN_WEB3_TRANSACTION_REQUESTED; - payload: EthTx; -} interface SignWeb3TransactionSucceededAction { type: TypeKeys.SIGN_WEB3_TRANSACTION_SUCCEEDED; payload: { transaction: Buffer; indexingHash: string; noVerify?: boolean }; @@ -30,16 +26,14 @@ interface SignTransactionFailedAction { } type SignAction = - | SignLocalTransactionRequestedAction + | SignTransactionRequestedAction | SignLocalTransactionSucceededAction - | SignWeb3TransactionRequestedAction | SignWeb3TransactionSucceededAction | SignTransactionFailedAction; export { - SignLocalTransactionRequestedAction, + SignTransactionRequestedAction, SignLocalTransactionSucceededAction, - SignWeb3TransactionRequestedAction, SignWeb3TransactionSucceededAction, SignTransactionFailedAction, SignAction diff --git a/common/actions/transaction/constants.ts b/common/actions/transaction/constants.ts index 8c8008d7..ba38d15e 100644 --- a/common/actions/transaction/constants.ts +++ b/common/actions/transaction/constants.ts @@ -12,9 +12,8 @@ export enum TypeKeys { GET_NONCE_SUCCEEDED = 'GET_NONCE_SUCCEEDED', GET_NONCE_FAILED = 'GET_NONCE_FAILED', - SIGN_WEB3_TRANSACTION_REQUESTED = 'SIGN_WEB3_TRANSACTION_REQUESTED', + SIGN_TRANSACTION_REQUESTED = 'SIGN_TRANSACTION_REQUESTED', SIGN_WEB3_TRANSACTION_SUCCEEDED = 'SIGN_WEB3_TRANSACTION_SUCCEEDED', - SIGN_LOCAL_TRANSACTION_REQUESTED = 'SIGN_LOCAL_TRANSACTION_REQUESTED', SIGN_LOCAL_TRANSACTION_SUCCEEDED = 'SIGN_LOCAL_TRANSACTION_SUCCEEDED', SIGN_TRANSACTION_FAILED = 'SIGN_TRANSACTION_FAILED', @@ -30,6 +29,7 @@ export enum TypeKeys { DATA_FIELD_INPUT = 'DATA_FIELD_INPUT', GAS_LIMIT_INPUT = 'GAS_LIMIT_INPUT', GAS_PRICE_INPUT = 'GAS_PRICE_INPUT', + GAS_PRICE_INPUT_INTENT = 'GAS_PRICE_INPUT_INTENT', NONCE_INPUT = 'NONCE_INPUT', DATA_FIELD_SET = 'DATA_FIELD_SET', diff --git a/common/actions/wallet/actionCreators.ts b/common/actions/wallet/actionCreators.ts index 23ee04d4..ad4cfc6a 100644 --- a/common/actions/wallet/actionCreators.ts +++ b/common/actions/wallet/actionCreators.ts @@ -159,3 +159,10 @@ export function setWalletConfig(config: WalletConfig): types.SetWalletConfigActi payload: config }; } + +export type TSetAccountBalance = typeof setAccountBalance; +export function setAccountBalance(): types.SetAccountBalanceAction { + return { + type: TypeKeys.WALLET_SET_ACCOUNT_BALANCE + }; +} diff --git a/common/actions/wallet/actionTypes.ts b/common/actions/wallet/actionTypes.ts index f154f2e8..bda66390 100644 --- a/common/actions/wallet/actionTypes.ts +++ b/common/actions/wallet/actionTypes.ts @@ -125,6 +125,10 @@ export interface SetPasswordPendingAction { type: TypeKeys.WALLET_SET_PASSWORD_PENDING; } +export interface SetAccountBalanceAction { + type: TypeKeys.WALLET_SET_ACCOUNT_BALANCE; +} + /*** Union Type ***/ export type WalletAction = | UnlockPrivateKeyAction @@ -143,4 +147,5 @@ export type WalletAction = | ScanWalletForTokensAction | SetWalletTokensAction | SetWalletConfigAction - | SetPasswordPendingAction; + | SetPasswordPendingAction + | SetAccountBalanceAction; diff --git a/common/actions/wallet/constants.ts b/common/actions/wallet/constants.ts index c1c2ff9d..24990ea4 100644 --- a/common/actions/wallet/constants.ts +++ b/common/actions/wallet/constants.ts @@ -19,5 +19,6 @@ export enum TypeKeys { WALLET_SET_WALLET_TOKENS = 'WALLET_SET_WALLET_TOKENS', WALLET_SET_CONFIG = 'WALLET_SET_CONFIG', WALLET_RESET = 'WALLET_RESET', - WALLET_SET_PASSWORD_PENDING = 'WALLET_SET_PASSWORD_PENDING' + WALLET_SET_PASSWORD_PENDING = 'WALLET_SET_PASSWORD_PENDING', + WALLET_SET_ACCOUNT_BALANCE = 'WALLET_SET_ACCOUNT_BALANCE' } diff --git a/common/api/bity.ts b/common/api/bity.ts index 979d47b4..8ba10d8d 100644 --- a/common/api/bity.ts +++ b/common/api/bity.ts @@ -1,4 +1,4 @@ -import bityConfig, { WhitelistedCoins } from 'config/bity'; +import { WhitelistedCoins, bityConfig } from 'config'; import { checkHttpStatus, parseJSON, filter } from './utils'; import bitcoinIcon from 'assets/images/bitcoin.png'; import repIcon from 'assets/images/augur.png'; diff --git a/common/actions/rates/actionPayloads.ts b/common/api/rates.ts similarity index 61% rename from common/actions/rates/actionPayloads.ts rename to common/api/rates.ts index 123e1fe0..95837b1b 100644 --- a/common/actions/rates/actionPayloads.ts +++ b/common/api/rates.ts @@ -1,14 +1,56 @@ import { handleJSONResponse } from 'api/utils'; +interface IRateSymbols { + symbols: { + all: TAllSymbols; + fiat: TFiatSymbols; + coinAndToken: TCoinAndTokenSymbols; + }; + isFiat: isFiat; +} -export const rateSymbols: Symbols = ['USD', 'EUR', 'GBP', 'BTC', 'CHF', 'REP', 'ETH']; +type isFiat = (rate: string) => boolean; + +export type TAllSymbols = (keyof ISymbol)[]; +export type TFiatSymbols = (keyof IFiatSymbols)[]; +export type TCoinAndTokenSymbols = (keyof ICoinAndTokenSymbols)[]; +interface ISymbol { + USD: number; + EUR: number; + GBP: number; + CHF: number; + BTC: number; + ETH: number; + REP: number; +} +interface IFiatSymbols { + USD: number; + EUR: number; + GBP: number; + CHF: number; +} +interface ICoinAndTokenSymbols { + BTC: number; + ETH: number; + REP: number; +} + +const fiat: TFiatSymbols = ['USD', 'EUR', 'GBP', 'CHF']; +const coinAndToken: TCoinAndTokenSymbols = ['BTC', 'ETH', 'REP']; +export const rateSymbols: IRateSymbols = { + symbols: { + all: [...fiat, ...coinAndToken], + fiat, + coinAndToken + }, + isFiat: (rate: string) => (fiat as string[]).includes(rate) +}; -export type Symbols = (keyof ISymbol)[]; // TODO - internationalize const ERROR_MESSAGE = 'Could not fetch rate data.'; const CCApi = 'https://min-api.cryptocompare.com'; const CCRates = (symbols: string[]) => { - const tsyms = rateSymbols.concat(symbols as any).join(','); + const tsyms = rateSymbols.symbols.all.concat(symbols as any).join(','); return `${CCApi}/data/price?fsym=ETH&tsyms=${tsyms}`; }; @@ -16,16 +58,6 @@ export interface CCResponse { [symbol: string]: ISymbol; } -interface ISymbol { - USD: number; - EUR: number; - GBP: number; - BTC: number; - CHF: number; - REP: number; - ETH: number; -} - interface IRates extends ISymbol { Response?: 'Error'; } @@ -45,7 +77,7 @@ export const fetchRates = (symbols: string[] = []): Promise => return symbols.reduce( (eqRates, sym: keyof ISymbol) => { if (rates[sym]) { - eqRates[sym] = rateSymbols.reduce( + eqRates[sym] = rateSymbols.symbols.all.reduce( (symRates, rateSym) => { symRates[rateSym] = 1 / rates[sym] * rates[rateSym]; return symRates; @@ -60,10 +92,10 @@ export const fetchRates = (symbols: string[] = []): Promise => USD: rates.USD, EUR: rates.EUR, GBP: rates.GBP, - BTC: rates.BTC, CHF: rates.CHF, - REP: rates.REP, - ETH: 1 + BTC: rates.BTC, + ETH: 1, + REP: rates.REP } } as CCResponse ); diff --git a/common/api/shapeshift.ts b/common/api/shapeshift.ts index 9ca6f9bf..01100505 100644 --- a/common/api/shapeshift.ts +++ b/common/api/shapeshift.ts @@ -1,5 +1,8 @@ import { checkHttpStatus, parseJSON } from 'api/utils'; +const SHAPESHIFT_API_KEY = + '8abde0f70ca69d5851702d57b10305705d7333e93263124cc2a2649dab7ff9cf86401fc8de7677e8edcd0e7f1eed5270b1b49be8806937ef95d64839e319e6d9'; + const SHAPESHIFT_BASE_URL = 'https://shapeshift.io'; export const SHAPESHIFT_TOKEN_WHITELIST = [ @@ -28,7 +31,7 @@ export const SHAPESHIFT_WHITELIST = [...SHAPESHIFT_TOKEN_WHITELIST, 'ETH', 'ETC' class ShapeshiftService { public whitelist = SHAPESHIFT_WHITELIST; private url = SHAPESHIFT_BASE_URL; - private apiKey = '0ca1ccd50b708a3f8c02327f0caeeece06d3ddc1b0ac749a987b453ee0f4a29bdb5da2e53bc35e57fb4bb7ae1f43c93bb098c3c4716375fc1001c55d8c94c160'; + private apiKey = SHAPESHIFT_API_KEY; private postHeaders = { 'Content-Type': 'application/json' }; diff --git a/common/assets/fonts/Roboto-Mono-Light.woff b/common/assets/fonts/Roboto-Mono-Light.woff new file mode 100644 index 00000000..68d8403e Binary files /dev/null and b/common/assets/fonts/Roboto-Mono-Light.woff differ diff --git a/common/assets/fonts/Roboto-Mono-Light.woff2 b/common/assets/fonts/Roboto-Mono-Light.woff2 new file mode 100644 index 00000000..ab64df38 Binary files /dev/null and b/common/assets/fonts/Roboto-Mono-Light.woff2 differ diff --git a/common/assets/fonts/Roboto-Mono-Regular.woff b/common/assets/fonts/Roboto-Mono-Regular.woff new file mode 100644 index 00000000..0ea5db89 Binary files /dev/null and b/common/assets/fonts/Roboto-Mono-Regular.woff differ diff --git a/common/assets/fonts/Roboto-Mono-Regular.woff2 b/common/assets/fonts/Roboto-Mono-Regular.woff2 new file mode 100644 index 00000000..6163de7b Binary files /dev/null and b/common/assets/fonts/Roboto-Mono-Regular.woff2 differ diff --git a/common/assets/images/close.svg b/common/assets/images/close.svg new file mode 100644 index 00000000..2805991d --- /dev/null +++ b/common/assets/images/close.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/common/assets/images/favicon.png b/common/assets/images/favicon.png new file mode 100644 index 00000000..bb61b1d9 Binary files /dev/null and b/common/assets/images/favicon.png differ diff --git a/common/assets/images/fb-mewtwo.jpg b/common/assets/images/fb-mewtwo.jpg deleted file mode 100755 index 3f7a3222..00000000 Binary files a/common/assets/images/fb-mewtwo.jpg and /dev/null differ diff --git a/common/assets/images/fb-myetherwallet.png b/common/assets/images/fb-myetherwallet.png deleted file mode 100755 index 014978e9..00000000 Binary files a/common/assets/images/fb-myetherwallet.png and /dev/null differ diff --git a/common/assets/images/header-bg.jpg b/common/assets/images/header-bg.jpg new file mode 100644 index 00000000..03317e17 Binary files /dev/null and b/common/assets/images/header-bg.jpg differ diff --git a/common/assets/images/icon-eye-closed.svg b/common/assets/images/icon-eye-closed.svg deleted file mode 100755 index 96e69c1b..00000000 --- a/common/assets/images/icon-eye-closed.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/common/assets/images/icon-eye.svg b/common/assets/images/icon-eye.svg deleted file mode 100755 index afbc051e..00000000 --- a/common/assets/images/icon-eye.svg +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - diff --git a/common/assets/images/icons/icon128.png b/common/assets/images/icons/icon128.png deleted file mode 100755 index 5db66054..00000000 Binary files a/common/assets/images/icons/icon128.png and /dev/null differ diff --git a/common/assets/images/icons/icon16.png b/common/assets/images/icons/icon16.png deleted file mode 100755 index 2d04462b..00000000 Binary files a/common/assets/images/icons/icon16.png and /dev/null differ diff --git a/common/assets/images/icons/icon19.png b/common/assets/images/icons/icon19.png deleted file mode 100755 index ebaba1bd..00000000 Binary files a/common/assets/images/icons/icon19.png and /dev/null differ diff --git a/common/assets/images/icons/icon48.png b/common/assets/images/icons/icon48.png deleted file mode 100755 index 55b864b1..00000000 Binary files a/common/assets/images/icons/icon48.png and /dev/null differ diff --git a/common/assets/images/loading.gif b/common/assets/images/loading.gif deleted file mode 100755 index 6fd567c0..00000000 Binary files a/common/assets/images/loading.gif and /dev/null differ diff --git a/common/assets/images/logo-mycrypto.svg b/common/assets/images/logo-mycrypto.svg new file mode 100755 index 00000000..11329fc3 --- /dev/null +++ b/common/assets/images/logo-mycrypto.svg @@ -0,0 +1,33 @@ + + + + Artboard + Created with Sketch. + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/common/assets/images/logo-myetherwallet.svg b/common/assets/images/logo-myetherwallet.svg deleted file mode 100755 index 3e3fbeef..00000000 --- a/common/assets/images/logo-myetherwallet.svg +++ /dev/null @@ -1,59 +0,0 @@ - - - - - - - - - - - - - - etherwallet-logo - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/common/assets/images/logo-myetherwalletcx.svg b/common/assets/images/logo-myetherwalletcx.svg deleted file mode 100755 index 6b641dcf..00000000 --- a/common/assets/images/logo-myetherwalletcx.svg +++ /dev/null @@ -1,63 +0,0 @@ - - - - - - - - - - - - - - etherwalletcx-logo - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/common/assets/images/logo-shapeshift.svg b/common/assets/images/logo-shapeshift.svg index 6cdbce03..8c6ecc54 100644 --- a/common/assets/images/logo-shapeshift.svg +++ b/common/assets/images/logo-shapeshift.svg @@ -1,147 +1,116 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + Artboard + Created with Sketch. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/common/assets/images/mewtwo.jpg b/common/assets/images/mewtwo.jpg deleted file mode 100755 index e2f3a9e4..00000000 Binary files a/common/assets/images/mewtwo.jpg and /dev/null differ diff --git a/common/assets/images/onboarding/slide-01.svg b/common/assets/images/onboarding/slide-01.svg new file mode 100644 index 00000000..a9f0c75c --- /dev/null +++ b/common/assets/images/onboarding/slide-01.svg @@ -0,0 +1,72 @@ + + + + Artboard + Created with Sketch. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/common/assets/images/onboarding_icon-02.svg b/common/assets/images/onboarding/slide-02.svg similarity index 100% rename from common/assets/images/onboarding_icon-02.svg rename to common/assets/images/onboarding/slide-02.svg diff --git a/common/assets/images/onboarding_icon-03.svg b/common/assets/images/onboarding/slide-03.svg similarity index 100% rename from common/assets/images/onboarding_icon-03.svg rename to common/assets/images/onboarding/slide-03.svg diff --git a/common/assets/images/onboarding_icon-04.svg b/common/assets/images/onboarding/slide-04.svg similarity index 100% rename from common/assets/images/onboarding_icon-04.svg rename to common/assets/images/onboarding/slide-04.svg diff --git a/common/assets/images/onboarding_icon-05.svg b/common/assets/images/onboarding/slide-05.svg similarity index 100% rename from common/assets/images/onboarding_icon-05.svg rename to common/assets/images/onboarding/slide-05.svg diff --git a/common/assets/images/onboarding/slide-06.svg b/common/assets/images/onboarding/slide-06.svg new file mode 100644 index 00000000..a10c866d --- /dev/null +++ b/common/assets/images/onboarding/slide-06.svg @@ -0,0 +1,47 @@ + + + + Artboard + Created with Sketch. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/common/assets/images/onboarding_icon-07.svg b/common/assets/images/onboarding/slide-07.svg similarity index 100% rename from common/assets/images/onboarding_icon-07.svg rename to common/assets/images/onboarding/slide-07.svg diff --git a/common/assets/images/onboarding_icon-08.svg b/common/assets/images/onboarding/slide-08.svg similarity index 100% rename from common/assets/images/onboarding_icon-08.svg rename to common/assets/images/onboarding/slide-08.svg diff --git a/common/assets/images/onboarding_icon-09.svg b/common/assets/images/onboarding/slide-09.svg similarity index 100% rename from common/assets/images/onboarding_icon-09.svg rename to common/assets/images/onboarding/slide-09.svg diff --git a/common/assets/images/onboarding_icon-10.svg b/common/assets/images/onboarding/slide-10.svg similarity index 100% rename from common/assets/images/onboarding_icon-10.svg rename to common/assets/images/onboarding/slide-10.svg diff --git a/common/assets/images/onboarding_icon-01.svg b/common/assets/images/onboarding_icon-01.svg deleted file mode 100644 index 3a26533b..00000000 --- a/common/assets/images/onboarding_icon-01.svg +++ /dev/null @@ -1 +0,0 @@ -onboarding_icon-01 \ No newline at end of file diff --git a/common/assets/images/onboarding_icon-06.svg b/common/assets/images/onboarding_icon-06.svg deleted file mode 100644 index 781c83d5..00000000 --- a/common/assets/images/onboarding_icon-06.svg +++ /dev/null @@ -1 +0,0 @@ -onboarding_icons \ No newline at end of file diff --git a/common/assets/images/print-sidebar.png b/common/assets/images/print-sidebar.png index 4a2c0d38..169973c0 100755 Binary files a/common/assets/images/print-sidebar.png and b/common/assets/images/print-sidebar.png differ diff --git a/common/assets/images/tail-triangle-down.svg b/common/assets/images/tail-triangle-down.svg new file mode 100644 index 00000000..f25df9b3 --- /dev/null +++ b/common/assets/images/tail-triangle-down.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/common/assets/images/unlock-guide/provide-keystore.png b/common/assets/images/unlock-guide/provide-keystore.png index 50d73285..abd0027b 100644 Binary files a/common/assets/images/unlock-guide/provide-keystore.png and b/common/assets/images/unlock-guide/provide-keystore.png differ diff --git a/common/assets/images/unlock-guide/select-keystore.png b/common/assets/images/unlock-guide/select-keystore.png index 66484f84..c0e0996e 100644 Binary files a/common/assets/images/unlock-guide/select-keystore.png and b/common/assets/images/unlock-guide/select-keystore.png differ diff --git a/common/assets/images/unlock-guide/select-mnemonic.png b/common/assets/images/unlock-guide/select-mnemonic.png index 7ef09c08..6406b74e 100644 Binary files a/common/assets/images/unlock-guide/select-mnemonic.png and b/common/assets/images/unlock-guide/select-mnemonic.png differ diff --git a/common/assets/images/unlock-guide/site.png b/common/assets/images/unlock-guide/site.png index fc612020..da1355ed 100644 Binary files a/common/assets/images/unlock-guide/site.png and b/common/assets/images/unlock-guide/site.png differ diff --git a/common/assets/images/unlock-guide/tab.png b/common/assets/images/unlock-guide/tab.png index edbf0bf0..88131b12 100644 Binary files a/common/assets/images/unlock-guide/tab.png and b/common/assets/images/unlock-guide/tab.png differ diff --git a/common/assets/styles/etherwallet-custom.less b/common/assets/styles/etherwallet-custom.less index 0e10a4de..753059bf 100755 --- a/common/assets/styles/etherwallet-custom.less +++ b/common/assets/styles/etherwallet-custom.less @@ -163,34 +163,6 @@ textarea { } } -input[type="text"] + .eye { - cursor: pointer; - &:before { - content: ""; - width: 20px; - height: 20px; - margin-left: 6px; - margin-right: 6px; - display: inline-block; - vertical-align: middle; - background: url("../images/icon-eye.svg"); - } -} - -input[type="password"] + .eye { - cursor: pointer; - &:before { - content: ""; - width: 20px; - height: 20px; - margin-left: 6px; - margin-right: 6px; - display: inline-block; - vertical-align: middle; - background: url("../images/icon-eye-closed.svg"); - } -} - // collapsable containers .collapse-container { h2, @@ -398,4 +370,4 @@ label small { .ens-response { color: @gray; -} \ No newline at end of file +} diff --git a/common/assets/styles/etherwallet-fonts.less b/common/assets/styles/etherwallet-fonts.less deleted file mode 100755 index 711faa4f..00000000 --- a/common/assets/styles/etherwallet-fonts.less +++ /dev/null @@ -1,23 +0,0 @@ -@font-face { - font-family: 'Lato'; - src: url('../fonts/Lato-Light.woff2') format('woff2'), - url('../fonts/Lato-Light.woff') format('woff'); - font-style: normal; - font-weight: 300; -} - -@font-face { - font-family: 'Lato'; - src: url('../fonts/Lato-Regular.woff2') format('woff2'), - url('../fonts/Lato-Regular.woff') format('woff'); - font-style: normal; - font-weight: 400; -} - -@font-face { - font-family: 'Lato'; - src: url('../fonts/Lato-Bold.woff2') format('woff2'), - url('../fonts/Lato-Bold.woff') format('woff'); - font-style: normal; - font-weight: 700; -} diff --git a/common/assets/styles/etherwallet-master.less b/common/assets/styles/etherwallet-master.less index 9fb6e6f4..b14dc323 100755 --- a/common/assets/styles/etherwallet-master.less +++ b/common/assets/styles/etherwallet-master.less @@ -1,8 +1,7 @@ -@import "etherwallet-variables.less"; -@import "etherwallet-fonts.less"; +@import 'etherwallet-variables.less'; // Core variables and mixins -@import "bootstrap/mixins.less"; +@import 'bootstrap/mixins.less'; // Utility classes -@import "etherwallet-custom.less"; -@import "etherwallet-ext-custom.less"; -@import "etherwallet-utilities.less"; +@import 'etherwallet-custom.less'; +@import 'etherwallet-ext-custom.less'; +@import 'etherwallet-utilities.less'; diff --git a/common/assets/styles/etherwallet-variables.less b/common/assets/styles/etherwallet-variables.less index b84cb556..569ba858 100755 --- a/common/assets/styles/etherwallet-variables.less +++ b/common/assets/styles/etherwallet-variables.less @@ -3,19 +3,19 @@ @ether-blue: #0e97c0; @space-xs: 0.25rem; -@space-sm: 0.50rem; +@space-sm: 0.5rem; @space-md: 0.75rem; -@space: 1.00rem; -@space-lg: 1.50rem; -@space-xl: 2.00rem; +@space: 1rem; +@space-lg: 1.5rem; +@space-xl: 2rem; @gray-base: #000; @gray-darker: lighten(@gray-base, 13.5%); @gray-dark: lighten(@gray-base, 20%); @gray: #737373; -@gray-light: #9A9A9A; -@gray-lighter: #ECECEC; -@gray-lightest: #FAFAFA; +@gray-light: #9a9a9a; +@gray-lighter: #ececec; +@gray-lightest: #fafafa; @brand-primary: @ether-blue; @brand-success: #5dba5a; @@ -34,8 +34,8 @@ // Typography @font-family-sans-serif: 'Lato', sans-serif; -@font-family-serif: Georgia, "Times New Roman", Times, serif; -@font-family-monospace: Menlo, Monaco, Consolas, "Courier New", monospace; +@font-family-serif: Georgia, 'Times New Roman', Times, serif; +@font-family-monospace: 'Roboto Mono', Menlo, Monaco, Consolas, 'Courier New', monospace; @font-family-base: @font-family-sans-serif; @base: 15; @@ -44,14 +44,14 @@ @font-size-pixels-sm: @base+px; // for small screens @font-size-large-bump: 2.25rem; // 33.75 -@font-size-large: 1.90rem; // 28.5 -@font-size-medium-bump: 1.50rem; // 22.5 -@font-size-medium: 1.30rem; // 19.5 +@font-size-large: 1.9rem; // 28.5 +@font-size-medium-bump: 1.5rem; // 22.5 +@font-size-medium: 1.3rem; // 19.5 @font-size-bump-more: 1.15rem; // 17.25 @font-size-bump: 1.07rem; // 16.05 -@font-size-base: 1.00rem; // 15 +@font-size-base: 1rem; // 15 @font-size-small: 0.92rem; // 13.8 -@font-size-xs: 0.80rem; // 12 +@font-size-xs: 0.8rem; // 12 @font-size-h1: @font-size-large-bump; @font-size-h2: @font-size-large; @@ -158,7 +158,7 @@ @cursor-disabled: default; @dropdown-bg: #fff; -@dropdown-border: rgba(0, 0, 0, .15); +@dropdown-border: rgba(0, 0, 0, 0.15); @dropdown-fallback-border: @gray-lighter; @dropdown-divider-bg: #e5e5e5; @@ -240,7 +240,7 @@ @tooltip-max-width: 200px; @tooltip-color: #fff; @tooltip-bg: #000; -@tooltip-opacity: .9; +@tooltip-opacity: 0.9; @tooltip-arrow-width: @space-sm; @tooltip-arrow-color: @tooltip-bg; @@ -261,11 +261,11 @@ @modal-title-line-height: @line-height-base; @modal-content-bg: #fff; -@modal-content-border-color: rgba(0, 0, 0, .2); +@modal-content-border-color: rgba(0, 0, 0, 0.2); @modal-content-fallback-border-color: #999; @modal-backdrop-bg: #000; -@modal-backdrop-opacity: .5; +@modal-backdrop-opacity: 0.5; @modal-header-border-color: #e5e5e5; @modal-footer-border-color: @modal-header-border-color; diff --git a/common/components/AddressField.tsx b/common/components/AddressField.tsx index 28d97b5d..aff92573 100644 --- a/common/components/AddressField.tsx +++ b/common/components/AddressField.tsx @@ -1,6 +1,6 @@ import React from 'react'; import { AddressFieldFactory } from './AddressFieldFactory'; -import { donationAddressMap } from 'config/data'; +import { donationAddressMap } from 'config'; interface Props { isReadOnly?: boolean; diff --git a/common/components/AlphaAgreement/index.scss b/common/components/AlphaAgreement/index.scss index ea5fab97..18665d51 100644 --- a/common/components/AlphaAgreement/index.scss +++ b/common/components/AlphaAgreement/index.scss @@ -3,7 +3,7 @@ .AlphaAgreement { @include cover-message; - background: $brand-warning; + background: $brand-info; &-content { &-buttons { diff --git a/common/components/AlphaAgreement/index.tsx b/common/components/AlphaAgreement/index.tsx index cce81bbc..7ec4b42a 100644 --- a/common/components/AlphaAgreement/index.tsx +++ b/common/components/AlphaAgreement/index.tsx @@ -7,7 +7,7 @@ interface State { isFading: boolean; hasAcknowledged: boolean; } -export default class AlphaAgreement extends React.Component<{}, State> { +export default class AlphaAgreement extends React.PureComponent<{}, State> { public state = { hasAcknowledged: !!localStorage.getItem(LS_KEY), isFading: false @@ -23,28 +23,27 @@ export default class AlphaAgreement extends React.Component<{}, State> { return (
-

This is an Unstable Version of MEW

+

This is an Unstable Version of MyCrypto

- You are about to access an alpha version of MyEtherWallet that is currently in - development. In its current state, it should only be used for testing, not for important - transactions. + You are about to access a beta version of MyCrypto that is currently in development. In + its current state, it should only be used for testing, not for important transactions.

Any wallets you generate should not hold a significant value, and any transactions you - make should be for small amounts. MEW does not claim responsibility for any issues that - happen while using the alpha version. + make should be for small amounts. MyCrypto does not claim responsibility for any issues + that happen while using the beta version.

Are you sure you would like to continue?

@@ -62,6 +61,6 @@ export default class AlphaAgreement extends React.Component<{}, State> { }; private reject = () => { - window.location.assign('https://myetherwallet.com'); + window.location.assign('https://mycrypto.com'); }; } diff --git a/common/components/AmountFieldFactory/AmountInputFactory.tsx b/common/components/AmountFieldFactory/AmountInputFactory.tsx index a2ddfcfe..1c52c476 100644 --- a/common/components/AmountFieldFactory/AmountInputFactory.tsx +++ b/common/components/AmountFieldFactory/AmountInputFactory.tsx @@ -1,6 +1,6 @@ import React, { Component } from 'react'; import { Query } from 'components/renderCbs'; -import { ICurrentValue, getCurrentValue, isValidAmount } from 'selectors/transaction'; +import { ICurrentValue, getCurrentValue, nonStandardTransaction } from 'selectors/transaction'; import { AppState } from 'reducers'; import { connect } from 'react-redux'; import { CallbackProps } from 'components/AmountFieldFactory'; @@ -11,15 +11,15 @@ interface OwnProps { } interface StateProps { + isNonStandard: boolean; currentValue: ICurrentValue; - validAmount: boolean; } type Props = OwnProps & StateProps; class AmountInputClass extends Component { public render() { - const { currentValue, onChange, withProps, validAmount } = this.props; + const { currentValue, onChange, withProps, isNonStandard } = this.props; return ( { withQuery={({ readOnly }) => withProps({ currentValue, - isValid: validAmount, + isValid: !!currentValue.value || isNonStandard, readOnly: !!readOnly, onChange }) @@ -39,5 +39,5 @@ class AmountInputClass extends Component { export const AmountInput = connect((state: AppState) => ({ currentValue: getCurrentValue(state), - validAmount: isValidAmount(state) + isNonStandard: nonStandardTransaction(state) }))(AmountInputClass); diff --git a/common/components/BalanceSidebar/AccountInfo.scss b/common/components/BalanceSidebar/AccountInfo.scss index 1286d6a0..a0309d9d 100644 --- a/common/components/BalanceSidebar/AccountInfo.scss +++ b/common/components/BalanceSidebar/AccountInfo.scss @@ -11,6 +11,29 @@ &-header { margin-top: 0; + display: inline-block; + } + + &-refresh { + background: transparent; + border: none; + padding: 0; + height: 1.4rem; + width: 1.2rem; + opacity: 0.3; + transition: opacity 0.3s; + > img { + height: inherit; + width: inherit; + vertical-align: top; + } + &:hover { + opacity: 0.54; + } + &:active { + transition: opacity 120ms; + opacity: 1; + } } } @@ -53,6 +76,16 @@ } } + &-balance { + display: flex; + justify-content: flex-start; + align-items: center; + + &-amount { + margin-right: $space-sm; + } + } + &-list { &-item { margin-bottom: 0; diff --git a/common/components/BalanceSidebar/AccountInfo.tsx b/common/components/BalanceSidebar/AccountInfo.tsx index 059d1c6f..c5f791ec 100644 --- a/common/components/BalanceSidebar/AccountInfo.tsx +++ b/common/components/BalanceSidebar/AccountInfo.tsx @@ -1,15 +1,23 @@ import { Identicon, UnitDisplay } from 'components/ui'; -import { NetworkConfig } from 'config/data'; import { IWallet, Balance, TrezorWallet, LedgerWallet } from 'libs/wallet'; import React from 'react'; import translate from 'translations'; import './AccountInfo.scss'; import Spinner from 'components/ui/Spinner'; +import { getNetworkConfig, getOffline } from 'selectors/config'; +import { AppState } from 'reducers'; +import { connect } from 'react-redux'; +import { NetworkConfig } from 'types/network'; +import { TSetAccountBalance, setAccountBalance } from 'actions/wallet'; -interface Props { - balance: Balance; +interface OwnProps { wallet: IWallet; +} + +interface StateProps { + balance: Balance; network: NetworkConfig; + isOffline: boolean; } interface State { @@ -17,7 +25,14 @@ interface State { address: string; confirmAddr: boolean; } -export default class AccountInfo extends React.Component { + +interface DispatchProps { + setAccountBalance: TSetAccountBalance; +} + +type Props = OwnProps & StateProps & DispatchProps; + +class AccountInfo extends React.Component { public state = { showLongBalance: false, address: '', @@ -55,9 +70,16 @@ export default class AccountInfo extends React.Component { }; public render() { - const { network, balance } = this.props; + const { network, balance, isOffline } = this.props; const { address, showLongBalance, confirmAddr } = this.state; - const { blockExplorer, tokenExplorer } = network; + let blockExplorer; + let tokenExplorer; + if (!network.isCustom) { + // this is kind of ugly but its the result of typeguards, maybe we can find a cleaner solution later on such as just dedicating it to a selector + blockExplorer = network.blockExplorer; + tokenExplorer = network.tokenExplorer; + } + const wallet = this.props.wallet as LedgerWallet | TrezorWallet; return (
@@ -99,23 +121,31 @@ export default class AccountInfo extends React.Component {
{translate('sidebar_AccountBal')}
    -
  • +
  • - {balance.isPending ? ( - - ) : ( - - )} + - {!balance.isPending ? balance.wei ? {network.name} : null : null} + {balance.isPending ? ( + + ) : ( + !isOffline && ( + + ) + )}
@@ -127,11 +157,11 @@ export default class AccountInfo extends React.Component { {!!blockExplorer && (
  • - {`${network.name} (${blockExplorer.name})`} + {`${network.name} (${blockExplorer.origin})`}
  • )} @@ -153,3 +183,12 @@ export default class AccountInfo extends React.Component { ); } } +function mapStateToProps(state: AppState): StateProps { + return { + balance: state.wallet.balance, + network: getNetworkConfig(state), + isOffline: getOffline(state) + }; +} +const mapDispatchToProps: DispatchProps = { setAccountBalance }; +export default connect(mapStateToProps, mapDispatchToProps)(AccountInfo); diff --git a/common/components/BalanceSidebar/EquivalentValues.scss b/common/components/BalanceSidebar/EquivalentValues.scss index 9e4256fb..a304ca3b 100644 --- a/common/components/BalanceSidebar/EquivalentValues.scss +++ b/common/components/BalanceSidebar/EquivalentValues.scss @@ -2,43 +2,44 @@ @import 'common/sass/mixins'; .EquivalentValues { - &-title { - margin-top: 0; + &-header { + display: flex; + align-items: center; margin-bottom: $space; + + .Select { + flex-grow: 1; + } + } + + &-spinner { + text-align: center; + padding: 1.6rem; + } + + &-title { + margin: 0; + margin-right: 16px; } &-values { - list-style: none; - padding: 0; - @include clearfix; - + display: flex; + flex-wrap: wrap; &-currency { - float: left; width: 50%; margin-bottom: $space-xs; - - &:nth-child(odd) { - padding-right: $space-sm; - } - &:nth-child(even) { - padding-left: $space-sm; - } - &-label { white-space: pre-wrap; display: inline-block; min-width: 36px; + opacity: 0.54; + margin-right: 8px; } &-value { font-weight: 600; @include mono; } } - - &-loader { - padding: 25px 0; - text-align: center; - } } &-offline { diff --git a/common/components/BalanceSidebar/EquivalentValues.tsx b/common/components/BalanceSidebar/EquivalentValues.tsx index ed885a91..5af1589f 100644 --- a/common/components/BalanceSidebar/EquivalentValues.tsx +++ b/common/components/BalanceSidebar/EquivalentValues.tsx @@ -1,49 +1,82 @@ -import * as React from 'react'; -import BN from 'bn.js'; +import React from 'react'; import translate from 'translations'; -import { State } from 'reducers/rates'; -import { rateSymbols, TFetchCCRates } from 'actions/rates'; -import { TokenBalance } from 'selectors/wallet'; +import { UnitDisplay, Spinner } from 'components/ui'; +import Select from 'react-select'; +import { TFetchCCRatesRequested, fetchCCRatesRequested } from 'actions/rates'; +import { rateSymbols } from 'api/rates'; +import { chain, flatMap } from 'lodash'; +import { TokenBalance, getShownTokenBalances } from 'selectors/wallet'; import { Balance } from 'libs/wallet'; -import { NetworkConfig } from 'config/data'; -import { ETH_DECIMAL, convertTokenBase } from 'libs/units'; -import Spinner from 'components/ui/Spinner'; -import UnitDisplay from 'components/ui/UnitDisplay'; import './EquivalentValues.scss'; +import { Wei } from 'libs/units'; +import { AppState } from 'reducers'; +import { getNetworkConfig, getOffline } from 'selectors/config'; +import { connect } from 'react-redux'; +import { NetworkConfig } from 'types/network'; -const ALL_OPTION = 'All'; +interface AllValue { + symbol: string; + balance: Balance['wei']; +} -interface Props { - balance?: Balance; - tokenBalances?: TokenBalance[]; - rates: State['rates']; - ratesError?: State['ratesError']; - fetchCCRates: TFetchCCRates; +interface DefaultOption { + label: string; + value: AllValue[]; +} + +interface Option { + label: string; + value: Balance['wei'] | AllValue[]; +} + +interface State { + equivalentValues: Option; + options: Option[]; +} + +interface StateProps { + balance: Balance; network: NetworkConfig; - isOffline: boolean; + + tokenBalances: TokenBalance[]; + rates: AppState['rates']['rates']; + ratesError: AppState['rates']['ratesError']; + isOffline: AppState['config']['meta']['offline']; } -interface CmpState { - currency: string; +interface DispatchProps { + fetchCCRates: TFetchCCRatesRequested; } -export default class EquivalentValues extends React.Component { - public state = { - currency: ALL_OPTION - }; - private balanceLookup: { [key: string]: Balance['wei'] | undefined } = {}; - private decimalLookup: { [key: string]: number } = {}; +type Props = StateProps & DispatchProps; + +class EquivalentValues extends React.Component { private requestedCurrencies: string[] | null = null; public constructor(props: Props) { super(props); - this.makeBalanceLookup(props); + const { balance, tokenBalances, network } = this.props; + this.state = { + equivalentValues: this.defaultOption(balance, tokenBalances, network), + options: [] + }; if (props.balance && props.tokenBalances) { this.fetchRates(props); } } + public defaultOption( + balance: Balance, + tokenBalances: TokenBalance[], + network: StateProps['network'] + ): DefaultOption { + return { + label: 'All', + value: [{ symbol: network.unit, balance: balance.wei }, ...tokenBalances] + }; + } + public componentWillReceiveProps(nextProps: Props) { const { balance, tokenBalances, isOffline } = this.props; if ( @@ -51,127 +84,166 @@ export default class EquivalentValues extends React.Component { nextProps.tokenBalances !== tokenBalances || nextProps.isOffline !== isOffline ) { - this.makeBalanceLookup(nextProps); + const defaultOption = this.defaultOption( + nextProps.balance, + nextProps.tokenBalances, + nextProps.network + ); + const options: Option[] = [ + defaultOption, + { label: nextProps.network.unit, value: nextProps.balance.wei }, + ...Object.values(nextProps.tokenBalances).map(token => { + return { label: token.symbol, value: token.balance }; + }) + ]; + const equivalentValues = + options.find(opt => opt.label === this.state.equivalentValues.label) || defaultOption; + this.setState({ + equivalentValues, + options + }); this.fetchRates(nextProps); } } - public render() { - const { balance, tokenBalances, rates, ratesError, isOffline, network } = this.props; - const { currency } = this.state; + public selectOption = equivalentValues => { + this.setState({ equivalentValues }); + }; - // There are a bunch of reasons why the incorrect balances might be rendered - // while we have incomplete data that's being fetched. + public render(): JSX.Element { + const { balance, isOffline, tokenBalances, rates, network, ratesError } = this.props; + const { equivalentValues, options } = this.state; const isFetching = !balance || balance.isPending || !tokenBalances || Object.keys(rates).length === 0; - // Currency exists in rates or the all option is selected - const rateExistsOrAll = rates[currency] || currency === ALL_OPTION; + const pairRates = this.generateValues(equivalentValues.label, equivalentValues.value); - let valuesEl; - if (!isFetching && rateExistsOrAll && !network.isTestnet) { - const values = this.getEquivalentValues(currency); - valuesEl = rateSymbols.map(key => { - if (!values[key] || key === currency) { - return null; - } - - return ( -
  • - {key}:{' '} - - - -
  • - ); - }); - } else if (network.isTestnet) { - valuesEl = ( -
    -
    - On test network, equivalent values will not be displayed. -
    -
    - ); - } else if (ratesError) { - valuesEl =
    {ratesError}
    ; - } else if (tokenBalances && tokenBalances.length === 0) { - valuesEl =
    No tokens found!
    ; - } else { - valuesEl = ( -
    - -
    - ); - } + const Value = ({ rate, value }) => ( +
    + {rate}{' '} + + + +
    + ); return (
    -
    - {translate('sidebar_Equiv')} for{' '} - -
    +
    +
    {translate('sidebar_Equiv')}
    +