diff --git a/package.json b/package.json index d43dfba2a..f20e7b377 100644 --- a/package.json +++ b/package.json @@ -25,96 +25,96 @@ }, "homepage": "https://github.com/jorilallo/atlas#readme", "dependencies": { - "babel-core": "^6.4.5", - "babel-eslint": "^4.1.8", - "babel-loader": "^6.2.1", - "babel-plugin-transform-decorators-legacy": "^1.3.4", - "babel-polyfill": "^6.7.4", - "babel-preset-es2015": "^6.3.13", - "babel-preset-react": "^6.3.13", - "babel-preset-react-hmre": "^1.0.1", - "babel-preset-stage-0": "^6.5.0", - "classnames": "^2.2.3", - "codemirror": "^5.11.0", - "cross-env": "^1.0.7", + "babel-core": "6.10.4", + "babel-eslint": "6.1.0", + "babel-loader": "6.2.1", + "babel-plugin-transform-decorators-legacy": "1.3.4", + "babel-polyfill": "6.7.4", + "babel-preset-es2015": "6.3.13", + "babel-preset-react": "6.3.13", + "babel-preset-react-hmre": "1.0.1", + "babel-preset-stage-0": "6.5.0", + "classnames": "2.2.3", + "codemirror": "5.16.0", + "cross-env": "1.0.7", "crypto": "0.0.3", - "css-loader": "^0.23.1", - "debug": "^2.2.0", - "dotenv": "^2.0.0", - "emoji-name-map": "^1.1.1", - "eslint": "^1.10.3", - "eslint-config-airbnb": "^5.0.0", - "eslint-plugin-react": "^3.16.1", - "exports-loader": "^0.6.3", - "extract-text-webpack-plugin": "^1.0.1", - "file-loader": "^0.8.5", - "highlight.js": "^9.4.0", - "history": "^1.17.0", - "html-webpack-plugin": "^2.17.0", - "http-errors": "^1.4.0", - "imports-loader": "^0.6.5", - "isomorphic-fetch": "^2.2.1", - "js-tree": "^1.1.0", - "json-loader": "^0.5.4", - "jsonwebtoken": "^5.7.0", - "koa": "^2.0.0", - "koa-bodyparser": "^2.0.1", - "koa-compress": "^2.0.0", - "koa-connect": "^1.0.0", - "koa-convert": "^1.2.0", - "koa-helmet": "^1.0.0", - "koa-jwt": "^1.2.0", - "koa-logger": "^2.0.0", - "koa-mount": "^2.0.0", - "koa-router": "^7.0.1", - "koa-sendfile": "^2.0.0", - "koa-webpack-dev-middleware": "^1.2.0", - "koa-webpack-hot-middleware": "^1.0.3", - "localenv": "^0.2.2", - "localforage": "^1.4.2", - "lodash": "^4.13.1", - "lodash.orderby": "^4.4.0", - "marked": "^0.3.5", - "mobx": "^2.2.2", - "mobx-react": "^3.3.0", - "mobx-react-devtools": "^4.2.0", - "moment": "^2.13.0", - "node-dev": "^3.1.0", - "node-sass": "^3.4.2", - "nodemon": "^1.9.1", - "normalize.css": "^3.0.3", - "normalizr": "^2.0.1", - "pg": "^4.5.3", - "pg-hstore": "^2.3.2", - "querystring": "^0.2.0", - "randomstring": "^1.1.5", - "react": "^0.14.7", - "react-codemirror": "^0.2.5", - "react-dom": "^0.14.7", - "react-dropzone": "^3.3.2", - "react-helmet": "^3.1.0", - "react-router": "^2.0.0", - "rebass": "^0.2.6", - "safestart": "^0.8.0", - "sass-loader": "^3.2.0", - "sequelize": "^3.21.0", - "sequelize-cli": "^2.4.0", - "sequelize-encrypted": "^0.1.0", - "slug": "^0.9.1", - "style-loader": "^0.13.0", + "css-loader": "0.23.1", + "debug": "2.2.0", + "dotenv": "2.0.0", + "emoji-name-map": "1.1.2", + "eslint": "2.13.1", + "eslint-config-airbnb": "9.0.1", + "eslint-plugin-react": "5.2.2", + "exports-loader": "0.6.3", + "extract-text-webpack-plugin": "1.0.1", + "file-loader": "0.9.0", + "highlight.js": "9.4.0", + "history": "3.0.0", + "html-webpack-plugin": "2.17.0", + "http-errors": "1.4.0", + "imports-loader": "0.6.5", + "isomorphic-fetch": "2.2.1", + "js-tree": "1.1.0", + "json-loader": "0.5.4", + "jsonwebtoken": "7.0.1", + "koa": "2.0.0", + "koa-bodyparser": "2.0.1", + "koa-compress": "2.0.0", + "koa-connect": "1.0.0", + "koa-convert": "1.2.0", + "koa-helmet": "1.0.0", + "koa-jwt": "1.2.0", + "koa-logger": "2.0.0", + "koa-mount": "2.0.0", + "koa-router": "7.0.1", + "koa-sendfile": "2.0.0", + "koa-webpack-dev-middleware": "1.2.0", + "koa-webpack-hot-middleware": "1.0.3", + "localenv": "0.2.2", + "localforage": "1.4.2", + "lodash": "4.13.1", + "lodash.orderby": "4.4.0", + "marked": "0.3.5", + "mobx": "2.3.3", + "mobx-react": "3.4.0", + "mobx-react-devtools": "4.2.0", + "moment": "2.13.0", + "node-dev": "3.1.0", + "node-sass": "3.4.2", + "nodemon": "1.9.1", + "normalize.css": "4.1.1", + "normalizr": "2.0.1", + "pg": "6.0.1", + "pg-hstore": "2.3.2", + "querystring": "0.2.0", + "randomstring": "1.1.5", + "react": "15.1.0", + "react-codemirror": "0.2.5", + "react-dom": "15.1.0", + "react-dropzone": "3.3.2", + "react-helmet": "3.1.0", + "react-router": "2.5.1", + "rebass": "0.2.6", + "safestart": "0.8.0", + "sass-loader": "3.2.1", + "sequelize": "3.23.4", + "sequelize-cli": "2.4.0", + "sequelize-encrypted": "0.1.0", + "slug": "0.9.1", + "style-loader": "0.13.0", "truncate-html": "0.0.6", - "url-loader": "^0.5.7", - "uuid": "^2.0.2", - "validator": "^5.2.0", - "webpack": "^1.12.12" + "url-loader": "0.5.7", + "uuid": "2.0.2", + "validator": "5.2.0", + "webpack": "1.12.12" }, "devDependencies": { - "babel-regenerator-runtime": "^6.5.0", - "fsevents": "^1.0.11", - "ignore-loader": "^0.1.1", - "koa-webpack-dev-middleware": "^1.2.0", - "koa-webpack-hot-middleware": "^1.0.3", - "node-dev": "^3.1.0", - "nodemon": "^1.9.1" + "babel-regenerator-runtime": "6.5.0", + "fsevents": "1.0.11", + "ignore-loader": "0.1.1", + "koa-webpack-dev-middleware": "1.2.0", + "koa-webpack-hot-middleware": "1.0.3", + "node-dev": "3.1.0", + "nodemon": "1.9.1" } } diff --git a/src/components/Layout/Layout.js b/src/components/Layout/Layout.js index e1a6c9fff..c4793295f 100644 --- a/src/components/Layout/Layout.js +++ b/src/components/Layout/Layout.js @@ -1,9 +1,7 @@ import React from 'react'; import Link from 'react-router/lib/Link'; import Helmet from 'react-helmet'; -import { observe } from 'mobx'; - -import store from 'stores/UserStore'; +import { observer } from 'mobx-react'; import DropdownMenu, { MenuItem } from 'components/DropdownMenu'; import Flex from 'components/Flex'; @@ -14,6 +12,7 @@ import styles from './Layout.scss'; import classNames from 'classnames/bind'; const cx = classNames.bind(styles); +@observer(['user']) class Layout extends React.Component { static propTypes = { actions: React.PropTypes.node, @@ -21,9 +20,12 @@ class Layout extends React.Component { titleText: React.PropTypes.node, fixed: React.PropTypes.bool, loading: React.PropTypes.bool, + user: React.PropTypes.object.isRequired, } render() { + const user = this.props.user; + return (
- { store.team.name } + { user.team.name } { this.props.title && ( / ) }{ this.props.title } @@ -53,10 +55,10 @@ class Layout extends React.Component { }> - Logout + Logout
diff --git a/src/components/SlackAuthLink/SlackAuthLink.js b/src/components/SlackAuthLink/SlackAuthLink.js index 14d36b1d0..0fa6aebab 100644 --- a/src/components/SlackAuthLink/SlackAuthLink.js +++ b/src/components/SlackAuthLink/SlackAuthLink.js @@ -1,12 +1,13 @@ import React from 'react'; -import { observe } from 'mobx' -import store from 'stores/UserStore'; +import { observer } from 'mobx-react'; import styles from './SlackAuthLink.scss'; +@observer(['user']) class SlackAuthLink extends React.Component { static propTypes = { scopes: React.PropTypes.arrayOf(React.PropTypes.string), + user: React.PropTypes.object.isRequired, } static defaultProps = { @@ -26,7 +27,7 @@ class SlackAuthLink extends React.Component { redirect_uri: __DEV__ ? 'http://localhost:3000/auth/slack/' : 'https://www.beautifulatlas.com/auth/slack/', - state: store.getOauthState(), + state: this.props.user.getOauthState(), }; const urlParams = Object.keys(params).map(function(key) { diff --git a/src/index.js b/src/index.js index c9195be42..9b5c5ef9d 100644 --- a/src/index.js +++ b/src/index.js @@ -1,11 +1,13 @@ import React from 'react'; import { render } from 'react-dom'; +import { Provider } from 'mobx-react'; import Router from 'react-router/lib/Router'; import Route from 'react-router/lib/Route'; import IndexRoute from 'react-router/lib/IndexRoute'; import History from 'utils/History'; -import userStore from 'stores/UserStore'; +import stores from 'stores'; +window.stores = stores; import 'normalize.css/normalize.css'; import 'utils/base-styles.scss'; @@ -28,7 +30,7 @@ if (__DEV__) { } function requireAuth(nextState, replace) { - if (!userStore.authenticated) { + if (!stores.user.authenticated) { replace({ pathname: '/', state: { nextPathname: nextState.location.pathname }, @@ -38,20 +40,22 @@ function requireAuth(nextState, replace) { render((
- - - + + + + - - - - - - + + + + + + - - - + + + + { __DEV__ ? : null }
), document.getElementById('root')); diff --git a/src/scenes/Application.js b/src/scenes/Application.js index dd6c14e11..346848474 100644 --- a/src/scenes/Application.js +++ b/src/scenes/Application.js @@ -1,7 +1,8 @@ import React from "react"; +import { observer } from 'mobx-react'; import Helmet from "react-helmet"; -const Application = (props) => { +const Application = observer((props) => { return (
{ { props.children }
); -}; +}); Application.propTypes = { children: React.PropTypes.node.isRequired, diff --git a/src/scenes/Dashboard/Dashboard.js b/src/scenes/Dashboard/Dashboard.js index 447ae2c70..9cb5b473e 100644 --- a/src/scenes/Dashboard/Dashboard.js +++ b/src/scenes/Dashboard/Dashboard.js @@ -1,7 +1,6 @@ import React from 'react'; import { observer } from 'mobx-react'; -import userStore from 'stores/UserStore'; import store from './DashboardStore'; import Flex from 'components/Flex'; @@ -14,10 +13,14 @@ import FullscreenField from 'components/FullscreenField'; import styles from './Dashboard.scss'; -@observer +@observer(['user']) class Dashboard extends React.Component { + static propTypes = { + user: React.PropTypes.object.isRequired, + } + componentDidMount = () => { - store.fetchAtlases(userStore.team.id); + store.fetchAtlases(this.props.user.team.id); } state = { diff --git a/src/scenes/Home/Home.js b/src/scenes/Home/Home.js index 82c908531..270503da6 100644 --- a/src/scenes/Home/Home.js +++ b/src/scenes/Home/Home.js @@ -1,14 +1,19 @@ import React from 'react'; -import store from 'stores/UserStore'; +import { observer } from 'mobx-react'; import { browserHistory } from 'react-router' import SlackAuthLink from 'components/SlackAuthLink'; import styles from './Home.scss'; +@observer(['user']) export default class Home extends React.Component { + static propTypes = { + user: React.PropTypes.object.isRequired, + } + componentDidMount = () => { - if (store.authenticated) { + if (this.props.user.authenticated) { browserHistory.replace('/dashboard'); } } diff --git a/src/scenes/SlackAuth/SlackAuth.js b/src/scenes/SlackAuth/SlackAuth.js index ffb6dbf4f..4362e304c 100644 --- a/src/scenes/SlackAuth/SlackAuth.js +++ b/src/scenes/SlackAuth/SlackAuth.js @@ -1,10 +1,15 @@ import React from 'react'; -import store from 'stores/UserStore'; +import { observer } from 'mobx-react'; + +@observer(['user']) +class SlackAuth extends React.Component { + static propTypes = { + user: React.PropTypes.object.isRequired, + } -export default class SlackAuth extends React.Component { componentDidMount = () => { const { code, state } = this.props.location.query; - store.authWithSlack(code, state); + this.props.user.authWithSlack(code, state); } render() { @@ -13,3 +18,5 @@ export default class SlackAuth extends React.Component { ); } } + +export default SlackAuth; \ No newline at end of file diff --git a/src/stores/UserStore.js b/src/stores/UserStore.js index fd73bdb52..ac156095c 100644 --- a/src/stores/UserStore.js +++ b/src/stores/UserStore.js @@ -1,11 +1,10 @@ -import { observable, action, computed, autorun } from 'mobx'; +import { observable, action, computed } from 'mobx'; import { browserHistory } from 'react-router'; import { client } from 'utils/ApiClient'; -import localforage from 'localforage'; const USER_STORE = 'USER_STORE'; -const store = new class UserStore { +class UserStore { @observable user; @observable team; @@ -71,12 +70,9 @@ const store = new class UserStore { this.token = data.token; this.oauthState = data.oauthState; } -}(); +}; -// Persist store to localStorage -autorun(() => { - localStorage.setItem(USER_STORE, store.asJson); -}); - - -export default store; +export default UserStore; +export { + USER_STORE, +}; diff --git a/src/stores/index.js b/src/stores/index.js new file mode 100644 index 000000000..88effef75 --- /dev/null +++ b/src/stores/index.js @@ -0,0 +1,13 @@ +import UserStore, { USER_STORE } from './UserStore'; +import { autorun } from 'mobx'; + +const stores = { + user: new UserStore(), +}; + +// Persist store to localStorage +autorun(() => { + localStorage.setItem(USER_STORE, stores.user.asJson); +}); + +export default stores; \ No newline at end of file diff --git a/src/utils/ApiClient.js b/src/utils/ApiClient.js index dcfcaec8d..79a3ada09 100644 --- a/src/utils/ApiClient.js +++ b/src/utils/ApiClient.js @@ -1,5 +1,5 @@ import _map from 'lodash/map'; -import store from 'stores/UserStore'; +import stores from 'stores'; import constants from '../constants'; @@ -25,8 +25,8 @@ class ApiClient { 'Content-Type': 'application/json', 'User-Agent': this.userAgent, }); - if (store.authenticated) { - headers.set('Authorization', `Bearer ${store.token}`); + if (stores.user.authenticated) { + headers.set('Authorization', `Bearer ${stores.user.token}`); } // Construct request @@ -48,7 +48,7 @@ class ApiClient { // Handle 401, log out user if (response.status === 401) { - store.logout(); + stores.user.logout(); } // Handle failed responses