SyncFatalError: show nag instead of hard-crashing. (#331)
* SyncFatalError: show nag instead of hard-crashing. ## Issue When sync fails, we crash the app. ## Ticket Maybe closes 39 "Better handle both internal and web backend interruptions / downtime" ## Approach I'm tackling this from the standpoint that (1) sync errors are not that fatal -- we'll just lost a few recent changes (2) network disconnection is the common cause. ## Changes - If we are offline: - Inform user through a nag. All other status is meaningless if we are offline. - If we are online: - If api is STATUS_DOWN, show the existing crash page. - If there is a sync error, show a nag saying settings are now potentially unsynchronized, and add a button to retry sync. - If there is a chunk error, nag to reload. * Attempt to detect `status=DOWN` Previous code resolves the status to either "ok" or "not", which makes the app unable to differentiate between the "degraded" (nag) and "down" (crash) states.
This commit is contained in:
parent
13cbbc8342
commit
87c3dcc057
3 changed files with 63 additions and 14 deletions
|
@ -2221,5 +2221,8 @@
|
|||
"Enter the full channel name or URL to search.\n\nExamples:\n - @channel\n - @channel#3\n - https://odysee.com/@Odysee:8\n - lbry://@Odysee#8": "Enter the full channel name or URL to search.\n\nExamples:\n - @channel\n - @channel#3\n - https://odysee.com/@Odysee:8\n - lbry://@Odysee#8",
|
||||
"Choose %asset%": "Choose %asset%",
|
||||
"Showing %filtered% results of %total%": "Showing %filtered% results of %total%",
|
||||
"Failed to synchronize settings. Wait a while before retrying.": "Failed to synchronize settings. Wait a while before retrying.",
|
||||
"You are offline. Check your internet connection.": "You are offline. Check your internet connection.",
|
||||
"A new version of Odysee is available.": "A new version of Odysee is available.",
|
||||
"--end--": "--end--"
|
||||
}
|
||||
|
|
|
@ -134,6 +134,7 @@ function App(props: Props) {
|
|||
const { pathname, hash, search } = props.location;
|
||||
const [upgradeNagClosed, setUpgradeNagClosed] = useState(false);
|
||||
const [resolvedSubscriptions, setResolvedSubscriptions] = useState(false);
|
||||
const [retryingSync, setRetryingSync] = useState(false);
|
||||
const [sidebarOpen] = usePersistedState('sidebar', true);
|
||||
const [seenSunsestMessage, setSeenSunsetMessage] = usePersistedState('lbrytv-sunset', false);
|
||||
const showUpgradeButton =
|
||||
|
@ -153,6 +154,7 @@ function App(props: Props) {
|
|||
const hasActiveChannelClaim = activeChannelClaim !== undefined;
|
||||
const isPersonalized = !IS_WEB || hasVerifiedEmail;
|
||||
const renderFiledrop = !IS_WEB || isAuthenticated;
|
||||
const isOnline = navigator.onLine;
|
||||
|
||||
let uri;
|
||||
try {
|
||||
|
@ -164,6 +166,50 @@ function App(props: Props) {
|
|||
setShowAnalyticsNag(false);
|
||||
}
|
||||
|
||||
function getStatusNag() {
|
||||
// Handle "offline" first. Everything else is meaningless if it's offline.
|
||||
if (!isOnline) {
|
||||
return <Nag type="helpful" message={__('You are offline. Check your internet connection.')} />;
|
||||
}
|
||||
|
||||
// Only 1 nag is possible, so show the most important:
|
||||
|
||||
if (user === null) {
|
||||
return <NagNoUser />;
|
||||
}
|
||||
|
||||
if (lbryTvApiStatus === STATUS_DEGRADED || lbryTvApiStatus === STATUS_FAILING) {
|
||||
if (!shouldHideNag) {
|
||||
return <NagDegradedPerformance onClose={() => setLbryTvApiStatus(STATUS_OK)} />;
|
||||
}
|
||||
}
|
||||
|
||||
if (syncFatalError) {
|
||||
if (!retryingSync) {
|
||||
return (
|
||||
<Nag
|
||||
type="error"
|
||||
message={__('Failed to synchronize settings. Wait a while before retrying.')}
|
||||
actionText={__('Retry')}
|
||||
onClick={() => {
|
||||
syncLoop(true);
|
||||
setRetryingSync(true);
|
||||
setTimeout(() => setRetryingSync(false), 4000);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
} else if (isReloadRequired) {
|
||||
return (
|
||||
<Nag
|
||||
message={__('A new version of Odysee is available.')}
|
||||
actionText={__('Refresh')}
|
||||
onClick={() => window.location.reload()}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (userId) {
|
||||
analytics.setUser(userId);
|
||||
|
@ -333,7 +379,8 @@ function App(props: Props) {
|
|||
);
|
||||
}
|
||||
|
||||
if (syncFatalError) {
|
||||
if (isOnline && lbryTvApiStatus === STATUS_DOWN) {
|
||||
// TODO: Rename `SyncFatalError` since it has nothing to do with syncing.
|
||||
return (
|
||||
<React.Suspense fallback={null}>
|
||||
<SyncFatalError lbryTvApiStatus={lbryTvApiStatus} />
|
||||
|
@ -384,22 +431,11 @@ function App(props: Props) {
|
|||
{fromLbrytvParam && !seenSunsestMessage && !shouldHideNag && (
|
||||
<NagSunset email={hasVerifiedEmail} onClose={() => setSeenSunsetMessage(true)} />
|
||||
)}
|
||||
{(lbryTvApiStatus === STATUS_DEGRADED || lbryTvApiStatus === STATUS_FAILING) && !shouldHideNag && (
|
||||
<NagDegradedPerformance onClose={() => setLbryTvApiStatus(STATUS_OK)} />
|
||||
)}
|
||||
{!SIMPLE_SITE && lbryTvApiStatus === STATUS_OK && showAnalyticsNag && !shouldHideNag && (
|
||||
<NagDataCollection onClose={handleAnalyticsDismiss} />
|
||||
)}
|
||||
{user === null && <NagNoUser />}
|
||||
{getStatusNag()}
|
||||
</React.Suspense>
|
||||
|
||||
{isReloadRequired && (
|
||||
<Nag
|
||||
message={__('A new version of Odysee is available.')}
|
||||
actionText={__('Refresh')}
|
||||
onClick={() => window.location.reload()}
|
||||
/>
|
||||
)}
|
||||
</React.Fragment>
|
||||
)}
|
||||
</div>
|
||||
|
|
|
@ -5,6 +5,14 @@ import { X_LBRY_AUTH_TOKEN } from 'constants/token';
|
|||
|
||||
import fetchWithTimeout from 'util/fetch';
|
||||
|
||||
const STATUS_GENERAL_STATE = {
|
||||
// internal/status/status.go#L44
|
||||
OK: 'ok',
|
||||
NOT_READY: 'not_ready',
|
||||
OFFLINE: 'offline',
|
||||
FAILING: 'failing',
|
||||
};
|
||||
|
||||
const STATUS_TIMEOUT_LIMIT = 10000;
|
||||
export const STATUS_OK = 'ok';
|
||||
export const STATUS_DEGRADED = 'degraded';
|
||||
|
@ -33,7 +41,9 @@ export function useDegradedPerformance(onDegradedPerformanceCallback, user) {
|
|||
fetchWithTimeout(STATUS_TIMEOUT_LIMIT, fetch(STATUS_ENDPOINT, getParams(user)))
|
||||
.then((response) => response.json())
|
||||
.then((status) => {
|
||||
if (status.general_state !== STATUS_OK) {
|
||||
if (status.general_state === STATUS_GENERAL_STATE.OFFLINE) {
|
||||
onDegradedPerformanceCallback(STATUS_DOWN);
|
||||
} else if (status.general_state !== STATUS_GENERAL_STATE.OK) {
|
||||
onDegradedPerformanceCallback(STATUS_FAILING);
|
||||
}
|
||||
})
|
||||
|
|
Loading…
Reference in a new issue