finally saving things

This commit is contained in:
muerwre 2018-08-30 17:10:33 +07:00
parent e0f5d0238a
commit 47e4f4a97d
22 changed files with 277 additions and 38 deletions

10
package-lock.json generated
View file

@ -10130,6 +10130,11 @@
"integrity": "sha512-AeUmQ0oLN02flVHXWh9sSJF7mcdFq0ppid/JkErufc3hGIV/AMa8Fo9VgDo/cT2jFdOWoFvHp90qqBH54W+gjQ==",
"optional": true
},
"pt-sans-cyrillic": {
"version": "0.0.4",
"resolved": "https://registry.npmjs.org/pt-sans-cyrillic/-/pt-sans-cyrillic-0.0.4.tgz",
"integrity": "sha512-QbXgUHp5pbSbxbLdfpe5/MzuYPufqv36UMQUUI7QwceaaCJA8NQilysjlexjHLyK0GFv7NB5kl6ZAcIMBBBRXA=="
},
"public-encrypt": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.2.tgz",
@ -10209,6 +10214,11 @@
"integrity": "sha512-eTPo5t/4bgaMNZxyjWx6N2a6AuE0mq51KWvpc7nU/MAqixcI6v6KrGUKES0HaomdnolQBBXU/++X6/QQ9KL4tw==",
"dev": true
},
"raleway-cyrillic": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/raleway-cyrillic/-/raleway-cyrillic-4.0.2.tgz",
"integrity": "sha1-HcKzrqYwKwhTbs7jGIyS0li4jOE="
},
"ramda": {
"version": "0.24.1",
"resolved": "https://registry.npmjs.org/ramda/-/ramda-0.24.1.tgz",

View file

@ -57,6 +57,8 @@
"leaflet-routing-machine": "muerwre/leaflet-routing-machine#no-osrm-text",
"less": "^3.8.1",
"lodash": "^4.17.10",
"pt-sans-cyrillic": "0.0.4",
"raleway-cyrillic": "^4.0.2",
"react": "^16.3.2",
"react-dom": "^16.3.2",
"react-hot-loader": "^4.1.1",

View file

@ -5,15 +5,17 @@ import { RouterDialog } from '$components/router/RouterDialog';
import { StickersDialog } from '$components/stickers/StickersDialog';
import { TrashDialog } from '$components/trash/TrashDialog';
import { LogoDialog } from '$components/logo/LogoDialog';
import { SaveDialog } from '$components/save/SaveDialog';
export const EditorDialog = ({
mode, routerPoints, editor, activeSticker, logo
mode, routerPoints, editor, activeSticker, logo, user,
}) => {
const showDialog = (
mode === MODES.ROUTER
|| (mode === MODES.STICKERS && !activeSticker)
|| mode === MODES.TRASH
|| mode === MODES.LOGO
|| mode === MODES.SAVE
);
return (
@ -23,6 +25,7 @@ export const EditorDialog = ({
{ mode === MODES.STICKERS && <StickersDialog editor={editor} /> }
{ mode === MODES.TRASH && <TrashDialog editor={editor} /> }
{ mode === MODES.LOGO && <LogoDialog editor={editor} logo={logo} /> }
{ mode === MODES.SAVE && <SaveDialog editor={editor} user={user} /> }
</div>
);
};

View file

@ -2,7 +2,7 @@ import React from 'react';
import { MODES } from '$constants/modes';
import classnames from 'classnames';
import { toHours } from '$utils/time';
import { toHours } from '$utils/format';
import { Icon } from '$components/panels/Icon';
import { EditorDialog } from '$components/panels/EditorDialog';
@ -21,9 +21,11 @@ export class EditorPanel extends React.PureComponent {
startLogoMode = () => this.props.editor.changeMode(MODES.LOGO);
startSaveMode = () => this.props.editor.changeMode(MODES.SAVE);
render() {
const {
mode, routerPoints, editor, totalDistance, estimateTime, activeSticker, logo,
mode, routerPoints, editor, totalDistance, estimateTime, activeSticker, logo, user
} = this.props;
return (
@ -35,6 +37,7 @@ export class EditorPanel extends React.PureComponent {
activeSticker={activeSticker}
editor={editor}
logo={logo}
user={user}
/>
<LogoPreview logo={logo} />
@ -96,6 +99,7 @@ export class EditorPanel extends React.PureComponent {
<button
className="highlighted"
onClick={this.startSaveMode}
>
<span>СХОРОНИТЬ</span>
<Icon icon="icon-save" />

View file

@ -0,0 +1,77 @@
import React from 'react';
import { getUrlData } from '$utils/history';
import { toTranslit } from '$utils/format';
import { TIPS } from '$constants/tips';
import { MODES } from '$constants/modes';
import { postMap } from '$utils/api';
export class SaveDialog extends React.Component {
state = {
address: '',
title: '',
error: '',
sending: false,
finished: false,
success: false,
};
setTitle = ({ target: { value } }) => this.setState({ title: (value || '') });
setAddress = ({ target: { value } }) => this.setState({ address: (value || '') });
cancelSaving = () => this.props.editor.changeMode(MODES.NONE);
sendSaveRequest = () => {
const { route, stickers } = this.props.editor.dumpData();
const { title, address } = this.state;
const { id, token } = this.props.user;
postMap({
id,
token,
route,
stickers,
title,
address,
}).then(console.log).catch(console.warn);
};
render() {
const { address, title, error } = this.state;
const { path, host } = getUrlData();
return (
<div className="helper save-helper">
<div className="save-title">
<div className="save-title-input">
<label className="save-title-label">Название</label>
<input type="text" value={title} onChange={this.setTitle} autoFocus />
</div>
</div>
<div className="save-description">
<div className="save-address-input">
<label className="save-address-label">http://{host}/</label>
<input type="text" value={toTranslit(address.trim() || title.trim() || path).substr(0, 32)} onChange={this.setAddress} />
</div>
<div className="save-text">
{
error || TIPS.SAVE_INFO
}
</div>
<div className="save-buttons">
<div className="save-buttons-text" />
<div className="button-group">
<div className="button" onClick={this.cancelSaving}>Отмена</div>
<div className="button primary" onClick={this.sendSaveRequest}>Сохранить</div>
</div>
</div>
</div>
</div>
);
}
};

View file

@ -1,7 +1,8 @@
export const SERVER = 'http://alpha-map.vault48.org';
export const API = {
COMPOSE: `${SERVER}/engine/composerOrchid.php`,
GET_GUEST: `${SERVER}/engine/auth.php`,
CHECK_TOKEN: `${SERVER}/engine/auth.php`,
GET_MAP: `${SERVER}/engine/auth.php`,
GET_GUEST: `${SERVER}/engine/authOrchid.php`,
CHECK_TOKEN: `${SERVER}/engine/authOrchid.php`,
GET_MAP: `${SERVER}/engine/authOrchid.php`,
POST_MAP: `${SERVER}/engine/authOrchid.php?action=store`,
};

View file

@ -6,4 +6,5 @@ export const MODES = {
TRASH: 'TRASH',
NONE: 'NONE',
LOGO: 'LOGO',
SAVE: 'SAVE',
};

3
src/constants/tips.jsx Normal file
View file

@ -0,0 +1,3 @@
export const TIPS = {
SAVE_INFO: 'Вы можете задать своё название маршрута и адрес, по которому он будет доступен.'
};

View file

@ -9,7 +9,7 @@ import { DEFAULT_USER } from '$constants/auth';
import { getGuestToken, checkUserToken, getStoredMap } from '$utils/api';
import { storeData, getData } from '$utils/storage';
import { UserPanel } from '$components/panels/UserPanel';
import { getUrlData, replacePath } from '$utils/history';
import { getUrlData, pushPath } from '$utils/history';
export class App extends React.Component {
state = {
@ -31,7 +31,6 @@ export class App extends React.Component {
mapInit = () => {
const { path, mode } = getUrlData();
if (path) {
getStoredMap({ name: path })
.then(this.setDataOnLoad)
@ -42,6 +41,7 @@ export class App extends React.Component {
this.editor.stopEditing();
}
})
.catch(console.warn)
.catch(this.startEmptyEditor);
} else {
// this.hideLoader();
@ -50,10 +50,11 @@ export class App extends React.Component {
};
startEmptyEditor = () => {
console.log('starting empty');
const { user } = this.state;
if (!user || !user.random_url || !user.id) return;
replacePath(`/${user.random_url}/edit`);
pushPath(`/${user.random_url}/edit`);
this.editor.owner = user.id;
this.editor.startEditing();

View file

@ -5,6 +5,8 @@ import ReactDOM from 'react-dom';
import { App } from '$containers/App';
import '$styles/main.less';
import 'raleway-cyrillic';
// import { Provider } from 'react-redux';
// import { ConnectedRouter } from 'react-router-redux';
// import { PersistGate } from 'redux-persist/integration/react';

View file

@ -168,16 +168,17 @@ export class Editor {
this.changeMode(MODES.NONE);
};
setData = ({ route, stickers, format = 'old', owner }) => {
setData = ({ route, stickers, version = 1, owner }) => {
console.log('setting?', stickers);
if (route) {
this.poly.setPoints(route);
}
if (stickers) {
stickers.map(({ latlng, ang: angle, style }) => this.stickers.createSticker({
latlng,
angle: parseStickerAngle({ angle, format }),
sticker: parseStickerStyle({ style, format }),
stickers.map(sticker => this.stickers.createSticker({
latlng: sticker.latlng,
angle: parseStickerAngle({ sticker, version }),
sticker: parseStickerStyle({ sticker, version }),
}));
}
@ -185,7 +186,11 @@ export class Editor {
this.owner = owner;
}
this.map.map.fitBounds(this.poly.poly.getBounds());
if (!route || route.length <= 1) return;
const bounds = this.poly.poly.getBounds();
if (Object.values(bounds)) this.map.map.fitBounds(bounds);
};
startEditing = () => {
@ -194,5 +199,10 @@ export class Editor {
stopEditing = () => {
this.poly.poly.disableEdit();
}
};
dumpData = () => ({
route: this.poly.dumpData(),
stickers: this.stickers.dumpData(),
});
}

View file

@ -152,4 +152,6 @@ export class Poly {
};
clearArrows = () => this.arrows.clearLayers();
dumpData = () => this.latlngs;
}

View file

@ -9,9 +9,11 @@ export class Sticker {
constructor({
latlng, deleteSticker, map, lockMapClicks, sticker, angle = 2.2
}) {
this.latlng = latlng;
this.angle = angle;
this.isDragging = false;
this.map = map;
this.sticker = sticker;
this.deleteSticker = deleteSticker;
this.lockMapClicks = lockMapClicks;
@ -37,7 +39,7 @@ export class Sticker {
className: 'sticker-container',
});
this.sticker = marker(latlng, { icon: mark });
this.marker = marker(latlng, { icon: mark });
this.setAngle(angle);
@ -56,7 +58,7 @@ export class Sticker {
this.preventPropagations(e);
this.isDragging = true;
this.sticker.disableEdit();
this.marker.disableEdit();
this.lockMapClicks(true);
@ -75,7 +77,7 @@ export class Sticker {
this.preventPropagations(e);
this.isDragging = false;
this.sticker.enableEdit();
this.marker.enableEdit();
window.removeEventListener('mousemove', this.onDrag);
window.removeEventListener('mouseup', this.onDragStop);
@ -97,8 +99,6 @@ export class Sticker {
};
setAngle = angle => {
// $(active_sticker.container).css('left',6+x-parseInt(active_sticker.ctrl.css('left'))).css('top',6+y-parseInt(active_sticker.ctrl.css('top')));
//
const rad = 44;
const mrad = 76;
const x = ((Math.cos(angle + 3.14) * rad) - 30);
@ -114,7 +114,7 @@ export class Sticker {
this.stickerDelete.style.top = ay;
this.stickerArrow.style.transform = `rotate(${angle + 3.14}rad)`;
}
};
generateStickerSVG = sticker => (
`
@ -122,5 +122,11 @@ export class Sticker {
<use xlink:href="${stickers}#sticker-${sticker}" x="0" y="0" width="64" height="64" />
</svg>
`
)
);
dumpData = () => ({
angle: this.angle,
latlng: this.latlng,
sticker: this.sticker,
})
}

View file

@ -20,6 +20,7 @@ export class Stickers {
// };
createSticker = ({ latlng, sticker, angle = 2.2 }) => {
console.log('creating', latlng, sticker, angle);
const marker = new Sticker({
latlng,
angle,
@ -30,8 +31,8 @@ export class Stickers {
});
this.stickers.push(marker);
marker.sticker.addTo(this.map);
marker.sticker.enableEdit();
marker.marker.addTo(this.map);
marker.marker.enableEdit();
};
deleteStickerByReference = ref => {
@ -39,7 +40,7 @@ export class Stickers {
if (index < 0) return;
this.map.removeLayer(ref.sticker);
this.map.removeLayer(ref.marker);
this.stickers.splice(index, 1);
};
@ -49,5 +50,7 @@ export class Stickers {
this.deleteStickerByReference(sticker);
return true;
});
}
};
dumpData = () => this.stickers.map(sticker => sticker.dumpData());
}

View file

@ -8,3 +8,6 @@
@dialog_background: #222222;
@location_line: #ff3344;
@green_primary: #abc837;
@green_secondary: #009c80;

View file

@ -7,9 +7,10 @@
@import 'button.less';
@import 'logo.less';
@import 'user-button.less';
@import 'save.less';
body {
font-family: sans-serif;
font-family: 'Raleway', sans-serif;
font-size: 14px;
}

84
src/styles/save.less Normal file
View file

@ -0,0 +1,84 @@
.save-helper {
width: 443px;
padding: 0;
flex-direction: column;
}
.save-title {
padding: 10px;
width: 100%;
background: linear-gradient(160deg, @green_primary, @green_secondary);
flex-direction: column;
border-radius: 3px 3px 0 0;
font-weight: 200;
box-sizing: border-box;
}
.save-description {
padding: 10px;
}
.save-title-input {
background: rgba(0, 0, 0, 0.2);
border-radius: 2px;
display: flex;
input {
width: 100%;
padding: 5px;
background: transparent;
border: none;
outline: none;
color: white;
font-family: inherit;
font-size: 14px;
font-weight: 200;
}
}
.save-title-label {
display: flex;
padding: 5px 10px;
background: rgba(0,0,0,0.1);
height: 100%;
}
.save-address-input {
background: rgba(0, 0, 0, 0.2);
border-radius: 2px;
display: flex;
input {
width: 100%;
padding: 5px 5px 5px 2px;
background: transparent;
border: none;
outline: none;
color: white;
font-family: inherit;
font-size: 14px;
font-weight: 200;
}
}
.save-address-label {
display: flex;
padding: 5px 0 5px 10px;
height: 100%;
opacity: 0.5;
}
.save-text {
padding: 10px;
}
.save-buttons {
display: flex;
padding: 10px;
}
.save-buttons-text {
flex: 1;
}

View file

@ -32,3 +32,13 @@ export const getStoredMap = ({ name }) => axios.get(API.GET_MAP, {
action: 'load'
}
}).then(result => (result && result.data && result.data.data && result.data.owner && { ...result.data.data, owner: result.data.owner }));
export const postMap = ({ title, address, route, stickers, id, token }) => axios.post(API.POST_MAP, {
action: 'store',
title,
address,
route,
stickers,
id,
token,
}).then(result => (result && result.data && result.data.data && result.data.owner && { ...result.data.data, owner: result.data.owner }));

12
src/utils/format.js Normal file
View file

@ -0,0 +1,12 @@
const ru = [' ','\\.',',',':','\\?','#','Я','я','Ю','ю','Ч','ч','Ш','ш','Щ','щ','Ж','ж','А','а','Б','б','В','в','Г','г','Д','д','Е','е','Ё','ё','З','з','И','и','Й','й','К','к','Л','л','М','м','Н','н', 'О','о','П','п','Р','р','С','с','Т','т','У','у','Ф','ф','Х','х','Ц','ц','Ы','ы','Ь','ь','Ъ','ъ','Э','э'];
const en = ['_','','','','','','Ya','ya','Yu','yu','Ch','ch','Sh','sh','Sh','sh','Zh','zh','A','a','B','b','V','v','G','g','D','d','E','e','E','e','Z','z','I','i','J','j','K','k','L','l','M','m','N','n', 'O','o','P','p','R','r','S','s','T','t','U','u','F','f','H','h','C','c','Y','y','`','`','\'','\'','E', 'e'];
export const toHours = (info) => {
const hrs = parseInt(Number(info), 10);
const min = Math.round((Number(info) - hrs) * 60);
const lmin = min < 10 ? '0' + min : min;
return `${hrs}:${lmin}`;
};
export const toTranslit = string => ru.reduce((text, el, i) => (text.replace(new RegExp(ru[i], 'g'), en[i])), string);

View file

@ -1,12 +1,13 @@
export const getPath = () => (window.location && window.location.pathname &&
window.location.pathname.replace(/^\//, ''));
export const replacePath = url => window.history.replaceState(url, 'Редактирование маршрута', url);
export const pushPath = url => window.history.pushState(url, 'Редактирование маршрута', url);
export const getUrlData = () => {
const url = getPath();
const [path, mode] = url.split('/');
const { host } = window.location;
return { path, mode };
return { path, mode, host };
};

View file

@ -2,6 +2,15 @@
functions to parse old maps data
*/
export const parseStickerAngle = ({ angle, format }) => parseFloat((format === 'old' ? angle - 3.14 : angle));
export const parseStickerAngle = ({ sticker, version }) => {
console.log('stick', sticker, version);
return sticker && version && parseInt(version, 10) === 2
? parseFloat(sticker.angle)
: parseFloat(sticker.ang - 3.14);
};
export const parseStickerStyle = ({ style, format }) => (format === 'old' ? 'green' : style);
export const parseStickerStyle = ({ sticker, version }) => (
sticker && version && parseInt(version, 10) === 2
? sticker.sticker
: 'basic'
);

View file

@ -1,6 +0,0 @@
export const toHours = (info) => {
const hrs = parseInt(Number(info), 10);
const min = Math.round((Number(info) - hrs) * 60);
const lmin = min < 10 ? '0' + min : min;
return `${hrs}:${lmin}`;
};