redux: auth and map init

This commit is contained in:
muerwre 2018-11-26 11:56:19 +07:00
parent dca55a3bc4
commit df6202c32d
17 changed files with 325 additions and 163 deletions

View file

@ -16,7 +16,6 @@
"global-require": 1, "global-require": 1,
"react/no-multi-comp": 1, "react/no-multi-comp": 1,
"react/jsx-filename-extension": 0, "react/jsx-filename-extension": 0,
"camelcase": 1,
"import/no-unresolved": 1, "import/no-unresolved": 1,
"import/prefer-default-export": 0, "import/prefer-default-export": 0,
"import/extensions": 1, "import/extensions": 1,
@ -29,7 +28,9 @@
"jsx-a11y/no-autofocus": 0, "jsx-a11y/no-autofocus": 0,
"react/jsx-closing-tag-location": 0, "react/jsx-closing-tag-location": 0,
"prefer-promise-reject-errors": 0, "prefer-promise-reject-errors": 0,
"jsx-a11y/mouse-events-have-key-events": 0 "jsx-a11y/mouse-events-have-key-events": 0,
"camelcase": 0,
"no-trailing-spaces": 0,
}, },
"globals": { "globals": {
"document": false, "document": false,

18
src/constants/types.js Normal file
View file

@ -0,0 +1,18 @@
import { ROLES } from '$constants/auth';
export type UserType = {
new_messages: Number,
place_types: Object,
random_url: String,
role: String,
routes: Array<Object>,
success: Boolean,
id: String,
token: String,
userdata: {
name: String,
agent: String,
ip: String,
photo: String,
}
};

View file

@ -1,6 +1,7 @@
// @flow
import React from 'react'; import React from 'react';
import { Editor } from '$modules/Editor'; import { Editor, editor } from '$modules/Editor';
import { EditorPanel } from '$components/panels/EditorPanel'; import { EditorPanel } from '$components/panels/EditorPanel';
import { Fills } from '$components/Fills'; import { Fills } from '$components/Fills';
import { DEFAULT_LOGO } from '$constants/logos'; import { DEFAULT_LOGO } from '$constants/logos';
@ -10,14 +11,30 @@ import { getGuestToken, checkUserToken, getStoredMap } from '$utils/api';
import { storeData, getData } from '$utils/storage'; import { storeData, getData } from '$utils/storage';
import { UserPanel } from '$components/panels/UserPanel'; import { UserPanel } from '$components/panels/UserPanel';
import { getUrlData, pushPath } from '$utils/history'; import { getUrlData, pushPath } from '$utils/history';
import { Provider } from 'react-redux'; import { connect } from 'react-redux';
import { PersistGate } from 'redux-persist/integration/react'; import { bindActionCreators } from 'redux';
import { configureStore } from '$redux/store'; import { hot } from 'react-hot-loader';
import type { UserType } from '$constants/types';
const { store, persistor } = configureStore(); type Props = {
user: UserType,
}
export class App extends React.Component { type State = {
mode: String,
editing: Boolean,
logo: String,
routerPoints: Number,
totalDistance: Number,
estimateTime: Number,
activeSticker: String,
title: String,
address: String,
changed: Boolean,
}
class Component extends React.Component<Props, State> {
state = { state = {
mode: 'none', mode: 'none',
editing: false, editing: false,
@ -35,28 +52,28 @@ export class App extends React.Component {
}; };
componentDidMount() { componentDidMount() {
this.authInit(); // this.authInit();
window.editor = this.editor; window.editor = editor;
} }
//
mapInit = () => { // mapInit = () => {
const { path, mode } = getUrlData(); // const { path, mode } = getUrlData();
if (path) { // if (path) {
getStoredMap({ name: path }) // getStoredMap({ name: path })
.then(this.setDataOnLoad) // .then(this.setDataOnLoad)
.then(() => { // .then(() => {
if (mode && mode === 'edit') { // if (mode && mode === 'edit') {
this.editor.startEditing(); // editor.startEditing();
} else { // } else {
this.editor.stopEditing(); // editor.stopEditing();
} // }
}) // })
.catch(this.startEmptyEditor); // .catch(this.startEmptyEditor);
} else { // } else {
// this.hideLoader(); // // this.hideLoader();
this.startEmptyEditor(); // this.startEmptyEditor();
} // }
}; // };
startEmptyEditor = () => { startEmptyEditor = () => {
const { user } = this.state; const { user } = this.state;
@ -64,8 +81,8 @@ export class App extends React.Component {
pushPath(`/${user.random_url}/edit`); pushPath(`/${user.random_url}/edit`);
this.editor.owner = user.id; editor.owner = user.id;
this.editor.startEditing(); editor.startEditing();
this.hideLoader(); this.hideLoader();
@ -81,7 +98,7 @@ export class App extends React.Component {
setDataOnLoad = data => { setDataOnLoad = data => {
this.clearChanged(); this.clearChanged();
this.editor.setData(data); editor.setData(data);
this.hideLoader(); this.hideLoader();
}; };
@ -128,47 +145,47 @@ export class App extends React.Component {
this.setState({ changed: false }); this.setState({ changed: false });
}; };
editor = new Editor({ // editor = new Editor({
container: 'map', // container: 'map',
mode: this.state.mode, // mode: this.state.mode,
setMode: this.setMode, // setMode: this.setMode,
setRouterPoints: this.setRouterPoints, // setRouterPoints: this.setRouterPoints,
setTotalDist: this.setTotalDist, // setTotalDist: this.setTotalDist,
setActiveSticker: this.setActiveSticker, // setActiveSticker: this.setActiveSticker,
setLogo: this.setLogo, // setLogo: this.setLogo,
setEditing: this.setEditing, // setEditing: this.setEditing,
setTitle: this.setTitle, // setTitle: this.setTitle,
setAddress: this.setAddress, // setAddress: this.setAddress,
getUser: this.getUser, // getUser: this.getUser,
triggerOnChange: this.triggerOnChange, // triggerOnChange: this.triggerOnChange,
clearChanged: this.clearChanged, // clearChanged: this.clearChanged,
getTitle: this.getTitle, // getTitle: this.getTitle,
}); // });
authInit = () => { // authInit = () => {
const user = this.getUserData(); // const user = this.getUserData();
//
const { id, token } = (user || {}); // const { id, token } = (user || {});
//
if (id && token) { // if (id && token) {
checkUserToken({ // checkUserToken({
id, // id,
token // token
}) // })
.then(this.setUser) // .then(this.setUser)
.then(this.mapInit); // .then(this.mapInit);
} else { // } else {
getGuestToken() // getGuestToken()
.then(this.setUser) // .then(this.setUser)
.then(this.mapInit); // .then(this.mapInit);
} // }
}; // };
setUser = user => { setUser = user => {
if (!user.token || !user.id) return; if (!user.token || !user.id) return;
if (this.state.user.id === this.editor.owner) { if (this.state.user.id === editor.owner) {
this.editor.owner = user.id; editor.owner = user.id;
} }
this.setState({ this.setState({
@ -188,8 +205,8 @@ export class App extends React.Component {
getUserData = () => getData('user') || null; getUserData = () => getData('user') || null;
userLogout = () => { userLogout = () => {
if (this.state.user.id === this.editor.owner) { if (this.state.user.id === editor.owner) {
this.editor.owner = null; editor.owner = null;
} }
// //
this.setState({ this.setState({
@ -201,45 +218,61 @@ export class App extends React.Component {
render() { render() {
const { const {
editor,
state: { state: {
mode, routerPoints, totalDistance, estimateTime, activeSticker, logo, user, editing, title, address, changed, mode, routerPoints, totalDistance, estimateTime, activeSticker, logo, editing, title, address, changed,
}, },
props: {
user,
}
} = this; } = this;
return ( return (
<Provider store={store}> <div>
<PersistGate loading={null} persistor={persistor}> <Fills />
<div>
<Fills />
<UserLocation editor={editor} /> <UserLocation editor={editor} />
<UserPanel <UserPanel
editor={editor} editor={editor}
user={user} user={user}
setUser={this.setUser} setUser={this.setUser}
userLogout={this.userLogout} userLogout={this.userLogout}
/> />
<EditorPanel <EditorPanel
editor={editor} editor={editor}
mode={mode} mode={mode}
routerPoints={routerPoints} routerPoints={routerPoints}
totalDistance={totalDistance} totalDistance={totalDistance}
estimateTime={estimateTime} estimateTime={estimateTime}
activeSticker={activeSticker} activeSticker={activeSticker}
logo={logo} logo={logo}
user={user} user={user}
editing={editing} editing={editing}
title={title} title={title}
address={address} address={address}
changed={changed} changed={changed}
/> />
</div> </div>
</PersistGate>
</Provider>
); );
} }
} }
function mapStateToProps(state) {
const {
user,
} = state;
return {
user
};
}
const mapDispatchToProps = dispatch => bindActionCreators({
}, dispatch);
export const App = connect(
mapStateToProps,
mapDispatchToProps
)(hot(module)(Component));

View file

@ -5,16 +5,23 @@ import ReactDOM from 'react-dom';
import { App } from '$containers/App'; import { App } from '$containers/App';
import '$styles/main.less'; import '$styles/main.less';
import 'raleway-cyrillic'; import 'raleway-cyrillic/raleway.css';
// import { Provider } from 'react-redux'; import { Provider } from 'react-redux';
// import { ConnectedRouter } from 'react-router-redux'; // import { ConnectedRouter } from 'react-router-redux';
// import { PersistGate } from 'redux-persist/integration/react'; import { PersistGate } from 'redux-persist/integration/react';
// import configureStore, { history } from '$redux/store'; import { configureStore } from '$redux/store';
// const { store, persistor } = configureStore();
const { store, persistor } = configureStore();
export const Index = () => ( export const Index = () => (
<App /> <Provider store={store}>
<PersistGate loading={null} persistor={persistor}>
<App />
</PersistGate>
</Provider>
); );
ReactDOM.render(<Index />, document.getElementById('index')); ReactDOM.render(<Index />, document.getElementById('index'));
// <PersistGate loading={null} persistor={persistor}>

View file

@ -8,27 +8,28 @@ import { DEFAULT_LOGO } from '$constants/logos';
import { parseStickerAngle, parseStickerStyle } from '$utils/import'; import { parseStickerAngle, parseStickerStyle } from '$utils/import';
import { getUrlData, pushPath } from '$utils/history'; import { getUrlData, pushPath } from '$utils/history';
import { store } from '$redux/store';
import { setEditing } from '$redux/user/actions';
export class Editor { export class Editor {
constructor({ constructor({
container, // container,
mode, // mode,
setMode, setMode,
setRouterPoints, setRouterPoints,
setTotalDist, setTotalDist,
setActiveSticker, setActiveSticker,
setLogo, setLogo,
setEditing, // setEditing,
setTitle, setTitle,
setAddress, setAddress,
getUser,
triggerOnChange, triggerOnChange,
clearChanged, clearChanged,
getTitle, // getTitle,
}) { }) {
this.logo = DEFAULT_LOGO; this.logo = DEFAULT_LOGO;
this.owner = null; this.owner = null;
this.map = new Map({ container }); this.map = new Map({ container: 'map' });
this.initialData = {}; this.initialData = {};
const { const {
@ -76,18 +77,22 @@ export class Editor {
this.setActiveSticker = setActiveSticker; this.setActiveSticker = setActiveSticker;
this.setLogo = setLogo; this.setLogo = setLogo;
this.setMode = setMode; this.setMode = setMode;
this.setEditing = setEditing; // this.setEditing = setEditing;
this.setTitle = setTitle; this.setTitle = setTitle;
this.setAddress = setAddress; this.setAddress = setAddress;
this.getUser = getUser; // this.getUser = getUser;
this.mode = mode; this.mode = 'none';
this.getTitle = getTitle; // this.getTitle = getTitle;
map.addEventListener('mouseup', this.onClick); map.addEventListener('mouseup', this.onClick);
map.addEventListener('dragstart', () => lockMapClicks(true)); map.addEventListener('dragstart', () => lockMapClicks(true));
map.addEventListener('dragstop', () => lockMapClicks(false)); map.addEventListener('dragstop', () => lockMapClicks(false));
} }
getUser = () => store.getState().user;
getTitle = () => store.getState().title;
setEditing = editing => store.dispatch(setEditing(editing));
createStickerOnClick = (e) => { createStickerOnClick = (e) => {
if (!e || !e.latlng || !this.activeSticker) return; if (!e || !e.latlng || !this.activeSticker) return;
const { latlng } = e; const { latlng } = e;
@ -293,3 +298,20 @@ export class Editor {
return (!route || route.length < 1) && (!stickers || stickers.length <= 0); return (!route || route.length < 1) && (!stickers || stickers.length <= 0);
} }
} }
export const editor = new Editor({
container: 'map',
mode: 'none',
// setMode: this.setMode,
// setRouterPoints: this.setRouterPoints,
// setTotalDist: this.setTotalDist,
// setActiveSticker: this.setActiveSticker,
// setLogo: this.setLogo,
// setEditing: this.setEditing,
// setTitle: this.setTitle,
// setAddress: this.setAddress,
// getUser: this.getUser,
// triggerOnChange: this.triggerOnChange,
// clearChanged: this.clearChanged,
// getTitle: this.getTitle,
});

View file

@ -25,8 +25,8 @@ const point_array = {
const points = L.layerGroup(); const points = L.layerGroup();
let mode = 'none'; // let mode = 'none';
const current_map_style = 'default'; const current_map_style = 'dgis';
// Интересные места; // Интересные места;
// const places_layer; // const places_layer;

View file

@ -1,15 +0,0 @@
export const ACTIONS = {
};
export const ROLES = {
guest: 'guest',
vk: 'vk',
};
export const EMPTY_USER = {
token: '',
name: '',
role: ROLES.guest,
picture: '',
};

View file

@ -1,12 +0,0 @@
import { createReducer } from 'reduxsauce';
import { ACTIONS, EMPTY_USER } from '$redux/auth/constants';
const HANDLERS = {
};
export const INITIAL_STATE = {
...EMPTY_USER
};
export const authReducer = createReducer(INITIAL_STATE, HANDLERS);

View file

@ -1,4 +0,0 @@
export function* authSaga() {
// Login
// yield takeLatest(AUTH_ACTIONS.SEND_LOGIN_REQUEST, sendLoginRequestSaga);
}

View file

@ -3,15 +3,16 @@ import { createStore, applyMiddleware, combineReducers, compose } from 'redux';
import { persistStore, persistReducer } from 'redux-persist'; import { persistStore, persistReducer } from 'redux-persist';
import storage from 'redux-persist/lib/storage'; import storage from 'redux-persist/lib/storage';
import createSagaMiddleware from 'redux-saga'; import createSagaMiddleware from 'redux-saga';
import createHistory from 'history/createBrowserHistory';
// import createHistory from 'history/createBrowserHistory';
// import { routerReducer, routerMiddleware } from 'react-router-redux'; // import { routerReducer, routerMiddleware } from 'react-router-redux';
import { authReducer } from '$redux/auth/reducer'; import { userReducer } from '$redux/user/reducer';
import { authSaga } from '$redux/auth/sagas'; import { userSaga } from '$redux/user/sagas';
const authPersistConfig = { const userPersistConfig = {
key: 'auth', key: 'user',
blacklist: [], blacklist: ['editing'],
storage, storage,
}; };
@ -31,18 +32,15 @@ const composeEnhancers =
export const store = createStore( export const store = createStore(
combineReducers({ combineReducers({
auth: persistReducer(authPersistConfig, authReducer), user: persistReducer(userPersistConfig, userReducer),
// routing: routerReducer // routing: routerReducer
}), }),
composeEnhancers(applyMiddleware( composeEnhancers(applyMiddleware(/* routerMiddleware(history), */ sagaMiddleware))
// routerMiddleware(history),
sagaMiddleware
))
); );
export function configureStore() { export function configureStore() {
// run sagas // run sagas
sagaMiddleware.run(authSaga); sagaMiddleware.run(userSaga);
const persistor = persistStore(store); const persistor = persistStore(store);

View file

@ -0,0 +1,4 @@
import { ACTIONS } from '$redux/user/constants';
export const setUser = user => ({ type: ACTIONS.SET_USER, user });
export const setEditing = editing => ({ type: ACTIONS.SET_EDITING, editing });

View file

@ -0,0 +1,4 @@
export const ACTIONS = {
SET_USER: 'SET_USER',
SET_EDITING: 'SET_EDITING',
};

25
src/redux/user/reducer.js Normal file
View file

@ -0,0 +1,25 @@
import { createReducer } from 'reduxsauce';
import { ACTIONS, EMPTY_USER } from '$redux/user/constants';
import { DEFAULT_USER } from '$constants/auth';
const setUser = (state, { user }) => ({
...state,
...user,
});
const setEditing = (state, { editing }) => ({
...state,
editing,
});
const HANDLERS = {
[ACTIONS.SET_USER]: setUser,
[ACTIONS.SET_EDITING]: setEditing,
};
export const INITIAL_STATE = {
...DEFAULT_USER
};
export const userReducer = createReducer(INITIAL_STATE, HANDLERS);

80
src/redux/user/sagas.js Normal file
View file

@ -0,0 +1,80 @@
import { REHYDRATE } from 'redux-persist';
import { takeLatest, select, call, put } from 'redux-saga/effects';
import { checkUserToken, getGuestToken, getStoredMap } from '$utils/api';
import { setUser } from '$redux/user/actions';
import { getUrlData, pushPath } from '$utils/history';
import { editor } from '$modules/Editor';
const getUser = state => (state.user);
const hideLoader = () => {
document.getElementById('loader').style.opacity = 0;
document.getElementById('loader').style.pointerEvents = 'none';
return true;
};
function* generateGuestSaga() {
const user = yield call(getGuestToken);
yield put(setUser(user));
return user;
}
function* startEmptyEditorSaga() {
const { id, random_url } = yield select(getUser);
console.log('RURL', random_url);
pushPath(`/${random_url}/edit`);
editor.owner = id;
editor.startEditing();
return hideLoader();
// todo: this.clearChanged();
}
function* mapInitSaga() {
const { path, mode } = getUrlData();
if (path) {
const map = yield call(getStoredMap, { name: path });
if (map) {
// todo: this.clearChanged();
editor.setData(map);
if (mode && mode === 'edit') {
editor.startEditing();
} else {
editor.stopEditing();
}
return hideLoader();
}
}
return yield call(startEmptyEditorSaga);
}
function* authChechSaga() {
const { id, token } = yield select(getUser);
if (id && token) {
const user = yield call(checkUserToken, { id, token });
if (user && user.success) {
yield put(setUser(user));
return yield call(mapInitSaga);
}
}
yield call(generateGuestSaga);
return yield call(mapInitSaga);
}
export function* userSaga() {
// Login
// yield takeLatest(AUTH_ACTIONS.SEND_LOGIN_REQUEST, sendLoginRequestSaga);
yield takeLatest(REHYDRATE, authChechSaga);
}

View file

@ -12,6 +12,7 @@
body { body {
font-family: 'Raleway', sans-serif; font-family: 'Raleway', sans-serif;
font-size: 14px; font-size: 14px;
padding: 0;
} }
.gray { .gray {

View file

@ -10,7 +10,7 @@ export const checkUserToken = ({ id, token }) => axios.get(API.CHECK_TOKEN, {
token, token,
action: 'check_token', action: 'check_token',
} }
}).then(result => (result && result.data && { ...result.data, id, token })) }).then(result => (result && result.data && { ...result.data, id, token }));
export const getGuestToken = () => axios.get(API.GET_GUEST, { export const getGuestToken = () => axios.get(API.GET_GUEST, {
params: { params: {