now user can login

This commit is contained in:
muerwre 2018-08-28 16:58:36 +07:00
parent e7960a6bf8
commit e19001ca82
25 changed files with 493 additions and 32844 deletions

71
package-lock.json generated
View file

@ -8034,6 +8034,11 @@
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.10.tgz",
"integrity": "sha512-UejweD1pDoXu+AD825lWwp4ZGtSwgnpZxb3JDViD7StjQz+Nb/6l093lx4OQ0foGWNRoc19mWy7BzL+UAK2iVg=="
},
"lodash-es": {
"version": "4.17.10",
"resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.10.tgz",
"integrity": "sha512-iesFYPmxYYGTcmQK0sL8bX3TGHyM6b2qREaB4kamHfQyfPJP0xgoGxp19nsH16nsfquLdiyKyX3mQkfiSGV8Rg=="
},
"lodash.camelcase": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz",
@ -10204,6 +10209,19 @@
"integrity": "sha512-eTPo5t/4bgaMNZxyjWx6N2a6AuE0mq51KWvpc7nU/MAqixcI6v6KrGUKES0HaomdnolQBBXU/++X6/QQ9KL4tw==",
"dev": true
},
"ramda": {
"version": "0.24.1",
"resolved": "https://registry.npmjs.org/ramda/-/ramda-0.24.1.tgz",
"integrity": "sha1-w7d1UZfzW43DUCIoJixMkd22uFc="
},
"ramdasauce": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/ramdasauce/-/ramdasauce-2.1.0.tgz",
"integrity": "sha512-BdHm//tlCCmeXxY5EvIvlczuWvZU5QcRybdxZ4mkDOIasWzbBs+bjt3iEVsThKCMWLIiFZpggtQmIyjtL7eOvA==",
"requires": {
"ramda": "^0.24.1"
}
},
"randomatic": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/randomatic/-/randomatic-3.1.0.tgz",
@ -10337,6 +10355,24 @@
"resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz",
"integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA=="
},
"react-redux": {
"version": "5.0.7",
"resolved": "https://registry.npmjs.org/react-redux/-/react-redux-5.0.7.tgz",
"integrity": "sha512-5VI8EV5hdgNgyjfmWzBbdrqUkrVRKlyTKk1sGH3jzM2M2Mhj/seQgPXaz6gVAj2lz/nz688AdTqMO18Lr24Zhg==",
"requires": {
"hoist-non-react-statics": "^2.5.0",
"invariant": "^2.0.0",
"lodash": "^4.17.5",
"lodash-es": "^4.17.5",
"loose-envify": "^1.1.0",
"prop-types": "^15.6.0"
}
},
"react-router-redux": {
"version": "4.0.8",
"resolved": "https://registry.npmjs.org/react-router-redux/-/react-router-redux-4.0.8.tgz",
"integrity": "sha1-InQDWWtRUeGCN32rg1tdRfD4BU4="
},
"read-chunk": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/read-chunk/-/read-chunk-2.1.0.tgz",
@ -10476,6 +10512,41 @@
}
}
},
"redux": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/redux/-/redux-4.0.0.tgz",
"integrity": "sha512-NnnHF0h0WVE/hXyrB6OlX67LYRuaf/rJcbWvnHHEPCF/Xa/AZpwhs/20WyqzQae5x4SD2F9nPObgBh2rxAgLiA==",
"requires": {
"loose-envify": "^1.1.0",
"symbol-observable": "^1.2.0"
},
"dependencies": {
"symbol-observable": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz",
"integrity": "sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ=="
}
}
},
"redux-persist": {
"version": "5.10.0",
"resolved": "https://registry.npmjs.org/redux-persist/-/redux-persist-5.10.0.tgz",
"integrity": "sha512-sSJAzNq7zka3qVHKce1hbvqf0Vf5DuTVm7dr4GtsqQVOexnrvbV47RWFiPxQ8fscnyiuWyD2O92DOxPl0tGCRg=="
},
"redux-saga": {
"version": "0.16.0",
"resolved": "https://registry.npmjs.org/redux-saga/-/redux-saga-0.16.0.tgz",
"integrity": "sha1-CiMdsKFIkwHdmA9vL4jYztQY9yQ="
},
"reduxsauce": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/reduxsauce/-/reduxsauce-1.0.0.tgz",
"integrity": "sha512-+HXHbr9Xq9VRFRXAt/LTcNpQf3wix/ynPyVnTLAJYEULwRUylZZJaid5TAVzdBUtAp6V00bukKItLxxEVbGxvg==",
"requires": {
"ramda": "^0.24.1",
"ramdasauce": "^2.0.0"
}
},
"regenerate": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.0.tgz",

