1
0
mirror of https://github.com/mgerb/classic-wow-forums synced 2026-01-11 17:42:48 +00:00

client - use mobx as state container

This commit is contained in:
2018-01-09 20:24:09 -06:00
parent 8a39f687a8
commit 2d6ff0875f
13 changed files with 100 additions and 42 deletions

View File

@@ -1,4 +1,5 @@
import axios, { AxiosInstance, AxiosResponse } from 'axios'; import axios, { AxiosInstance, AxiosResponse } from 'axios';
import userStore from '../stores/user-store';
// create our own instance of axios so we can set request headers // create our own instance of axios so we can set request headers
const ax: AxiosInstance = axios.create(); const ax: AxiosInstance = axios.create();
@@ -9,11 +10,10 @@ ax.interceptors.response.use(
return config; return config;
}, },
(error: any) => { (error: any) => {
// TODO: log the user out if they get a 401
// if code is unauthorized (401) then logout if already logged in // if code is unauthorized (401) then logout if already logged in
// if (error.response.status === 401 && store.getState().user.loggedIn) { if (error.response.status === 401 && userStore.user) {
// store.dispatch(userActions.logout()); userStore.resetUser();
// } }
return Promise.reject(error); return Promise.reject(error);
}, },

View File

@@ -1,7 +1,10 @@
import React from 'react'; import React from 'react';
import { inject, observer } from 'mobx-react';
import { UserStore } from '../../stores/user-store';
interface Props { interface Props {
className?: string; className?: string;
userStore?: UserStore;
} }
interface State {} interface State {}
@@ -12,14 +15,17 @@ const oauthUrl: string =
? '' ? ''
: 'https://us.battle.net/oauth/authorize?redirect_uri=https://localhost/oauth&scope=wow.profile&client_id=2pfsnmd57svcpr5c93k7zb5zrug29xvp&response_type=code'; : 'https://us.battle.net/oauth/authorize?redirect_uri=https://localhost/oauth&scope=wow.profile&client_id=2pfsnmd57svcpr5c93k7zb5zrug29xvp&response_type=code';
@inject('userStore')
@observer
export class LoginButton extends React.Component<Props, State> { export class LoginButton extends React.Component<Props, State> {
login() { login() {
window.open(oauthUrl, '_blank', 'resizeable=yes, height=900, width=1200'); window.open(oauthUrl, '_blank', 'resizeable=yes, height=900, width=1200');
} }
render() { render() {
return ( return (
<div {...this.props}> <div className={this.props.className}>
<img src={require('../../assets/login-bot-left.gif')} /> <img src={require('../../assets/login-bot-left.gif')} />
<img src={require('../../assets/login-bot-login.gif')} style={{ cursor: 'pointer' }} onClick={this.login.bind(this)} /> <img src={require('../../assets/login-bot-login.gif')} style={{ cursor: 'pointer' }} onClick={this.login.bind(this)} />
<img src={require('../../assets/login-bot-right.gif')} /> <img src={require('../../assets/login-bot-right.gif')} />

View File

@@ -1,3 +1,8 @@
export interface UserModel { export interface UserModel {
access_token: string;
battle_net_id: number;
battletag: string;
id: number;
permissions: string;
token: string;
} }

View File

@@ -1,7 +1,6 @@
import React from 'react'; import React from 'react';
import { RouteComponentProps } from 'react-router-dom'; import { RouteComponentProps } from 'react-router-dom';
import { ContentContainer } from '../../components'; import { ContentContainer } from '../../components';
import { UserService } from '../../services';
interface Props extends RouteComponentProps<any> {} interface Props extends RouteComponentProps<any> {}
@@ -9,9 +8,7 @@ interface State {}
export class UserAccount extends React.Component<Props, State> { export class UserAccount extends React.Component<Props, State> {
componentDidMount() { componentDidMount() {}
console.log(UserService.getUser());
}
render() { render() {
return ( return (

View File

@@ -1,7 +1,9 @@
import React from 'react'; import React from 'react';
import { BrowserRouter, Route, Switch } from 'react-router-dom'; import { BrowserRouter, Route, Switch } from 'react-router-dom';
import { Provider } from 'mobx-react';
import { Footer, Header } from './components'; import { Footer, Header } from './components';
import { Forum, Home, NotFound, Oauth, Realms, UserAccount } from './pages'; import { Forum, Home, NotFound, Oauth, Realms, UserAccount } from './pages';
import { stores } from './stores/stores';
// styling // styling
import './scss/index.scss'; import './scss/index.scss';
@@ -14,20 +16,22 @@ export class Routes extends React.Component<Props, State> {
public render() { public render() {
return ( return (
<BrowserRouter> <Provider {...stores}>
<div> <BrowserRouter>
<Header /> <div>
<Switch> <Header />
<Route exact path="/" component={Home} /> <Switch>
<Route exact path="/realms" component={Realms} /> <Route exact path="/" component={Home} />
<Route exact path="/f/:id" component={Forum} /> <Route exact path="/realms" component={Realms} />
<Route exact path="/oauth" component={Oauth} /> <Route exact path="/f/:id" component={Forum} />
<Route exact path="/user-account" component={UserAccount} /> <Route exact path="/oauth" component={Oauth} />
<Route component={NotFound} /> <Route exact path="/user-account" component={UserAccount} />
</Switch> <Route component={NotFound} />
<Footer /> </Switch>
</div> <Footer />
</BrowserRouter> </div>
</BrowserRouter>
</Provider>
); );
} }
} }

View File

@@ -12,7 +12,7 @@ const getCategories = async () => {
const res = await axios.get('/api/category'); const res = await axios.get('/api/category');
categoryCache = cloneDeep(res.data.data); categoryCache = cloneDeep(res.data.data);
return res.data.data; return res.data.data;
} };
export const CategoryService = { export const CategoryService = {
getCategories, getCategories,

View File

@@ -1,27 +1,16 @@
import axios from '../axios/axios'; import axios from '../axios/axios';
import { UserModel } from '../model'; import userStore from '../stores/user-store';
const storeUser = (user: UserModel): void => {
localStorage.setItem('user', JSON.stringify(user));
}
const getUser = (): UserModel => {
const u = localStorage.getItem('user');
return u ? JSON.parse(u) : null;
}
// fetch user and store in local storage // fetch user and store in local storage
const authorize = async (code: string): Promise<void> => { const authorize = async (code: string): Promise<void> => {
try { try {
const res = await axios.post('/api/battlenet/authorize', { code }); const res = await axios.post('/api/battlenet/authorize', { code });
UserService.storeUser(res.data.data); userStore.setUser(res.data.data);
} catch (e) { } catch (e) {
console.error(e); console.error(e);
} }
} };
export const UserService = { export const UserService = {
storeUser,
getUser,
authorize, authorize,
} };

View File

@@ -0,0 +1,5 @@
import userStore from './user-store';
export const stores = {
userStore,
};

View File

@@ -0,0 +1,38 @@
import { action, observable } from 'mobx';
import { UserModel } from '../model';
import { resetAuthorizationHeader, setAuthorizationHeader } from '../axios/axios';
export class UserStore {
@observable user?: UserModel;
constructor() {
// use timeout or axios won't be defined
setTimeout(() => {
this.getUserFromStorage();
});
}
@action setUser(user: UserModel) {
localStorage.setItem('user', JSON.stringify(user));
this.getUserFromStorage();
}
@action private getUserFromStorage(): void {
const u = localStorage.getItem('user');
if (u) {
this.user = JSON.parse(u);
setAuthorizationHeader(this.user!.token);
}
}
// when the user logs out
@action resetUser() {
this.user = undefined;
resetAuthorizationHeader();
localStorage.removeItem('user');
}
}
export default new UserStore();

View File

@@ -34,6 +34,8 @@
"font-awesome": "^4.7.0", "font-awesome": "^4.7.0",
"html-webpack-plugin": "^2.30.1", "html-webpack-plugin": "^2.30.1",
"lodash": "^4.17.4", "lodash": "^4.17.4",
"mobx": "^3.4.1",
"mobx-react": "^4.3.5",
"node-sass": "^4.7.2", "node-sass": "^4.7.2",
"normalize.css": "^7.0.0", "normalize.css": "^7.0.0",
"postcss-loader": "^2.0.9", "postcss-loader": "^2.0.9",

View File

@@ -1,5 +1,6 @@
{ {
"compilerOptions": { "compilerOptions": {
"experimentalDecorators": true,
"target": "es2015", "target": "es2015",
"module": "es2015", "module": "es2015",
"moduleResolution": "node", "moduleResolution": "node",

View File

@@ -3,6 +3,7 @@
"rules": { "rules": {
"import-name": false, "import-name": false,
"max-line-length": [true, 140], "max-line-length": [true, 140],
"no-unused-variable": [true] "no-unused-variable": [true],
"variable-name": [false]
} }
} }

View File

@@ -2830,7 +2830,7 @@ hoek@4.x.x:
version "4.2.0" version "4.2.0"
resolved "https://registry.yarnpkg.com/hoek/-/hoek-4.2.0.tgz#72d9d0754f7fe25ca2d01ad8f8f9a9449a89526d" resolved "https://registry.yarnpkg.com/hoek/-/hoek-4.2.0.tgz#72d9d0754f7fe25ca2d01ad8f8f9a9449a89526d"
hoist-non-react-statics@^2.3.0: hoist-non-react-statics@^2.3.0, hoist-non-react-statics@^2.3.1:
version "2.3.1" version "2.3.1"
resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-2.3.1.tgz#343db84c6018c650778898240135a1420ee22ce0" resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-2.3.1.tgz#343db84c6018c650778898240135a1420ee22ce0"
@@ -3833,6 +3833,16 @@ mkdirp@0.5.1, mkdirp@0.5.x, "mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1, mkdi
dependencies: dependencies:
minimist "0.0.8" minimist "0.0.8"
mobx-react@^4.3.5:
version "4.3.5"
resolved "https://registry.yarnpkg.com/mobx-react/-/mobx-react-4.3.5.tgz#76853f2f2ef4a6f960c374bcd9f01e875929c04c"
dependencies:
hoist-non-react-statics "^2.3.1"
mobx@^3.4.1:
version "3.4.1"
resolved "https://registry.yarnpkg.com/mobx/-/mobx-3.4.1.tgz#37abe5ee882d401828d9f26c6c1a2f47614bbbef"
ms@0.7.1: ms@0.7.1:
version "0.7.1" version "0.7.1"
resolved "https://registry.yarnpkg.com/ms/-/ms-0.7.1.tgz#9cd13c03adbff25b65effde7ce864ee952017098" resolved "https://registry.yarnpkg.com/ms/-/ms-0.7.1.tgz#9cd13c03adbff25b65effde7ce864ee952017098"