diff --git a/package-lock.json b/package-lock.json index a283bb5..54a5623 100644 --- a/package-lock.json +++ b/package-lock.json @@ -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", diff --git a/package.json b/package.json index 80e8ae4..d37bb7a 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/src/components/panels/EditorDialog.jsx b/src/components/panels/EditorDialog.jsx index e8a4565..a230e5f 100644 --- a/src/components/panels/EditorDialog.jsx +++ b/src/components/panels/EditorDialog.jsx @@ -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 && } { mode === MODES.TRASH && } { mode === MODES.LOGO && } + { mode === MODES.SAVE && } ); }; diff --git a/src/components/panels/EditorPanel.jsx b/src/components/panels/EditorPanel.jsx index 97ee003..fab5895 100644 --- a/src/components/panels/EditorPanel.jsx +++ b/src/components/panels/EditorPanel.jsx @@ -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} /> @@ -96,6 +99,7 @@ export class EditorPanel extends React.PureComponent { СХОРОНИТЬ diff --git a/src/components/save/SaveDialog.jsx b/src/components/save/SaveDialog.jsx new file mode 100644 index 0000000..5554b75 --- /dev/null +++ b/src/components/save/SaveDialog.jsx @@ -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 ( + + + + Название + + + + + + + http://{host}/ + + + + + { + error || TIPS.SAVE_INFO + } + + + + + + Отмена + Сохранить + + + + + + + ); + } +}; diff --git a/src/constants/api.js b/src/constants/api.js index 08fd49e..e7e9e22 100644 --- a/src/constants/api.js +++ b/src/constants/api.js @@ -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`, }; diff --git a/src/constants/modes.js b/src/constants/modes.js index f7ef134..a90de73 100644 --- a/src/constants/modes.js +++ b/src/constants/modes.js @@ -6,4 +6,5 @@ export const MODES = { TRASH: 'TRASH', NONE: 'NONE', LOGO: 'LOGO', + SAVE: 'SAVE', }; diff --git a/src/constants/tips.jsx b/src/constants/tips.jsx new file mode 100644 index 0000000..1d16936 --- /dev/null +++ b/src/constants/tips.jsx @@ -0,0 +1,3 @@ +export const TIPS = { + SAVE_INFO: 'Вы можете задать своё название маршрута и адрес, по которому он будет доступен.' +}; diff --git a/src/containers/App.jsx b/src/containers/App.jsx index 06184a2..a5a7056 100644 --- a/src/containers/App.jsx +++ b/src/containers/App.jsx @@ -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(); diff --git a/src/index.js b/src/index.js index ebc4153..e6f3bb6 100644 --- a/src/index.js +++ b/src/index.js @@ -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'; diff --git a/src/modules/Editor.js b/src/modules/Editor.js index 76b58ab..807dc4a 100644 --- a/src/modules/Editor.js +++ b/src/modules/Editor.js @@ -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(), + }); } diff --git a/src/modules/Poly.js b/src/modules/Poly.js index 888a79e..955d1f6 100644 --- a/src/modules/Poly.js +++ b/src/modules/Poly.js @@ -152,4 +152,6 @@ export class Poly { }; clearArrows = () => this.arrows.clearLayers(); + + dumpData = () => this.latlngs; } diff --git a/src/modules/Sticker.js b/src/modules/Sticker.js index 5f5d574..225b6de 100644 --- a/src/modules/Sticker.js +++ b/src/modules/Sticker.js @@ -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 { ` - ) + ); + + dumpData = () => ({ + angle: this.angle, + latlng: this.latlng, + sticker: this.sticker, + }) } diff --git a/src/modules/Stickers.js b/src/modules/Stickers.js index 48d2a9b..7549947 100644 --- a/src/modules/Stickers.js +++ b/src/modules/Stickers.js @@ -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()); } diff --git a/src/styles/colors.less b/src/styles/colors.less index c8553f4..bda5f89 100644 --- a/src/styles/colors.less +++ b/src/styles/colors.less @@ -8,3 +8,6 @@ @dialog_background: #222222; @location_line: #ff3344; + +@green_primary: #abc837; +@green_secondary: #009c80; diff --git a/src/styles/main.less b/src/styles/main.less index b95038c..a280a4f 100644 --- a/src/styles/main.less +++ b/src/styles/main.less @@ -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; } diff --git a/src/styles/save.less b/src/styles/save.less new file mode 100644 index 0000000..0f2361f --- /dev/null +++ b/src/styles/save.less @@ -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; +} diff --git a/src/utils/api.js b/src/utils/api.js index ceefc7a..ecfe9c2 100644 --- a/src/utils/api.js +++ b/src/utils/api.js @@ -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 })); diff --git a/src/utils/format.js b/src/utils/format.js new file mode 100644 index 0000000..8b2162a --- /dev/null +++ b/src/utils/format.js @@ -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); diff --git a/src/utils/history.js b/src/utils/history.js index b18d42b..4eb9172 100644 --- a/src/utils/history.js +++ b/src/utils/history.js @@ -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 }; }; diff --git a/src/utils/import.js b/src/utils/import.js index a0e2852..e7cb1f0 100644 --- a/src/utils/import.js +++ b/src/utils/import.js @@ -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' +); diff --git a/src/utils/time.js b/src/utils/time.js deleted file mode 100644 index d9b5c15..0000000 --- a/src/utils/time.js +++ /dev/null @@ -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}`; -};