2018-08-13 18:23:01 +02:00
// @flow
2018-10-29 18:23:53 +01:00
import * as MODALS from 'constants/modal_types' ;
2019-08-08 22:30:03 +02:00
import * as SETTINGS from 'constants/settings' ;
2019-05-29 21:48:44 +02:00
import React from 'react' ;
2018-10-29 18:23:53 +01:00
import { Lbry } from 'lbry-redux' ;
2019-05-29 21:48:44 +02:00
import Button from 'component/button' ;
2018-07-18 21:48:30 +02:00
import ModalWalletUnlock from 'modal/modalWalletUnlock' ;
2017-12-21 22:08:54 +01:00
import ModalIncompatibleDaemon from 'modal/modalIncompatibleDaemon' ;
import ModalUpgrade from 'modal/modalUpgrade' ;
import ModalDownloading from 'modal/modalDownloading' ;
2019-11-22 22:13:00 +01:00
import Card from 'component/common/card' ;
import I18nMessage from 'component/i18nMessage' ;
2019-05-29 21:48:44 +02:00
import 'css-doodle' ;
2016-11-22 21:19:08 +01:00
2019-05-29 21:48:44 +02:00
const FORTY _FIVE _SECONDS = 45 * 1000 ;
2020-03-31 18:35:32 +02:00
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
2018-11-08 20:48:38 +01:00
2018-03-26 23:32:43 +02:00
type Props = {
checkDaemonVersion : ( ) => Promise < any > ,
2019-10-22 02:12:43 +02:00
notifyUnlockWallet : ( ? boolean ) => Promise < any > ,
2018-08-13 18:23:01 +02:00
daemonVersionMatched : boolean ,
onReadyToLaunch : ( ) => void ,
2019-01-10 02:38:26 +01:00
hideModal : ( ) => void ,
2018-10-29 18:23:53 +01:00
modal : ? {
2018-04-23 20:02:06 +02:00
id : string ,
} ,
2019-08-08 22:30:03 +02:00
animationHidden : boolean ,
2019-08-09 19:12:28 +02:00
setClientSetting : ( string , boolean ) => void ,
2019-12-11 21:09:27 +01:00
clearWalletServers : ( ) => void ,
doShowSnackBar : string => void ,
2018-03-26 23:32:43 +02:00
} ;
2017-05-17 10:10:25 +02:00
2018-03-26 23:32:43 +02:00
type State = {
details : string ,
message : string ,
2018-07-18 21:48:30 +02:00
launchedModal : boolean ,
2018-11-08 20:48:38 +01:00
error : boolean ,
2019-01-10 02:38:26 +01:00
isRunning : boolean ,
launchWithIncompatibleDaemon : boolean ,
2019-12-11 21:09:27 +01:00
waitingForWallet : number ,
2020-03-31 18:35:32 +02:00
waitingForSync : number ,
2018-03-26 23:32:43 +02:00
} ;
2019-01-10 02:38:26 +01:00
export default class SplashScreen extends React . PureComponent < Props , State > {
2018-03-26 23:32:43 +02:00
constructor ( props : Props ) {
2017-05-17 10:10:25 +02:00
super ( props ) ;
this . state = {
2019-08-08 22:30:03 +02:00
details : _ _ ( 'Starting...' ) ,
2017-12-21 22:08:54 +01:00
message : _ _ ( 'Connecting' ) ,
2018-07-18 21:48:30 +02:00
launchedModal : false ,
2019-03-08 20:20:17 +01:00
error : false ,
2020-02-05 19:01:07 +01:00
launchWithIncompatibleDaemon : ! process . env . NODE _ENV === 'production' ,
2019-01-10 02:38:26 +01:00
isRunning : false ,
2019-12-11 21:09:27 +01:00
waitingForWallet : 0 ,
2020-03-31 18:35:32 +02:00
waitingForSync : 0 ,
2017-05-17 10:10:25 +02:00
} ;
2018-08-20 18:33:36 +02:00
2018-11-12 19:22:51 +01:00
( this : any ) . renderModals = this . renderModals . bind ( this ) ;
2019-01-10 02:38:26 +01:00
( this : any ) . runWithIncompatibleDaemon = this . runWithIncompatibleDaemon . bind ( this ) ;
2018-11-08 20:48:38 +01:00
this . timeout = undefined ;
2017-05-17 10:10:25 +02:00
}
2018-08-13 18:23:01 +02:00
componentDidMount ( ) {
const { checkDaemonVersion } = this . props ;
2018-11-08 20:48:38 +01:00
this . adjustErrorTimeout ( ) ;
2019-07-22 06:31:25 +02:00
2018-08-13 18:23:01 +02:00
Lbry . connect ( )
2018-12-05 22:09:11 +01:00
. then ( checkDaemonVersion )
2018-08-13 18:23:01 +02:00
. then ( ( ) => {
this . updateStatus ( ) ;
} )
. catch ( ( ) => {
this . setState ( {
message : _ _ ( 'Connection Failure' ) ,
details : _ _ (
2019-03-20 22:43:00 +01:00
'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.'
2018-08-13 18:23:01 +02:00
) ,
} ) ;
} ) ;
}
2018-11-08 20:48:38 +01:00
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 } ) ;
2019-05-29 21:48:44 +02:00
} , FORTY _FIVE _SECONDS ) ;
2018-11-08 20:48:38 +01:00
}
2017-05-17 10:10:25 +02:00
updateStatus ( ) {
2019-12-11 21:09:27 +01:00
const { modal , notifyUnlockWallet , clearWalletServers , doShowSnackBar } = this . props ;
2019-10-17 20:32:36 +02:00
const { launchedModal } = this . state ;
2018-12-05 22:09:11 +01:00
Lbry . status ( ) . then ( status => {
2020-03-31 18:35:32 +02:00
const sdkStatus = status ;
2019-12-19 14:53:15 +01:00
const { wallet } = status ;
2020-03-31 18:35:32 +02:00
Lbry . wallet _status ( ) . then ( status => {
if ( sdkStatus . is _running && wallet && wallet . available _servers ) {
if ( status . is _locked ) {
2019-10-17 20:32:36 +02:00
// 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)
2020-03-31 18:35:32 +02:00
this . updateStatusCallback ( sdkStatus , status , true ) ;
2019-10-17 20:32:36 +02:00
if ( launchedModal === false && ! modal ) {
this . setState ( { launchedModal : true } , ( ) => notifyUnlockWallet ( ) ) ;
}
} else {
2020-03-31 18:35:32 +02:00
this . updateStatusCallback ( sdkStatus , status ) ;
2019-10-17 20:32:36 +02:00
}
2020-03-31 18:35:32 +02:00
} 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 ) ;
}
} ) ;
2018-12-05 22:09:11 +01:00
} ) ;
2017-05-17 10:10:25 +02:00
}
2020-03-31 18:35:32 +02:00
updateStatusCallback ( status : StatusResponse , walletStatus : WalletStatusResponse , waitingForUnlock : boolean = false ) {
2019-04-24 16:02:08 +02:00
if ( status . connection _status . code !== 'connected' ) {
2019-03-05 05:46:57 +01:00
this . setState ( { error : true } ) ;
2018-11-08 20:48:38 +01:00
return ;
}
2019-12-13 20:47:50 +01:00
const { wallet , startup _status : startupStatus } = status ;
2018-11-08 20:48:38 +01:00
// If the wallet is locked, stop doing anything and make the user input their password
2019-12-19 14:53:15 +01:00
if ( startupStatus && wallet && wallet . available _servers < 1 ) {
this . setState ( { waitingForWallet : this . state . waitingForWallet + UPDATE _INTERVAL / 1000 } ) ;
} else if ( status . is _running && ! waitingForUnlock ) {
2019-10-17 20:32:36 +02:00
Lbry . resolve ( { urls : 'lbry://one' } ) . then ( ( ) => {
this . setState ( { isRunning : true } , ( ) => this . continueAppLaunch ( ) ) ;
2016-04-14 08:27:06 +02:00
} ) ;
2019-10-17 20:32:36 +02:00
return ;
2020-03-31 18:35:32 +02:00
} 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 ) {
2019-10-17 19:49:00 +02:00
this . setState ( {
message : 'Finalizing' ,
details : 'Almost ready...' ,
} ) ;
2019-10-17 17:37:57 +02:00
}
2019-10-17 19:49:00 +02:00
setTimeout ( ( ) => {
this . updateStatus ( ) ;
2019-12-11 21:09:27 +01:00
} , UPDATE _INTERVAL ) ;
2017-05-17 10:10:25 +02:00
}
2019-01-10 02:38:26 +01:00
runWithIncompatibleDaemon ( ) {
const { hideModal } = this . props ;
hideModal ( ) ;
this . setState ( { launchWithIncompatibleDaemon : true } , ( ) => this . continueAppLaunch ( ) ) ;
}
continueAppLaunch ( ) {
const { daemonVersionMatched , onReadyToLaunch } = this . props ;
const { isRunning , launchWithIncompatibleDaemon } = this . state ;
2019-05-29 21:48:44 +02:00
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 ( ) ;
}
2019-01-10 02:38:26 +01:00
}
2018-11-08 20:48:38 +01:00
timeout : ? TimeoutID ;
2018-08-20 18:33:36 +02:00
2018-11-12 19:22:51 +01:00
renderModals ( ) {
2018-10-29 18:23:53 +01:00
const { modal } = this . props ;
const modalId = modal && modal . id ;
2018-04-23 20:02:06 +02:00
2018-11-12 19:22:51 +01:00
if ( ! modalId ) {
return null ;
}
switch ( modalId ) {
case MODALS . INCOMPATIBLE _DAEMON :
2019-01-10 02:38:26 +01:00
return < ModalIncompatibleDaemon onContinueAnyway = { this . runWithIncompatibleDaemon } / > ;
2018-11-12 19:22:51 +01:00
case MODALS . WALLET _UNLOCK :
return < ModalWalletUnlock / > ;
case MODALS . UPGRADE :
return < ModalUpgrade / > ;
case MODALS . DOWNLOADING :
return < ModalDownloading / > ;
default :
return null ;
}
}
render ( ) {
2019-08-08 22:30:03 +02:00
const { error , details } = this . state ;
const { animationHidden , setClientSetting } = this . props ;
2018-11-12 19:22:51 +01:00
2017-06-06 23:19:12 +02:00
return (
2019-05-29 21:48:44 +02:00
< div className = "splash" >
2019-08-08 22:30:03 +02:00
< h1 className = "splash__title" > LBRY < / h1 >
< div className = "splash__details" > { details } < / div >
2020-03-02 15:42:43 +01:00
{ ! animationHidden && ! error && (
2019-08-08 22:30:03 +02:00
< css - doodle class = "doodle" >
{ `
2019-11-22 22:13:00 +01:00
-- color : @ p ( var ( -- color - primary ) , var ( -- color - secondary ) , var ( -- color - focus ) , var ( -- color - nothing ) ) ;
2019-06-05 06:14:15 +02:00
: doodle {
@ grid : 30 x1 / 18 vmin ;
-- deg : @ p ( - 180 deg , 180 deg ) ;
}
: container {
perspective : 30 vmin ;
}
2019-07-18 02:04:17 +02:00
2019-06-05 06:14:15 +02:00
@ place - cell : center ;
@ size : 100 % ;
2019-07-18 02:04:17 +02:00
box - shadow : @ m2 ( 0 0 50 px var ( -- color ) ) ;
2019-06-05 06:14:15 +02:00
will - change : transform , opacity ;
animation : scale - up 12 s linear infinite ;
animation - delay : calc ( - 12 s / @ size ( ) * @ i ( ) ) ;
@ keyframes scale - up {
0 % , 95.01 % , 100 % {
transform : translateZ ( 0 ) rotate ( 0 ) ;
opacity : 0 ;
}
2019-07-18 02:04:17 +02:00
10 % {
opacity : 1 ;
2019-06-05 06:14:15 +02:00
}
95 % {
2019-07-18 02:04:17 +02:00
transform :
2019-06-05 06:14:15 +02:00
translateZ ( 35 vmin ) rotateZ ( @ var ( -- deg ) ) ;
}
}
)
` }
2019-08-08 22:30:03 +02:00
< / c s s - d o o d l e >
) }
2020-03-02 15:42:43 +01:00
{ ! error && (
< Button
className = "splash__animation-toggle"
label = { ! animationHidden ? _ _ ( 'I feel woosy! Stop spinning!' ) : _ _ ( 'Spin Spin Sugar' ) }
onClick = { ( ) => setClientSetting ( SETTINGS . HIDE _SPLASH _ANIMATION , ! animationHidden ) }
/ >
) }
2019-05-29 21:48:44 +02:00
{ error && (
2019-11-22 22:13:00 +01:00
< Card
title = { _ _ ( 'Error Starting Up' ) }
subtitle = {
< React.Fragment >
< p >
2020-03-02 15:42:43 +01:00
{ _ _ (
'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 >
2019-11-22 22:13:00 +01:00
< / p >
< / React.Fragment >
}
actions = { < Button button = "primary" label = { _ _ ( 'Refresh' ) } onClick = { ( ) => window . location . reload ( ) } / > }
/ >
2019-05-29 21:48:44 +02:00
) }
2017-07-19 23:05:08 +02:00
{ / * T e m p h a c k : d o n ' t s h o w a n y m o d a l s o n s p l a s h s c r e e n d a e m o n i s r u n n i n g ;
daemon doesn ' t let you quit during startup , so the "Quit" buttons
2019-05-29 21:48:44 +02:00
in the modals won ' t work . * / }
2018-12-05 22:09:11 +01:00
{ this . renderModals ( ) }
2019-05-29 21:48:44 +02:00
< / div >
2017-06-06 23:19:12 +02:00
) ;
2016-04-10 02:00:56 +02:00
}
2017-05-17 10:10:25 +02:00
}