diff --git a/common/components/ExtendedNotifications/TransactionSucceeded.tsx b/common/components/ExtendedNotifications/TransactionSucceeded.tsx index ea0cb9b0..c2198890 100644 --- a/common/components/ExtendedNotifications/TransactionSucceeded.tsx +++ b/common/components/ExtendedNotifications/TransactionSucceeded.tsx @@ -1,26 +1,33 @@ import React from 'react'; -import { translateRaw } from 'translations'; +import { Link } from 'react-router-dom'; +import translate from 'translations'; +import { NewTabLink } from 'components/ui'; import { BlockExplorerConfig } from 'types/network'; export interface TransactionSucceededProps { txHash: string; - blockExplorer: BlockExplorerConfig; + blockExplorer?: BlockExplorerConfig; } const TransactionSucceeded = ({ txHash, blockExplorer }: TransactionSucceededProps) => { - const txHashLink = blockExplorer.txUrl(txHash); + let verifyBtn: React.ReactElement | undefined; + if (blockExplorer) { + verifyBtn = ( + + Verify Transaction on {blockExplorer.name} + + ); + } return (
-

{translateRaw('SUCCESS_3') + txHash}

- - Verify Transaction - +

+ {translate('SUCCESS_3')} {txHash} +

+ {verifyBtn} + + {translate('NAV_CheckTxStatus')} +
); }; diff --git a/common/components/Footer/DisclaimerModal.tsx b/common/components/Footer/DisclaimerModal.tsx index 8ec1b62b..3c18a17a 100644 --- a/common/components/Footer/DisclaimerModal.tsx +++ b/common/components/Footer/DisclaimerModal.tsx @@ -41,7 +41,11 @@ const DisclaimerModal: React.SFC = ({ isOpen, handleClose }) => { English and, because of this, the English version of our website is the official text.

- MIT License Copyright © 2015-2017 MyCrypto LLC + MIT License +
+ 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 diff --git a/common/components/LogOutPrompt.tsx b/common/components/LogOutPrompt.tsx index a7921b75..dadde8f1 100644 --- a/common/components/LogOutPrompt.tsx +++ b/common/components/LogOutPrompt.tsx @@ -62,7 +62,7 @@ class LogOutPromptClass extends React.Component { }; private onConfirm = () => { - const { nextLocation } = this.state; + const { nextLocation: next } = this.state; this.props.resetWallet(); this.setState( { @@ -70,8 +70,8 @@ class LogOutPromptClass extends React.Component { nextLocation: null }, () => { - if (nextLocation) { - this.props.history.push(nextLocation.pathname); + if (next) { + this.props.history.push(`${next.pathname}${next.search}${next.hash}`); } } ); diff --git a/common/components/TXMetaDataPanel/TXMetaDataPanel.tsx b/common/components/TXMetaDataPanel/TXMetaDataPanel.tsx index 5428b31e..71f9165f 100644 --- a/common/components/TXMetaDataPanel/TXMetaDataPanel.tsx +++ b/common/components/TXMetaDataPanel/TXMetaDataPanel.tsx @@ -10,7 +10,8 @@ import { getNonceRequested, TGetNonceRequested, reset, - TReset + TReset, + ResetAction } from 'actions/transaction'; import { fetchCCRatesRequested, TFetchCCRatesRequested } from 'actions/rates'; import { getNetworkConfig, getOffline } from 'selectors/config'; @@ -44,6 +45,7 @@ interface DefaultProps { } interface OwnProps { + resetIncludeExcludeProperties?: ResetAction['payload']; initialState?: SliderStates; disableToggle?: boolean; advancedGasOptions?: AdvancedOptions; @@ -69,7 +71,7 @@ class TXMetaDataPanel extends React.Component { public componentDidMount() { if (!this.props.offline) { - this.props.reset(); + this.props.reset(this.props.resetIncludeExcludeProperties); this.props.fetchCCRates([this.props.network.unit]); this.props.getNonceRequested(); } diff --git a/common/components/WalletDecrypt/WalletDecrypt.tsx b/common/components/WalletDecrypt/WalletDecrypt.tsx index c1546264..b94a1c67 100644 --- a/common/components/WalletDecrypt/WalletDecrypt.tsx +++ b/common/components/WalletDecrypt/WalletDecrypt.tsx @@ -17,7 +17,7 @@ import { resetWallet, TResetWallet } from 'actions/wallet'; -import { reset, TReset } from 'actions/transaction'; +import { reset, TReset, ResetAction } from 'actions/transaction'; import translate from 'translations'; import { KeystoreDecrypt, @@ -55,6 +55,7 @@ interface OwnProps { hidden?: boolean; disabledWallets?: DisabledWallets; showGenerateLink?: boolean; + resetIncludeExcludeProperties?: ResetAction['payload']; } interface DispatchProps { @@ -348,7 +349,7 @@ export class WalletDecrypt extends Component { {this.props.showGenerateLink && (

- Don’t have a wallet? Click here to get one. + Don’t have an account yet? Click here to get one.
)} @@ -430,7 +431,7 @@ export class WalletDecrypt extends Component { // the payload to contain the unlocked wallet info. const unlockValue = value && !isEmpty(value) ? value : payload; this.WALLETS[selectedWalletKey].unlock(unlockValue); - this.props.resetTransactionState(); + this.props.resetTransactionState(this.props.resetIncludeExcludeProperties); }; private isWalletDisabled = (walletKey: WalletName) => { diff --git a/common/components/ui/Input.tsx b/common/components/ui/Input.tsx index 6643287f..16895e80 100644 --- a/common/components/ui/Input.tsx +++ b/common/components/ui/Input.tsx @@ -9,6 +9,7 @@ class Input extends React.Component, State> { public state: State = { hasBlurred: false }; + public render() { return ( , State> { this.props.onBlur(e); } }} + onWheel={this.props.type === 'number' ? this.preventNumberScroll : undefined} className={`input-group-input ${this.props.className} ${ this.state.hasBlurred ? 'has-blurred' : '' } ${!!this.props.value && this.props.value.toString().length > 0 ? 'has-value' : ''}`} /> ); } + + // When number inputs are scrolled on while in focus, the number changes. So we blur + // it if it's focused to prevent that behavior, without preventing the scroll. + private preventNumberScroll(ev: React.WheelEvent) { + if (document.activeElement === ev.currentTarget) { + ev.currentTarget.blur(); + } + } } export default Input; diff --git a/common/components/ui/Modal.scss b/common/components/ui/Modal.scss index fa036cae..ea9d2d02 100644 --- a/common/components/ui/Modal.scss +++ b/common/components/ui/Modal.scss @@ -4,6 +4,8 @@ $m-background: #fff; $m-window-padding-w: 20px; $m-window-padding-h: 30px; +$m-window-padding-w-mobile: 10px; +$m-window-padding-h-mobile: 10px; $m-header-height: 62px; $m-header-padding: 1rem 2rem 0.5rem 2rem; $m-content-padding: 1.5rem 2rem; @@ -24,7 +26,7 @@ $m-anim-speed: 400ms; .Modal { position: fixed; - top: 50%; + top: $m-window-padding-h; left: 50%; width: initial; max-width: 95%; @@ -33,7 +35,7 @@ $m-anim-speed: 400ms; max-height: calc(100% - #{$m-window-padding-h * 2}); background: $m-background; border-radius: 2px; - transform: translate(-50%, -50%); + transform: translateX(-50%); z-index: $zindex-modal; overflow: hidden; display: flex; @@ -111,7 +113,10 @@ $m-anim-speed: 400ms; // Mobile styles @media(max-width: $screen-sm) { - width: calc(100% - 40px) !important; + top: $m-window-padding-h-mobile; + width: calc(100% - #{$m-window-padding-w-mobile}) !important; + max-width: calc(100% - #{$m-window-padding-w-mobile * 2}); + max-height: calc(100% - #{$m-window-padding-h-mobile * 2}); } } diff --git a/common/components/ui/Modal.tsx b/common/components/ui/Modal.tsx index dbe3ca5b..ff0e2f13 100644 --- a/common/components/ui/Modal.tsx +++ b/common/components/ui/Modal.tsx @@ -97,6 +97,10 @@ export default class Modal extends PureComponent { }; private escapeListner = (ev: KeyboardEvent) => { + if (!this.props.isOpen) { + return; + } + // Don't trigger if they hit escape while on an input if (ev.target) { if ( diff --git a/common/containers/Tabs/CheckTransaction/index.tsx b/common/containers/Tabs/CheckTransaction/index.tsx index a4bb4429..0fae96f0 100644 --- a/common/containers/Tabs/CheckTransaction/index.tsx +++ b/common/containers/Tabs/CheckTransaction/index.tsx @@ -1,15 +1,17 @@ import React from 'react'; import { connect } from 'react-redux'; +import { RouteComponentProps } from 'react-router'; import TabSection from 'containers/TabSection'; import TxHashInput from './components/TxHashInput'; import { TransactionStatus as TransactionStatusComponent } from 'components'; import { NewTabLink } from 'components/ui'; import { getNetworkConfig } from 'selectors/config'; +import { getParamFromURL } from 'utils/helpers'; import { AppState } from 'reducers'; import { NetworkConfig } from 'types/network'; import './index.scss'; -interface Props { +interface StateProps { network: NetworkConfig; } @@ -17,11 +19,20 @@ interface State { hash: string; } +type Props = StateProps & RouteComponentProps<{}>; + class CheckTransaction extends React.Component { public state: State = { hash: '' }; + public componentDidMount() { + const hash = getParamFromURL(this.props.location.search, 'txHash'); + if (hash) { + this.setState({ hash }); + } + } + public render() { const { network } = this.props; const { hash } = this.state; @@ -43,7 +54,7 @@ class CheckTransaction extends React.Component { )}

- + {hash && ( @@ -64,6 +75,6 @@ class CheckTransaction extends React.Component { }; } -export default connect((state: AppState) => ({ +export default connect((state: AppState): StateProps => ({ network: getNetworkConfig(state) }))(CheckTransaction); diff --git a/common/containers/Tabs/Contracts/components/Interact/components/InteractExplorer/components/Fields.tsx b/common/containers/Tabs/Contracts/components/Interact/components/InteractExplorer/components/Fields.tsx index b3e0e920..7ff8a829 100644 --- a/common/containers/Tabs/Contracts/components/Interact/components/InteractExplorer/components/Fields.tsx +++ b/common/containers/Tabs/Contracts/components/Interact/components/InteractExplorer/components/Fields.tsx @@ -7,6 +7,7 @@ import { FullWalletOnly } from 'components/renderCbs'; interface OwnProps { button: React.ReactElement; } + export class Fields extends Component { public render() { const makeContent = () => ( @@ -17,6 +18,7 @@ export class Fields extends Component { initialState="advanced" disableToggle={true} advancedGasOptions={{ dataField: false }} + resetIncludeExcludeProperties={{ exclude: { fields: ['to'] }, include: {} }} /> {this.props.button} @@ -24,7 +26,12 @@ export class Fields extends Component { ); - const makeDecrypt = () => ; + const makeDecrypt = () => ( + + ); return ; } diff --git a/common/sagas/transaction/broadcast/helpers.tsx b/common/sagas/transaction/broadcast/helpers.tsx index 81e0fdf0..bacb0ae3 100644 --- a/common/sagas/transaction/broadcast/helpers.tsx +++ b/common/sagas/transaction/broadcast/helpers.tsx @@ -20,6 +20,7 @@ import React from 'react'; import { getNetworkConfig } from 'selectors/config'; import TransactionSucceeded from 'components/ExtendedNotifications/TransactionSucceeded'; import { computeIndexingHash } from 'libs/transaction'; +import { NetworkConfig } from 'types/network'; export const broadcastTransactionWrapper = (func: (serializedTx: string) => SagaIterator) => function* handleBroadcastTransaction(action: BroadcastRequestedAction) { @@ -29,7 +30,7 @@ export const broadcastTransactionWrapper = (func: (serializedTx: string) => Saga ); try { - const shouldBroadcast = yield call(shouldBroadcastTransaction, indexingHash); + const shouldBroadcast: boolean = yield call(shouldBroadcastTransaction, indexingHash); if (!shouldBroadcast) { yield put( showNotification( @@ -46,16 +47,19 @@ export const broadcastTransactionWrapper = (func: (serializedTx: string) => Saga }); yield put(queueAction); const stringTx: string = yield call(bufferToHex, serializedTransaction); - const broadcastedHash = yield call(func, stringTx); // convert to string because node / web3 doesnt support buffers + const broadcastedHash: string = yield call(func, stringTx); // convert to string because node / web3 doesnt support buffers yield put(broadcastTransactionSucceeded({ indexingHash, broadcastedHash })); - const network = yield select(getNetworkConfig); - //TODO: make this not ugly + const network: NetworkConfig = yield select(getNetworkConfig); + yield put( showNotification( 'success', - , - 0 + , + Infinity ) ); } catch (error) { diff --git a/common/sagas/transaction/meta/unitSwap.ts b/common/sagas/transaction/meta/unitSwap.ts index c5c056d0..7c31226c 100644 --- a/common/sagas/transaction/meta/unitSwap.ts +++ b/common/sagas/transaction/meta/unitSwap.ts @@ -32,7 +32,7 @@ export function* handleSetUnitMeta({ payload: currentUnit }: SetUnitMetaAction): const tokenToToken = !currUnit && !prevUnit; const decimal: number = yield select(getDecimalFromUnit, currentUnit); - if (etherToEther) { + if (etherToEther || previousUnit === '') { return; } diff --git a/common/sass/styles/overrides/alerts.scss b/common/sass/styles/overrides/alerts.scss index 6d3f7a39..4ce8690a 100644 --- a/common/sass/styles/overrides/alerts.scss +++ b/common/sass/styles/overrides/alerts.scss @@ -1,7 +1,7 @@ // Extends Bootstrap's `.alert` -@import "common/sass/variables"; -@import "common/sass/mixins"; -@import "~bootstrap-sass/assets/stylesheets/bootstrap/alerts"; +@import 'common/sass/variables'; +@import 'common/sass/mixins'; +@import '~bootstrap-sass/assets/stylesheets/bootstrap/alerts'; .alert { margin-bottom: 1rem; @@ -16,6 +16,13 @@ opacity: 0.8; } } + + // Alerts have their own button style + .btn { + @include button-variant($text-color, #FFF, darken(#FFF, 5%)); + text-decoration: none; + margin-right: $space-xs; + } } // Alert icons diff --git a/common/sass/styles/overrides/buttons.scss b/common/sass/styles/overrides/buttons.scss index 63858628..28654b3a 100644 --- a/common/sass/styles/overrides/buttons.scss +++ b/common/sass/styles/overrides/buttons.scss @@ -100,24 +100,6 @@ } // Contextual color overrides (?) -.alert .btn-info { - background-color: white; - text-decoration: none; - color: $brand-info; - &:hover, - &:focus, - &.focus { - text-decoration: none; - opacity: 1; - } - &.disabled { - background-color: white; - text-decoration: none; - color: $brand-info; - opacity: 0.6; - } -} - .btn-group .btn-default { border-bottom-width: 1px; border-color: transparent; diff --git a/common/utils/consoleAdvertisement.ts b/common/utils/consoleAdvertisement.ts index 83f544e5..dd78f6a7 100644 --- a/common/utils/consoleAdvertisement.ts +++ b/common/utils/consoleAdvertisement.ts @@ -18,7 +18,7 @@ export default function consoleAdvertisement() { ┃ ,;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |__| | (_) | | | | | | |__| \\__ \\_| ┃ ┃ ;;;;;;;;;;;; ;;;;;;;;; ;;;;;;;;;;;; \\____/ \\___/|_|_| |_| \\____/|___(_) ┃ ┃ ;;;;;;;;;;;; ;;;;; ;;;;;;;;;;;, ┃ -┃ ;;;;;;;;;;;;: :;;;;;;;;;;;; https://github.com/MyCrypto/MyCrypto ┃ +┃ ;;;;;;;;;;;;: :;;;;;;;;;;;; https://github.com/MyCryptoHQ/MyCrypto ┃ ┃ ;;;;;;;;;;;;;: :;;;;;;;;;;;;; ┃ ┃ ;;;;;;;;;;;;;;: :;;;;;;;;;;;;;; ┃ ┃ ';;;;;;;;;;;;;;;;;;;;;;;;;;;;; ┃ diff --git a/common/utils/helpers.ts b/common/utils/helpers.ts index ddeb5009..b6d1fe2c 100644 --- a/common/utils/helpers.ts +++ b/common/utils/helpers.ts @@ -1,3 +1,4 @@ +import qs from 'query-string'; import has from 'lodash/has'; export function objectContainsObjectKeys(checkingObject, containingObject) { @@ -19,6 +20,10 @@ export function getParam(query: { [key: string]: string }, key: string) { return query[keys[index]]; } +export function getParamFromURL(url: string, param: string): string | undefined { + return qs.parse(qs.extract(url))[param]; +} + export function isPositiveInteger(n: number) { return Number.isInteger(n) && n > 0; } diff --git a/package.json b/package.json index 380ed46b..4672bb13 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "MyCrypto", "author": "MyCryptoHQ", - "version": "0.4.0", + "version": "0.5.0", "main": "main.js", "description": "MyCrypto web and electron app", "repository": "https://github.com/MyCryptoHQ/MyCrypto", @@ -15,20 +15,20 @@ "bn.js": "4.11.8", "bootstrap-sass": "3.3.7", "classnames": "2.2.5", - "electron-updater": "2.20.1", + "electron-updater": "2.21.0", "ethereum-blockies": "git+https://github.com/MyCryptoHQ/blockies.git", "ethereumjs-abi": "0.6.5", "ethereumjs-tx": "1.3.3", "ethereumjs-util": "5.1.5", "ethereumjs-wallet": "0.6.0", "font-awesome": "4.7.0", - "hard-source-webpack-plugin": "0.5.16", + "hard-source-webpack-plugin": "0.6.4", "hdkey": "0.8.0", "idna-uts46": "1.1.0", "jsonschema": "1.2.2", "ledgerco": "1.2.1", "lodash": "4.17.5", - "moment": "2.20.1", + "moment": "2.21.0", "normalizr": "3.2.4", "qrcode": "1.2.0", "qrcode.react": "0.8.0", @@ -81,7 +81,7 @@ "copy-webpack-plugin": "4.5.0", "css-loader": "0.28.10", "electron": "1.8.2", - "electron-builder": "20.2.0", + "electron-builder": "20.2.1", "empty": "0.10.1", "enzyme": "3.3.0", "enzyme-adapter-react-16": "1.1.1", @@ -111,12 +111,12 @@ "react-test-renderer": "16.2.0", "redux-devtools-extension": "2.13.2", "redux-test-utils": "0.2.2", - "resolve-url-loader": "2.2.1", + "resolve-url-loader": "2.3.0", "rimraf": "2.6.2", - "sass-loader": "6.0.6", + "sass-loader": "6.0.7", "style-loader": "0.20.2", "thread-loader": "1.1.5", - "ts-jest": "22.4.0", + "ts-jest": "22.4.1", "ts-loader": "3.3.1", "tslint": "5.9.1", "tslint-config-prettier": "1.9.0", @@ -124,7 +124,7 @@ "tslint-react": "3.5.1", "types-rlp": "0.0.1", "typescript": "2.6.2", - "url-loader": "0.6.2", + "url-loader": "1.0.1", "url-search-params-polyfill": "2.0.3", "webpack": "3.11.0", "webpack-dev-middleware": "2.0.6", diff --git a/spec/sagas/transaction/broadcast/helpers.spec.tsx b/spec/sagas/transaction/broadcast/helpers.spec.tsx index 11eb3657..0a8845f5 100644 --- a/spec/sagas/transaction/broadcast/helpers.spec.tsx +++ b/spec/sagas/transaction/broadcast/helpers.spec.tsx @@ -128,7 +128,7 @@ describe('broadcastTransactionWrapper*', () => { showNotification( 'success', , - 0 + Infinity ) ) );