Implemented offline indicator
This commit is contained in:
@ -2,6 +2,7 @@ import React from 'react';
|
|||||||
import { browserHistory, Link } from 'react-router';
|
import { browserHistory, Link } from 'react-router';
|
||||||
import Helmet from 'react-helmet';
|
import Helmet from 'react-helmet';
|
||||||
import { observer } from 'mobx-react';
|
import { observer } from 'mobx-react';
|
||||||
|
import { injectOffline } from 'components/Offline';
|
||||||
import keydown from 'react-keydown';
|
import keydown from 'react-keydown';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
|
|
||||||
@ -16,6 +17,7 @@ import classNames from 'classnames/bind';
|
|||||||
const cx = classNames.bind(styles);
|
const cx = classNames.bind(styles);
|
||||||
|
|
||||||
@observer(['user'])
|
@observer(['user'])
|
||||||
|
@injectOffline
|
||||||
class Layout extends React.Component {
|
class Layout extends React.Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
children: React.PropTypes.node,
|
children: React.PropTypes.node,
|
||||||
@ -25,6 +27,7 @@ class Layout extends React.Component {
|
|||||||
loading: React.PropTypes.bool,
|
loading: React.PropTypes.bool,
|
||||||
user: React.PropTypes.object.isRequired,
|
user: React.PropTypes.object.isRequired,
|
||||||
search: React.PropTypes.bool,
|
search: React.PropTypes.bool,
|
||||||
|
offline: React.PropTypes.bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
@ -54,9 +57,15 @@ class Layout extends React.Component {
|
|||||||
: 'Atlas' }
|
: 'Atlas' }
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{ this.props.loading ? (
|
{ this.props.loading && (
|
||||||
<LoadingIndicator />
|
<LoadingIndicator />
|
||||||
) : null }
|
) }
|
||||||
|
|
||||||
|
{ this.props.offline && (
|
||||||
|
<Alert offline>
|
||||||
|
It looks like you're offline. Reconnect to restore access to all of your documents 📚
|
||||||
|
</Alert>
|
||||||
|
) }
|
||||||
|
|
||||||
<div className={ cx(styles.header) }>
|
<div className={ cx(styles.header) }>
|
||||||
<div className={ styles.headerLeft }>
|
<div className={ styles.headerLeft }>
|
||||||
|
43
frontend/components/Offline/Offline.js
Normal file
43
frontend/components/Offline/Offline.js
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
class Offline extends React.Component {
|
||||||
|
static propTypes = {
|
||||||
|
children: React.PropTypes.node,
|
||||||
|
};
|
||||||
|
|
||||||
|
static childContextTypes = {
|
||||||
|
offline: React.PropTypes.bool,
|
||||||
|
};
|
||||||
|
|
||||||
|
state = {
|
||||||
|
offline: false,
|
||||||
|
}
|
||||||
|
|
||||||
|
getChildContext() {
|
||||||
|
return {
|
||||||
|
offline: this.state.offline,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount = () => {
|
||||||
|
window.addEventListener('offline', this.handleConnectionState);
|
||||||
|
window.addEventListener('online', this.handleConnectionState);
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillUnmount = () => {
|
||||||
|
window.removeEventListener('offline', this.handleConnectionState);
|
||||||
|
window.removeEventListener('online', this.handleConnectionState);
|
||||||
|
};
|
||||||
|
|
||||||
|
handleConnectionState = () => {
|
||||||
|
this.setState({
|
||||||
|
offline: !navigator.onLine,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return React.Children.only(this.props.children);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Offline;
|
7
frontend/components/Offline/index.js
Normal file
7
frontend/components/Offline/index.js
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import Offline from './Offline';
|
||||||
|
import injectOffline from './injectOffline';
|
||||||
|
|
||||||
|
export {
|
||||||
|
Offline,
|
||||||
|
injectOffline,
|
||||||
|
}
|
19
frontend/components/Offline/injectOffline.js
Normal file
19
frontend/components/Offline/injectOffline.js
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
const injectOffline = (WrappedComponent) => {
|
||||||
|
return class OfflineWrapper extends React.Component {
|
||||||
|
static contextTypes = {
|
||||||
|
offline: React.PropTypes.bool,
|
||||||
|
};
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const newProps = {
|
||||||
|
offline: this.context.offline,
|
||||||
|
};
|
||||||
|
|
||||||
|
return (<WrappedComponent { ...this.props } { ...newProps } />);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default injectOffline;
|
@ -6,6 +6,7 @@ import Route from 'react-router/lib/Route';
|
|||||||
import IndexRoute from 'react-router/lib/IndexRoute';
|
import IndexRoute from 'react-router/lib/IndexRoute';
|
||||||
import Redirect from 'react-router/lib/Redirect';
|
import Redirect from 'react-router/lib/Redirect';
|
||||||
import History from 'utils/History';
|
import History from 'utils/History';
|
||||||
|
import { Offline } from 'components/Offline';
|
||||||
|
|
||||||
import stores from 'stores';
|
import stores from 'stores';
|
||||||
|
|
||||||
@ -45,6 +46,7 @@ function requireAuth(nextState, replace) {
|
|||||||
render((
|
render((
|
||||||
<div style={{ display: 'flex', flex: 1, height: '100%' }}>
|
<div style={{ display: 'flex', flex: 1, height: '100%' }}>
|
||||||
<Provider { ...stores }>
|
<Provider { ...stores }>
|
||||||
|
<Offline>
|
||||||
<Router history={ History }>
|
<Router history={ History }>
|
||||||
<Route path="/" component={ Application }>
|
<Route path="/" component={ Application }>
|
||||||
<IndexRoute component={ Home } />
|
<IndexRoute component={ Home } />
|
||||||
@ -74,6 +76,7 @@ render((
|
|||||||
<Redirect from="*" to="/404" />
|
<Redirect from="*" to="/404" />
|
||||||
</Route>
|
</Route>
|
||||||
</Router>
|
</Router>
|
||||||
|
</Offline>
|
||||||
</Provider>
|
</Provider>
|
||||||
{ __DEV__ && <DevTools position={{ bottom: 0, right: 0 }} /> }
|
{ __DEV__ && <DevTools position={{ bottom: 0, right: 0 }} /> }
|
||||||
</div>
|
</div>
|
||||||
|
Reference in New Issue
Block a user