Skip to content

Commit

Permalink
Improve handling of Marketmaker crashing or being unavailable during …
Browse files Browse the repository at this point in the history
…login (#461)

There were two things that could cause the `Cannot read property 'focus' of null` error:

### Marketmaker crashing right at logging in

You can reproduce this in `master` by having `killall marketmaker` ready in the terminal and execute it multiple times right after pressing "Login".

##### Solution

Seems we were not detecting Marketmaker crashing on login, so I fixed that.

### Not being able to connect to Marketmaker

For example if the user had entered an invalid remote Marketmaker URL or some firewall preventing the communication.

##### Solution

The problem is that we tried to change the state of the `LoginBox` component after it was unmounted, replaced by the `LoggingIn` component, and since it was unmounted already, `this.passwordInputRef.current` was undefined a threw.

The solution is to check whether the component is mounted, and if it is, we hadle the error normally by resetting the state, otherwise mount it again and show the error in an error dialog.

I also improved the error message when HyperDEX cannot connect to Marketmaker.
  • Loading branch information
sindresorhus committed Aug 7, 2018
1 parent 3a56e46 commit f12fe3c
Show file tree
Hide file tree
Showing 3 changed files with 42 additions and 16 deletions.
8 changes: 4 additions & 4 deletions app/marketmaker/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,9 @@ class Marketmaker {
}

async start(options) {
this.isStarting = true;
this.isKillingPreviousMarketmaker = true;
await this._killProcess();
this.isKillingPreviousMarketmaker = false;

options = Object.assign({}, options, {
client: 1,
Expand Down Expand Up @@ -94,12 +95,12 @@ class Marketmaker {
});

this.cp.on('exit', () => {
if (this.isStarting || !this.isRunning) {
if (this.isKillingPreviousMarketmaker || !this.isRunning) {
return;
}

this.isRunning = false;
electron.dialog.showErrorBox('Marketmaker crashed', 'HyperDEX will now relaunch.');
electron.dialog.showErrorBox('Marketmaker Crashed', 'HyperDEX will now relaunch.');
electron.app.relaunch();
electron.app.quit();
});
Expand All @@ -116,7 +117,6 @@ class Marketmaker {

// `marketmaker` takes ~500ms to get ready to accepts requests
await this._isReady();
this.isStarting = false;
}

async stop() {
Expand Down
21 changes: 16 additions & 5 deletions app/renderer/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,11 +57,22 @@ export default class Api {
async request(data) {
ow(data, ow.object.label('data'));

return this._request({
needjson: 1,
...data,
...{userpass: await this.token},
});
let result;
try {
result = await this._request({
needjson: 1,
...data,
...{userpass: await this.token},
});
} catch (err) {
if (err.message === 'Failed to fetch') {
err.message = 'Could not connect to Marketmaker';
}

throw err;
}

return result;
}

async debug(data) {
Expand Down
29 changes: 22 additions & 7 deletions app/renderer/views/LoginBox.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import {remote} from 'electron';
import React from 'react';
import Button from 'components/Button';
import Input from 'components/Input';
Expand All @@ -13,6 +14,7 @@ import {translate} from '../translate';
import './LoginBox.scss';

const t = translate('login');
const {dialog} = remote;

const SettingsButton = () => (
<CogIcon
Expand Down Expand Up @@ -68,18 +70,31 @@ class LoginBox extends React.Component {
try {
await loginContainer.handleLogin(selectedPortfolioId, passwordInputValue);
} catch (err) {
console.error(err);
if (this._isMounted) {
await this.setState({
isLoggingIn: false,
passwordInputValue: '',
passwordError: err.message,
});

this.setState({passwordInputValue: ''});
this.passwordInputRef.current.focus();
return;
}

await this.setState({
isLoggingIn: false,
passwordError: err.message,
});
this.passwordInputRef.current.focus();
console.error(err);
loginContainer.setActiveView(LoginBox.name);
dialog.showErrorBox('Login Failed', err.message || err || 'Unknown reason');
}
};

componentDidMount() {
this._isMounted = true;
}

componentWillUnmount() {
this._isMounted = false;
}

render() {
const {portfolios, selectedPortfolioId} = loginContainer.state;

Expand Down

0 comments on commit f12fe3c

Please sign in to comment.