Merge pull request #2098 from lbryio/daemon-startup

Handle daemon startup errors/timeouts
This commit is contained in:
Sean Yesmunt 2018-11-08 15:51:24 -05:00 committed by GitHub
commit fa5cff7287
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 198 additions and 44 deletions

View file

@ -14,6 +14,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
* Subscription improvements ([#2031](https://github.com/lbryio/lbry-desktop/pull/2031))
* Adds Persistence to File List Filter Selections ([#2050](https://github.com/lbryio/lbry-desktop/pull/2050))
* Add more share options for channel page ([#2088](https://github.com/lbryio/lbry-desktop/pull/2088))
* Better error handling on app startup ([#2098](https://github.com/lbryio/lbry-desktop/pull/2098))
### Changed
* Upgraded to lbrynet v0.30.0 ([#1998](https://github.com/lbryio/lbry-desktop/pull/1998))

2
flow-typed/i18n.js vendored
View file

@ -1 +1 @@
declare function __(a: string): string;
declare function __(a: string, b?: string | number): string;

View file

@ -22,7 +22,6 @@ class ToolTip extends React.PureComponent<Props, State> {
constructor(props: Props) {
super(props);
this.tooltip = React.createRef();
this.state = {
direction: this.props.direction,
};
@ -33,11 +32,11 @@ class ToolTip extends React.PureComponent<Props, State> {
}
getVisibility = () => {
if (!this.tooltip.current) {
if (!this.tooltip) {
return;
}
const node = this.tooltip.current;
const node = this.tooltip;
const rect = node.getBoundingClientRect();
// Get parent-container
@ -80,12 +79,12 @@ class ToolTip extends React.PureComponent<Props, State> {
const visibility = this.getVisibility();
// Invert direction if tooltip is outside viewport bounds
if (!visibility[direction]) {
if (!visibility || !visibility[direction]) {
this.invertDirection();
}
};
tooltip: { current: null | HTMLSpanElement };
tooltip: ?HTMLSpanElement;
render() {
const { direction } = this.state;
@ -111,7 +110,9 @@ class ToolTip extends React.PureComponent<Props, State> {
>
{tooltipContent}
<span
ref={this.tooltip}
ref={ref => {
this.tooltip = ref;
}}
className={classnames('tooltip__body', {
'tooltip__body--short': isShortDescription,
})}

View file

@ -1,13 +1,15 @@
// @flow
import * as React from 'react';
import React, { Fragment } from 'react';
import Icon from 'component/common/icon';
import * as icons from 'constants/icons';
import Spinner from 'component/spinner';
import Button from 'component/button';
type Props = {
message: string,
details: ?string,
isWarning: boolean,
error: boolean,
};
class LoadScreen extends React.PureComponent<Props> {
@ -16,7 +18,7 @@ class LoadScreen extends React.PureComponent<Props> {
};
render() {
const { details, message, isWarning } = this.props;
const { details, message, isWarning, error } = this.props;
return (
<div className="load-screen">
@ -24,17 +26,42 @@ class LoadScreen extends React.PureComponent<Props> {
<h1 className="load-screen__title">{__('LBRY')}</h1>
<sup className="load-screen__beta">beta</sup>
</div>
{isWarning ? (
<span className="load-screen__message">
<Icon size={20} icon={icons.ALERT} />
{` ${message}`}
</span>
{error ? (
<Fragment>
<h3>{__('Uh oh. Sean must have messed something up. Try refreshing to fix it.')}</h3>
<div className="card__actions">
<Button label="Refresh" button="alt" onClick={() => window.location.reload()} />
</div>
<div className="load-screen--help">
<p>
{__(
'If you still have issues, your anti-virus software or firewall may be preventing startup.'
)}
</p>
<p>
{__('Reach out to hello@lbry.io for help, or check out')}{' '}
<Button
className="btn--load-screen"
href="https://lbry.io/faq/startup-troubleshooting"
label="this link"
/>.
</p>
</div>
</Fragment>
) : (
<div className="load-screen__message">{message}</div>
<Fragment>
{isWarning ? (
<span className="load-screen__message">
<Icon size={20} icon={icons.ALERT} />
{` ${message}`}
</span>
) : (
<div className="load-screen__message">{message}</div>
)}
{details && <div className="load-screen__details">{details}</div>}
<Spinner type="splash" />
</Fragment>
)}
{details && <div className="load-screen__details">{details}</div>}
<Spinner type="splash" />
</div>
);
}

View file

@ -1,4 +1,5 @@
// @flow
import type { Status } from 'types/status';
import * as React from 'react';
import { Lbry, MODALS } from 'lbry-redux';
import ModalWalletUnlock from 'modal/modalWalletUnlock';
@ -7,6 +8,8 @@ import ModalUpgrade from 'modal/modalUpgrade';
import ModalDownloading from 'modal/modalDownloading';
import LoadScreen from './internal/load-screen';
const ONE_MINUTE = 60 * 1000;
type Props = {
checkDaemonVersion: () => Promise<any>,
notifyUnlockWallet: () => Promise<any>,
@ -23,6 +26,7 @@ type State = {
message: string,
isRunning: boolean,
launchedModal: boolean,
error: boolean,
};
export class SplashScreen extends React.PureComponent<Props, State> {
@ -34,14 +38,17 @@ export class SplashScreen extends React.PureComponent<Props, State> {
message: __('Connecting'),
isRunning: false,
launchedModal: false,
error: false,
};
this.hasRecordedUser = false;
this.timeout = undefined;
}
componentDidMount() {
const { checkDaemonVersion } = this.props;
this.adjustErrorTimeout();
Lbry.connect()
.then(checkDaemonVersion)
.then(() => {
@ -57,22 +64,54 @@ export class SplashScreen extends React.PureComponent<Props, State> {
});
}
componentDidUpdate() {
this.adjustErrorTimeout();
}
componentWillUnmount() {
if (this.timeout) {
clearTimeout(this.timeout);
}
}
adjustErrorTimeout() {
if (this.timeout) {
clearTimeout(this.timeout);
}
// Every time we make it to a new step in the daemon startup process, reset the timer
// If nothing changes after 1 minute, show the error message.
this.timeout = setTimeout(() => {
this.setState({ error: true });
}, ONE_MINUTE);
}
updateStatus() {
Lbry.status().then(status => {
this.updateStatusCallback(status);
});
}
updateStatusCallback(status) {
updateStatusCallback(status: Status) {
const { notifyUnlockWallet, authenticate } = this.props;
const { launchedModal } = this.state;
if (status.error) {
this.setState({
error: true,
});
return;
}
if (!this.hasRecordedUser && status) {
authenticate();
this.hasRecordedUser = true;
}
if (status.wallet && status.wallet.is_locked) {
const { wallet, blockchain_headers: blockchainHeaders } = status;
// If the wallet is locked, stop doing anything and make the user input their password
if (wallet && wallet.is_locked) {
this.setState({
isRunning: true,
});
@ -97,43 +136,49 @@ export class SplashScreen extends React.PureComponent<Props, State> {
this.props.onReadyToLaunch();
}
});
return;
} else if (status.blockchain_headers && status.blockchain_headers.download_progress < 100) {
} else if (blockchainHeaders) {
const blockChainHeaders = blockchainHeaders;
if (blockChainHeaders.download_progress < 100) {
this.setState({
message: __('Blockchain Sync'),
details: `${__('Catching up with the blockchain')} (${
blockchainHeaders.download_progress
}%)`,
});
}
} else if (wallet && wallet.blocks_behind > 0) {
const format = wallet.blocks_behind === 1 ? '%s block behind' : '%s blocks behind';
this.setState({
message: __('Blockchain Sync'),
details: `${__('Catching up with the blockchain')} (${
status.blockchain_headers.download_progress
}%)`,
details: __(format, wallet.blocks_behind),
});
} else if (status.wallet && status.wallet.blocks_behind > 0) {
const format = status.wallet.blocks_behind === 1 ? '%s block behind' : '%s blocks behind';
this.setState({
message: __('Blockchain Sync'),
details: __(format, status.wallet.blocks_behind),
});
} else if (status.wallet && status.wallet.blocks_behind === 0) {
} else if (wallet && wallet.blocks_behind === 0) {
this.setState({
message: 'Network Loading',
details: 'Initializing LBRY service...',
});
}
setTimeout(() => {
this.updateStatus();
}, 500);
}
hasRecordedUser: boolean;
timeout: ?TimeoutID;
render() {
const { notification } = this.props;
const { message, details, isRunning } = this.state;
const { message, details, isRunning, error } = this.state;
const notificationId = notification && notification.id;
// {notificationId === MODALS.WALLET_UNLOCK && <ModalWalletUnlock />}
return (
<React.Fragment>
<LoadScreen message={message} details={details} />
<LoadScreen message={message} details={details} error={error} />
{/* Temp hack: don't show any modals on splash screen daemon is running;
daemon doesn't let you quit during startup, so the "Quit" buttons
in the modals won't work. */}

View file

@ -183,6 +183,16 @@
text-transform: uppercase;
}
&.btn--load-screen {
display: inline-block;
border-bottom: 1px solid $lbry-white;
&:hover {
color: $lbry-blue-1;
border-bottom: 1px solid $lbry-blue-1;
}
}
.icon + .btn__label {
padding-left: 5px;
}

View file

@ -8,11 +8,11 @@
color: $lbry-white;
display: flex;
flex-direction: column;
justify-content: center;
}
.load-screen__header {
display: flex;
margin-top: 35vh;
}
.load-screen__title {
@ -41,3 +41,8 @@
max-width: 400px;
padding-top: $spacing-vertical * 2/3;
}
.load-screen--help {
font-size: 14px;
padding-top: $spacing-vertical;
}

View file

@ -0,0 +1,48 @@
// @flow
export type Status = {
error?: {},
is_running: boolean,
is_first_run: boolean,
installation_id: string,
skipped_components: Array<string>,
startup_status: {
blob_manager: boolean,
blockchain_headers: boolean,
database: boolean,
dht: boolean,
exchange_rate_manager: boolean,
file_manager: boolean,
hash_announcer: boolean,
payment_rate_manager: boolean,
peer_protocol_server: boolean,
rate_limiter: boolean,
stream_identifier: boolean,
upnp: boolean,
wallet: boolean,
},
blob_manager: {
finished_blobs: number,
},
connection_status: {
code: string,
message: string,
},
dht: {
node_id: string,
peers_in_routing_table: number,
},
hash_announcer: {
announce_queue_size: number,
},
wallet?: {
best_blockhash: string,
blocks: number,
blocks_behind: number,
is_encrypted: boolean,
is_locked: boolean,
},
blockchain_headers?: {
download_progress: number,
},
};

View file

@ -2247,7 +2247,7 @@ copy-descriptor@^0.1.0:
core-js@^1.0.0:
version "1.2.7"
resolved "https://registry.yarnpkg.com/core-js/-/core-js-1.2.7.tgz#652294c14651db28fa93bd2d5ff2983a4f08c636"
resolved "http://registry.npmjs.org/core-js/-/core-js-1.2.7.tgz#652294c14651db28fa93bd2d5ff2983a4f08c636"
core-js@^2.4.0, core-js@^2.5.0:
version "2.5.7"
@ -4076,7 +4076,7 @@ flatten@^1.0.2:
flow-bin@^0.69.0:
version "0.69.0"
resolved "https://registry.yarnpkg.com/flow-bin/-/flow-bin-0.69.0.tgz#053159a684a6051fcbf0b71a2eb19a9679082da6"
resolved "http://registry.npmjs.org/flow-bin/-/flow-bin-0.69.0.tgz#053159a684a6051fcbf0b71a2eb19a9679082da6"
flow-typed@^2.3.0:
version "2.5.1"
@ -4870,12 +4870,18 @@ iconv-lite@0.4.19:
version "0.4.19"
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.19.tgz#f7468f60135f5e5dad3399c0a81be9a1603a082b"
iconv-lite@^0.4.17, iconv-lite@^0.4.23, iconv-lite@^0.4.4, iconv-lite@^0.4.5, iconv-lite@~0.4.13:
iconv-lite@^0.4.17, iconv-lite@^0.4.23, iconv-lite@^0.4.4, iconv-lite@^0.4.5:
version "0.4.23"
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.23.tgz#297871f63be507adcfbfca715d0cd0eed84e9a63"
dependencies:
safer-buffer ">= 2.1.2 < 3"
iconv-lite@~0.4.13:
version "0.4.24"
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b"
dependencies:
safer-buffer ">= 2.1.2 < 3"
icss-replace-symbols@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/icss-replace-symbols/-/icss-replace-symbols-1.1.0.tgz#06ea6f83679a7749e386cfe1fe812ae5db223ded"
@ -7899,13 +7905,13 @@ react@^0.14.5:
fbjs "^0.6.1"
react@^16.3.0:
version "16.4.1"
resolved "https://registry.yarnpkg.com/react/-/react-16.4.1.tgz#de51ba5764b5dbcd1f9079037b862bd26b82fe32"
version "16.6.1"
resolved "https://registry.yarnpkg.com/react/-/react-16.6.1.tgz#ee2aef4f0a09e494594882029821049772f915fe"
dependencies:
fbjs "^0.8.16"
loose-envify "^1.1.0"
object-assign "^4.1.1"
prop-types "^15.6.0"
prop-types "^15.6.2"
scheduler "^0.11.0"
read-config-file@3.1.0, read-config-file@^3.0.0:
version "3.1.0"
@ -8498,6 +8504,13 @@ sax@~1.1.1:
version "1.1.6"
resolved "https://registry.yarnpkg.com/sax/-/sax-1.1.6.tgz#5d616be8a5e607d54e114afae55b7eaf2fcc3240"
scheduler@^0.11.0:
version "0.11.0"
resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.11.0.tgz#def1f1bfa6550cc57981a87106e65e8aea41a6b5"
dependencies:
loose-envify "^1.1.0"
object-assign "^4.1.1"
schema-utils@^0.3.0:
version "0.3.0"
resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-0.3.0.tgz#f5877222ce3e931edae039f17eb3716e7137f8cf"
@ -9561,7 +9574,11 @@ typo-js@*:
version "1.0.3"
resolved "https://registry.yarnpkg.com/typo-js/-/typo-js-1.0.3.tgz#54d8ebc7949f1a7810908b6002c6841526c99d5a"
ua-parser-js@^0.7.18, ua-parser-js@^0.7.9:
ua-parser-js@^0.7.18:
version "0.7.19"
resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.19.tgz#94151be4c0a7fb1d001af7022fdaca4642659e4b"
ua-parser-js@^0.7.9:
version "0.7.18"
resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.18.tgz#a7bfd92f56edfb117083b69e31d2aa8882d4b1ed"
@ -10146,8 +10163,8 @@ websocket-extensions@>=0.1.1:
resolved "https://registry.yarnpkg.com/websocket-extensions/-/websocket-extensions-0.1.3.tgz#5d2ff22977003ec687a4b87073dfbbac146ccf29"
whatwg-fetch@>=0.10.0:
version "2.0.4"
resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-2.0.4.tgz#dde6a5df315f9d39991aa17621853d720b85566f"
version "3.0.0"
resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-3.0.0.tgz#fc804e458cc460009b1a2b966bc8817d2578aefb"
whatwg-fetch@^0.9.0:
version "0.9.0"