diff --git a/ui/component/app/index.js b/ui/component/app/index.js index 2802bbd2f..db2a7ec2f 100644 --- a/ui/component/app/index.js +++ b/ui/component/app/index.js @@ -21,6 +21,7 @@ import { selectAutoUpdateDownloaded, selectModal, selectActiveChannelClaim, + selectIsReloadRequired, } from 'redux/selectors/app'; import { doGetWalletSyncPreference, doSetLanguage, doSetHomepage } from 'redux/actions/settings'; import { doSyncLoop } from 'redux/actions/sync'; @@ -44,6 +45,7 @@ const select = (state) => ({ languages: selectLoadedLanguages(state), autoUpdateDownloaded: selectAutoUpdateDownloaded(state), isUpgradeAvailable: selectIsUpgradeAvailable(state), + isReloadRequired: selectIsReloadRequired(state), syncError: selectGetSyncErrorMessage(state), uploadCount: selectUploadCount(state), rewards: selectUnclaimedRewards(state), diff --git a/ui/component/app/view.jsx b/ui/component/app/view.jsx index 68604f599..e42b76f05 100644 --- a/ui/component/app/view.jsx +++ b/ui/component/app/view.jsx @@ -6,6 +6,7 @@ import classnames from 'classnames'; import analytics from 'analytics'; import { buildURI, parseURI } from 'util/lbryURI'; import { SIMPLE_SITE } from 'config'; +import Nag from 'component/common/nag'; import Router from 'component/router/index'; import ReactModal from 'react-modal'; import { openContextMenu } from 'util/context-menu'; @@ -17,10 +18,12 @@ import REWARDS from 'rewards'; import usePersistedState from 'effects/use-persisted-state'; import Spinner from 'component/spinner'; import LANGUAGES from 'constants/languages'; + // @if TARGET='app' import useZoom from 'effects/use-zoom'; import useHistoryNav from 'effects/use-history-nav'; // @endif + // @if TARGET='web' import { useDegradedPerformance, @@ -30,11 +33,11 @@ import { STATUS_DOWN, } from 'web/effects/use-degraded-performance'; // @endif + import LANGUAGE_MIGRATIONS from 'constants/language-migrations'; const FileDrop = lazyImport(() => import('component/fileDrop' /* webpackChunkName: "secondary" */)); const ModalRouter = lazyImport(() => import('modal/modalRouter' /* webpackChunkName: "secondary" */)); -const Nag = lazyImport(() => import('component/common/nag' /* webpackChunkName: "secondary" */)); const NagContinueFirstRun = lazyImport(() => import('component/nagContinueFirstRun' /* webpackChunkName: "secondary" */) ); @@ -92,6 +95,7 @@ type Props = { setLanguage: (string) => void, doSetHomepage: (string) => void, isUpgradeAvailable: boolean, + isReloadRequired: boolean, autoUpdateDownloaded: boolean, updatePreferences: () => Promise, getWalletSyncPref: () => Promise, @@ -126,6 +130,7 @@ function App(props: Props) { signIn, autoUpdateDownloaded, isUpgradeAvailable, + isReloadRequired, requestDownloadUpgrade, uploadCount, history, @@ -502,6 +507,14 @@ function App(props: Props) { {user === null && } {/* @endif */} + + {isReloadRequired && ( + window.location.reload()} + /> + )} )} diff --git a/ui/constants/action_types.js b/ui/constants/action_types.js index 249d28d7a..06c5ed663 100644 --- a/ui/constants/action_types.js +++ b/ui/constants/action_types.js @@ -32,6 +32,7 @@ export const TOGGLE_YOUTUBE_SYNC_INTEREST = 'TOGGLE_YOUTUBE_SYNC_INTEREST'; export const TOGGLE_SPLASH_ANIMATION = 'TOGGLE_SPLASH_ANIMATION'; export const SET_ACTIVE_CHANNEL = 'SET_ACTIVE_CHANNEL'; export const SET_INCOGNITO = 'SET_INCOGNITO'; +export const RELOAD_REQUIRED = 'RELOAD_REQUIRED'; // Navigation export const CHANGE_AFTER_AUTH_PATH = 'CHANGE_AFTER_AUTH_PATH'; diff --git a/ui/redux/reducers/app.js b/ui/redux/reducers/app.js index 4772ebb2d..c7abe855b 100644 --- a/ui/redux/reducers/app.js +++ b/ui/redux/reducers/app.js @@ -34,6 +34,7 @@ export type AppState = { checkUpgradeTimer: ?number, isUpgradeAvailable: ?boolean, isUpgradeSkipped: ?boolean, + isReloadRequired: ?boolean, hasClickedComment: boolean, enhancedLayout: boolean, splashAnimationEnabled: boolean, @@ -71,6 +72,7 @@ const defaultState: AppState = { checkUpgradeTimer: undefined, isUpgradeAvailable: undefined, isUpgradeSkipped: undefined, + isReloadRequired: undefined, enhancedLayout: false, splashAnimationEnabled: true, searchOptionsExpanded: false, @@ -213,6 +215,11 @@ reducers[ACTIONS.UPGRADE_DOWNLOAD_PROGRESSED] = (state, action) => downloadProgress: action.data.percent, }); +reducers[ACTIONS.RELOAD_REQUIRED] = (state, action) => + Object.assign({}, state, { + isReloadRequired: true, + }); + reducers[ACTIONS.DOWNLOADING_COMPLETED] = (state) => { const { badgeNumber } = state; diff --git a/ui/redux/selectors/app.js b/ui/redux/selectors/app.js index 59f2e80d8..923556e72 100644 --- a/ui/redux/selectors/app.js +++ b/ui/redux/selectors/app.js @@ -21,6 +21,7 @@ export const selectUpdateUrl = createSelector(selectPlatform, (platform) => { export const selectHasClickedComment = (state) => selectState(state).hasClickedComment; export const selectRemoteVersion = (state) => selectState(state).remoteVersion; export const selectIsUpgradeAvailable = (state) => selectState(state).isUpgradeAvailable; +export const selectIsReloadRequired = (state) => selectState(state).isReloadRequired; export const selectUpgradeFilename = createSelector(selectPlatform, selectRemoteVersion, (platform, version) => { switch (platform) { diff --git a/ui/util/lazyImport.js b/ui/util/lazyImport.js index da3e64b04..597fa67c8 100644 --- a/ui/util/lazyImport.js +++ b/ui/util/lazyImport.js @@ -1,34 +1,26 @@ import React from 'react'; +import * as ACTIONS from 'constants/action_types'; -let localStorageAvailable; -try { - localStorageAvailable = Boolean(window.localStorage); -} catch (e) { - localStorageAvailable = false; +const RETRY_DELAY_MS = 2000; +const RETRY_ATTEMPTS = 2; + +function componentLoader(lazyComponent, attemptsLeft) { + return new Promise((resolve, reject) => { + lazyComponent() + .then(resolve) + .catch((error) => { + setTimeout(() => { + if (attemptsLeft === 1) { + window.store.dispatch({ type: ACTIONS.RELOAD_REQUIRED }); + console.error(error.message); // Spew the error so users can report to us if reloading doesn't help. + } else { + componentLoader(lazyComponent, attemptsLeft - 1).then(resolve, reject); + } + }, RETRY_DELAY_MS); + }); + }); } -export const lazyImport = (componentImport) => - React.lazy(async () => { - const pageHasAlreadyBeenForceRefreshed = localStorageAvailable - ? JSON.parse(window.localStorage.getItem('page-has-been-force-refreshed') || 'false') - : false; - - try { - const component = await componentImport(); - if (localStorageAvailable) { - window.localStorage.setItem('page-has-been-force-refreshed', 'false'); - } - return component; - } catch (error) { - if (!pageHasAlreadyBeenForceRefreshed) { - // It's highly likely that the user's session is old. Try reloading once. - if (localStorageAvailable) { - window.localStorage.setItem('page-has-been-force-refreshed', 'true'); - } - return window.location.reload(); - } - - // If it still didn't work, then relay the error. - throw error; - } - }); +export function lazyImport(componentImport) { + return React.lazy(() => componentLoader(componentImport, RETRY_ATTEMPTS)); +}