Merge pull request #1256 from MyCryptoHQ/develop

Tag Beta Release 0.5.0
This commit is contained in:
Daniel Ternyak 2018-03-05 14:37:18 -06:00 committed by GitHub
commit 71b68dd5cb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 116 additions and 67 deletions

View File

@ -1,26 +1,33 @@
import React from 'react'; 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'; import { BlockExplorerConfig } from 'types/network';
export interface TransactionSucceededProps { export interface TransactionSucceededProps {
txHash: string; txHash: string;
blockExplorer: BlockExplorerConfig; blockExplorer?: BlockExplorerConfig;
} }
const TransactionSucceeded = ({ txHash, blockExplorer }: TransactionSucceededProps) => { const TransactionSucceeded = ({ txHash, blockExplorer }: TransactionSucceededProps) => {
const txHashLink = blockExplorer.txUrl(txHash); let verifyBtn: React.ReactElement<string> | undefined;
if (blockExplorer) {
verifyBtn = (
<NewTabLink className="btn btn-xs" href={blockExplorer.txUrl(txHash)}>
Verify Transaction on {blockExplorer.name}
</NewTabLink>
);
}
return ( return (
<div> <div>
<p>{translateRaw('SUCCESS_3') + txHash}</p> <p>
<a {translate('SUCCESS_3')} {txHash}
className="btn btn-xs btn-info string" </p>
href={txHashLink} {verifyBtn}
target="_blank" <Link to={`/tx-status?txHash=${txHash}`} className="btn btn-xs">
rel="noopener noreferrer" {translate('NAV_CheckTxStatus')}
> </Link>
Verify Transaction
</a>
</div> </div>
); );
}; };

View File