View file

@ -60,6 +60,12 @@
"react": "^16.3.2",
"react-dom": "^16.3.2",
"react-hot-loader": "^4.1.1",
"react-redux": "^5.0.7",
"react-router-redux": "^4.0.8",
"redux": "^4.0.0",
"redux-persist": "^5.10.0",
"redux-saga": "^0.16.0",
"reduxsauce": "^1.0.0",
"regenerator-runtime": "^0.11.1",
"scrypt": "^6.0.3",
"styled-components": "^3.2.6",

View file

@ -39,16 +39,6 @@ export class EditorPanel extends React.PureComponent {
<LogoPreview logo={logo} />
<div className="panel">
<div className="control-bar">
<button
onClick={this.startShotterMode}
>
<span>РЕДАКТОР</span>
</button>
</div>
</div>
<div className="panel right">
<div className="control-dist">
{totalDistance} км

View file

@ -0,0 +1,79 @@
import React from 'react';
import { GuestButton } from '$components/user/GuestButton';
import { SERVER } from '$constants/api';
import { DEFAULT_USER, ROLES } from '$constants/auth';
import { UserButton } from '$components/user/UserButton';
import { Icon } from '$components/panels/Icon';
export class UserPanel extends React.PureComponent {
componentDidMount() {
window.doLogin = console.log;
window.addEventListener('message', e => {
const { data } = e;
if (!data || !data.type || data.type !== 'oauth_login' || !data.user || !data.user.id || !data.user.token) return;
const {
id, token, role = 'vk', name = '', ip = '', photo = '', agent = '',
} = data.user;
const user = {
...DEFAULT_USER,
role,
id,
token,
userdata: {
name,
ip,
agent,
photo,
}
};
this.props.setUser(user);
});
}
openOauthFrame = () => {
const width = parseInt(window.innerWidth, 10);
const height = parseInt(window.innerHeight, 10);
const top = (height - 370) / 2;
const left = (width - 700) / 2;
window.open(
`https://oauth.vk.com/authorize?client_id=5987644&scope=&redirect_uri=${SERVER}/engine/oauthOrchid.php&response_type=code`,
'socialPopupWindow',
`location=no,width=700,height=370,scrollbars=no,top=${top},left=${left},resizable=no`
);
};
render() {
const {
user
} = this.props;
return (
<div>
<div className="panel">
<div className="control-bar">
<button
onClick={this.startShotterMode}
>
<Icon icon="icon-poly" />
</button>
</div>
<div className="control-sep" />
{
!user || user.role === ROLES.guest
? <GuestButton onClick={this.openOauthFrame} />
: <UserButton user={user} />
}
</div>
</div>
);
}
}

View file

@ -0,0 +1,11 @@
import React from 'react';
export const GuestButton = ({ onClick }) => (
<div className="control-bar user-bar">
<button
onClick={onClick}
>
<span>ВОЙТИ</span>
</button>
</div>
);

View file

@ -0,0 +1,23 @@
import React from 'react';
import { UserPicture } from '$components/user/UserPicture';
export const UserButton = ({
user: {
id,
userdata: {
name,
photo,
}
}
}) => (
<div className="control-bar user-bar">
<div className="user-button">
<UserPicture photo={photo} />
<div className="user-button-fields">
<div className="user-button-name">{(name || id || '...')}</div>
<div className="user-button-text">{(id || 'пользователь')}</div>
</div>
</div>
</div>
);

View file

@ -0,0 +1,10 @@
import React from 'react';
export const UserPicture = ({ photo }) => (
<div
className="user-button-picture"
style={{
backgroundImage: `url(${photo})`
}}
/>
);

5
src/constants/api.js Normal file
View file

@ -0,0 +1,5 @@
export const SERVER = 'http://alpha-map.vault48.org';
export const API = {
COMPOSE: `${SERVER}/engine/composerOrchid.php`,
GET_GUEST: `${SERVER}/engine/auth.php`,
};

19
src/constants/auth.js Normal file
View file

@ -0,0 +1,19 @@
export const ROLES = {
guest: 'guest',
vk: 'vk',
};
export const DEFAULT_USER = {
new_messages: 0,
place_types: {},
random_url: '',
role: ROLES.guest,
routes: [],
success: false,
userdata: {
name: '',
agent: '',
ip: '',
photo: '',
}
};

View file

@ -5,6 +5,10 @@ import { EditorPanel } from '$components/panels/EditorPanel';
import { Fills } from '$components/Fills';
import { DEFAULT_LOGO } from '$constants/logos';
import { UserLocation } from '$components/UserLocation';
import { DEFAULT_USER } from '$constants/auth';
import { getGuestToken, checkUserToken } from '$utils/api';
import { storeData, getData } from '$utils/storage';
import { UserPanel } from '$components/panels/UserPanel';
export class App extends React.Component {
state = {
@ -14,8 +18,15 @@ export class App extends React.Component {
totalDistance: 0,
estimateTime: 0,
activeSticker: null,
user: {
...DEFAULT_USER,
},
};
componentDidMount() {
this.authInit();
}
setMode = mode => {
this.setState({ mode });
};
@ -48,11 +59,54 @@ export class App extends React.Component {
setLogo: this.setLogo,
});
authInit = () => {
const user = this.getUserData();
const { id, token } = (user || {});
const fallback = () => getGuestToken({ callback: this.setUser });
if (id && token) {
console.log('checking');
checkUserToken({
callback: this.setUser,
fallback,
id,
token
});
} else {
getGuestToken({ callback: fallback });
}
};
setUser = user => {
console.log('hup', user);
if (!user.token || !user.id) return;
console.log('setting', user);
this.setState({
user: {
...DEFAULT_USER,
...user,
}
});
this.storeUserData();
};
storeUserData = () => {
storeData('user', this.state.user);
};
getUserData = () => {
return getData('user') || null;
};
render() {
const {
editor,
state: {
mode, routerPoints, totalDistance, estimateTime, activeSticker, logo,
mode, routerPoints, totalDistance, estimateTime, activeSticker, logo, user,
},
} = this;
@ -63,6 +117,11 @@ export class App extends React.Component {
<UserLocation editor={editor} />
<UserPanel
user={user}
setUser={this.setUser}
/>
<EditorPanel
editor={editor}
mode={mode}
@ -71,6 +130,7 @@ export class App extends React.Component {
estimateTime={estimateTime}
activeSticker={activeSticker}
logo={logo}
user={user}
/>
</div>
);

View file

@ -1,10 +1,16 @@
import React from 'react';
import ReactDOM from 'react-dom';
import { App } from '$containers/App';
import { App } from '$containers/App';
import '$styles/main.less';
// import { Provider } from 'react-redux';
// import { ConnectedRouter } from 'react-router-redux';
// import { PersistGate } from 'redux-persist/integration/react';
// import configureStore, { history } from '$redux/store';
// const { store, persistor } = configureStore();
export const Index = () => (
<App />
);

View file

@ -46,6 +46,9 @@ export class Editor {
},
[MODES.STICKERS]: {
toggle: this.clearSticker,
},
[MODES.TRASH]: {
toggle: this.clearAll,
}
};

View file

@ -8,7 +8,7 @@ export class Map {
constructor({ container }) {
this.map = map(container, { editable: true }).setView([55.0153275, 82.9071235], 13);
this.tileLayer = tileLayer(providers.dgis, {
this.tileLayer = tileLayer(providers.default, {
attribution: 'Независимое Велосообщество',
maxNativeZoom: 18,
maxZoom: 18,

View file

@ -1,4 +1,5 @@
import axios from 'axios';
import { getMergedImage } from '$utils/api';
export class Shotter {
constructor({ map }) {
@ -54,13 +55,8 @@ export class Shotter {
}
makeShot = () => {
// console.log('shot', this.getTilePlacement());
const placement = this.getTilePlacement();
axios.get('http://alpha-map.vault48.org/engine/composerOrchid.php', {
params: { placement }
})
.then(console.log)
.catch(console.warn);
getMergedImage(placement);
}
}

View file

View file

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

12
src/redux/auth/reducer.js Normal file
View file

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

4
src/redux/auth/sagas.js Normal file
View file

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

50
src/redux/store.js Normal file
View file

@ -0,0 +1,50 @@
import { createStore, applyMiddleware, combineReducers, compose } from 'redux';
import { persistStore, persistReducer } from 'redux-persist';
import storage from 'redux-persist/lib/storage';
import createSagaMiddleware from 'redux-saga';
import createHistory from 'history/createBrowserHistory';
import { routerReducer, routerMiddleware } from 'react-router-redux';
import { authReducer } from '$redux/auth/reducer';
import { authSaga } from '$redux/auth/sagas';
const authPersistConfig = {
key: 'auth',
blacklist: [],
storage,
};
// create the saga middleware
export const sagaMiddleware = createSagaMiddleware();
// redux extension composer
/* eslint-disable no-underscore-dangle */
const composeEnhancers =
typeof window === 'object' &&
window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__
? window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({})
: compose;
/* eslint-enable no-underscore-dangle */
export const history = createHistory();
export const store = createStore(
combineReducers({
auth: persistReducer(authPersistConfig, authReducer),
routing: routerReducer
}),
composeEnhancers(applyMiddleware(routerMiddleware(history), sagaMiddleware))
);
export default function configureStore() {
// run sagas
sagaMiddleware.run(authSaga);
const persistor = persistStore(store);
return {
store,
persistor
};
}

View file

@ -6,6 +6,7 @@
@import 'stickers.less';
@import 'button.less';
@import 'logo.less';
@import 'user-button.less';
body {
font-family: sans-serif;

View file

@ -0,0 +1,52 @@
.user-bar {
width: 200px;
.button {
width: 100%;
}
}
.user-button {
width: 100%;
height: 48px;
display: flex;
user-select: none;
}
.user-button-picture {
width: 48px;
height: 48px;
background-size: cover;
background-position: 50% 50%;
background: rgba(0,0,0,0.3);
border-radius: 3px 0 0 3px;
}
.user-button-fields {
height: 100%;
display: flex;
flex-direction: column;
justify-content: center;
padding: 0 10px 0 10px;
overflow: hidden;
box-sizing: border-box;
div {
width: 100%;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
}
.user-button-name {
font-size: 12px;
}
.user-button-text {
font-size: 12px;
opacity: 0.5;
padding-top: 2px;
font-weight: 200;
}

33
src/utils/api.js Normal file
View file

@ -0,0 +1,33 @@
import axios from 'axios/index';
import { API } from '$constants/api';
const report = console.warn;
export const checkUserToken = ({
callback, fallback, id, token
}) => (
axios.get(API.GET_GUEST, {
params: { action: 'check_token', id, token }
})
.then(result => (result && result.data))
.then(data => ({ ...data, id, token }))
.then(callback)
.catch(fallback)
);
export const getGuestToken = ({ callback }) => (
axios.get(API.GET_GUEST, {
params: { action: 'gen_guest_token' }
})
.then(result => (result && result.data))
.then(callback)
.catch(report)
);
export const getMergedImage = ({ placement, callback }) => (
axios.get(API.COMPOSE, {
params: { placement }
})
.then(callback)
.catch(report)
);

25
src/utils/storage.js Normal file
View file

@ -0,0 +1,25 @@
const canStore = typeof window.localStorage !== 'undefined'
export const storeData = (key, data) => {
if (!canStore) return;
const value = JSON.stringify(data);
localStorage.setItem(key, value);
};
export const getData = key => {
if (!canStore) return;
let result = null;
try {
result = JSON.parse(localStorage.getItem(key));
} catch(e) {
result = null;
}
if (!result) return;
return result;
};

32826
stats.json

File diff suppressed because one or more lines are too long

View file

@ -50,7 +50,7 @@ const resolve = {
module.exports = () => {
/* Export */
const plugins = [
concatPlugin,
// concatPlugin,
htmlPlugin,
// flowPlugin,
gitPlugin,