Implemented offline indicator

This commit is contained in:
Jori Lallo
2016-08-19 08:02:56 -07:00
parent e3e5ead9e0
commit 8fb88529a0
5 changed files with 108 additions and 27 deletions

View File

@ -2,6 +2,7 @@ import React from 'react';
import { browserHistory, Link } from 'react-router';
import Helmet from 'react-helmet';
import { observer } from 'mobx-react';
import { injectOffline } from 'components/Offline';
import keydown from 'react-keydown';
import _ from 'lodash';
@ -16,6 +17,7 @@ import classNames from 'classnames/bind';
const cx = classNames.bind(styles);
@observer(['user'])
@injectOffline
class Layout extends React.Component {
static propTypes = {
children: React.PropTypes.node,
@ -25,6 +27,7 @@ class Layout extends React.Component {
loading: React.PropTypes.bool,
user: React.PropTypes.object.isRequired,
search: React.PropTypes.bool,
offline: React.PropTypes.bool,
}
static defaultProps = {
@ -54,9 +57,15 @@ class Layout extends React.Component {
: 'Atlas' }
/>
{ this.props.loading ? (
{ this.props.loading && (
<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={ styles.headerLeft }>

View 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;

View File

@ -0,0 +1,7 @@
import Offline from './Offline';
import injectOffline from './injectOffline';
export {
Offline,
injectOffline,
}

View 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;

View File

@ -6,6 +6,7 @@ import Route from 'react-router/lib/Route';
import IndexRoute from 'react-router/lib/IndexRoute';
import Redirect from 'react-router/lib/Redirect';
import History from 'utils/History';
import { Offline } from 'components/Offline';
import stores from 'stores';
@ -45,6 +46,7 @@ function requireAuth(nextState, replace) {
render((
<div style={{ display: 'flex', flex: 1, height: '100%' }}>
<Provider { ...stores }>
<Offline>
<Router history={ History }>
<Route path="/" component={ Application }>
<IndexRoute component={ Home } />
@ -74,6 +76,7 @@ render((
<Redirect from="*" to="/404" />
</Route>
</Router>
</Offline>
</Provider>
{ __DEV__ && <DevTools position={{ bottom: 0, right: 0 }} /> }
</div>