ChunkLoadError: ask user to reload instead of automatically reloading (#139)
## Issue We previously automatically reload when there is a chunk error. This works fine if it's the case of new code was pushed recently while the user was active. But if the failure was caused by other things like network problems or the file IS actually missing, we end up in an infinite loop of refreshes. ## New approach Tell the user to reload instead of automatically doing it.
This commit is contained in:
parent
03f69eff86
commit
247ee757d1
6 changed files with 47 additions and 31 deletions
|
@ -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),
|
||||
|
|
|
@ -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<any>,
|
||||
getWalletSyncPref: () => Promise<any>,
|
||||
|
@ -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 && <NagNoUser />}
|
||||
{/* @endif */}
|
||||
</React.Suspense>
|
||||
|
||||
{isReloadRequired && (
|
||||
<Nag
|
||||
message={__('A new version of Odysee is available.')}
|
||||
actionText={__('Refresh')}
|
||||
onClick={() => window.location.reload()}
|
||||
/>
|
||||
)}
|
||||
</React.Fragment>
|
||||
)}
|
||||
</div>
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
|
||||
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;
|
||||
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 function lazyImport(componentImport) {
|
||||
return React.lazy(() => componentLoader(componentImport, RETRY_ATTEMPTS));
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue