Handle daemon startup errors/timeouts #2098

Merged
neb-b merged 2 commits from daemon-startup into master 2018-11-08 21:51:25 +01:00
8 changed files with 197 additions and 44 deletions
Showing only changes of commit 5c5e736137 - Show all commits

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

View file

@ -1,13 +1,15 @@
// @flow // @flow
import * as React from 'react'; import React, { Fragment } from 'react';
import Icon from 'component/common/icon'; import Icon from 'component/common/icon';
import * as icons from 'constants/icons'; import * as icons from 'constants/icons';
import Spinner from 'component/spinner'; import Spinner from 'component/spinner';
import Button from 'component/button';
type Props = { type Props = {
message: string, message: string,
details: ?string, details: ?string,
isWarning: boolean, isWarning: boolean,
error: boolean,
}; };
class LoadScreen extends React.PureComponent<Props> { class LoadScreen extends React.PureComponent<Props> {
@ -16,7 +18,7 @@ class LoadScreen extends React.PureComponent<Props> {
}; };
render() { render() {
const { details, message, isWarning } = this.props; const { details, message, isWarning, error } = this.props;
return ( return (
<div className="load-screen"> <div className="load-screen">
@ -24,6 +26,30 @@ class LoadScreen extends React.PureComponent<Props> {
<h1 className="load-screen__title">{__('LBRY')}</h1> <h1 className="load-screen__title">{__('LBRY')}</h1>
<sup className="load-screen__beta">beta</sup> <sup className="load-screen__beta">beta</sup>
</div> </div>
{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>
) : (
<Fragment>
{isWarning ? ( {isWarning ? (
<span className="load-screen__message"> <span className="load-screen__message">
<Icon size={20} icon={icons.ALERT} /> <Icon size={20} icon={icons.ALERT} />
@ -32,9 +58,10 @@ class LoadScreen extends React.PureComponent<Props> {
) : ( ) : (
<div className="load-screen__message">{message}</div> <div className="load-screen__message">{message}</div>
)} )}
{details && <div className="load-screen__details">{details}</div>} {details && <div className="load-screen__details">{details}</div>}
<Spinner type="splash" /> <Spinner type="splash" />
</Fragment>
)}
</div> </div>
); );
} }

View file

@ -1,4 +1,5 @@
// @flow // @flow
import type { Status } from 'types/status';
import * as React from 'react'; import * as React from 'react';
import { Lbry, MODALS } from 'lbry-redux'; import { Lbry, MODALS } from 'lbry-redux';
import ModalWalletUnlock from 'modal/modalWalletUnlock'; import ModalWalletUnlock from 'modal/modalWalletUnlock';
@ -7,6 +8,8 @@ import ModalUpgrade from 'modal/modalUpgrade';
import ModalDownloading from 'modal/modalDownloading'; import ModalDownloading from 'modal/modalDownloading';
import LoadScreen from './internal/load-screen'; import LoadScreen from './internal/load-screen';
const ONE_MINUTE = 60 * 1000;
type Props = { type Props = {
checkDaemonVersion: () => Promise<any>, checkDaemonVersion: () => Promise<any>,
notifyUnlockWallet: () => Promise<any>, notifyUnlockWallet: () => Promise<any>,
@ -23,6 +26,7 @@ type State = {
message: string, message: string,
isRunning: boolean, isRunning: boolean,
launchedModal: boolean, launchedModal: boolean,
error: boolean,
}; };
export class SplashScreen extends React.PureComponent<Props, State> { export class SplashScreen extends React.PureComponent<Props, State> {
@ -34,14 +38,17 @@ export class SplashScreen extends React.PureComponent<Props, State> {
message: __('Connecting'), message: __('Connecting'),
isRunning: false, isRunning: false,
launchedModal: false, launchedModal: false,
error: false,
}; };
this.hasRecordedUser = false; this.hasRecordedUser = false;
this.timeout = undefined;
} }
componentDidMount() { componentDidMount() {
const { checkDaemonVersion } = this.props; const { checkDaemonVersion } = this.props;
this.adjustErrorTimeout();
Lbry.connect() Lbry.connect()
.then(checkDaemonVersion) .then(checkDaemonVersion)
.then(() => { .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() { updateStatus() {
Lbry.status().then(status => { Lbry.status().then(status => {
this.updateStatusCallback(status); this.updateStatusCallback(status);
}); });
} }
updateStatusCallback(status) { updateStatusCallback(status: Status) {
const { notifyUnlockWallet, authenticate } = this.props; const { notifyUnlockWallet, authenticate } = this.props;
const { launchedModal } = this.state; const { launchedModal } = this.state;
if (status.error) {
this.setState({
error: true,
});
return;
}
if (!this.hasRecordedUser && status) { if (!this.hasRecordedUser && status) {
authenticate(); authenticate();
this.hasRecordedUser = true; 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({ this.setState({
isRunning: true, isRunning: true,
}); });
@ -97,43 +136,49 @@ export class SplashScreen extends React.PureComponent<Props, State> {
this.props.onReadyToLaunch(); this.props.onReadyToLaunch();
} }
}); });
return; 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({ this.setState({
message: __('Blockchain Sync'), message: __('Blockchain Sync'),
details: `${__('Catching up with the blockchain')} (${ details: `${__('Catching up with the blockchain')} (${
status.blockchain_headers.download_progress blockchainHeaders.download_progress
}%)`, }%)`,
}); });
} else if (status.wallet && status.wallet.blocks_behind > 0) { }
const format = status.wallet.blocks_behind === 1 ? '%s block behind' : '%s blocks behind'; } else if (wallet && wallet.blocks_behind > 0) {
const format = wallet.blocks_behind === 1 ? '%s block behind' : '%s blocks behind';
this.setState({ this.setState({
message: __('Blockchain Sync'), message: __('Blockchain Sync'),
details: __(format, status.wallet.blocks_behind), details: __(format, wallet.blocks_behind),
}); });
} else if (status.wallet && status.wallet.blocks_behind === 0) { } else if (wallet && wallet.blocks_behind === 0) {
this.setState({ this.setState({
message: 'Network Loading', message: 'Network Loading',
details: 'Initializing LBRY service...', details: 'Initializing LBRY service...',
}); });
} }
setTimeout(() => { setTimeout(() => {
this.updateStatus(); this.updateStatus();
}, 500); }, 500);
} }
hasRecordedUser: boolean; hasRecordedUser: boolean;
timeout: ?TimeoutID;
render() { render() {
const { notification } = this.props; const { notification } = this.props;
const { message, details, isRunning } = this.state; const { message, details, isRunning, error } = this.state;
const notificationId = notification && notification.id; const notificationId = notification && notification.id;
// {notificationId === MODALS.WALLET_UNLOCK && <ModalWalletUnlock />} // {notificationId === MODALS.WALLET_UNLOCK && <ModalWalletUnlock />}
return ( return (
<React.Fragment> <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; {/* 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 daemon doesn't let you quit during startup, so the "Quit" buttons
in the modals won't work. */} in the modals won't work. */}

View file

@ -183,6 +183,16 @@
text-transform: uppercase; 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 { .icon + .btn__label {
padding-left: 5px; padding-left: 5px;
} }

View file

@ -8,11 +8,11 @@
color: $lbry-white; color: $lbry-white;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
justify-content: center;
} }
.load-screen__header { .load-screen__header {
display: flex; display: flex;
margin-top: 35vh;
} }
.load-screen__title { .load-screen__title {
@ -41,3 +41,8 @@
max-width: 400px; max-width: 400px;
padding-top: $spacing-vertical * 2/3; 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: core-js@^1.0.0:
version "1.2.7" 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: core-js@^2.4.0, core-js@^2.5.0:
version "2.5.7" version "2.5.7"
@ -4076,7 +4076,7 @@ flatten@^1.0.2:
flow-bin@^0.69.0: flow-bin@^0.69.0:
version "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: flow-typed@^2.3.0:
version "2.5.1" version "2.5.1"
@ -4870,12 +4870,18 @@ iconv-lite@0.4.19:
version "0.4.19" version "0.4.19"
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.19.tgz#f7468f60135f5e5dad3399c0a81be9a1603a082b" 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" version "0.4.23"
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.23.tgz#297871f63be507adcfbfca715d0cd0eed84e9a63" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.23.tgz#297871f63be507adcfbfca715d0cd0eed84e9a63"
dependencies: dependencies:
safer-buffer ">= 2.1.2 < 3" 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: icss-replace-symbols@^1.1.0:
version "1.1.0" version "1.1.0"
resolved "https://registry.yarnpkg.com/icss-replace-symbols/-/icss-replace-symbols-1.1.0.tgz#06ea6f83679a7749e386cfe1fe812ae5db223ded" 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" fbjs "^0.6.1"
react@^16.3.0: react@^16.3.0:
version "16.4.1" version "16.6.1"
resolved "https://registry.yarnpkg.com/react/-/react-16.4.1.tgz#de51ba5764b5dbcd1f9079037b862bd26b82fe32" resolved "https://registry.yarnpkg.com/react/-/react-16.6.1.tgz#ee2aef4f0a09e494594882029821049772f915fe"
dependencies: dependencies:
fbjs "^0.8.16"
loose-envify "^1.1.0" loose-envify "^1.1.0"
object-assign "^4.1.1" 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: read-config-file@3.1.0, read-config-file@^3.0.0:
version "3.1.0" version "3.1.0"
@ -8498,6 +8504,13 @@ sax@~1.1.1:
version "1.1.6" version "1.1.6"
resolved "https://registry.yarnpkg.com/sax/-/sax-1.1.6.tgz#5d616be8a5e607d54e114afae55b7eaf2fcc3240" 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: schema-utils@^0.3.0:
version "0.3.0" version "0.3.0"
resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-0.3.0.tgz#f5877222ce3e931edae039f17eb3716e7137f8cf" resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-0.3.0.tgz#f5877222ce3e931edae039f17eb3716e7137f8cf"
@ -9561,7 +9574,11 @@ typo-js@*:
version "1.0.3" version "1.0.3"
resolved "https://registry.yarnpkg.com/typo-js/-/typo-js-1.0.3.tgz#54d8ebc7949f1a7810908b6002c6841526c99d5a" 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" version "0.7.18"
resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.18.tgz#a7bfd92f56edfb117083b69e31d2aa8882d4b1ed" 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" resolved "https://registry.yarnpkg.com/websocket-extensions/-/websocket-extensions-0.1.3.tgz#5d2ff22977003ec687a4b87073dfbbac146ccf29"
whatwg-fetch@>=0.10.0: whatwg-fetch@>=0.10.0:
version "2.0.4" version "3.0.0"
resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-2.0.4.tgz#dde6a5df315f9d39991aa17621853d720b85566f" resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-3.0.0.tgz#fc804e458cc460009b1a2b966bc8817d2578aefb"
whatwg-fetch@^0.9.0: whatwg-fetch@^0.9.0:
version "0.9.0" version "0.9.0"