d9bf72a351
sync settings backout nav cases and anon preference key more robust backout header put notificationSettings under backout bar review changes
340 lines
11 KiB
JavaScript
340 lines
11 KiB
JavaScript
// @flow
|
|
import type { Node } from 'react';
|
|
import * as MODALS from 'constants/modal_types';
|
|
import * as ICONS from 'constants/icons';
|
|
import React from 'react';
|
|
import { Lbry, SETTINGS } from 'lbry-redux';
|
|
import Button from 'component/button';
|
|
import ModalWalletUnlock from 'modal/modalWalletUnlock';
|
|
import ModalIncompatibleDaemon from 'modal/modalIncompatibleDaemon';
|
|
import ModalUpgrade from 'modal/modalUpgrade';
|
|
import ModalDownloading from 'modal/modalDownloading';
|
|
import Card from 'component/common/card';
|
|
import I18nMessage from 'component/i18nMessage';
|
|
import 'css-doodle';
|
|
|
|
const FORTY_FIVE_SECONDS = 45 * 1000;
|
|
const UPDATE_INTERVAL = 1000; // 1 second
|
|
const MAX_WALLET_WAIT = 20; // 20 seconds for wallet to be started, but servers to be unavailable
|
|
const MAX_SYNC_WAIT = 45; // 45 seconds to sync wallet, show message if taking long
|
|
|
|
type Props = {
|
|
checkDaemonVersion: () => Promise<any>,
|
|
notifyUnlockWallet: (?boolean) => Promise<any>,
|
|
daemonVersionMatched: boolean,
|
|
onReadyToLaunch: () => void,
|
|
hideModal: () => void,
|
|
modal: ?{
|
|
id: string,
|
|
},
|
|
animationHidden: boolean,
|
|
setClientSetting: (string, boolean) => void,
|
|
clearWalletServers: () => void,
|
|
doShowSnackBar: string => void,
|
|
};
|
|
|
|
type State = {
|
|
details: string | Node,
|
|
message: string,
|
|
launchedModal: boolean,
|
|
error: boolean,
|
|
isRunning: boolean,
|
|
launchWithIncompatibleDaemon: boolean,
|
|
waitingForWallet: number,
|
|
waitingForSync: number,
|
|
};
|
|
|
|
export default class SplashScreen extends React.PureComponent<Props, State> {
|
|
constructor(props: Props) {
|
|
super(props);
|
|
|
|
this.state = {
|
|
details: __('Starting...'),
|
|
message: __('Connecting'),
|
|
launchedModal: false,
|
|
error: false,
|
|
launchWithIncompatibleDaemon: !process.env.NODE_ENV === 'production',
|
|
isRunning: false,
|
|
waitingForWallet: 0,
|
|
waitingForSync: 0,
|
|
};
|
|
|
|
(this: any).renderModals = this.renderModals.bind(this);
|
|
(this: any).runWithIncompatibleDaemon = this.runWithIncompatibleDaemon.bind(this);
|
|
this.timeout = undefined;
|
|
}
|
|
|
|
componentDidMount() {
|
|
const { checkDaemonVersion } = this.props;
|
|
this.adjustErrorTimeout();
|
|
|
|
Lbry.connect()
|
|
.then(checkDaemonVersion)
|
|
.then(() => {
|
|
this.updateStatus();
|
|
})
|
|
.catch(() => {
|
|
this.setState({
|
|
message: __('Connection Failure'),
|
|
details: __(
|
|
'Try closing all LBRY processes and starting again. If this still happens, your anti-virus software or firewall may be preventing LBRY from connecting. Contact hello@lbry.com if you think this is a software bug.'
|
|
),
|
|
});
|
|
});
|
|
}
|
|
|
|
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 });
|
|
}, FORTY_FIVE_SECONDS);
|
|
}
|
|
|
|
updateStatus() {
|
|
const { modal, notifyUnlockWallet, clearWalletServers, doShowSnackBar } = this.props;
|
|
const { launchedModal } = this.state;
|
|
|
|
Lbry.status().then(status => {
|
|
const sdkStatus = status;
|
|
const { wallet } = status;
|
|
Lbry.wallet_status().then(status => {
|
|
if (sdkStatus.is_running && wallet && wallet.available_servers) {
|
|
if (status.is_locked) {
|
|
// Clear the error timeout, it might sit on this step for a while until someone enters their password
|
|
if (this.timeout) {
|
|
clearTimeout(this.timeout);
|
|
}
|
|
// Make sure there isn't another active modal (like INCOMPATIBLE_DAEMON)
|
|
this.updateStatusCallback(sdkStatus, status, true);
|
|
if (launchedModal === false && !modal) {
|
|
this.setState({ launchedModal: true }, () => notifyUnlockWallet());
|
|
}
|
|
} else {
|
|
this.updateStatusCallback(sdkStatus, status);
|
|
}
|
|
} else if (!sdkStatus.is_running && status.is_syncing) {
|
|
// Clear the timeout if wallet is still syncing
|
|
if (this.timeout) {
|
|
clearTimeout(this.timeout);
|
|
}
|
|
this.updateStatusCallback(sdkStatus, status);
|
|
} else if (this.state.waitingForWallet > MAX_WALLET_WAIT && launchedModal === false && !modal) {
|
|
clearWalletServers();
|
|
doShowSnackBar(
|
|
__(
|
|
'The wallet server took a bit too long. Resetting defaults just in case. Shutdown (Cmd/Ctrl+Q) LBRY and restart if this continues.'
|
|
)
|
|
);
|
|
this.setState({ waitingForWallet: 0 });
|
|
this.updateStatusCallback(sdkStatus, status);
|
|
} else {
|
|
this.updateStatusCallback(sdkStatus, status);
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
updateStatusCallback(status: StatusResponse, walletStatus: WalletStatusResponse, waitingForUnlock: boolean = false) {
|
|
if (status.connection_status.code !== 'connected') {
|
|
this.setState({ error: true });
|
|
return;
|
|
}
|
|
|
|
const { wallet, startup_status: startupStatus } = status;
|
|
|
|
// If the wallet is locked, stop doing anything and make the user input their password
|
|
if (startupStatus && wallet && wallet.available_servers < 1) {
|
|
this.setState({ waitingForWallet: this.state.waitingForWallet + UPDATE_INTERVAL / 1000 });
|
|
} else if (status.is_running && !waitingForUnlock) {
|
|
Lbry.resolve({ urls: 'lbry://one' }).then(() => {
|
|
this.setState({ isRunning: true }, () => this.continueAppLaunch());
|
|
});
|
|
|
|
return;
|
|
} else if (wallet && !status.is_running && walletStatus.is_syncing) {
|
|
this.setState({ waitingForSync: this.state.waitingForSync + UPDATE_INTERVAL / 1000 });
|
|
if (this.state.waitingForSync < MAX_SYNC_WAIT) {
|
|
this.setState({
|
|
message: __('Loading Wallet'),
|
|
details: __('Updating wallet data...'),
|
|
});
|
|
} else {
|
|
this.setState({
|
|
message: __('Loading Wallet'),
|
|
details: (
|
|
<React.Fragment>
|
|
<div>{__('Large account history')}</div>
|
|
<div>{__('Please wait...')}</div>
|
|
</React.Fragment>
|
|
),
|
|
});
|
|
}
|
|
} else if (wallet && !status.is_running && startupStatus.database) {
|
|
this.setState({
|
|
message: __('Finalizing'),
|
|
details: __('Almost ready...'),
|
|
});
|
|
}
|
|
|
|
setTimeout(() => {
|
|
this.updateStatus();
|
|
}, UPDATE_INTERVAL);
|
|
}
|
|
|
|
runWithIncompatibleDaemon() {
|
|
const { hideModal } = this.props;
|
|
hideModal();
|
|
this.setState({ launchWithIncompatibleDaemon: true }, () => this.continueAppLaunch());
|
|
}
|
|
|
|
continueAppLaunch() {
|
|
const { daemonVersionMatched, onReadyToLaunch } = this.props;
|
|
const { isRunning, launchWithIncompatibleDaemon } = this.state;
|
|
|
|
if (daemonVersionMatched) {
|
|
onReadyToLaunch();
|
|
} else if (launchWithIncompatibleDaemon && isRunning) {
|
|
// The user may have decided to run the app with mismatched daemons
|
|
// They could make this decision before the daemon is finished starting up
|
|
// If it isn't running, this function will be called after the daemon is started
|
|
onReadyToLaunch();
|
|
}
|
|
}
|
|
|
|
timeout: ?TimeoutID;
|
|
|
|
renderModals() {
|
|
const { modal } = this.props;
|
|
const modalId = modal && modal.id;
|
|
|
|
if (!modalId) {
|
|
return null;
|
|
}
|
|
|
|
switch (modalId) {
|
|
case MODALS.INCOMPATIBLE_DAEMON:
|
|
return <ModalIncompatibleDaemon onContinueAnyway={this.runWithIncompatibleDaemon} />;
|
|
case MODALS.WALLET_UNLOCK:
|
|
return <ModalWalletUnlock />;
|
|
case MODALS.UPGRADE:
|
|
return <ModalUpgrade />;
|
|
case MODALS.DOWNLOADING:
|
|
return <ModalDownloading />;
|
|
default:
|
|
return null;
|
|
}
|
|
}
|
|
|
|
render() {
|
|
const { error, details } = this.state;
|
|
const { animationHidden, setClientSetting } = this.props;
|
|
|
|
return (
|
|
<div className="splash">
|
|
<h1 className="splash__title">LBRY</h1>
|
|
<div className="splash__details">{details}</div>
|
|
|
|
{!animationHidden && !error && (
|
|
<css-doodle class="doodle">
|
|
{`
|
|
--color: @p(var(--color-primary), var(--color-secondary), var(--color-focus), var(--color-nothing));
|
|
:doodle {
|
|
@grid: 30x1 / 18vmin;
|
|
--deg: @p(-180deg, 180deg);
|
|
}
|
|
:container {
|
|
perspective: 30vmin;
|
|
}
|
|
|
|
@place-cell: center;
|
|
@size: 100%;
|
|
|
|
box-shadow: @m2(0 0 50px var(--color));
|
|
will-change: transform, opacity;
|
|
animation: scale-up 12s linear infinite;
|
|
animation-delay: calc(-12s / @size() * @i());
|
|
|
|
@keyframes scale-up {
|
|
0%, 95.01%, 100% {
|
|
transform: translateZ(0) rotate(0);
|
|
opacity: 0;
|
|
}
|
|
10% {
|
|
opacity: 1;
|
|
}
|
|
95% {
|
|
transform:
|
|
translateZ(35vmin) rotateZ(@var(--deg));
|
|
}
|
|
}
|
|
)
|
|
`}
|
|
</css-doodle>
|
|
)}
|
|
{!error && (
|
|
<Button
|
|
className="splash__animation-toggle"
|
|
label={!animationHidden ? __('I feel woosy! Stop spinning!') : __('Spin Spin Sugar')}
|
|
onClick={() => setClientSetting(SETTINGS.HIDE_SPLASH_ANIMATION, !animationHidden)}
|
|
/>
|
|
)}
|
|
{error && (
|
|
<Card
|
|
title={__('Error Starting Up')}
|
|
subtitle={
|
|
<React.Fragment>
|
|
<p>
|
|
{__(
|
|
'You can try refreshing to fix it. If you still have issues, your anti-virus software or firewall may be preventing startup.'
|
|
)}
|
|
</p>
|
|
<p>
|
|
<I18nMessage
|
|
tokens={{
|
|
help_link: (
|
|
<Button
|
|
button="link"
|
|
href="https://lbry.com/faq/startup-troubleshooting"
|
|
label={__('this link')}
|
|
/>
|
|
),
|
|
}}
|
|
>
|
|
Reach out to hello@lbry.com for help, or check out %help_link%.
|
|
</I18nMessage>
|
|
</p>
|
|
</React.Fragment>
|
|
}
|
|
actions={
|
|
<Button
|
|
button="primary"
|
|
icon={ICONS.REFRESH}
|
|
label={__('Refresh')}
|
|
onClick={() => window.location.reload()}
|
|
/>
|
|
}
|
|
/>
|
|
)}
|
|
{/* 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. */}
|
|
{this.renderModals()}
|
|
</div>
|
|
);
|
|
}
|
|
}
|