@ -41,7 +41,11 @@ const DisclaimerModal: React.SFC<Props> = ({ isOpen, handleClose }) => {
English and, because of this, the English version of our website is the official text. English and, because of this, the English version of our website is the official text.
</p> </p>
<p> <p>
<b>MIT License</b> Copyright © 2015-2017 MyCrypto LLC <b>MIT License</b>
<br />
Copyright (c) 2015-2017 MyEtherWallet LLC
<br />
Copyright (c) 2018 MyCrypto, Inc.
</p> </p>
<p> <p>
Permission is hereby granted, free of charge, to any person obtaining a copy of this Permission is hereby granted, free of charge, to any person obtaining a copy of this

View File

@ -62,7 +62,7 @@ class LogOutPromptClass extends React.Component<Props, State> {
}; };
private onConfirm = () => { private onConfirm = () => {
const { nextLocation } = this.state; const { nextLocation: next } = this.state;
this.props.resetWallet(); this.props.resetWallet();
this.setState( this.setState(
{ {
@ -70,8 +70,8 @@ class LogOutPromptClass extends React.Component<Props, State> {
nextLocation: null nextLocation: null
}, },
() => { () => {
if (nextLocation) { if (next) {
this.props.history.push(nextLocation.pathname); this.props.history.push(`${next.pathname}${next.search}${next.hash}`);
} }
} }
); );

View File

@ -10,7 +10,8 @@ import {
getNonceRequested, getNonceRequested,
TGetNonceRequested, TGetNonceRequested,
reset, reset,
TReset TReset,
ResetAction
} from 'actions/transaction'; } from 'actions/transaction';
import { fetchCCRatesRequested, TFetchCCRatesRequested } from 'actions/rates'; import { fetchCCRatesRequested, TFetchCCRatesRequested } from 'actions/rates';
import { getNetworkConfig, getOffline } from 'selectors/config'; import { getNetworkConfig, getOffline } from 'selectors/config';
@ -44,6 +45,7 @@ interface DefaultProps {
} }
interface OwnProps { interface OwnProps {
resetIncludeExcludeProperties?: ResetAction['payload'];
initialState?: SliderStates; initialState?: SliderStates;
disableToggle?: boolean; disableToggle?: boolean;
advancedGasOptions?: AdvancedOptions; advancedGasOptions?: AdvancedOptions;
@ -69,7 +71,7 @@ class TXMetaDataPanel extends React.Component<Props, State> {
public componentDidMount() { public componentDidMount() {
if (!this.props.offline) { if (!this.props.offline) {
this.props.reset(); this.props.reset(this.props.resetIncludeExcludeProperties);
this.props.fetchCCRates([this.props.network.unit]); this.props.fetchCCRates([this.props.network.unit]);
this.props.getNonceRequested(); this.props.getNonceRequested();
} }

View File

@ -17,7 +17,7 @@ import {
resetWallet, resetWallet,
TResetWallet TResetWallet
} from 'actions/wallet'; } from 'actions/wallet';
import { reset, TReset } from 'actions/transaction'; import { reset, TReset, ResetAction } from 'actions/transaction';
import translate from 'translations'; import translate from 'translations';
import { import {
KeystoreDecrypt, KeystoreDecrypt,
@ -55,6 +55,7 @@ interface OwnProps {
hidden?: boolean; hidden?: boolean;
disabledWallets?: DisabledWallets; disabledWallets?: DisabledWallets;
showGenerateLink?: boolean; showGenerateLink?: boolean;
resetIncludeExcludeProperties?: ResetAction['payload'];
} }
interface DispatchProps { interface DispatchProps {
@ -348,7 +349,7 @@ export class WalletDecrypt extends Component<Props, State> {
{this.props.showGenerateLink && ( {this.props.showGenerateLink && (
<div className="WalletDecrypt-wallets-generate"> <div className="WalletDecrypt-wallets-generate">
Dont have a wallet? <Link to="/generate">Click here to get one</Link>. Dont have an account yet? <Link to="/generate">Click here to get one</Link>.
</div> </div>
)} )}
</div> </div>
@ -430,7 +431,7 @@ export class WalletDecrypt extends Component<Props, State> {
// the payload to contain the unlocked wallet info. // the payload to contain the unlocked wallet info.
const unlockValue = value && !isEmpty(value) ? value : payload; const unlockValue = value && !isEmpty(value) ? value : payload;
this.WALLETS[selectedWalletKey].unlock(unlockValue); this.WALLETS[selectedWalletKey].unlock(unlockValue);
this.props.resetTransactionState(); this.props.resetTransactionState(this.props.resetIncludeExcludeProperties);
}; };
private isWalletDisabled = (walletKey: WalletName) => { private isWalletDisabled = (walletKey: WalletName) => {

View File

@ -9,6 +9,7 @@ class Input extends React.Component<HTMLProps<HTMLInputElement>, State> {
public state: State = { public state: State = {
hasBlurred: false hasBlurred: false
}; };
public render() { public render() {
return ( return (
<input <input
@ -19,12 +20,21 @@ class Input extends React.Component<HTMLProps<HTMLInputElement>, State> {
this.props.onBlur(e); this.props.onBlur(e);
} }
}} }}
onWheel={this.props.type === 'number' ? this.preventNumberScroll : undefined}
className={`input-group-input ${this.props.className} ${ className={`input-group-input ${this.props.className} ${
this.state.hasBlurred ? 'has-blurred' : '' this.state.hasBlurred ? 'has-blurred' : ''
} ${!!this.props.value && this.props.value.toString().length > 0 ? 'has-value' : ''}`} } ${!!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<HTMLInputElement>) {
if (document.activeElement === ev.currentTarget) {
ev.currentTarget.blur();
}
}
} }
export default Input; export default Input;

View File

@ -4,6 +4,8 @@
$m-background: #fff; $m-background: #fff;
$m-window-padding-w: 20px; $m-window-padding-w: 20px;
$m-window-padding-h: 30px; $m-window-padding-h: 30px;
$m-window-padding-w-mobile: 10px;
$m-window-padding-h-mobile: 10px;
$m-header-height: 62px; $m-header-height: 62px;
$m-header-padding: 1rem 2rem 0.5rem 2rem; $m-header-padding: 1rem 2rem 0.5rem 2rem;
$m-content-padding: 1.5rem 2rem; $m-content-padding: 1.5rem 2rem;
@ -24,7 +26,7 @@ $m-anim-speed: 400ms;
.Modal { .Modal {
position: fixed; position: fixed;
top: 50%; top: $m-window-padding-h;
left: 50%; left: 50%;
width: initial; width: initial;
max-width: 95%; max-width: 95%;
@ -33,7 +35,7 @@ $m-anim-speed: 400ms;
max-height: calc(100% - #{$m-window-padding-h * 2}); max-height: calc(100% - #{$m-window-padding-h * 2});
background: $m-background; background: $m-background;
border-radius: 2px; border-radius: 2px;
transform: translate(-50%, -50%); transform: translateX(-50%);
z-index: $zindex-modal; z-index: $zindex-modal;
overflow: hidden; overflow: hidden;
display: flex; display: flex;
@ -111,7 +113,10 @@ $m-anim-speed: 400ms;
// Mobile styles // Mobile styles
@media(max-width: $screen-sm) { @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});
} }
} }

View File

@ -97,6 +97,10 @@ export default class Modal extends PureComponent<Props, {}> {
}; };
private escapeListner = (ev: KeyboardEvent) => { private escapeListner = (ev: KeyboardEvent) => {
if (!this.props.isOpen) {
return;
}
// Don't trigger if they hit escape while on an input // Don't trigger if they hit escape while on an input
if (ev.target) { if (ev.target) {
if ( if (

View File

@ -1,15 +1,17 @@
import React from 'react'; import React from 'react';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { RouteComponentProps } from 'react-router';
import TabSection from 'containers/TabSection'; import TabSection from 'containers/TabSection';
import TxHashInput from './components/TxHashInput'; import TxHashInput from './components/TxHashInput';
import { TransactionStatus as TransactionStatusComponent } from 'components'; import { TransactionStatus as TransactionStatusComponent } from 'components';
import { NewTabLink } from 'components/ui'; import { NewTabLink } from 'components/ui';
import { getNetworkConfig } from 'selectors/config'; import { getNetworkConfig } from 'selectors/config';
import { getParamFromURL } from 'utils/helpers';
import { AppState } from 'reducers'; import { AppState } from 'reducers';
import { NetworkConfig } from 'types/network'; import { NetworkConfig } from 'types/network';
import './index.scss'; import './index.scss';
interface Props { interface StateProps {
network: NetworkConfig; network: NetworkConfig;
} }
@ -17,11 +19,20 @@ interface State {
hash: string; hash: string;
} }
type Props = StateProps & RouteComponentProps<{}>;
class CheckTransaction extends React.Component<Props, State> { class CheckTransaction extends React.Component<Props, State> {
public state: State = { public state: State = {
hash: '' hash: ''
}; };
public componentDidMount() {
const hash = getParamFromURL(this.props.location.search, 'txHash');
if (hash) {
this.setState({ hash });
}
}
public render() { public render() {
const { network } = this.props; const { network } = this.props;
const { hash } = this.state; const { hash } = this.state;
@ -43,7 +54,7 @@ class CheckTransaction extends React.Component<Props, State> {
</React.Fragment> </React.Fragment>
)} )}
</p> </p>
<TxHashInput onSubmit={this.handleHashSubmit} /> <TxHashInput hash={hash} onSubmit={this.handleHashSubmit} />
</section> </section>
{hash && ( {hash && (
@ -64,6 +75,6 @@ class CheckTransaction extends React.Component<Props, State> {
}; };
} }
export default connect((state: AppState) => ({ export default connect((state: AppState): StateProps => ({
network: getNetworkConfig(state) network: getNetworkConfig(state)
}))(CheckTransaction); }))(CheckTransaction);

View File

@ -7,6 +7,7 @@ import { FullWalletOnly } from 'components/renderCbs';
interface OwnProps { interface OwnProps {
button: React.ReactElement<any>; button: React.ReactElement<any>;
} }
export class Fields extends Component<OwnProps> { export class Fields extends Component<OwnProps> {
public render() { public render() {
const makeContent = () => ( const makeContent = () => (
@ -17,6 +18,7 @@ export class Fields extends Component<OwnProps> {
initialState="advanced" initialState="advanced"
disableToggle={true} disableToggle={true}
advancedGasOptions={{ dataField: false }} advancedGasOptions={{ dataField: false }}
resetIncludeExcludeProperties={{ exclude: { fields: ['to'] }, include: {} }}
/> />
{this.props.button} {this.props.button}
<SigningStatus /> <SigningStatus />
@ -24,7 +26,12 @@ export class Fields extends Component<OwnProps> {
</React.Fragment> </React.Fragment>
); );
const makeDecrypt = () => <WalletDecrypt disabledWallets={DISABLE_WALLETS.READ_ONLY} />; const makeDecrypt = () => (
<WalletDecrypt
disabledWallets={DISABLE_WALLETS.READ_ONLY}
resetIncludeExcludeProperties={{ exclude: { fields: ['to'] }, include: {} }}
/>
);
return <FullWalletOnly withFullWallet={makeContent} withoutFullWallet={makeDecrypt} />; return <FullWalletOnly withFullWallet={makeContent} withoutFullWallet={makeDecrypt} />;
} }

View File

@ -20,6 +20,7 @@ import React from 'react';
import { getNetworkConfig } from 'selectors/config'; import { getNetworkConfig } from 'selectors/config';
import TransactionSucceeded from 'components/ExtendedNotifications/TransactionSucceeded'; import TransactionSucceeded from 'components/ExtendedNotifications/TransactionSucceeded';
import { computeIndexingHash } from 'libs/transaction'; import { computeIndexingHash } from 'libs/transaction';
import { NetworkConfig } from 'types/network';
export const broadcastTransactionWrapper = (func: (serializedTx: string) => SagaIterator) => export const broadcastTransactionWrapper = (func: (serializedTx: string) => SagaIterator) =>
function* handleBroadcastTransaction(action: BroadcastRequestedAction) { function* handleBroadcastTransaction(action: BroadcastRequestedAction) {
@ -29,7 +30,7 @@ export const broadcastTransactionWrapper = (func: (serializedTx: string) => Saga
); );
try { try {
const shouldBroadcast = yield call(shouldBroadcastTransaction, indexingHash); const shouldBroadcast: boolean = yield call(shouldBroadcastTransaction, indexingHash);
if (!shouldBroadcast) { if (!shouldBroadcast) {
yield put( yield put(
showNotification( showNotification(
@ -46,16 +47,19 @@ export const broadcastTransactionWrapper = (func: (serializedTx: string) => Saga
}); });
yield put(queueAction); yield put(queueAction);
const stringTx: string = yield call(bufferToHex, serializedTransaction); 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 })); yield put(broadcastTransactionSucceeded({ indexingHash, broadcastedHash }));
const network = yield select(getNetworkConfig); const network: NetworkConfig = yield select(getNetworkConfig);
//TODO: make this not ugly
yield put( yield put(
showNotification( showNotification(
'success', 'success',
<TransactionSucceeded txHash={broadcastedHash} blockExplorer={network.blockExplorer} />, <TransactionSucceeded
0 txHash={broadcastedHash}
blockExplorer={network.isCustom ? undefined : network.blockExplorer}
/>,
Infinity
) )
); );
} catch (error) { } catch (error) {

View File

@ -32,7 +32,7 @@ export function* handleSetUnitMeta({ payload: currentUnit }: SetUnitMetaAction):
const tokenToToken = !currUnit && !prevUnit; const tokenToToken = !currUnit && !prevUnit;
const decimal: number = yield select(getDecimalFromUnit, currentUnit); const decimal: number = yield select(getDecimalFromUnit, currentUnit);
if (etherToEther) { if (etherToEther || previousUnit === '') {
return; return;
} }

View File

@ -1,7 +1,7 @@
// Extends Bootstrap's `.alert` // Extends Bootstrap's `.alert`
@import "common/sass/variables"; @import 'common/sass/variables';
@import "common/sass/mixins"; @import 'common/sass/mixins';
@import "~bootstrap-sass/assets/stylesheets/bootstrap/alerts"; @import '~bootstrap-sass/assets/stylesheets/bootstrap/alerts';
.alert { .alert {
margin-bottom: 1rem; margin-bottom: 1rem;
@ -16,6 +16,13 @@
opacity: 0.8; 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 // Alert icons

View File

@ -100,24 +100,6 @@
} }
// Contextual color overrides (?) // 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 { .btn-group .btn-default {
border-bottom-width: 1px; border-bottom-width: 1px;
border-color: transparent; border-color: transparent;

View File

@ -18,7 +18,7 @@ export default function consoleAdvertisement() {
,;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |__| | (_) | | | | | | |__| \\__ \\_| ,;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |__| | (_) | | | | | | |__| \\__ \\_|
;;;;;;;;;;;; ;;;;;;;;; ;;;;;;;;;;;; \\____/ \\___/|_|_| |_| \\____/|___(_) ;;;;;;;;;;;; ;;;;;;;;; ;;;;;;;;;;;; \\____/ \\___/|_|_| |_| \\____/|___(_)
;;;;;;;;;;;; ;;;;; ;;;;;;;;;;;, ;;;;;;;;;;;; ;;;;; ;;;;;;;;;;;,
;;;;;;;;;;;;: :;;;;;;;;;;;; https://github.com/MyCrypto/MyCrypto ;;;;;;;;;;;;: :;;;;;;;;;;;; https://github.com/MyCryptoHQ/MyCrypto ┃
;;;;;;;;;;;;;: :;;;;;;;;;;;;; ;;;;;;;;;;;;;: :;;;;;;;;;;;;;
;;;;;;;;;;;;;;: :;;;;;;;;;;;;;; ;;;;;;;;;;;;;;: :;;;;;;;;;;;;;;
';;;;;;;;;;;;;;;;;;;;;;;;;;;;; ';;;;;;;;;;;;;;;;;;;;;;;;;;;;;

View File

@ -1,3 +1,4 @@
import qs from 'query-string';
import has from 'lodash/has'; import has from 'lodash/has';
export function objectContainsObjectKeys(checkingObject, containingObject) { export function objectContainsObjectKeys(checkingObject, containingObject) {
@ -19,6 +20,10 @@ export function getParam(query: { [key: string]: string }, key: string) {
return query[keys[index]]; 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) { export function isPositiveInteger(n: number) {
return Number.isInteger(n) && n > 0; return Number.isInteger(n) && n > 0;
} }

View File

@ -1,7 +1,7 @@
{ {
"name": "MyCrypto", "name": "MyCrypto",
"author": "MyCryptoHQ", "author": "MyCryptoHQ",
"version": "0.4.0", "version": "0.5.0",
"main": "main.js", "main": "main.js",
"description": "MyCrypto web and electron app", "description": "MyCrypto web and electron app",
"repository": "https://github.com/MyCryptoHQ/MyCrypto", "repository": "https://github.com/MyCryptoHQ/MyCrypto",
@ -15,20 +15,20 @@
"bn.js": "4.11.8", "bn.js": "4.11.8",
"bootstrap-sass": "3.3.7", "bootstrap-sass": "3.3.7",
"classnames": "2.2.5", "classnames": "2.2.5",
"electron-updater": "2.20.1", "electron-updater": "2.21.0",
"ethereum-blockies": "git+https://github.com/MyCryptoHQ/blockies.git", "ethereum-blockies": "git+https://github.com/MyCryptoHQ/blockies.git",
"ethereumjs-abi": "0.6.5", "ethereumjs-abi": "0.6.5",
"ethereumjs-tx": "1.3.3", "ethereumjs-tx": "1.3.3",
"ethereumjs-util": "5.1.5", "ethereumjs-util": "5.1.5",
"ethereumjs-wallet": "0.6.0", "ethereumjs-wallet": "0.6.0",
"font-awesome": "4.7.0", "font-awesome": "4.7.0",
"hard-source-webpack-plugin": "0.5.16", "hard-source-webpack-plugin": "0.6.4",
"hdkey": "0.8.0", "hdkey": "0.8.0",
"idna-uts46": "1.1.0", "idna-uts46": "1.1.0",
"jsonschema": "1.2.2", "jsonschema": "1.2.2",
"ledgerco": "1.2.1", "ledgerco": "1.2.1",
"lodash": "4.17.5", "lodash": "4.17.5",
"moment": "2.20.1", "moment": "2.21.0",
"normalizr": "3.2.4", "normalizr": "3.2.4",
"qrcode": "1.2.0", "qrcode": "1.2.0",
"qrcode.react": "0.8.0", "qrcode.react": "0.8.0",
@ -81,7 +81,7 @@
"copy-webpack-plugin": "4.5.0", "copy-webpack-plugin": "4.5.0",
"css-loader": "0.28.10", "css-loader": "0.28.10",
"electron": "1.8.2", "electron": "1.8.2",
"electron-builder": "20.2.0", "electron-builder": "20.2.1",
"empty": "0.10.1", "empty": "0.10.1",
"enzyme": "3.3.0", "enzyme": "3.3.0",
"enzyme-adapter-react-16": "1.1.1", "enzyme-adapter-react-16": "1.1.1",
@ -111,12 +111,12 @@
"react-test-renderer": "16.2.0", "react-test-renderer": "16.2.0",
"redux-devtools-extension": "2.13.2", "redux-devtools-extension": "2.13.2",
"redux-test-utils": "0.2.2", "redux-test-utils": "0.2.2",
"resolve-url-loader": "2.2.1", "resolve-url-loader": "2.3.0",
"rimraf": "2.6.2", "rimraf": "2.6.2",
"sass-loader": "6.0.6", "sass-loader": "6.0.7",
"style-loader": "0.20.2", "style-loader": "0.20.2",
"thread-loader": "1.1.5", "thread-loader": "1.1.5",
"ts-jest": "22.4.0", "ts-jest": "22.4.1",
"ts-loader": "3.3.1", "ts-loader": "3.3.1",
"tslint": "5.9.1", "tslint": "5.9.1",
"tslint-config-prettier": "1.9.0", "tslint-config-prettier": "1.9.0",
@ -124,7 +124,7 @@
"tslint-react": "3.5.1", "tslint-react": "3.5.1",
"types-rlp": "0.0.1", "types-rlp": "0.0.1",
"typescript": "2.6.2", "typescript": "2.6.2",
"url-loader": "0.6.2", "url-loader": "1.0.1",
"url-search-params-polyfill": "2.0.3", "url-search-params-polyfill": "2.0.3",
"webpack": "3.11.0", "webpack": "3.11.0",
"webpack-dev-middleware": "2.0.6", "webpack-dev-middleware": "2.0.6",

View File

@ -128,7 +128,7 @@ describe('broadcastTransactionWrapper*', () => {
showNotification( showNotification(
'success', 'success',
<TransactionSucceeded txHash={broadcastedHash} blockExplorer={network.blockExplorer} />, <TransactionSucceeded txHash={broadcastedHash} blockExplorer={network.blockExplorer} />,
0 Infinity
) )
) )
); );