Improved offline handling
This commit is contained in:
@@ -158,11 +158,7 @@ class Document extends BaseModel {
|
||||
@action
|
||||
view = async () => {
|
||||
this.views++;
|
||||
try {
|
||||
await client.post('/views.create', { id: this.id });
|
||||
} catch (e) {
|
||||
this.ui.showToast('Document failed to record view');
|
||||
}
|
||||
await client.post('/views.create', { id: this.id });
|
||||
};
|
||||
|
||||
@action
|
||||
|
||||
@@ -32,6 +32,7 @@ import CenteredContent from 'components/CenteredContent';
|
||||
import PageTitle from 'components/PageTitle';
|
||||
import Search from 'scenes/Search';
|
||||
import Error404 from 'scenes/Error404';
|
||||
import ErrorOffline from 'scenes/ErrorOffline';
|
||||
|
||||
const AUTOSAVE_INTERVAL = 3000;
|
||||
const MARK_AS_VIEWED_AFTER = 3000;
|
||||
@@ -245,7 +246,15 @@ class DocumentScene extends React.Component<Props> {
|
||||
const isShare = match.params.shareId;
|
||||
|
||||
if (this.notFound) {
|
||||
return isShare ? <Error404 /> : <Search notFound />;
|
||||
return navigator.onLine ? (
|
||||
isShare ? (
|
||||
<Error404 />
|
||||
) : (
|
||||
<Search notFound />
|
||||
)
|
||||
) : (
|
||||
<ErrorOffline />
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
|
||||
@@ -2,13 +2,14 @@
|
||||
import * as React from 'react';
|
||||
import CenteredContent from 'components/CenteredContent';
|
||||
import PageTitle from 'components/PageTitle';
|
||||
import Empty from 'components/Empty';
|
||||
|
||||
const Error404 = () => {
|
||||
return (
|
||||
<CenteredContent>
|
||||
<PageTitle title="Not Found" />
|
||||
<h1>Not Found</h1>
|
||||
<p>We were unable to find the page you’re looking for.</p>
|
||||
<Empty>We were unable to find the page you’re looking for.</Empty>
|
||||
<p>
|
||||
Go to <a href="/">homepage</a>.
|
||||
</p>
|
||||
@@ -1,3 +0,0 @@
|
||||
// @flow
|
||||
import Error404 from './Error404';
|
||||
export default Error404;
|
||||
17
app/scenes/ErrorOffline.js
Normal file
17
app/scenes/ErrorOffline.js
Normal file
@@ -0,0 +1,17 @@
|
||||
// @flow
|
||||
import * as React from 'react';
|
||||
import CenteredContent from 'components/CenteredContent';
|
||||
import PageTitle from 'components/PageTitle';
|
||||
import Empty from 'components/Empty';
|
||||
|
||||
const ErrorOffline = () => {
|
||||
return (
|
||||
<CenteredContent>
|
||||
<PageTitle title="Offline" />
|
||||
<h1>Offline</h1>
|
||||
<Empty>We were unable to load the document while offline.</Empty>
|
||||
</CenteredContent>
|
||||
);
|
||||
};
|
||||
|
||||
export default ErrorOffline;
|
||||
@@ -186,7 +186,7 @@ class Search extends React.Component<Props> {
|
||||
{notFound && (
|
||||
<div>
|
||||
<h1>Not Found</h1>
|
||||
<p>We’re unable to find the page you’re accessing.</p>
|
||||
<Empty>We were unable to find the page you’re looking for.</Empty>
|
||||
</div>
|
||||
)}
|
||||
<ResultsWrapper pinToTop={this.pinToTop} column auto>
|
||||
|
||||
@@ -196,8 +196,10 @@ class DocumentsStore extends BaseStore {
|
||||
});
|
||||
|
||||
return document;
|
||||
} catch (e) {
|
||||
this.ui.showToast('Failed to load document');
|
||||
} catch (_err) {
|
||||
if (!options.prefetch && navigator.onLine) {
|
||||
this.ui.showToast('Failed to load document');
|
||||
}
|
||||
} finally {
|
||||
this.isFetching = false;
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ class ApiClient {
|
||||
this.userAgent = 'OutlineFrontend';
|
||||
}
|
||||
|
||||
fetch = (
|
||||
fetch = async (
|
||||
path: string,
|
||||
method: string,
|
||||
data: ?Object,
|
||||
@@ -45,9 +45,8 @@ class ApiClient {
|
||||
headers.set('Authorization', `Bearer ${stores.auth.token}`);
|
||||
}
|
||||
|
||||
// Construct request
|
||||
// $FlowFixMe don't care much about this right now
|
||||
const request = fetch(this.baseUrl + (modifiedPath || path), {
|
||||
const response = await fetch(this.baseUrl + (modifiedPath || path), {
|
||||
method,
|
||||
body,
|
||||
headers,
|
||||
@@ -55,40 +54,21 @@ class ApiClient {
|
||||
credentials: 'include',
|
||||
});
|
||||
|
||||
// Handle request promises and return a new promise
|
||||
return new Promise((resolve, reject) => {
|
||||
request
|
||||
.then(response => {
|
||||
// Handle successful responses
|
||||
if (response.status >= 200 && response.status < 300) {
|
||||
return response;
|
||||
}
|
||||
if (response.status >= 200 && response.status < 300) {
|
||||
return response.json();
|
||||
}
|
||||
|
||||
// Handle 401, log out user
|
||||
if (response.status === 401) {
|
||||
stores.auth.logout();
|
||||
return;
|
||||
}
|
||||
// Handle 401, log out user
|
||||
if (response.status === 401) {
|
||||
stores.auth.logout();
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle failed responses
|
||||
const error = {};
|
||||
error.statusCode = response.status;
|
||||
error.response = response;
|
||||
throw error;
|
||||
})
|
||||
.then(response => {
|
||||
return response && response.json();
|
||||
})
|
||||
.then(json => {
|
||||
resolve(json);
|
||||
})
|
||||
.catch(error => {
|
||||
error.response.json().then(json => {
|
||||
error.error = json;
|
||||
reject(error);
|
||||
});
|
||||
});
|
||||
});
|
||||
// Handle failed responses
|
||||
const error = {};
|
||||
error.statusCode = response.status;
|
||||
error.response = response;
|
||||
throw error;
|
||||
};
|
||||
|
||||
get = (path: string, data: ?Object, options?: Object) => {
|
||||
|
||||
Reference in New Issue
Block a user