From 87670770b0a685a6bb540ea5a1ba7fc58f2bd9a6 Mon Sep 17 00:00:00 2001 From: Fedor Katurov Date: Thu, 9 Jan 2020 10:59:26 +0700 Subject: [PATCH] moved editor to separate reducer --- src/components/Cursor.tsx | 43 +- src/components/dialogs/CancelDialog.tsx | 16 +- src/components/dialogs/MapListDialog.tsx | 124 +++--- src/components/dialogs/RouterDialog.tsx | 35 +- src/components/dialogs/SaveDialog.tsx | 35 +- src/components/dialogs/ShotPrefetchDialog.tsx | 4 +- src/components/dialogs/StickersDialog.tsx | 61 +-- src/components/dialogs/TitleDialog.tsx | 6 +- src/components/panels/DistanceBar.tsx | 33 +- src/components/panels/EditorDialog.tsx | 37 +- src/components/panels/EditorPanel.tsx | 127 +++--- src/components/panels/TopRightPanel.tsx | 20 +- src/components/panels/UserPanel.tsx | 73 ++-- src/components/renderer/Renderer.tsx | 38 +- src/constants/auth.ts | 2 +- src/containers/App.tsx | 23 +- src/containers/LeftDialog.tsx | 10 +- src/containers/map/Map/index.tsx | 4 +- src/redux/editor/actions.ts | 120 +++++ src/redux/editor/constants.ts | 47 ++ src/redux/editor/handlers.ts | 200 +++++++++ src/redux/editor/index.ts | 85 ++++ src/redux/editor/sagas.ts | 256 +++++++++++ src/redux/editor/selectors.ts | 7 + src/redux/map/actions.ts | 5 + src/redux/map/constants.ts | 1 + src/redux/map/handlers.ts | 7 + src/redux/map/index.ts | 2 + src/redux/map/sagas.ts | 181 ++++---- src/redux/store.ts | 23 +- src/redux/user/actions.ts | 106 ++--- src/redux/user/constants.ts | 68 +-- src/redux/user/handlers.ts | 178 +------- src/redux/user/index.ts | 92 +--- src/redux/user/sagas.ts | 411 ++++++++---------- src/redux/user/selectors.ts | 6 +- src/utils/format.ts | 5 +- tsconfig.json | 3 + 38 files changed, 1425 insertions(+), 1069 deletions(-) create mode 100644 src/redux/editor/actions.ts create mode 100644 src/redux/editor/constants.ts create mode 100644 src/redux/editor/handlers.ts create mode 100644 src/redux/editor/index.ts create mode 100644 src/redux/editor/sagas.ts create mode 100644 src/redux/editor/selectors.ts diff --git a/src/components/Cursor.tsx b/src/components/Cursor.tsx index 1cf5707..4f5b114 100644 --- a/src/components/Cursor.tsx +++ b/src/components/Cursor.tsx @@ -1,17 +1,18 @@ import React from 'react'; import { Icon } from '~/components/panels/Icon'; import { MODES } from '~/constants/modes'; -import { IStickerPack, STICKERS } from '~/constants/stickers'; +import { STICKERS } from '~/constants/stickers'; import { StickerIcon } from '~/components/StickerIcon'; -import { IRootReducer } from '~/redux/user'; +import { connect } from 'react-redux'; +import { selectEditor } from '~/redux/editor/selectors' -interface Props { - mode: IRootReducer['mode'], - sticker: string, - set: keyof IStickerPack, -} +const mapStateToProps = state => ({ + editor: selectEditor +}); -export class Cursor extends React.PureComponent { +const mapDispatchToProps = {}; + +class CursorUnconnected extends React.PureComponent { componentDidMount() { window.addEventListener('mousemove', this.moveCursor); } @@ -27,15 +28,27 @@ export class Cursor extends React.PureComponent { cursor: HTMLElement = null; render() { - const { mode, set, sticker } = this.props; - const activeSticker = (sticker && set && STICKERS[set] && STICKERS[set].layers[sticker]); + const { + editor: { mode, set, sticker }, + } = this.props; + + const activeSticker = sticker && set && STICKERS[set] && STICKERS[set].layers[sticker]; return ( -
{ this.cursor = el; }}> - { mode === MODES.ROUTER && } - { mode === MODES.POLY && } - { mode === MODES.STICKERS && activeSticker && } +
{ + this.cursor = el; + }} + > + {mode === MODES.ROUTER && } + {mode === MODES.POLY && } + {mode === MODES.STICKERS && activeSticker && }
); } -}; +} + +const Cursor = connect()(CursorUnconnected); + +export { Cursor } \ No newline at end of file diff --git a/src/components/dialogs/CancelDialog.tsx b/src/components/dialogs/CancelDialog.tsx index d2aa630..ef01f87 100644 --- a/src/components/dialogs/CancelDialog.tsx +++ b/src/components/dialogs/CancelDialog.tsx @@ -2,21 +2,23 @@ import React from 'react'; import { MODES } from '~/constants/modes'; import { Icon } from '~/components/panels/Icon'; -import { setMode, stopEditing } from "~/redux/user/actions"; +import { editorSetMode, editorStopEditing } from '~/redux/editor/actions'; -type Props = { - stopEditing: typeof stopEditing, - setMode: typeof setMode, - width: number, +const mapStateToProps = () => ({}); +const mapDispatchToProps = { + editorSetMode, + editorStopEditing, }; +type Props = ReturnType & typeof mapDispatchToProps & { width?: number }; + export class CancelDialog extends React.Component { cancel = () => { - this.props.stopEditing(); + this.props.editorStopEditing(); }; proceed = () => { - this.props.setMode(MODES.NONE); + this.props.editorSetMode(MODES.NONE); }; render() { diff --git a/src/components/dialogs/MapListDialog.tsx b/src/components/dialogs/MapListDialog.tsx index fb35991..bc6328b 100644 --- a/src/components/dialogs/MapListDialog.tsx +++ b/src/components/dialogs/MapListDialog.tsx @@ -6,12 +6,14 @@ import { searchSetDistance, searchSetTitle, searchSetTab, - setDialogActive, mapsLoadMore, dropRoute, modifyRoute, toggleRouteStarred, } from '~/redux/user/actions'; + +import { editorSetDialogActive } from '~/redux/editor/actions'; + import { isMobile } from '~/utils/window'; import classnames from 'classnames'; @@ -19,28 +21,60 @@ import Range from 'rc-slider/lib/Range'; import { TABS, TABS_TITLES } from '~/constants/dialogs'; import { Icon } from '~/components/panels/Icon'; import { pushPath } from '~/utils/history'; -import { IRootState, IRouteListItem } from '~/redux/user'; +import { IRouteListItem } from '~/redux/user'; import { ROLES } from '~/constants/auth'; import { IState } from '~/redux/store'; -export interface IMapListDialogProps extends IRootState { - marks: { [x: number]: string }; - routes_sorted: Array; - routes: IRootState['routes']; - ready: IRootState['ready']; - role: IRootState['user']['role']; - mapsLoadMore: typeof mapsLoadMore; - searchSetDistance: typeof searchSetDistance; - searchSetTitle: typeof searchSetTitle; - searchSetTab: typeof searchSetTab; - setDialogActive: typeof setDialogActive; - dropRoute: typeof dropRoute; - modifyRoute: typeof modifyRoute; - toggleRouteStarred: typeof toggleRouteStarred; -} +const mapStateToProps = ({ + editor: { editing }, + user: { + routes, + user: { role }, + }, +}: IState) => { + if (routes.filter.max >= 9999) { + return { + routes, + editing, + marks: {}, + ready: false, + role, + }; + } -export interface IMapListDialogState { + return { + role, + routes, + editing, + ready: true, + marks: [...new Array(Math.floor((routes.filter.max - routes.filter.min) / 25) + 1)].reduce( + (obj, el, i) => ({ + ...obj, + [routes.filter.min + i * 25]: ` ${routes.filter.min + i * 25}${ + routes.filter.min + i * 25 >= 200 ? '+' : '' + } + `, + }), + {} + ), + }; +}; + +const mapDispatchToProps = { + searchSetDistance, + searchSetTitle, + searchSetTab, + editorSetDialogActive, + mapsLoadMore, + dropRoute, + modifyRoute, + toggleRouteStarred, +}; + +type Props = ReturnType & typeof mapDispatchToProps & {} + +export interface State { menu_target: IRouteListItem['address']; editor_target: IRouteListItem['address']; @@ -48,7 +82,7 @@ export interface IMapListDialogState { is_dropping: boolean; } -class MapListDialogUnconnected extends React.Component { +class MapListDialogUnconnected extends React.Component { state = { menu_target: null, editor_target: null, @@ -92,14 +126,11 @@ class MapListDialogUnconnected extends React.Component { - if (isMobile()) this.props.setDialogActive(false); + if (isMobile()) this.props.editorSetDialogActive(false); - // pushPath(`/${_id}/${this.props.editing ? 'edit' : ''}`); this.stopEditing(); pushPath(`/${_id}`); - - // pushPath(`/${_id}/${this.props.editing ? 'edit' : ''}`); }; onScroll = (e: { @@ -148,7 +179,7 @@ class MapListDialogUnconnected extends React.Component { - if (routes.filter.max >= 9999) { - return { - routes, - editing, - marks: {}, - ready: false, - role, - }; - } - - return { - role, - routes, - editing, - ready: true, - marks: [...new Array(Math.floor((routes.filter.max - routes.filter.min) / 25) + 1)].reduce( - (obj, el, i) => ({ - ...obj, - [routes.filter.min + i * 25]: ` ${routes.filter.min + i * 25}${ - routes.filter.min + i * 25 >= 200 ? '+' : '' - } - `, - }), - {} - ), - }; -}; - -const mapDispatchToProps = { - searchSetDistance, - searchSetTitle, - searchSetTab, - setDialogActive, - mapsLoadMore, - dropRoute, - modifyRoute, - toggleRouteStarred, -}; const MapListDialog = connect(mapStateToProps, mapDispatchToProps)(MapListDialogUnconnected); diff --git a/src/components/dialogs/RouterDialog.tsx b/src/components/dialogs/RouterDialog.tsx index adaa3bc..a362d9e 100644 --- a/src/components/dialogs/RouterDialog.tsx +++ b/src/components/dialogs/RouterDialog.tsx @@ -1,9 +1,6 @@ import React from 'react'; import { Icon } from '~/components/panels/Icon'; -import { - routerCancel as routerCancelAction, - routerSubmit as routerSubmitAction, -} from "~/redux/user/actions"; +import * as EDITOR_ACTIONS from '~/redux/editor/actions' import classnames from "classnames"; type Props = { @@ -11,11 +8,11 @@ type Props = { width: number, is_routing: boolean, - routerCancel: typeof routerCancelAction, - routerSubmit: typeof routerSubmitAction, + editorRouterCancel: typeof EDITOR_ACTIONS.editorRouterCancel, + editorRouterSubmit: typeof EDITOR_ACTIONS.editorRouterSubmit, } -const noPoints = ({ routerCancel }: { routerCancel: typeof routerCancelAction }) => ( +const noPoints = ({ editorRouterCancel }: { editorRouterCancel: typeof EDITOR_ACTIONS.editorRouterCancel }) => (
@@ -28,7 +25,7 @@ const noPoints = ({ routerCancel }: { routerCancel: typeof routerCancelAction })
-
+
Отмена
@@ -36,7 +33,7 @@ const noPoints = ({ routerCancel }: { routerCancel: typeof routerCancelAction }) ); -const firstPoint = ({ routerCancel }: { routerCancel: typeof routerCancelAction }) => ( +const firstPoint = ({ editorRouterCancel }: { editorRouterCancel: typeof EDITOR_ACTIONS.editorRouterCancel }) => (
@@ -47,7 +44,7 @@ const firstPoint = ({ routerCancel }: { routerCancel: typeof routerCancelAction
-
+
Отмена
@@ -56,10 +53,10 @@ const firstPoint = ({ routerCancel }: { routerCancel: typeof routerCancelAction ); const draggablePoints = ({ - routerCancel, routerSubmit + editorRouterCancel, editorRouterSubmit }: { - routerCancel: typeof routerCancelAction, - routerSubmit: typeof routerSubmitAction, + editorRouterCancel: typeof EDITOR_ACTIONS.editorRouterCancel, + editorRouterSubmit: typeof EDITOR_ACTIONS.editorRouterSubmit, }) => (
@@ -71,10 +68,10 @@ const draggablePoints = ({
-
+
Отмена
-
+
Применить
@@ -83,13 +80,13 @@ const draggablePoints = ({ ); export const RouterDialog = ({ - routerPoints, routerCancel, routerSubmit, width, is_routing, + routerPoints, editorRouterCancel, editorRouterSubmit, width, is_routing, }: Props) => (
- {!routerPoints && noPoints({ routerCancel })} - {routerPoints === 1 && firstPoint({ routerCancel })} - {routerPoints >= 2 && draggablePoints({ routerCancel, routerSubmit })} + {!routerPoints && noPoints({ editorRouterCancel })} + {routerPoints === 1 && firstPoint({ editorRouterCancel })} + {routerPoints >= 2 && draggablePoints({ editorRouterCancel, editorRouterSubmit })}
); diff --git a/src/components/dialogs/SaveDialog.tsx b/src/components/dialogs/SaveDialog.tsx index 67fb1ec..22ef43a 100644 --- a/src/components/dialogs/SaveDialog.tsx +++ b/src/components/dialogs/SaveDialog.tsx @@ -10,17 +10,17 @@ import classnames from 'classnames'; import ExpandableTextarea from 'react-expandable-textarea'; import { connect } from 'react-redux'; import { selectMap } from '~/redux/map/selectors'; -import { selectUser } from '~/redux/user/selectors'; -import * as USER_ACTIONS from '~/redux/user/actions'; +import * as EDITOR_ACTIONS from '~/redux/editor/actions'; +import { selectEditor } from '~/redux/editor/selectors'; const mapStateToProps = state => ({ map: selectMap(state), - user: selectUser(state), + editor: selectEditor(state), }); const mapDispatchToProps = { - setMode: USER_ACTIONS.setMode, - sendSaveRequest: USER_ACTIONS.sendSaveRequest, + editorSetMode: EDITOR_ACTIONS.editorSetMode, + editorSendSaveRequest: EDITOR_ACTIONS.editorSendSaveRequest, }; type Props = ReturnType & typeof mapDispatchToProps & { width: number }; @@ -33,14 +33,14 @@ interface State { } class SaveDialogUnconnected extends React.Component { - constructor(props) { + constructor(props: Props) { super(props); this.state = { - address: props.address || '', - title: props.title || '', - is_public: props.is_public || false, - description: props.description || '', + address: props.map.address || '', + title: props.map.title || '', + is_public: props.map.is_public || false, + description: props.map.description || '', }; } @@ -57,16 +57,18 @@ class SaveDialogUnconnected extends React.Component { setTitle = ({ target: { value } }) => this.setState({ title: (value && value.substr(0, 64)) || '' }); + setAddress = ({ target: { value } }) => this.setState({ address: (value && value.substr(0, 32)) || '' }); + setDescription = ({ target: { value } }) => this.setState({ description: (value && value.substr(0, 256)) || '' }); - sendSaveRequest = (e, force = false) => { + editorSendSaveRequest = (e, force = false) => { const { title, is_public, description } = this.state; const address = this.getAddress(); - this.props.sendSaveRequest({ + this.props.editorSendSaveRequest({ title, address, force, @@ -74,9 +76,10 @@ class SaveDialogUnconnected extends React.Component { description, }); }; - forceSaveRequest = e => this.sendSaveRequest(e, true); - cancelSaving = () => this.props.setMode(MODES.NONE); + forceSaveRequest = e => this.editorSendSaveRequest(e, true); + + cancelSaving = () => this.props.editorSetMode(MODES.NONE); onCopy = e => { e.preventDefault(); @@ -91,7 +94,7 @@ class SaveDialogUnconnected extends React.Component { render() { const { title, is_public, description } = this.state; const { - user: { save_error, save_finished, save_overwriting, save_loading }, + editor: { save_error, save_finished, save_overwriting, save_loading }, width, } = this.props; const { host, protocol } = getUrlData(); @@ -157,7 +160,7 @@ class SaveDialogUnconnected extends React.Component {
)} {!save_finished && !save_overwriting && ( -
+
Сохранить
)} diff --git a/src/components/dialogs/ShotPrefetchDialog.tsx b/src/components/dialogs/ShotPrefetchDialog.tsx index a03eb5e..d9aa82f 100644 --- a/src/components/dialogs/ShotPrefetchDialog.tsx +++ b/src/components/dialogs/ShotPrefetchDialog.tsx @@ -1,9 +1,9 @@ import React from 'react'; import { connect } from 'react-redux'; -import { selectUserRenderer } from '~/redux/user/selectors'; +import { selectEditorRenderer } from '~/redux/editor/selectors'; const mapStateToProps = state => ({ - renderer: selectUserRenderer(state), + renderer: selectEditorRenderer(state), }); type Props = ReturnType & {}; diff --git a/src/components/dialogs/StickersDialog.tsx b/src/components/dialogs/StickersDialog.tsx index 97d27c8..9506678 100644 --- a/src/components/dialogs/StickersDialog.tsx +++ b/src/components/dialogs/StickersDialog.tsx @@ -1,39 +1,44 @@ -// @flow import React from 'react'; import { STICKERS } from '~/constants/stickers'; -import { setActiveSticker as setActiveStickerAction } from "~/redux/user/actions"; +import * as EDITOR_ACTIONS from '~/redux/editor/actions'; +import { connect } from 'react-redux'; -interface Props { - setActiveSticker: typeof setActiveStickerAction, - width: number, -} +const mapStateToProps = () => ({}); +const mapDispatchToProps = { + editorSetActiveSticker: EDITOR_ACTIONS.editorSetActiveSticker, +}; -export const StickersDialog = ({ setActiveSticker, width }: Props) => ( +type Props = ReturnType & + typeof mapDispatchToProps & { + width: number; + }; + +const StickersDialogUnconnected = ({ editorSetActiveSticker, width }: Props) => (
- { - Object.keys(STICKERS).map(set => ( -
-
{STICKERS[set].title || null}
-
- { - Object.keys(STICKERS[set].layers).map(sticker => ( -
setActiveSticker({ set, sticker })} - /> - )) - } -
+ {Object.keys(STICKERS).map(set => ( +
+
{STICKERS[set].title || null}
+
+ {Object.keys(STICKERS[set].layers).map(sticker => ( +
editorSetActiveSticker({ set, sticker })} + /> + ))}
- )) - } +
+ ))}
); + +const StickersDialog = connect(mapStateToProps, mapDispatchToProps)(StickersDialogUnconnected); + +export { StickersDialog }; \ No newline at end of file diff --git a/src/components/dialogs/TitleDialog.tsx b/src/components/dialogs/TitleDialog.tsx index 50c3464..75fd328 100644 --- a/src/components/dialogs/TitleDialog.tsx +++ b/src/components/dialogs/TitleDialog.tsx @@ -5,11 +5,11 @@ import classnames from 'classnames'; import { getStyle } from '~/utils/dom'; import { nearestInt } from '~/utils/geom'; import { parseDesc } from '~/utils/format'; -import { selectUser } from '~/redux/user/selectors'; import { selectMap } from '~/redux/map/selectors'; +import { selectEditor } from '~/redux/editor/selectors'; const mapStateToProps = state => ({ - user: selectUser(state), + editor: selectEditor(state), map: selectMap(state), }); @@ -80,7 +80,7 @@ export class TitleDialogUnconnected extends React.PureComponent { render() { const { - user: { editing }, + editor: { editing }, map: { title, description }, } = this.props; const { raised, height, height_raised } = this.state; diff --git a/src/components/panels/DistanceBar.tsx b/src/components/panels/DistanceBar.tsx index edcf43c..866537d 100644 --- a/src/components/panels/DistanceBar.tsx +++ b/src/components/panels/DistanceBar.tsx @@ -1,20 +1,25 @@ -// flow import React from 'react'; import { toHours } from '~/utils/format'; import { Icon } from '~/components/panels/Icon'; import { connect } from 'react-redux'; -// import Slider from 'rc-slider'; import Slider from 'rc-slider/lib/Slider'; -import { bindActionCreators } from 'redux'; -import { setSpeed } from '~/redux/user/actions'; -import { IRootState } from "~/redux/user"; +import { editorSetSpeed } from '~/redux/editor/actions'; import { Tooltip } from "~/components/panels/Tooltip"; import { isMobile } from "~/utils/window"; +import { IState } from '~/redux/store'; -interface Props extends IRootState { - setSpeed: typeof setSpeed, +function mapStateToProps(state) { + const { + editor: { distance, estimated, speed }, + }: IState = state; + + return { distance, estimated, speed }; } +const mapDispatchToProps = { editorSetSpeed }; + +type Props = ReturnType & typeof mapDispatchToProps & {}; + interface State { dialogOpened: boolean, } @@ -68,7 +73,7 @@ class Component extends React.PureComponent { min={min} max={max} step={step} - onChange={this.props.setSpeed} + onChange={this.props.editorSetSpeed} defaultValue={15} value={speed} marks={marks} @@ -81,18 +86,6 @@ class Component extends React.PureComponent { } } -function mapStateToProps(state) { - const { - user: { distance, estimated, speed }, - } = state; - - return { distance, estimated, speed }; -} - -const mapDispatchToProps = dispatch => bindActionCreators({ - setSpeed, -}, dispatch); - export const DistanceBar = connect( mapStateToProps, mapDispatchToProps diff --git a/src/components/panels/EditorDialog.tsx b/src/components/panels/EditorDialog.tsx index 0ade17d..3ed02b0 100644 --- a/src/components/panels/EditorDialog.tsx +++ b/src/components/panels/EditorDialog.tsx @@ -12,29 +12,13 @@ import { connect } from 'react-redux'; import { ProviderDialog } from '~/components/dialogs/ProviderDialog'; import { ShotPrefetchDialog } from '~/components/dialogs/ShotPrefetchDialog'; -import { selectUserMode } from '~/redux/user/selectors'; +import { selectEditorMode } from '~/redux/editor/selectors'; -const mapStateToProps = state => ({ mode: selectUserMode(state) }); - -// const mapDispatchToProps = dispatch => bindActionCreators({ -// routerCancel: USER_ACTIONS.routerCancel, -// routerSubmit: USER_ACTIONS.routerSubmit, -// setActiveSticker: USER_ACTIONS.setActiveSticker, -// clearStickers: USER_ACTIONS.clearStickers, -// clearPoly: USER_ACTIONS.clearPoly, -// clearAll: USER_ACTIONS.clearAll, -// clearCancel: USER_ACTIONS.clearCancel, -// stopEditing: USER_ACTIONS.stopEditing, -// setEditing: USER_ACTIONS.setEditing, -// setMode: USER_ACTIONS.setMode, -// sendSaveRequest: USER_ACTIONS.sendSaveRequest, -// changeProvider: USER_ACTIONS.changeProvider, -// mapSetLogo: MAP_ACTIONS.mapSetLogo, -// }, dispatch); +const mapStateToProps = state => ({ mode: selectEditorMode(state) }); type Props = ReturnType & { - width: number; - }; + width: number; +}; const DIALOG_CONTENTS: { [x: string]: any } = { [MODES.ROUTER]: RouterDialog, @@ -47,12 +31,9 @@ const DIALOG_CONTENTS: { [x: string]: any } = { [MODES.SHOT_PREFETCH]: ShotPrefetchDialog, }; -export const Component = (props: Props) => - props.mode && DIALOG_CONTENTS[props.mode] - ? createElement(DIALOG_CONTENTS[props.mode]) - : null; +const EditorDialogUnconnected = (props: Props) => + props.mode && DIALOG_CONTENTS[props.mode] ? createElement(DIALOG_CONTENTS[props.mode]) : null; -export const EditorDialog = connect( - mapStateToProps - // mapDispatchToProps -)(Component); +const EditorDialog = connect(mapStateToProps)(EditorDialogUnconnected); + +export { EditorDialog }; diff --git a/src/components/panels/EditorPanel.tsx b/src/components/panels/EditorPanel.tsx index add6782..ef4bf4d 100644 --- a/src/components/panels/EditorPanel.tsx +++ b/src/components/panels/EditorPanel.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { PureComponent } from 'react'; import { MODES } from '~/constants/modes'; import classnames from 'classnames'; @@ -6,21 +6,34 @@ import { Icon } from '~/components/panels/Icon'; import { EditorDialog } from '~/components/panels/EditorDialog'; import { bindActionCreators } from 'redux'; import { connect } from 'react-redux'; -import { setMode, startEditing, stopEditing, takeAShot, keyPressed } from '~/redux/user/actions'; -import { IRootState } from "~/redux/user"; -import { Tooltip } from "~/components/panels/Tooltip"; +import { + editorSetMode, + editorStartEditing, + editorStopEditing, + editorTakeAShot, + editorKeyPressed, +} from '~/redux/editor/actions'; +import { Tooltip } from '~/components/panels/Tooltip'; +import { IState } from '~/redux/store'; +import { selectEditor } from '~/redux/editor/selectors'; -interface Props extends IRootState { - routing: IRootState['features']['routing'], - setMode: typeof setMode, - startEditing: typeof startEditing, - stopEditing: typeof stopEditing, - keyPressed: EventListenerOrEventListenerObject, -} +const mapStateToProps = (state: IState) => ({ + editor: selectEditor(state), +}); -class Component extends React.PureComponent { +const mapDispatchToProps = { + editorSetMode, + editorStartEditing, + editorStopEditing, + editorTakeAShot, + editorKeyPressed, +}; + +type Props = ReturnType & typeof mapDispatchToProps & {}; + +class EditorPanelUnconnected extends PureComponent { componentDidMount() { - window.addEventListener('keydown', this.props.keyPressed); + window.addEventListener('keydown', this.props.editorKeyPressed as any); const obj = document.getElementById('control-dialog'); const { width } = this.panel.getBoundingClientRect(); @@ -33,29 +46,38 @@ class Component extends React.PureComponent { panel: HTMLElement = null; componentWillUnmount() { - window.removeEventListener('keydown', this.props.keyPressed); + window.removeEventListener('keydown', this.props.editorKeyPressed as any); } - startPolyMode = () => this.props.setMode(MODES.POLY); - startStickerMode = () => this.props.setMode(MODES.STICKERS_SELECT); - startRouterMode = () => this.props.setMode(MODES.ROUTER); - startTrashMode = () => this.props.setMode(MODES.TRASH); + startPolyMode = () => this.props.editorSetMode(MODES.POLY); + startStickerMode = () => this.props.editorSetMode(MODES.STICKERS_SELECT); + startRouterMode = () => this.props.editorSetMode(MODES.ROUTER); + startTrashMode = () => this.props.editorSetMode(MODES.TRASH); startSaveMode = () => { // if (!this.props.changed) return; - this.props.setMode(MODES.SAVE); + this.props.editorSetMode(MODES.SAVE); }; render() { const { - mode, changed, editing, routing, + editor: { + mode, + changed, + editing, + features: { routing }, + }, } = this.props; return (
-
{ this.panel = el; }}> +
{ + this.panel = el; + }} + >
- { - routing && + {routing && ( - } - + )} -
@@ -99,10 +121,7 @@ class Component extends React.PureComponent {
- @@ -114,59 +133,21 @@ class Component extends React.PureComponent {
-
-
- - +
); } } -function mapStateToProps(state) { - const { - user: { - editing, - mode, - changed, - features: { - routing, - } - }, - } = state; - - return { - editing, - mode, - changed, - routing, - }; -} - -const mapDispatchToProps = dispatch => bindActionCreators({ - setMode, - // setLogo, - startEditing, - stopEditing, - takeAShot, - keyPressed, -}, dispatch); - -export const EditorPanel = connect( - mapStateToProps, - mapDispatchToProps -)(Component); +export const EditorPanel = connect(mapStateToProps, mapDispatchToProps)(EditorPanelUnconnected); diff --git a/src/components/panels/TopRightPanel.tsx b/src/components/panels/TopRightPanel.tsx index 63a7346..9394433 100644 --- a/src/components/panels/TopRightPanel.tsx +++ b/src/components/panels/TopRightPanel.tsx @@ -1,36 +1,34 @@ -// flow import React, { useCallback } from 'react'; import { Icon } from '~/components/panels/Icon'; import { PROVIDERS } from '~/constants/providers'; import { LOGOS } from '~/constants/logos'; -import * as USER_ACTIONS from '~/redux/user/actions'; +import * as EDITOR_ACTIONS from '~/redux/editor/actions'; import { connect } from 'react-redux'; import { MODES } from '~/constants/modes'; -import { IRootState } from '~/redux/user'; import { Tooltip } from '~/components/panels/Tooltip'; import { selectMap } from '~/redux/map/selectors'; -import { selectUser } from '~/redux/user/selectors'; +import { selectEditor } from '~/redux/editor/selectors'; const mapStateToProps = state => ({ map: selectMap(state), - user: selectUser(state), + editor: selectEditor(state), }); const mapDispatchToProps = { - setMode: USER_ACTIONS.setMode, + editorSetMode: EDITOR_ACTIONS.editorSetMode, }; type Props = ReturnType & typeof mapDispatchToProps & {}; const TopRightPanelUnconnected = ({ map: { provider, logo }, - user: { markers_shown, editing }, - setMode, + editor: { markers_shown, editing }, + editorSetMode, }: Props) => { - const startProviderMode = useCallback(() => setMode(MODES.PROVIDER), [setMode]); - const startLogoMode = useCallback(() => setMode(MODES.LOGO), [setMode]); - const clearMode = useCallback(() => setMode(MODES.NONE), [setMode]); + const startProviderMode = useCallback(() => editorSetMode(MODES.PROVIDER), [editorSetMode]); + const startLogoMode = useCallback(() => editorSetMode(MODES.LOGO), [editorSetMode]); + const clearMode = useCallback(() => editorSetMode(MODES.NONE), [editorSetMode]); return (
diff --git a/src/components/panels/UserPanel.tsx b/src/components/panels/UserPanel.tsx index a138747..b39d60a 100644 --- a/src/components/panels/UserPanel.tsx +++ b/src/components/panels/UserPanel.tsx @@ -4,35 +4,41 @@ import { GuestButton } from '~/components/user/GuestButton'; import { DEFAULT_USER, ROLES } from '~/constants/auth'; import { UserButton } from '~/components/user/UserButton'; import { UserMenu } from '~/components/user/UserMenu'; +import { setUser, userLogout, gotVkUser, openMapDialog } from '~/redux/user/actions'; import { - setUser, - userLogout, - takeAShot, - setDialog, - gotVkUser, - setDialogActive, - openMapDialog, - getGPXTrack, -} from '~/redux/user/actions'; + editorTakeAShot, + editorSetDialog, + editorSetDialogActive, + editorGetGPXTrack, +} from '~/redux/editor/actions'; import { connect } from 'react-redux'; import { Icon } from '~/components/panels/Icon'; import classnames from 'classnames'; import { CLIENT } from '~/config/frontend'; import { DIALOGS, TABS } from '~/constants/dialogs'; -import { IRootState } from '~/redux/user'; import { Tooltip } from '~/components/panels/Tooltip'; import { TitleDialog } from '~/components/dialogs/TitleDialog'; -interface Props extends IRootState { - userLogout: typeof userLogout; - setDialog: typeof setDialog; - setDialogActive: typeof setDialogActive; - gotVkUser: typeof gotVkUser; - takeAShot: typeof takeAShot; - openMapDialog: typeof openMapDialog; - getGPXTrack: typeof getGPXTrack; -} +const mapStateToProps = ({ user: { user }, editor: { dialog, dialog_active, is_empty } }) => ({ + dialog, + dialog_active, + user, + is_empty, +}); + +const mapDispatchToProps = { + setUser, + userLogout, + editorTakeAShot, + editorSetDialog, + gotVkUser, + editorSetDialogActive, + openMapDialog, + editorGetGPXTrack, +}; + +type Props = ReturnType & typeof mapDispatchToProps & {}; interface State { menuOpened: boolean; @@ -84,8 +90,8 @@ export class UserPanelUnconnected extends PureComponent { openAppInfoDialog = () => { this.setMenuOpened(); - this.props.setDialog(DIALOGS.APP_INFO); - this.props.setDialogActive(this.props.dialog !== DIALOGS.APP_INFO); + this.props.editorSetDialog(DIALOGS.APP_INFO); + this.props.editorSetDialogActive(this.props.dialog !== DIALOGS.APP_INFO); }; openOauthFrame = () => { @@ -143,7 +149,7 @@ export class UserPanelUnconnected extends PureComponent {
- @@ -153,7 +159,10 @@ export class UserPanelUnconnected extends PureComponent {
- @@ -166,24 +175,6 @@ export class UserPanelUnconnected extends PureComponent { } } -const mapStateToProps = ({ user: { dialog, dialog_active, user, is_empty } }) => ({ - dialog, - dialog_active, - user, - is_empty, -}); - -const mapDispatchToProps = { - setUser, - userLogout, - takeAShot, - setDialog, - gotVkUser, - setDialogActive, - openMapDialog, - getGPXTrack, -}; - const UserPanel = connect(mapStateToProps, mapDispatchToProps)(UserPanelUnconnected); export { UserPanel }; diff --git a/src/components/renderer/Renderer.tsx b/src/components/renderer/Renderer.tsx index b1af77b..a245da5 100644 --- a/src/components/renderer/Renderer.tsx +++ b/src/components/renderer/Renderer.tsx @@ -1,22 +1,26 @@ import React from 'react'; -import { hideRenderer, cropAShot } from '~/redux/user/actions'; -import { bindActionCreators } from 'redux'; import { connect } from 'react-redux'; import Croppr from 'croppr'; import 'croppr/dist/croppr.css'; import { LOGOS } from '~/constants/logos'; import { RendererPanel } from '~/components/panels/RendererPanel'; -import { IRootState } from "~/redux/user"; -import { IRoute } from '~/redux/map/types'; +import { selectEditor } from '~/redux/editor/selectors'; +import * as EDITOR_ACTIONS from '~/redux/editor/actions'; +import { selectMap } from '~/redux/map/selectors'; -type Props = { - data: IRootState['renderer']['data'], - logo: IRoute['logo'], - hideRenderer: typeof hideRenderer, - cropAShot: typeof cropAShot, +const mapStateToProps = state => ({ + editor: selectEditor(state), + map: selectMap(state), +}); + +const mapDispatchToProps = { + editorHideRenderer: EDITOR_ACTIONS.editorHideRenderer, + editorCropAShot: EDITOR_ACTIONS.editorCropAShot, }; +type Props = ReturnType & typeof mapDispatchToProps & {}; + type State = { opacity: number, }; @@ -47,7 +51,7 @@ class Component extends React.Component { this.logo.style.transform = `scale(${scale})`; this.logoImg = document.createElement('img'); - if (this.props.logo && LOGOS[this.props.logo][1]) this.logoImg.src = LOGOS[this.props.logo][1]; + if (this.props.map.logo && LOGOS[this.props.map.logo][1]) this.logoImg.src = LOGOS[this.props.map.logo][1]; this.logo.append(this.logoImg); regionEl.append(this.logo); @@ -58,10 +62,10 @@ class Component extends React.Component { image: HTMLImageElement; logoImg: HTMLImageElement; - getImage = () => this.props.cropAShot(this.croppr.getValue()); + getImage = () => this.props.editorCropAShot(this.croppr.getValue()); render() { - const { data } = this.props; + const { data } = this.props.editor.renderer; const { opacity } = this.state; const { innerWidth, innerHeight } = window; const padding = 30; @@ -95,7 +99,7 @@ class Component extends React.Component {
@@ -103,12 +107,4 @@ class Component extends React.Component { } } - -const mapStateToProps = state => ({ ...state.user.renderer, logo: state.user.logo }); - -const mapDispatchToProps = dispatch => bindActionCreators({ - hideRenderer, - cropAShot, -}, dispatch); - export const Renderer = connect(mapStateToProps, mapDispatchToProps)(Component); diff --git a/src/constants/auth.ts b/src/constants/auth.ts index 6f44ea0..8e011b8 100644 --- a/src/constants/auth.ts +++ b/src/constants/auth.ts @@ -11,7 +11,7 @@ export interface IUser { role: IRoles[keyof IRoles]; routes: {}; success: boolean; - id?: number; + id?: string; uid: string; token?: string; photo: string; diff --git a/src/containers/App.tsx b/src/containers/App.tsx index cc7c326..e7c7c85 100644 --- a/src/containers/App.tsx +++ b/src/containers/App.tsx @@ -1,4 +1,3 @@ -// @flow import React from 'react'; import { EditorPanel } from '~/components/panels/EditorPanel'; @@ -9,8 +8,7 @@ import { bindActionCreators } from 'redux'; import { hot } from 'react-hot-loader'; import { Renderer } from '~/components/renderer/Renderer'; -import { hideRenderer, setDialogActive } from '~/redux/user/actions'; -import { Cursor } from '~/components/Cursor'; +import { editorHideRenderer, editorSetDialogActive } from '~/redux/editor/actions'; import { LeftDialog } from '~/containers/LeftDialog'; import { TopLeftPanel } from '~/components/panels/TopLeftPanel'; import { TopRightPanel } from '~/components/panels/TopRightPanel'; @@ -19,18 +17,19 @@ import { IStickerPack } from '~/constants/stickers'; import { IDialogs } from '~/constants/dialogs'; import { Map } from '~/containers/map/Map'; -import { IRootReducer } from '~/redux/user'; +import { IEditorState } from '~/redux/editor'; +import { IState } from '~/redux/store'; type Props = { sticker: string; renderer_active: boolean; - mode: IRootReducer['mode']; + mode: IEditorState['mode']; dialog: keyof IDialogs; dialog_active: boolean; set: keyof IStickerPack; - hideRenderer: typeof hideRenderer; - setDialogActive: typeof setDialogActive; + editorHideRenderer: typeof editorHideRenderer; + editorSetDialogActive: typeof editorSetDialogActive; }; const Component = (props: Props) => ( @@ -45,26 +44,26 @@ const Component = (props: Props) => ( - {props.renderer_active && } + {props.renderer_active && }
); const mapStateToProps = ({ - user: { + editor: { mode, dialog, dialog_active, renderer, activeSticker: { sticker = null, set = null }, }, -}) => ({ +}: IState) => ({ renderer_active: renderer.renderer_active, mode, dialog, @@ -74,5 +73,5 @@ const mapStateToProps = ({ }); const mapDispatchToProps = dispatch => - bindActionCreators({ hideRenderer, setDialogActive }, dispatch); + bindActionCreators({ editorHideRenderer, editorSetDialogActive }, dispatch); export const App = connect(mapStateToProps, mapDispatchToProps)(hot(module)(Component)); diff --git a/src/containers/LeftDialog.tsx b/src/containers/LeftDialog.tsx index 06a84a8..f22a993 100644 --- a/src/containers/LeftDialog.tsx +++ b/src/containers/LeftDialog.tsx @@ -4,12 +4,12 @@ import classnames from 'classnames'; import { AppInfoDialog } from '~/components/dialogs/AppInfoDialog'; import { Icon } from '~/components/panels/Icon'; import { MapListDialog } from '~/components/dialogs/MapListDialog'; -import * as USER_ACTIONS from '~/redux/user/actions'; +import * as EDITOR_ACTIONS from '~/redux/editor/actions'; interface Props { dialog: keyof IDialogs; dialog_active: Boolean; - setDialogActive: typeof USER_ACTIONS.setDialogActive; + editorSetDialogActive: typeof EDITOR_ACTIONS.editorSetDialogActive; } const LEFT_DIALOGS = { @@ -17,7 +17,7 @@ const LEFT_DIALOGS = { [DIALOGS.APP_INFO]: AppInfoDialog, }; -const LeftDialog = ({ dialog, dialog_active, setDialogActive }: Props) => ( +const LeftDialog = ({ dialog, dialog_active, editorSetDialogActive }: Props) => ( {Object.keys(LEFT_DIALOGS).map(item => (
( > {dialog && LEFT_DIALOGS[item] && createElement(LEFT_DIALOGS[item], {})} -
setDialogActive(false)}> +
editorSetDialogActive(false)}>
-
setDialogActive(false)}> +
editorSetDialogActive(false)}>
diff --git a/src/containers/map/Map/index.tsx b/src/containers/map/Map/index.tsx index 7ea4489..2c42cb8 100644 --- a/src/containers/map/Map/index.tsx +++ b/src/containers/map/Map/index.tsx @@ -12,15 +12,15 @@ import * as MAP_ACTIONS from "~/redux/map/actions"; import { Route } from "~/containers/map/Route"; import { TileLayer } from "~/containers/map/TileLayer"; import { Stickers } from "~/containers/map/Stickers"; -import { selectUserEditing } from '~/redux/user/selectors' import 'leaflet/dist/leaflet.css'; +import { selectEditorEditing } from "~/redux/editor/selectors"; const mapStateToProps = state => ({ provider: selectMapProvider(state), route: selectMapRoute(state), stickers: selectMapStickers(state), - editing: selectUserEditing(state), + editing: selectEditorEditing(state), }); const mapDispatchToProps = { diff --git a/src/redux/editor/actions.ts b/src/redux/editor/actions.ts new file mode 100644 index 0000000..8e72985 --- /dev/null +++ b/src/redux/editor/actions.ts @@ -0,0 +1,120 @@ +import { EDITOR_ACTIONS } from './constants'; +import { IEditorState } from '.'; +import { IRoute } from '../map/types'; +import { KeyboardEvent } from 'react'; + +export const editorSetEditing = (editing: IEditorState['editing']) => ({ + type: EDITOR_ACTIONS.SET_EDITING, + editing, +}); +export const editorSetMode = (mode: IEditorState['mode']) => ({ + type: EDITOR_ACTIONS.SET_MODE, + mode, +}); +export const editorSetDistance = (distance: IEditorState['distance']) => ({ + type: EDITOR_ACTIONS.SET_DISTANCE, + distance, +}); +export const editorSetChanged = (changed: IEditorState['changed']) => ({ + type: EDITOR_ACTIONS.SET_CHANGED, + changed, +}); +export const editorSetSpeed = speed => ({ type: EDITOR_ACTIONS.SET_SPEED, speed }); + +export const editorStartEditing = () => ({ type: EDITOR_ACTIONS.START_EDITING }); +export const editorStopEditing = () => ({ type: EDITOR_ACTIONS.STOP_EDITING }); + +export const editorRouterCancel = () => ({ type: EDITOR_ACTIONS.ROUTER_CANCEL }); +export const editorRouterSubmit = () => ({ type: EDITOR_ACTIONS.ROUTER_SUBMIT }); + +export const editorClearPoly = () => ({ type: EDITOR_ACTIONS.CLEAR_POLY }); +export const editorClearStickers = () => ({ type: EDITOR_ACTIONS.CLEAR_STICKERS }); +export const editorClearAll = () => ({ type: EDITOR_ACTIONS.CLEAR_ALL }); +export const editorClearCancel = () => ({ type: EDITOR_ACTIONS.CLEAR_CANCEL }); + +export const editorSendSaveRequest = (payload: { + title: IRoute['title']; + address: IRoute['address']; + is_public: IRoute['is_public']; + description: IRoute['description']; + force: boolean; +}) => ({ + type: EDITOR_ACTIONS.SEND_SAVE_REQUEST, + ...payload, +}); + +export const editorResetSaveDialog = () => ({ type: EDITOR_ACTIONS.RESET_SAVE_DIALOG }); + +export const editorSetSaveLoading = (save_loading: IEditorState['save_loading']) => ({ + type: EDITOR_ACTIONS.SET_SAVE_LOADING, + save_loading, +}); + +export const editorSetSaveSuccess = (payload: { + address: IRoute['address']; + title: IRoute['address']; + is_public: IRoute['is_public']; + description: IRoute['description']; + + save_error: string; +}) => ({ type: EDITOR_ACTIONS.SET_SAVE_SUCCESS, ...payload }); + +export const editorSetSaveError = (save_error: IEditorState['save_error']) => ({ + type: EDITOR_ACTIONS.SET_SAVE_ERROR, + save_error, +}); +export const editorSetSaveOverwrite = () => ({ type: EDITOR_ACTIONS.SET_SAVE_OVERWRITE }); + +export const editorHideRenderer = () => ({ type: EDITOR_ACTIONS.HIDE_RENDERER }); +export const editorSetRenderer = payload => ({ type: EDITOR_ACTIONS.SET_RENDERER, payload }); +export const editorTakeAShot = () => ({ type: EDITOR_ACTIONS.TAKE_A_SHOT }); +export const editorCropAShot = payload => ({ type: EDITOR_ACTIONS.CROP_A_SHOT, ...payload }); +export const editorSetDialog = dialog => ({ type: EDITOR_ACTIONS.SET_DIALOG, dialog }); +export const editorSetDialogActive = dialog_active => ({ + type: EDITOR_ACTIONS.SET_DIALOG_ACTIVE, + dialog_active, +}); +export const editorSetReady = ready => ({ type: EDITOR_ACTIONS.SET_READY, ready }); + +export const editorGetGPXTrack = () => ({ type: EDITOR_ACTIONS.GET_GPX_TRACK }); +export const editorSetMarkersShown = markers_shown => ({ + type: EDITOR_ACTIONS.SET_MARKERS_SHOWN, + markers_shown, +}); +export const editorSetIsEmpty = is_empty => ({ type: EDITOR_ACTIONS.SET_IS_EMPTY, is_empty }); +export const editorSetFeature = (features: { [x: string]: boolean }) => ({ + type: EDITOR_ACTIONS.SET_FEATURE, + features, +}); + +export const editorSetIsRouting = (is_routing: boolean) => ({ + type: EDITOR_ACTIONS.SET_IS_ROUTING, + is_routing, +}); + +export const editorSetRouterPoints = (routerPoints: IEditorState['routerPoints']) => ({ + type: EDITOR_ACTIONS.SET_ROUTER_POINTS, + routerPoints, +}); + +export const editorSetActiveSticker = (activeSticker: IEditorState['activeSticker']) => ({ + type: EDITOR_ACTIONS.SET_ACTIVE_STICKER, + activeSticker, +}); + +export const editorLocationChanged = location => ({ + type: EDITOR_ACTIONS.LOCATION_CHANGED, + location, +}); + +export const editorKeyPressed = ({ + key, + target: { tagName }, +}: { + key: string; + target: { tagName: string }; +}) => ({ + type: EDITOR_ACTIONS.KEY_PRESSED, + key, + target: tagName, +}); diff --git a/src/redux/editor/constants.ts b/src/redux/editor/constants.ts new file mode 100644 index 0000000..d0015a6 --- /dev/null +++ b/src/redux/editor/constants.ts @@ -0,0 +1,47 @@ +const P = 'EDITOR'; + +export const EDITOR_ACTIONS = { + SET_EDITING: `${P}-SET_EDITING`, + SET_MODE: `${P}-SET_MODE`, + SET_DISTANCE: `${P}-SET_DISTANCE`, + SET_CHANGED: `${P}-SET_CHANGED`, + SET_SPEED: `${P}-SET_SPEED`, + SET_ROUTER_POINTS: `${P}-SET_ROUTER_POINTS`, + SET_ACTIVE_STICKER: `${P}-SET_ACTIVE_STICKER`, + START_EDITING: `${P}-START_EDITING`, + STOP_EDITING: `${P}-STOP_EDITING`, + ROUTER_CANCEL: `${P}-ROUTER_CANCEL`, + ROUTER_SUBMIT: `${P}-ROUTER_SUBMIT`, + + CLEAR_POLY: `${P}-CLEAR_POLY`, + CLEAR_STICKERS: `${P}-CLEAR_STICKERS`, + CLEAR_ALL: `${P}-CLEAR_ALL`, + CLEAR_CANCEL: `${P}-CLEAR_CANCEL`, + + SEND_SAVE_REQUEST: `${P}-SEND_SAVE_REQUEST`, + SET_SAVE_LOADING: `${P}-SET_SAVE_LOADING`, + CANCEL_SAVE_REQUEST: `${P}-CANCEL_SAVE_REQUEST`, + RESET_SAVE_DIALOG: `${P}-RESET_SAVE_DIALOG`, + + SET_SAVE_SUCCESS: `${P}-SET_SAVE_SUCCESS`, + SET_SAVE_ERROR: `${P}-SET_SAVE_ERROR`, + SET_SAVE_OVERWRITE: `${P}-SET_SAVE_OVERWRITE`, + + SHOW_RENDERER: `${P}-SHOW_RENDERER`, + HIDE_RENDERER: `${P}-HIDE_RENDERER`, + SET_RENDERER: `${P}-SET_RENDERER`, + TAKE_A_SHOT: `${P}-TAKE_A_SHOT`, + CROP_A_SHOT: `${P}-CROP_A_SHOT`, + + SET_DIALOG: `${P}-SET_DIALOG`, + SET_DIALOG_ACTIVE: `${P}-SET_DIALOG_ACTIVE`, + LOCATION_CHANGED: `${P}-LOCATION_CHANGED`, + SET_READY: `${P}-SET_READY`, + + SET_MARKERS_SHOWN: `${P}-SET_MARKERS_SHOWN`, + GET_GPX_TRACK: `${P}-GET_GPX_TRACK`, + SET_IS_EMPTY: `${P}-SET_IS_EMPTY`, + SET_FEATURE: `${P}-SET_FEATURE`, + SET_IS_ROUTING: `${P}-SET_IS_ROUTING`, + KEY_PRESSED: `${P}-KEY_PRESSED`, +}; diff --git a/src/redux/editor/handlers.ts b/src/redux/editor/handlers.ts new file mode 100644 index 0000000..17257a1 --- /dev/null +++ b/src/redux/editor/handlers.ts @@ -0,0 +1,200 @@ +import { getEstimated } from '~/utils/format'; +import * as ACTIONS from '~/redux/editor/actions'; +import { EDITOR_ACTIONS } from '~/redux/editor/constants'; +import { IEditorState } from '~/redux/editor'; +import { TIPS } from '~/constants/tips'; + +const setEditing = ( + state, + { editing }: ReturnType +): IEditorState => ({ + ...state, + editing, +}); + +const setChanged = ( + state, + { changed }: ReturnType +): IEditorState => ({ + ...state, + changed, +}); + +const setMode = (state, { mode }: ReturnType): IEditorState => ({ + ...state, + mode, +}); + +const setDistance = ( + state, + { distance }: ReturnType +): IEditorState => ({ + ...state, + distance, + estimated: getEstimated(distance, state.speed), +}); + +const setRouterPoints = ( + state, + { routerPoints }: ReturnType +): IEditorState => ({ + ...state, + routerPoints, +}); + +const setActiveSticker = ( + state, + { activeSticker }: ReturnType +): IEditorState => ({ + ...state, + activeSticker: activeSticker || { set: null, sticker: null }, +}); + +const hideRenderer = (state): IEditorState => ({ + ...state, + renderer: { ...state.renderer, renderer_active: false }, +}); + +const setRenderer = ( + state, + { payload }: ReturnType +): IEditorState => ({ + ...state, + renderer: { ...state.renderer, ...payload }, +}); + +const sendSaveRequest = (state): IEditorState => ({ + ...state, + save_processing: true, +}); + +const setSaveError = ( + state, + { save_error }: ReturnType +): IEditorState => ({ + ...state, + save_error, + save_finished: false, + save_processing: false, +}); + +const setSaveLoading = ( + state, + { save_loading }: ReturnType +): IEditorState => ({ + ...state, + save_loading, +}); + +const setSaveOverwrite = (state): IEditorState => ({ + ...state, + save_overwriting: true, + save_finished: false, + save_processing: false, + save_error: TIPS.SAVE_OVERWRITE, +}); + +const setSaveSuccess = ( + state, + { save_error }: ReturnType +): IEditorState => ({ + ...state, + save_overwriting: false, + save_finished: true, + save_processing: false, + save_error, +}); + +const resetSaveDialog = (state): IEditorState => ({ + ...state, + save_overwriting: false, + save_finished: false, + save_processing: false, + save_error: '', +}); + +const setDialog = (state, { dialog }: ReturnType): IEditorState => ({ + ...state, + dialog, +}); + +const setDialogActive = ( + state, + { dialog_active }: ReturnType +): IEditorState => ({ + ...state, + dialog_active: dialog_active || !state.dialog_active, +}); + +const setReady = (state, { ready = true }: ReturnType): IEditorState => ({ + ...state, + ready, +}); + +const setSpeed = ( + state, + { speed = 15 }: ReturnType +): IEditorState => ({ + ...state, + speed, + estimated: getEstimated(state.distance, speed), +}); + +const setMarkersShown = ( + state, + { markers_shown = true }: ReturnType +): IEditorState => ({ ...state, markers_shown }); + +const setIsEmpty = ( + state, + { is_empty = true }: ReturnType +): IEditorState => ({ ...state, is_empty }); + +const setFeature = ( + state, + { features }: ReturnType +): IEditorState => ({ + ...state, + features: { + ...state.features, + ...features, + }, +}); + +const setIsRouting = ( + state, + { is_routing }: ReturnType +): IEditorState => ({ + ...state, + is_routing, +}); + +export const EDITOR_HANDLERS = { + [EDITOR_ACTIONS.SET_EDITING]: setEditing, + [EDITOR_ACTIONS.SET_CHANGED]: setChanged, + [EDITOR_ACTIONS.SET_MODE]: setMode, + [EDITOR_ACTIONS.SET_DISTANCE]: setDistance, + [EDITOR_ACTIONS.SET_ROUTER_POINTS]: setRouterPoints, + [EDITOR_ACTIONS.SET_ACTIVE_STICKER]: setActiveSticker, + + [EDITOR_ACTIONS.SET_SAVE_ERROR]: setSaveError, + [EDITOR_ACTIONS.SET_SAVE_LOADING]: setSaveLoading, + [EDITOR_ACTIONS.SET_SAVE_OVERWRITE]: setSaveOverwrite, + [EDITOR_ACTIONS.SET_SAVE_SUCCESS]: setSaveSuccess, + [EDITOR_ACTIONS.SEND_SAVE_REQUEST]: sendSaveRequest, + [EDITOR_ACTIONS.RESET_SAVE_DIALOG]: resetSaveDialog, + + [EDITOR_ACTIONS.HIDE_RENDERER]: hideRenderer, + [EDITOR_ACTIONS.SET_RENDERER]: setRenderer, + + [EDITOR_ACTIONS.SET_DIALOG]: setDialog, + [EDITOR_ACTIONS.SET_DIALOG_ACTIVE]: setDialogActive, + [EDITOR_ACTIONS.SET_READY]: setReady, + + [EDITOR_ACTIONS.SET_SPEED]: setSpeed, + [EDITOR_ACTIONS.SET_MARKERS_SHOWN]: setMarkersShown, + [EDITOR_ACTIONS.SET_IS_EMPTY]: setIsEmpty, + + [EDITOR_ACTIONS.SET_FEATURE]: setFeature, + [EDITOR_ACTIONS.SET_IS_ROUTING]: setIsRouting, +}; diff --git a/src/redux/editor/index.ts b/src/redux/editor/index.ts new file mode 100644 index 0000000..1bc7b0e --- /dev/null +++ b/src/redux/editor/index.ts @@ -0,0 +1,85 @@ +import { IDialogs } from '~/constants/dialogs'; +import { MODES } from '~/constants/modes'; +import { createReducer } from 'reduxsauce'; +import { EDITOR_HANDLERS } from './handlers'; + +export interface IEditorState { + changed: boolean, + editing: boolean, + ready: boolean, + markers_shown: boolean; + + mode: typeof MODES[keyof typeof MODES], + + dialog: IDialogs[keyof IDialogs], + dialog_active: boolean, + + routerPoints: number, + distance: number, + estimated: number, + speed: number, + activeSticker: { set?: string, sticker?: string }, + is_empty: boolean, + is_published: boolean, + is_routing: boolean, + save_error: string, + save_finished: boolean, + save_overwriting: boolean, + save_processing: boolean, + save_loading: boolean, + + features: { + routing: boolean, + }, + + renderer: { + data: string, + width: number, + height: number + renderer_active: boolean, + info: string, + progress: number, + }, +} + +const EDITOR_INITIAL_STATE = { + changed: false, + editing: false, + ready: false, + markers_shown: false, + + mode: MODES.NONE, + dialog: null, + dialog_active: false, + + routerPoints: 0, + distance: 0, + estimated: 0, + speed: 15, + activeSticker: { set: null, sticker: null }, + + is_published: false, + is_empty: true, + is_routing: false, + + save_error: '', + save_finished: false, + save_overwriting: false, + save_processing: false, + save_loading: false, + + features: { + routing: false, + }, + + renderer: { + data: '', + width: 0, + height: 0, + renderer_active: false, + info: '', + progress: 0, + }, +} + +export const editor = createReducer(EDITOR_INITIAL_STATE, EDITOR_HANDLERS); diff --git a/src/redux/editor/sagas.ts b/src/redux/editor/sagas.ts new file mode 100644 index 0000000..c19fe1d --- /dev/null +++ b/src/redux/editor/sagas.ts @@ -0,0 +1,256 @@ +import { call, put, takeEvery, takeLatest, select, race } from 'redux-saga/effects'; +import { delay, SagaIterator } from 'redux-saga'; +import { selectEditor } from '~/redux/editor/selectors'; + +import { + editorHideRenderer, + editorSetChanged, + editorSetEditing, + editorSetMode, + editorSetReady, + editorSetRenderer, + editorSetDialog, + editorSetDialogActive, + editorClearAll, + editorSetFeature, + editorLocationChanged, + editorKeyPressed, +} from '~/redux/editor/actions'; +import { getUrlData, pushPath } from '~/utils/history'; +import { MODES } from '~/constants/modes'; +import { checkOSRMService } from '~/utils/api'; +import { LatLng } from 'leaflet'; +import { searchSetTab } from '../user/actions'; +import { TABS } from '~/constants/dialogs'; +import { EDITOR_ACTIONS } from './constants'; +import { getGPXString, downloadGPXTrack } from '~/utils/gpx'; +import { + getTilePlacement, + getPolyPlacement, + getStickersPlacement, + fetchImages, + composeArrows, + composeDistMark, + composeImages, + composePoly, + composeStickers, + imageFetcher, + downloadCanvas, +} from '~/utils/renderer'; +import { selectMap } from '../map/selectors'; +import { selectUser } from '../user/selectors'; +import { LOGOS } from '~/constants/logos'; +import { loadMapSaga, replaceAddressIfItsBusy } from '../map/sagas'; +import { mapSetAddressOrigin } from '../map/actions'; + +const hideLoader = () => { + document.getElementById('loader').style.opacity = String(0); + document.getElementById('loader').style.pointerEvents = 'none'; + + return true; +}; + +function* stopEditingSaga() { + const { changed, editing, mode }: ReturnType = yield select(selectEditor); + const { address_origin }: ReturnType = yield select(selectMap); + const { path } = getUrlData(); + + if (!editing) return; + if (changed && mode !== MODES.CONFIRM_CANCEL) { + yield put(editorSetMode(MODES.CONFIRM_CANCEL)); + return; + } + + yield put(editorSetMode(MODES.NONE)); + yield put(editorSetChanged(false)); + + yield pushPath(`/${address_origin || path}/`); +} + +function* checkOSRMServiceSaga() { + const routing = yield call(checkOSRMService, [new LatLng(1, 1), new LatLng(2, 2)]); + + yield put(editorSetFeature({ routing })); +} + +export function* setReadySaga() { + yield put(editorSetReady(true)); + hideLoader(); + + yield call(checkOSRMServiceSaga); + yield put(searchSetTab(TABS.MY)); +} + +function* getRenderData() { + yield put(editorSetRenderer({ info: 'Загрузка тайлов', progress: 0.1 })); + + const { route, stickers, provider }: ReturnType = yield select(selectMap); + + const canvas = document.getElementById('renderer'); + canvas.width = window.innerWidth; + canvas.height = window.innerHeight; + const ctx = canvas.getContext('2d'); + + const geometry = getTilePlacement(); + const points = getPolyPlacement(route); + const sticker_points = getStickersPlacement(stickers); + // TODO: get distance: + const distance = 0; + // const distance = editor.poly.poly.distance; + + ctx.clearRect(0, 0, canvas.width, canvas.height); + + const images = yield fetchImages(ctx, geometry, provider); + + yield put(editorSetRenderer({ info: 'Отрисовка', progress: 0.5 })); + + yield composeImages({ geometry, images, ctx }); + yield composePoly({ points, ctx }); + yield composeArrows({ points, ctx }); + yield composeDistMark({ ctx, points, distance }); + yield composeStickers({ stickers: sticker_points, ctx }); + + yield put(editorSetRenderer({ info: 'Готово', progress: 1 })); + + return yield canvas.toDataURL('image/jpeg'); +} + +function* takeAShotSaga() { + const worker = call(getRenderData); + + const { result, timeout } = yield race({ + result: worker, + timeout: delay(500), + }); + + if (timeout) yield put(editorSetMode(MODES.SHOT_PREFETCH)); + + const data = yield result || worker; + + yield put(editorSetMode(MODES.NONE)); + yield put( + editorSetRenderer({ + data, + renderer_active: true, + width: window.innerWidth, + height: window.innerHeight, + }) + ); +} + +function* getCropData({ x, y, width, height }) { + const { logo }: ReturnType = yield select(selectMap); + const { + renderer: { data }, + }: ReturnType = yield select(selectEditor); + + const canvas = document.getElementById('renderer'); + canvas.width = width; + canvas.height = height; + const ctx = canvas.getContext('2d'); + const image = yield imageFetcher(data); + + ctx.drawImage(image, -x, -y); + + if (logo && LOGOS[logo][1]) { + const logoImage = yield imageFetcher(LOGOS[logo][1]); + ctx.drawImage(logoImage, width - logoImage.width, height - logoImage.height); + } + + return yield canvas.toDataURL('image/jpeg'); +} + +function* cropAShotSaga(params) { + const { title, address }: ReturnType = yield select(selectMap); + + yield call(getCropData, params); + const canvas = document.getElementById('renderer') as HTMLCanvasElement; + + downloadCanvas(canvas, (title || address).replace(/\./gi, ' ')); + + return yield put(editorHideRenderer()); +} + +function* locationChangeSaga({ location }: ReturnType) { + const { + user: { id, random_url }, + }: ReturnType = yield select(selectUser); + + const { ready }: ReturnType = yield select(selectEditor); + + const { owner, address }: ReturnType = yield select(selectMap); + + if (!ready) return; + + const { path, mode } = getUrlData(location); + + if (address !== path) { + const map = yield call(loadMapSaga, path); + + if (map && map.route && map.route.owner && mode === 'edit' && map.route.owner !== id) { + return yield call(replaceAddressIfItsBusy, map.random_url, map.address); + } + } else if (mode === 'edit' && owner.id !== id) { + return yield call(replaceAddressIfItsBusy, random_url, address); + } else { + yield put(mapSetAddressOrigin('')); + } + + if (mode !== 'edit') { + yield put(editorSetEditing(false)); + // editor.stopEditing(); + } else { + yield put(editorSetEditing(true)); + // editor.startEditing(); + } +} + +function* keyPressedSaga({ key, target }: ReturnType): any { + if (target === 'INPUT' || target === 'TEXTAREA') { + return; + } + + if (key === 'Escape') { + const { + dialog_active, + mode, + renderer: { renderer_active }, + }: ReturnType = yield select(selectEditor); + + if (renderer_active) return yield put(editorHideRenderer()); + if (dialog_active) return yield put(editorSetDialogActive(false)); + if (mode !== MODES.NONE) return yield put(editorSetMode(MODES.NONE)); + } else if (key === 'Delete') { + const { editing } = yield select(selectEditor); + + if (!editing) return; + + const { mode } = yield select(selectUser); + + if (mode === MODES.TRASH) { + yield put(editorClearAll()); + } else { + yield put(editorSetMode(MODES.TRASH)); + } + } +} + +function* getGPXTrackSaga(): SagaIterator { + const { route, stickers, title, address }: ReturnType = yield select(selectMap); + // const { title, address }: = yield select(selectUser); + + if (!route || route.length <= 0) return; + + const track = getGPXString({ route, stickers, title: title || address }); + + return downloadGPXTrack({ track, title }); +} + +export function* editorSaga() { + yield takeEvery(EDITOR_ACTIONS.STOP_EDITING, stopEditingSaga); + yield takeLatest(EDITOR_ACTIONS.TAKE_A_SHOT, takeAShotSaga); + yield takeLatest(EDITOR_ACTIONS.CROP_A_SHOT, cropAShotSaga); + yield takeLatest(EDITOR_ACTIONS.LOCATION_CHANGED, locationChangeSaga); + yield takeLatest(EDITOR_ACTIONS.KEY_PRESSED, keyPressedSaga); + yield takeLatest(EDITOR_ACTIONS.GET_GPX_TRACK, getGPXTrackSaga); +} diff --git a/src/redux/editor/selectors.ts b/src/redux/editor/selectors.ts new file mode 100644 index 0000000..725c9af --- /dev/null +++ b/src/redux/editor/selectors.ts @@ -0,0 +1,7 @@ +import { IState } from "../store"; + +export const selectEditor = (state: IState) => state.editor; +export const selectEditorEditing = (state: IState) => state.editor.editing; +export const selectEditorMode = (state: IState) => state.editor.mode; +export const selectEditorActiveSticker = (state: IState) => state.editor.activeSticker; +export const selectEditorRenderer = (state: IState) => state.editor.renderer; \ No newline at end of file diff --git a/src/redux/map/actions.ts b/src/redux/map/actions.ts index 537caee..31d42cb 100644 --- a/src/redux/map/actions.ts +++ b/src/redux/map/actions.ts @@ -68,3 +68,8 @@ export const mapSetLogo = (logo: IMapReducer['logo']) => ({ type: MAP_ACTIONS.SET_LOGO, logo, }); + +export const mapSetAddressOrigin = (address_origin: IMapReducer['address_origin']) => ({ + type: MAP_ACTIONS.SET_ADDRESS_ORIGIN, + address_origin, +}); diff --git a/src/redux/map/constants.ts b/src/redux/map/constants.ts index 477f3a2..c7ffed4 100644 --- a/src/redux/map/constants.ts +++ b/src/redux/map/constants.ts @@ -7,6 +7,7 @@ export const MAP_ACTIONS = { SET_TITLE: `${P}-SET_TILE`, SET_DESCRIPTION: `${P}-SETDESCRIPTION`, SET_ADDRESS: `${P}-SET_ADDRESS`, + SET_ADDRESS_ORIGIN: `${P}-SET_ADDRESS_ORIGIN`, SET_OWNER: `${P}-SET_OWNER`, SET_PUBLIC: `${P}-SET_PUBLIC`, SET_LOGO: `${P}-SET_LOGO`, diff --git a/src/redux/map/handlers.ts b/src/redux/map/handlers.ts index baaa51e..ce4daf6 100644 --- a/src/redux/map/handlers.ts +++ b/src/redux/map/handlers.ts @@ -12,6 +12,7 @@ import { mapSetOwner, mapSetPublic, mapSetLogo, + mapSetAddressOrigin, } from './actions'; const setMap = (state: IMapReducer, { map }: ReturnType): IMapReducer => ({ @@ -86,6 +87,11 @@ const setLogo = (state: IMapReducer, { logo }: ReturnType): I logo, }); +const setAddressOrigin = (state, { address_origin }: ReturnType): IMapReducer => ({ + ...state, + address_origin +}); + export const MAP_HANDLERS = { [MAP_ACTIONS.SET_MAP]: setMap, [MAP_ACTIONS.SET_PROVIDER]: setProvider, @@ -99,4 +105,5 @@ export const MAP_HANDLERS = { [MAP_ACTIONS.SET_OWNER]: setOwner, [MAP_ACTIONS.SET_PUBLIC]: setPublic, [MAP_ACTIONS.SET_LOGO]: setLogo, + [MAP_ACTIONS.SET_ADDRESS_ORIGIN]: setAddressOrigin, }; diff --git a/src/redux/map/index.ts b/src/redux/map/index.ts index a72482d..4c726ff 100644 --- a/src/redux/map/index.ts +++ b/src/redux/map/index.ts @@ -12,6 +12,7 @@ export interface IMapReducer { title: string; logo: string; address: string; + address_origin: string; description: string; owner: { id: string }; is_public: boolean; @@ -24,6 +25,7 @@ export const MAP_INITIAL_STATE: IMapReducer = { stickers: [], title: '', address: '', + address_origin: '', description: '', owner: { id: null }, is_public: false, diff --git a/src/redux/map/sagas.ts b/src/redux/map/sagas.ts index c400922..c5c8906 100644 --- a/src/redux/map/sagas.ts +++ b/src/redux/map/sagas.ts @@ -1,39 +1,58 @@ -import { takeEvery, select, put, call, TakeEffect, race, take, takeLatest } from 'redux-saga/effects'; +import { + takeEvery, + select, + put, + call, + TakeEffect, + race, + take, + takeLatest, +} from 'redux-saga/effects'; import { MAP_ACTIONS } from './constants'; -import { mapClicked, mapAddSticker, mapSetProvider, mapSet, mapSetTitle, mapSetAddress, mapSetDescription, mapSetOwner, mapSetPublic } from './actions'; -import { selectUserMode, selectUserActiveSticker, selectUser, selectUserUser } from '~/redux/user/selectors'; +import { + mapClicked, + mapAddSticker, + mapSetProvider, + mapSet, + mapSetTitle, + mapSetAddressOrigin, +} from './actions'; +import { selectUser, selectUserUser } from '~/redux/user/selectors'; import { MODES } from '~/constants/modes'; import { - setMode, - setChanged, - setAddressOrigin, - setEditing, - setReady, - setActiveSticker, - setSaveError, - setSaveLoading, - sendSaveRequest, - setSaveSuccess, - setSaveOverwrite, -} from '~/redux/user/actions'; + editorSetMode, + editorSetChanged, + editorSetEditing, + editorSetReady, + editorSetActiveSticker, + editorSetSaveError, + editorSetSaveLoading, + editorSendSaveRequest, + editorSetSaveSuccess, + editorSetSaveOverwrite, +} from '~/redux/editor/actions'; import { pushLoaderState, getUrlData, pushPath, replacePath } from '~/utils/history'; -import { setReadySaga, searchSetSagaWorker } from '~/redux/user/sagas'; +import { searchSetSagaWorker } from '~/redux/user/sagas'; import { getStoredMap, postMap } from '~/utils/api'; import { Unwrap } from '~/utils/middleware'; -import { DEFAULT_PROVIDER } from '~/constants/providers'; import { USER_ACTIONS } from '~/redux/user/constants'; -import { selectMap } from './selectors'; +import { selectMap, selectMapProvider } from './selectors'; import { TIPS } from '~/constants/tips'; import { delay } from 'redux-saga'; +import { setReadySaga } from '../editor/sagas'; +import { selectEditor } from '../editor/selectors'; +import { EDITOR_ACTIONS } from '../editor/constants'; function* onMapClick({ latlng }: ReturnType) { - const mode = yield select(selectUserMode); - const { set, sticker } = yield select(selectUserActiveSticker); + const { + mode, + activeSticker: { set, sticker }, + }: ReturnType = yield select(selectEditor); switch (mode) { case MODES.STICKERS: yield put(mapAddSticker({ latlng, set, sticker, text: '', angle: 0 })); - yield put(setMode(MODES.NONE)); + yield put(editorSetMode(MODES.NONE)); break; default: @@ -64,7 +83,7 @@ function* onMapClick({ latlng }: ReturnType) { export function* replaceAddressIfItsBusy(destination, original) { if (original) { - yield put(setAddressOrigin(original)); + yield put(mapSetAddressOrigin(original)); } pushPath(`/${destination}/edit`); @@ -97,14 +116,15 @@ export function* loadMapSaga(path) { function* startEmptyEditorSaga() { const { user: { id, random_url }, - provider = DEFAULT_PROVIDER, - } = yield select(selectUser); + }: ReturnType = yield select(selectUser); + + const provider: ReturnType = yield select(selectMapProvider); // TODO: set owner { id } pushPath(`/${random_url}/edit`); - yield put(setChanged(false)); - yield put(setEditing(true)); + yield put(editorSetChanged(false)); + yield put(editorSetEditing(true)); return yield call(setReadySaga); } @@ -114,9 +134,9 @@ export function* mapInitSaga() { const { path, mode, hash } = getUrlData(); const { - provider, user: { id }, - } = yield select(selectUser); + }: ReturnType = yield select(selectUser); + const provider: ReturnType = yield select(selectMapProvider); yield put(mapSetProvider(provider)); @@ -139,14 +159,12 @@ export function* mapInitSaga() { yield call(setReadySaga); yield call(replaceAddressIfItsBusy, map.random_url, map.address); } else { - yield put(setAddressOrigin('')); + yield put(mapSetAddressOrigin('')); } - yield put(setEditing(true)); - // TODO: start editing + yield put(editorSetEditing(true)); } else { - yield put(setEditing(false)); - // TODO: stop editing + yield put(editorSetEditing(false)); } yield call(setReadySaga); @@ -155,7 +173,7 @@ export function* mapInitSaga() { } yield call(startEmptyEditorSaga); - yield put(setReady(true)); + yield put(editorSetReady(true)); pushLoaderState(100); @@ -163,7 +181,7 @@ export function* mapInitSaga() { } function* setActiveStickerSaga() { - yield put(setMode(MODES.STICKERS)); + yield put(editorSetMode(MODES.STICKERS)); } function* setTitleSaga({ title }: ReturnType) { @@ -172,10 +190,14 @@ function* setTitleSaga({ title }: ReturnType) { } } +function* startEditingSaga() { + const { path } = getUrlData(); + yield pushPath(`/${path}/edit`); +} + function* clearSaga({ type }) { switch (type) { - case USER_ACTIONS.CLEAR_POLY: - // TODO: clear router waypoints + case EDITOR_ACTIONS.CLEAR_POLY: yield put( mapSet({ route: [], @@ -183,7 +205,7 @@ function* clearSaga({ type }) { ); break; - case USER_ACTIONS.CLEAR_STICKERS: + case EDITOR_ACTIONS.CLEAR_STICKERS: yield put( mapSet({ stickers: [], @@ -191,8 +213,8 @@ function* clearSaga({ type }) { ); break; - case USER_ACTIONS.CLEAR_ALL: - yield put(setChanged(false)); + case EDITOR_ACTIONS.CLEAR_ALL: + yield put(editorSetChanged(false)); yield put( mapSet({ route: [], @@ -205,8 +227,8 @@ function* clearSaga({ type }) { break; } - yield put(setActiveSticker(null)); // TODO: move to maps - yield put(setMode(MODES.NONE)); + yield put(editorSetActiveSticker(null)); + yield put(editorSetMode(MODES.NONE)); } function* sendSaveRequestSaga({ @@ -215,17 +237,18 @@ function* sendSaveRequestSaga({ force, is_public, description, -}: ReturnType) { - const { route, stickers, provider } = yield select(selectMap); +}: ReturnType) { + const { route, stickers, provider }: ReturnType = yield select(selectMap); if (!route.length && !stickers.length) { - return yield put(setSaveError(TIPS.SAVE_EMPTY)); // TODO: move setSaveError to editor + return yield put(editorSetSaveError(TIPS.SAVE_EMPTY)); } - const { logo, distance } = yield select(selectUser); - const { token } = yield select(selectUserUser); + const { logo }: ReturnType = yield select(selectMap); + const { distance }: ReturnType = yield select(selectEditor); + const { token }: ReturnType = yield select(selectUserUser); - yield put(setSaveLoading(true)); // TODO: move setSaveLoading to maps + yield put(editorSetSaveLoading(true)); const { result, @@ -250,20 +273,21 @@ function* sendSaveRequestSaga({ description, }), timeout: delay(10000), - cancel: take(USER_ACTIONS.RESET_SAVE_DIALOG), + cancel: take(EDITOR_ACTIONS.RESET_SAVE_DIALOG), }); - yield put(setSaveLoading(false)); + yield put(editorSetSaveLoading(false)); - if (cancel) return yield put(setMode(MODES.NONE)); + if (cancel) return yield put(editorSetMode(MODES.NONE)); - if (result && result.data.code === 'already_exist') return yield put(setSaveOverwrite()); // TODO: move setSaveOverwrite to editor - if (result && result.data.code === 'conflict') return yield put(setSaveError(TIPS.SAVE_EXISTS)); + if (result && result.data.code === 'already_exist') return yield put(editorSetSaveOverwrite()); + if (result && result.data.code === 'conflict') + return yield put(editorSetSaveError(TIPS.SAVE_EXISTS)); if (timeout || !result || !result.data.route || !result.data.route.address) - return yield put(setSaveError(TIPS.SAVE_TIMED_OUT)); + return yield put(editorSetSaveError(TIPS.SAVE_TIMED_OUT)); - return yield put( // TODO: move setSaveSuccess to editor - setSaveSuccess({ + return yield put( + editorSetSaveSuccess({ address: result.data.route.address, title: result.data.route.title, is_public: result.data.route.is_public, @@ -279,18 +303,23 @@ function* setSaveSuccessSaga({ title, is_public, description, -}: ReturnType) { - const { id } = yield select(selectUser); - const { dialog_active } = yield select(selectUser); +}: ReturnType) { + const { id }: ReturnType = yield select(selectUserUser); + const { dialog_active }: ReturnType = yield select(selectEditor); replacePath(`/${address}/edit`); - yield put(mapSetTitle(title)); - yield put(mapSetAddress(address)); - yield put(mapSetPublic(is_public)); - yield put(mapSetDescription(description)); - yield put(setChanged(false)); - yield put(mapSetOwner({ id })); + yield put( + mapSet({ + title, + address, + is_public, + description, + owner: { id }, + }) + ); + + yield put(editorSetChanged(false)); if (dialog_active) { yield call(searchSetSagaWorker); @@ -298,27 +327,25 @@ function* setSaveSuccessSaga({ // yield editor.setInitialData(); // TODO: set initial data here - - return + + return; } export function* mapSaga() { // TODO: setChanged on set route, logo, provider, stickers - - yield takeEvery(USER_ACTIONS.SET_ACTIVE_STICKER, setActiveStickerSaga); // TODO: move active sticker to maps + yield takeEvery(EDITOR_ACTIONS.START_EDITING, startEditingSaga); + yield takeEvery(EDITOR_ACTIONS.SET_ACTIVE_STICKER, setActiveStickerSaga); yield takeEvery(MAP_ACTIONS.MAP_CLICKED, onMapClick); yield takeEvery(MAP_ACTIONS.SET_TITLE, setTitleSaga); - // yield takeEvery(USER_ACTIONS.SET_LOGO, setLogoSaga); - yield takeLatest(USER_ACTIONS.SEND_SAVE_REQUEST, sendSaveRequestSaga); - yield takeLatest(USER_ACTIONS.SET_SAVE_SUCCESS, setSaveSuccessSaga); + yield takeLatest(EDITOR_ACTIONS.SEND_SAVE_REQUEST, sendSaveRequestSaga); + yield takeLatest(EDITOR_ACTIONS.SET_SAVE_SUCCESS, setSaveSuccessSaga); yield takeEvery( - // TODO: move all actions to MAP [ - USER_ACTIONS.CLEAR_POLY, - USER_ACTIONS.CLEAR_STICKERS, - USER_ACTIONS.CLEAR_ALL, - USER_ACTIONS.CLEAR_CANCEL, + EDITOR_ACTIONS.CLEAR_POLY, + EDITOR_ACTIONS.CLEAR_STICKERS, + EDITOR_ACTIONS.CLEAR_ALL, + EDITOR_ACTIONS.CLEAR_CANCEL, ], clearSaga ); diff --git a/src/redux/store.ts b/src/redux/store.ts index 6da5577..b2a9195 100644 --- a/src/redux/store.ts +++ b/src/redux/store.ts @@ -4,13 +4,20 @@ import { persistStore, persistReducer } from 'redux-persist'; import storage from 'redux-persist/lib/storage'; import createSagaMiddleware from 'redux-saga'; + +import { createBrowserHistory } from 'history'; +import { editorLocationChanged } from '~/redux/editor/actions'; +import { PersistConfig, Persistor } from "redux-persist/es/types"; + + import { userReducer, IRootReducer } from '~/redux/user'; import { userSaga } from '~/redux/user/sagas'; -import { mapSaga } from '~/redux/map/sagas'; -import { createBrowserHistory } from 'history'; -import { locationChanged } from '~/redux/user/actions'; -import { PersistConfig, Persistor } from "redux-persist/es/types"; + +import { editor, IEditorState } from '~/redux/editor'; +import { editorSaga } from '~/redux/editor/sagas'; + import { map, IMapReducer } from '~/redux/map'; +import { mapSaga } from '~/redux/map/sagas'; const userPersistConfig: PersistConfig = { key: 'user', @@ -21,6 +28,7 @@ const userPersistConfig: PersistConfig = { export interface IState { user: IRootReducer map: IMapReducer, + editor: IEditorState, } // create the saga middleware export const sagaMiddleware = createSagaMiddleware(); @@ -36,14 +44,15 @@ export const store = createStore( combineReducers({ user: persistReducer(userPersistConfig, userReducer), map, - // routing: routerReducer + editor, }), - composeEnhancers(applyMiddleware(/* routerMiddleware(history), */ sagaMiddleware)) + composeEnhancers(applyMiddleware(sagaMiddleware)) ); export function configureStore(): { store: Store, persistor: Persistor } { sagaMiddleware.run(userSaga); sagaMiddleware.run(mapSaga); + sagaMiddleware.run(editorSaga); const persistor = persistStore(store); @@ -54,5 +63,5 @@ export const history = createBrowserHistory(); history.listen((location, action) => { if (action === 'REPLACE') return; - store.dispatch(locationChanged(location.pathname)); + store.dispatch(editorLocationChanged(location.pathname)); }); diff --git a/src/redux/user/actions.ts b/src/redux/user/actions.ts index 19d416f..e8eab3b 100644 --- a/src/redux/user/actions.ts +++ b/src/redux/user/actions.ts @@ -1,81 +1,81 @@ import { USER_ACTIONS } from '~/redux/user/constants'; import { IUser } from "~/constants/auth"; -import { IRootState } from "~/redux/user"; -import { IRoute } from '~/redux/map/types'; +// import { IRootState } from "~/redux/user"; +// import { IRoute } from '~/redux/map/types'; export const setUser = (user: IUser) => ({ type: USER_ACTIONS.SET_USER, user }); export const userLogout = () => ({ type: USER_ACTIONS.USER_LOGOUT }); -export const setEditing = (editing: IRootState['editing']) => ({ type: USER_ACTIONS.SET_EDITING, editing }); -export const setMode = (mode: IRootState['mode']) => ({ type: USER_ACTIONS.SET_MODE, mode }); -export const setDistance = (distance: IRootState['distance']) => ({ type: USER_ACTIONS.SET_DISTANCE, distance }); -export const setChanged = (changed: IRootState['changed']) => ({ type: USER_ACTIONS.SET_CHANGED, changed }); -export const setRouterPoints = routerPoints => ({ type: USER_ACTIONS.SET_ROUTER_POINTS, routerPoints }); -export const setActiveSticker = activeSticker => ({ type: USER_ACTIONS.SET_ACTIVE_STICKER, activeSticker }); +// export const setEditing = (editing: IRootState['editing']) => ({ type: USER_ACTIONS.SET_EDITING, editing }); +// export const setMode = (mode: IRootState['mode']) => ({ type: USER_ACTIONS.SET_MODE, mode }); +// export const setDistance = (distance: IRootState['distance']) => ({ type: USER_ACTIONS.SET_DISTANCE, distance }); +// export const setChanged = (changed: IRootState['changed']) => ({ type: USER_ACTIONS.SET_CHANGED, changed }); +// export const setRouterPoints = routerPoints => ({ type: USER_ACTIONS.SET_ROUTER_POINTS, routerPoints }); +// export const setActiveSticker = activeSticker => ({ type: USER_ACTIONS.SET_ACTIVE_STICKER, activeSticker }); // export const setLogo = logo => ({ type: USER_ACTIONS.SET_LOGO, logo }); // export const setTitle = title => ({ type: USER_ACTIONS.SET_TITLE, title }); // export const setDescription = description => ({ type: USER_ACTIONS.SET_DESCRIPTION, description }); // export const setAddress = address => ({ type: USER_ACTIONS.SET_ADDRESS, address }); -export const setAddressOrigin = address_origin => ({ type: USER_ACTIONS.SET_ADDRESS_ORIGIN, address_origin }); +// export const setAddressOrigin = address_origin => ({ type: USER_ACTIONS.SET_ADDRESS_ORIGIN, address_origin }); // export const setPublic = is_public => ({ type: USER_ACTIONS.SET_PUBLIC, is_public }); export const setStarred = is_published => ({ type: USER_ACTIONS.SET_STARRED, is_published }); -export const setSpeed = speed => ({ type: USER_ACTIONS.SET_SPEED, speed }); +// export const setSpeed = speed => ({ type: USER_ACTIONS.SET_SPEED, speed }); -export const startEditing = () => ({ type: USER_ACTIONS.START_EDITING }); -export const stopEditing = () => ({ type: USER_ACTIONS.STOP_EDITING }); +// export const startEditing = () => ({ type: USER_ACTIONS.START_EDITING }); +// export const stopEditing = () => ({ type: USER_ACTIONS.STOP_EDITING }); -export const routerCancel = () => ({ type: USER_ACTIONS.ROUTER_CANCEL }); -export const routerSubmit = () => ({ type: USER_ACTIONS.ROUTER_SUBMIT }); +// export const routerCancel = () => ({ type: USER_ACTIONS.ROUTER_CANCEL }); +// export const routerSubmit = () => ({ type: USER_ACTIONS.ROUTER_SUBMIT }); -export const clearPoly = () => ({ type: USER_ACTIONS.CLEAR_POLY }); -export const clearStickers = () => ({ type: USER_ACTIONS.CLEAR_STICKERS }); -export const clearAll = () => ({ type: USER_ACTIONS.CLEAR_ALL }); -export const clearCancel = () => ({ type: USER_ACTIONS.CLEAR_CANCEL }); +// export const clearPoly = () => ({ type: USER_ACTIONS.CLEAR_POLY }); +// export const clearStickers = () => ({ type: USER_ACTIONS.CLEAR_STICKERS }); +// export const clearAll = () => ({ type: USER_ACTIONS.CLEAR_ALL }); +// export const clearCancel = () => ({ type: USER_ACTIONS.CLEAR_CANCEL }); -export const sendSaveRequest = (payload: { - title: IRoute['title'], - address: IRoute['address'], - is_public: IRoute['is_public'], - description: IRoute['description'], - force: boolean, -}) => ({ - type: USER_ACTIONS.SEND_SAVE_REQUEST, - ...payload, -}); +// export const sendSaveRequest = (payload: { +// title: IRoute['title'], +// address: IRoute['address'], +// is_public: IRoute['is_public'], +// description: IRoute['description'], +// force: boolean, +// }) => ({ +// type: USER_ACTIONS.SEND_SAVE_REQUEST, +// ...payload, +// }); -export const resetSaveDialog = () => ({ type: USER_ACTIONS.RESET_SAVE_DIALOG }); +// export const resetSaveDialog = () => ({ type: USER_ACTIONS.RESET_SAVE_DIALOG }); -export const setSaveLoading = (save_loading: IRootState['save_loading']) => ({ type: USER_ACTIONS.SET_SAVE_LOADING, save_loading }); +// export const setSaveLoading = (save_loading: IRootState['save_loading']) => ({ type: USER_ACTIONS.SET_SAVE_LOADING, save_loading }); -export const setSaveSuccess = (payload: { - address: IRoute['address'], - title: IRoute['address'], - is_public: IRoute['is_public'], - description: IRoute['description'], +// export const setSaveSuccess = (payload: { +// address: IRoute['address'], +// title: IRoute['address'], +// is_public: IRoute['is_public'], +// description: IRoute['description'], - save_error: string, -}) => ({ type: USER_ACTIONS.SET_SAVE_SUCCESS, ...payload }); +// save_error: string, +// }) => ({ type: USER_ACTIONS.SET_SAVE_SUCCESS, ...payload }); -export const setSaveError = (save_error: IRootState['save_error']) => ({ type: USER_ACTIONS.SET_SAVE_ERROR, save_error }); -export const setSaveOverwrite = () => ({ type: USER_ACTIONS.SET_SAVE_OVERWRITE }); +// export const setSaveError = (save_error: IRootState['save_error']) => ({ type: USER_ACTIONS.SET_SAVE_ERROR, save_error }); +// export const setSaveOverwrite = () => ({ type: USER_ACTIONS.SET_SAVE_OVERWRITE }); -export const hideRenderer = () => ({ type: USER_ACTIONS.HIDE_RENDERER }); -export const setRenderer = payload => ({ type: USER_ACTIONS.SET_RENDERER, payload }); -export const takeAShot = () => ({ type: USER_ACTIONS.TAKE_A_SHOT }); -export const cropAShot = payload => ({ type: USER_ACTIONS.CROP_A_SHOT, ...payload }); +// export const hideRenderer = () => ({ type: USER_ACTIONS.HIDE_RENDERER }); +// export const setRenderer = payload => ({ type: USER_ACTIONS.SET_RENDERER, payload }); +// export const takeAShot = () => ({ type: USER_ACTIONS.TAKE_A_SHOT }); +// export const cropAShot = payload => ({ type: USER_ACTIONS.CROP_A_SHOT, ...payload }); // export const setProvider = provider => ({ type: USER_ACTIONS.SET_PROVIDER, provider }); // export const changeProvider = provider => ({ type: USER_ACTIONS.CHANGE_PROVIDER, provider }); -export const setDialog = dialog => ({ type: USER_ACTIONS.SET_DIALOG, dialog }); -export const setDialogActive = dialog_active => ({ type: USER_ACTIONS.SET_DIALOG_ACTIVE, dialog_active }); +// export const setDialog = dialog => ({ type: USER_ACTIONS.SET_DIALOG, dialog }); +// export const setDialogActive = dialog_active => ({ type: USER_ACTIONS.SET_DIALOG_ACTIVE, dialog_active }); export const openMapDialog = tab => ({ type: USER_ACTIONS.OPEN_MAP_DIALOG, tab }); -export const locationChanged = location => ({ type: USER_ACTIONS.LOCATION_CHANGED, location }); -export const setReady = ready => ({ type: USER_ACTIONS.SET_READY, ready }); +// export const locationChanged = location => ({ type: USER_ACTIONS.LOCATION_CHANGED, location }); +// export const setReady = ready => ({ type: USER_ACTIONS.SET_READY, ready }); export const gotVkUser = user => ({ type: USER_ACTIONS.GOT_VK_USER, user }); -export const keyPressed = ({ key, target: { tagName } }) => ({ type: USER_ACTIONS.KEY_PRESSED, key, target: tagName }); +// export const keyPressed = ({ key, target: { tagName } }) => ({ type: USER_ACTIONS.KEY_PRESSED, key, target: tagName }); export const searchSetTitle = title => ({ type: USER_ACTIONS.SEARCH_SET_TITLE, title }); export const searchSetDistance = distance => ({ type: USER_ACTIONS.SEARCH_SET_DISTANCE, distance }); @@ -85,15 +85,15 @@ export const searchSetLoading = loading => ({ type: USER_ACTIONS.SEARCH_SET_LOAD export const searchPutRoutes = payload => ({ type: USER_ACTIONS.SEARCH_PUT_ROUTES, ...payload }); -export const setMarkersShown = markers_shown => ({ type: USER_ACTIONS.SET_MARKERS_SHOWN, markers_shown }); -export const getGPXTrack = () => ({ type: USER_ACTIONS.GET_GPX_TRACK }); -export const setIsEmpty = is_empty => ({ type: USER_ACTIONS.SET_IS_EMPTY, is_empty }); +// export const getGPXTrack = () => ({ type: USER_ACTIONS.GET_GPX_TRACK }); +// export const setMarkersShown = markers_shown => ({ type: USER_ACTIONS.SET_MARKERS_SHOWN, markers_shown }); +// export const setIsEmpty = is_empty => ({ type: USER_ACTIONS.SET_IS_EMPTY, is_empty }); export const mapsLoadMore = () => ({ type: USER_ACTIONS.MAPS_LOAD_MORE }); export const mapsSetShift = (shift: number) => ({ type: USER_ACTIONS.MAPS_SET_SHIFT, shift }); -export const setFeature = (features: { [x: string]: boolean }) => ({ type: USER_ACTIONS.SET_FEATURE, features }); -export const setIsRouting = (is_routing: boolean) => ({ type: USER_ACTIONS.SET_IS_ROUTING, is_routing }); +// export const setFeature = (features: { [x: string]: boolean }) => ({ type: USER_ACTIONS.SET_FEATURE, features }); +// export const setIsRouting = (is_routing: boolean) => ({ type: USER_ACTIONS.SET_IS_ROUTING, is_routing }); export const dropRoute = (address: string) => ({ type: USER_ACTIONS.DROP_ROUTE, address }); export const modifyRoute = (address: string, { title, is_public }: { title: string, is_public: boolean }) => ({ diff --git a/src/redux/user/constants.ts b/src/redux/user/constants.ts index 34dea81..dcea9d0 100644 --- a/src/redux/user/constants.ts +++ b/src/redux/user/constants.ts @@ -1,61 +1,7 @@ -export interface IActions { - [x: string]: string, -} - -export const USER_ACTIONS: IActions = { +export const USER_ACTIONS = { SET_USER: 'SET_USER', USER_LOGOUT: 'USER_LOGOUT', - - SET_EDITING: 'SET_EDITING', - SET_MODE: 'SET_MODE', - SET_DISTANCE: 'SET_DISTANCE', - SET_CHANGED: 'SET_CHANGED', - SET_ROUTER_POINTS: 'SET_ROUTER_POINTS', - SET_ACTIVE_STICKER: 'SET_ACTIVE_STICKER', - SET_LOGO: 'SET_LOGO', - SET_TITLE: 'SET_TITLE', - SET_ADDRESS: 'SET_ADDRESS', - SET_ADDRESS_ORIGIN: 'SET_ADDRESS_ORIGIN', - SET_PUBLIC: 'SET_PUBLIC', - SET_STARRED: 'SET_STARRED', - SET_DESCRIPTION: 'SET_DESCRIPTION', - - START_EDITING: 'START_EDITING', - STOP_EDITING: 'STOP_EDITING', - - ROUTER_CANCEL: 'ROUTER_CANCEL', - ROUTER_SUBMIT: 'ROUTER_SUBMIT', - - CLEAR_POLY: 'CLEAR_POLY', - CLEAR_STICKERS: 'CLEAR_STICKERS', - CLEAR_ALL: 'CLEAR_ALL', - CLEAR_CANCEL: 'CLEAR_CANCEL', - - SEND_SAVE_REQUEST: 'SEND_SAVE_REQUEST', - SET_SAVE_LOADING: 'SET_SAVE_LOADING', - CANCEL_SAVE_REQUEST: 'CANCEL_SAVE_REQUEST', - RESET_SAVE_DIALOG: 'RESET_SAVE_DIALOG', - - SET_SAVE_SUCCESS: 'SET_SAVE_SUCCESS', - SET_SAVE_ERROR: 'SET_SAVE_ERROR', - SET_SAVE_OVERWRITE: 'SET_SAVE_OVERWRITE', - - SHOW_RENDERER: 'SHOW_RENDERER', - HIDE_RENDERER: 'HIDE_RENDERER', - SET_RENDERER: 'SET_RENDERER', - TAKE_A_SHOT: 'TAKE_A_SHOT', - CROP_A_SHOT: 'CROP_A_SHOT', - - SET_PROVIDER: 'SET_PROVIDER', - CHANGE_PROVIDER: 'CHANGE_PROVIDER', - - SET_DIALOG: 'SET_DIALOG', - SET_DIALOG_ACTIVE: 'SET_DIALOG_ACTIVE', - LOCATION_CHANGED: 'LOCATION_CHANGED', - SET_READY: 'SET_READY', - GOT_VK_USER: 'GOT_VK_USER', - KEY_PRESSED: 'KEY_PRESSED', IFRAME_LOGIN_VK: 'IFRAME_LOGIN_VK', @@ -68,21 +14,13 @@ export const USER_ACTIONS: IActions = { SEARCH_SET_LOADING: 'SEARCH_SET_LOADING', OPEN_MAP_DIALOG: 'OPEN_MAP_DIALOG', - SET_SPEED: 'SET_SPEED', - - SET_MARKERS_SHOWN: 'SET_MARKERS_SHOWN', - - GET_GPX_TRACK: 'GET_GPX_TRACK', - SET_IS_EMPTY: 'SET_IS_EMPTY', - MAPS_LOAD_MORE: 'MAPS_LOAD_MORE', MAPS_SET_SHIFT: 'MAPS_SET_SHIFT', - SET_FEATURE: 'SET_FEATURE', - SET_IS_ROUTING: 'SET_IS_ROUTING', - DROP_ROUTE: 'DROP_ROUTE', + SET_STARRED: 'SET_STARRED', MODIFY_ROUTE: 'MODIFY_ROUTE', + SET_ROUTE_STARRED: 'SET_ROUTE_STARRED', TOGGLE_ROUTE_STARRED: 'TOGGLE_ROUTE_STARRED', }; diff --git a/src/redux/user/handlers.ts b/src/redux/user/handlers.ts index 8d9cba9..3550434 100644 --- a/src/redux/user/handlers.ts +++ b/src/redux/user/handlers.ts @@ -1,6 +1,5 @@ import { IRootState } from "."; import * as ActionCreators from './actions' -import { TIPS } from "~/constants/tips"; import { TABS } from "~/constants/dialogs"; import { USER_ACTIONS } from "./constants"; @@ -10,11 +9,6 @@ export interface ActionHandler { (state: IRootState, payload: UnsafeReturnType): IRootState; } -const getEstimated = (distance: number, speed: number = 15): number => { - const time = (distance && (distance / speed)) || 0; - return (time && parseFloat(time.toFixed(1))); -}; - const setUser: ActionHandler = (state, { user }) => ({ ...state, user: { @@ -23,121 +17,6 @@ const setUser: ActionHandler = (state, { user }) }, }); -const setEditing: ActionHandler = (state, { editing }) => ({ - ...state, editing -}); - -const setChanged: ActionHandler = (state, { changed }) => ({ - ...state, - changed -}); - -const setMode: ActionHandler = (state, { mode }) => ({ - ...state, - mode -}); - -const setDistance: ActionHandler = (state, { distance }) => ({ - ...state, - distance, - estimated: getEstimated(distance, state.speed), -}); - -const setRouterPoints: ActionHandler = (state, { routerPoints }) => ({ - ...state, - routerPoints, -}); - -const setActiveSticker: ActionHandler = (state, { activeSticker }) => ({ - ...state, - activeSticker: activeSticker || { set: null, sticker: null } -}); - -// const setLogo: ActionHandler = (state, { logo }) => ({ -// ...state, -// logo -// }); - -// const setTitle: ActionHandler = (state, { title }) => ({ -// ...state, -// title -// }); - -// const setDescription: ActionHandler = (state, { description }) => ({ -// ...state, -// description -// }); - -// const setAddress: ActionHandler = (state, { address }) => ({ -// ...state, -// address -// }); - -const setAddressOrigin: ActionHandler = (state, { address_origin }) => ({ - ...state, - address_origin -}); - -const sendSaveRequest: ActionHandler = (state) => ({ - ...state, - save_processing: true, -}); - -const setSaveError: ActionHandler = (state, { save_error }) => ({ - ...state, save_error, save_finished: false, save_processing: false -}); - -const setSaveLoading: ActionHandler = (state, { save_loading }) => ({ - ...state, save_loading -}); - -const setSaveOverwrite: ActionHandler = (state) => ({ - ...state, - save_overwriting: true, - save_finished: false, - save_processing: false, - save_error: TIPS.SAVE_OVERWRITE, -}); - -const setSaveSuccess: ActionHandler = (state, { save_error }) => ({ - ...state, - save_overwriting: false, - save_finished: true, - save_processing: false, - save_error, -}); - -const resetSaveDialog: ActionHandler = (state) => ({ - ...state, save_overwriting: false, save_finished: false, save_processing: false, save_error: '', -}); - -const hideRenderer: ActionHandler = (state) => ({ - ...state, - renderer: { ...state.renderer, renderer_active: false } -}); - -const setRenderer: ActionHandler = (state, { payload }) => ({ - ...state, - renderer: { ...state.renderer, ...payload } -}); - -// const setProvider: ActionHandler = (state, { provider }) => ({ ...state, provider }); - -const setDialog: ActionHandler = (state, { dialog }) => ({ - ...state, - dialog, -}); - -const setDialogActive: ActionHandler = (state, { dialog_active }) => ({ - ...state, - dialog_active: dialog_active || !state.dialog_active, -}); - -const setReady: ActionHandler = (state, { ready = true }) => ({ - ...state, - ready, -}); - const searchSetTitle: ActionHandler = (state, { title = '' }) => ({ ...state, routes: { @@ -199,17 +78,8 @@ const searchSetLoading: ActionHandler = } }); -// const setPublic: ActionHandler = (state, { is_public = false }) => ({ ...state, is_public }); const setStarred: ActionHandler = (state, { is_published = false }) => ({ ...state, is_published }); -const setSpeed: ActionHandler = (state, { speed = 15 }) => ({ - ...state, - speed, - estimated: getEstimated(state.distance, speed), -}); - -const setMarkersShown: ActionHandler = (state, { markers_shown = true }) => ({ ...state, markers_shown }); -const setIsEmpty: ActionHandler = (state, { is_empty = true }) => ({ ...state, is_empty }); const mapsSetShift: ActionHandler = (state, { shift = 0 }) => ({ ...state, routes: { @@ -218,19 +88,6 @@ const mapsSetShift: ActionHandler = (state, } }); -const setFeature: ActionHandler = (state, { features }) => ({ - ...state, - features: { - ...state.features, - ...features, - } -}); - -const setIsRouting: ActionHandler = (state, { is_routing }) => ({ - ...state, - is_routing, -}); - const setRouteStarred: ActionHandler = (state, { address, is_published }) => ({ ...state, routes: { @@ -248,33 +105,6 @@ const setRouteStarred: ActionHandler = (s export const USER_HANDLERS = ({ [USER_ACTIONS.SET_USER]: setUser, - [USER_ACTIONS.SET_EDITING]: setEditing, - [USER_ACTIONS.SET_CHANGED]: setChanged, - [USER_ACTIONS.SET_MODE]: setMode, - [USER_ACTIONS.SET_DISTANCE]: setDistance, - [USER_ACTIONS.SET_ROUTER_POINTS]: setRouterPoints, - [USER_ACTIONS.SET_ACTIVE_STICKER]: setActiveSticker, - // [USER_ACTIONS.SET_LOGO]: setLogo, - // [USER_ACTIONS.SET_TITLE]: setTitle, - // [USER_ACTIONS.SET_DESCRIPTION]: setDescription, - // [USER_ACTIONS.SET_ADDRESS]: setAddress, - [USER_ACTIONS.SET_ADDRESS_ORIGIN]: setAddressOrigin, - - [USER_ACTIONS.SET_SAVE_ERROR]: setSaveError, - [USER_ACTIONS.SET_SAVE_LOADING]: setSaveLoading, - [USER_ACTIONS.SET_SAVE_OVERWRITE]: setSaveOverwrite, - [USER_ACTIONS.SET_SAVE_SUCCESS]: setSaveSuccess, - [USER_ACTIONS.SEND_SAVE_REQUEST]: sendSaveRequest, - [USER_ACTIONS.RESET_SAVE_DIALOG]: resetSaveDialog, - - [USER_ACTIONS.HIDE_RENDERER]: hideRenderer, - [USER_ACTIONS.SET_RENDERER]: setRenderer, - - // [USER_ACTIONS.SET_PROVIDER]: setProvider, - - [USER_ACTIONS.SET_DIALOG]: setDialog, - [USER_ACTIONS.SET_DIALOG_ACTIVE]: setDialogActive, - [USER_ACTIONS.SET_READY]: setReady, [USER_ACTIONS.SEARCH_SET_TITLE]: searchSetTitle, [USER_ACTIONS.SEARCH_SET_DISTANCE]: searchSetDistance, @@ -282,16 +112,10 @@ export const USER_HANDLERS = ({ [USER_ACTIONS.SEARCH_SET_TAB]: searchSetTab, [USER_ACTIONS.SEARCH_PUT_ROUTES]: searchPutRoutes, [USER_ACTIONS.SEARCH_SET_LOADING]: searchSetLoading, - // [USER_ACTIONS.SET_PUBLIC]: setPublic, - [USER_ACTIONS.SET_STARRED]: setStarred, - [USER_ACTIONS.SET_SPEED]: setSpeed, - [USER_ACTIONS.SET_MARKERS_SHOWN]: setMarkersShown, - [USER_ACTIONS.SET_IS_EMPTY]: setIsEmpty, [USER_ACTIONS.MAPS_SET_SHIFT]: mapsSetShift, - [USER_ACTIONS.SET_FEATURE]: setFeature, - [USER_ACTIONS.SET_IS_ROUTING]: setIsRouting, + [USER_ACTIONS.SET_STARRED]: setStarred, [USER_ACTIONS.SET_ROUTE_STARRED]: setRouteStarred, }); \ No newline at end of file diff --git a/src/redux/user/index.ts b/src/redux/user/index.ts index 56e177e..09ff24e 100644 --- a/src/redux/user/index.ts +++ b/src/redux/user/index.ts @@ -1,8 +1,5 @@ import { createReducer } from 'reduxsauce'; import { DEFAULT_USER, IUser } from '~/constants/auth'; -import { MODES } from '~/constants/modes'; -import { DIALOGS, IDialogs } from '~/constants/dialogs'; -import { IStickers } from "~/constants/stickers"; import { USER_HANDLERS } from './handlers'; export interface IRouteListItem { @@ -15,50 +12,8 @@ export interface IRouteListItem { } export interface IRootReducer { - ready: boolean, + // ready: boolean, user: IUser, - editing: boolean, - mode: typeof MODES[keyof typeof MODES], - // logo: keyof typeof LOGOS, - routerPoints: number, - distance: number, - // description: string, - estimated: number, - speed: number, - activeSticker: { set?: keyof IStickers, sticker?: string }, - // title: string, - // address: string, - // address_origin: string, - changed: boolean, - // provider: keyof typeof PROVIDERS, - markers_shown: boolean, - - is_published: boolean, - // is_public: boolean, - is_empty: boolean, - is_routing: boolean, - - save_error: string, - save_finished: boolean, - save_overwriting: boolean, - save_processing: boolean, - save_loading: boolean, - - dialog: IDialogs[keyof IDialogs], - dialog_active: boolean, - - features: { - routing: boolean, - }, - - renderer: { - data: string, - width: number, - height: number - renderer_active: boolean, - info: string, - progress: number, - }, routes: { limit: 0, @@ -81,53 +36,8 @@ export interface IRootReducer { export type IRootState = Readonly; export const INITIAL_STATE: IRootReducer = { - ready: false, user: { ...DEFAULT_USER }, - mode: MODES.NONE, - // logo: DEFAULT_LOGO, - routerPoints: 0, - distance: 0, - // description: '', - estimated: 0, - speed: 15, - activeSticker: { set: null, sticker: null }, - // title: '', - // address: '', - // address_origin: '', - // provider: DEFAULT_PROVIDER, - - markers_shown: true, - changed: false, - editing: false, - - is_published: false, - // is_public: false, - is_empty: true, - is_routing: false, - - save_error: '', - save_finished: false, - save_overwriting: false, - save_processing: false, - save_loading: false, - - dialog: DIALOGS.NONE, - dialog_active: false, - - features: { - routing: false, - }, - - renderer: { - data: '', - width: 0, - height: 0, - renderer_active: false, - info: '', - progress: 0, - }, - routes: { limit: 0, loading: false, // <-- maybe delete this diff --git a/src/redux/user/sagas.ts b/src/redux/user/sagas.ts index 241db78..5b067ef 100644 --- a/src/redux/user/sagas.ts +++ b/src/redux/user/sagas.ts @@ -19,23 +19,23 @@ import { sendRouteStarred, } from '~/utils/api'; import { - hideRenderer, - searchPutRoutes, - searchSetLoading, - setChanged, - setDialogActive, - setEditing, - setMode, - setReady, - setRenderer, + // hideRenderer, + // setChanged, + // setDialogActive, + // setEditing, + // setMode, + // setReady, + // setRenderer, + // setDialog, + // setAddressOrigin, + // clearAll, + // setFeature, searchSetTab, setUser, - setDialog, - setAddressOrigin, mapsSetShift, searchChangeDistance, - clearAll, - setFeature, + searchPutRoutes, + searchSetLoading, searchSetTitle, setRouteStarred, } from '~/redux/user/actions'; @@ -48,43 +48,22 @@ import { pushPath, } from '~/utils/history'; import { USER_ACTIONS } from '~/redux/user/constants'; -import { MODES } from '~/constants/modes'; import { DEFAULT_USER } from '~/constants/auth'; -import { - composeArrows, - composeDistMark, - composeImages, - composePoly, - composeStickers, - downloadCanvas, - fetchImages, - getPolyPlacement, - getStickersPlacement, - getTilePlacement, - imageFetcher, -} from '~/utils/renderer'; -import { LOGOS } from '~/constants/logos'; + import { DIALOGS, TABS } from '~/constants/dialogs'; import * as ActionCreators from '~/redux/user/actions'; -import { downloadGPXTrack, getGPXString } from '~/utils/gpx'; import { Unwrap } from '~/utils/middleware'; import { IState } from '~/redux/store'; import { selectUser, selectUserUser } from './selectors'; import { mapInitSaga, loadMapSaga, replaceAddressIfItsBusy } from '~/redux/map/sagas'; -import { LatLng } from 'leaflet'; -import { selectMap } from '~/redux/map/selectors'; +import { editorSetDialog, editorSetDialogActive } from '../editor/actions'; +import { selectEditor } from '../editor/selectors'; +import { EDITOR_ACTIONS } from '../editor/constants'; // const getUser = (state: IState) => state.user.user; // const selectUser = (state: IState) => state.user; -const hideLoader = () => { - document.getElementById('loader').style.opacity = String(0); - document.getElementById('loader').style.pointerEvents = 'none'; - - return true; -}; - function* generateGuestSaga() { const { data: { user, random_url }, @@ -95,52 +74,43 @@ function* generateGuestSaga() { return { ...user, random_url }; } -function* startEditingSaga() { - const { path } = getUrlData(); - yield pushPath(`/${path}/edit`); -} +// function* stopEditingSaga() { +// const { changed, editing, mode, address_origin } = yield select(selectUser); +// const { path } = getUrlData(); -function* stopEditingSaga() { - const { changed, editing, mode, address_origin } = yield select(selectUser); - const { path } = getUrlData(); +// if (!editing) return; +// if (changed && mode !== MODES.CONFIRM_CANCEL) { +// yield put(setMode(MODES.CONFIRM_CANCEL)); +// return; +// } - if (!editing) return; - if (changed && mode !== MODES.CONFIRM_CANCEL) { - yield put(setMode(MODES.CONFIRM_CANCEL)); - return; - } +// yield put(setMode(MODES.NONE)); +// yield put(setChanged(false)); - // TODO: cancel editing? - // yield editor.cancelEditing(); - yield put(setMode(MODES.NONE)); - yield put(setChanged(false)); - // TODO: dont close editor if theres no initial data - // yield put(setEditing(editor.hasEmptyHistory)); // don't close editor if no previous map +// yield pushPath(`/${address_origin || path}/`); +// } - yield pushPath(`/${address_origin || path}/`); -} +// function* checkOSRMServiceSaga() { +// const routing = yield call(checkOSRMService, [new LatLng(1,1), new LatLng(2,2)]); -function* checkOSRMServiceSaga() { - const routing = yield call(checkOSRMService, [new LatLng(1,1), new LatLng(2,2)]); +// yield put(setFeature({ routing })); +// } - yield put(setFeature({ routing })); -} +// export function* setReadySaga() { +// yield put(setReady(true)); +// hideLoader(); -export function* setReadySaga() { - yield put(setReady(true)); - hideLoader(); - - yield call(checkOSRMServiceSaga); - yield put(searchSetTab(TABS.MY)); -} +// yield call(checkOSRMServiceSaga); +// yield put(searchSetTab(TABS.MY)); +// } function* authCheckSaga({ key }: RehydrateAction) { if (key !== 'user') return; pushLoaderState(70); - const { id, token } = yield select(selectUserUser); - const { ready } = yield select(selectUser); + const { id, token }: ReturnType = yield select(selectUserUser); + const { ready }: ReturnType = yield select(selectEditor); if (window.location.search || true) { const { viewer_id, auth_key } = yield parseQuery(window.location.search); @@ -215,128 +185,127 @@ function* authCheckSaga({ key }: RehydrateAction) { // return true; // } +// function* getRenderData() { +// yield put(setRenderer({ info: 'Загрузка тайлов', progress: 0.1 })); -function* getRenderData() { - yield put(setRenderer({ info: 'Загрузка тайлов', progress: 0.1 })); +// const { route, stickers, provider }: ReturnType = yield select(selectMap); - const { route, stickers, provider }: ReturnType = yield select(selectMap); +// const canvas = document.getElementById('renderer'); +// canvas.width = window.innerWidth; +// canvas.height = window.innerHeight; +// const ctx = canvas.getContext('2d'); - const canvas = document.getElementById('renderer'); - canvas.width = window.innerWidth; - canvas.height = window.innerHeight; - const ctx = canvas.getContext('2d'); +// const geometry = getTilePlacement(); +// const points = getPolyPlacement(route); +// const sticker_points = getStickersPlacement(stickers); +// // TODO: get distance: +// const distance = 0; +// // const distance = editor.poly.poly.distance; - const geometry = getTilePlacement(); - const points = getPolyPlacement(route); - const sticker_points = getStickersPlacement(stickers); - // TODO: get distance: - const distance = 0; - // const distance = editor.poly.poly.distance; +// ctx.clearRect(0, 0, canvas.width, canvas.height); - ctx.clearRect(0, 0, canvas.width, canvas.height); +// const images = yield fetchImages(ctx, geometry, provider); - const images = yield fetchImages(ctx, geometry, provider); +// yield put(setRenderer({ info: 'Отрисовка', progress: 0.5 })); - yield put(setRenderer({ info: 'Отрисовка', progress: 0.5 })); +// yield composeImages({ geometry, images, ctx }); +// yield composePoly({ points, ctx }); +// yield composeArrows({ points, ctx }); +// yield composeDistMark({ ctx, points, distance }); +// yield composeStickers({ stickers: sticker_points, ctx }); - yield composeImages({ geometry, images, ctx }); - yield composePoly({ points, ctx }); - yield composeArrows({ points, ctx }); - yield composeDistMark({ ctx, points, distance }); - yield composeStickers({ stickers: sticker_points, ctx }); +// yield put(setRenderer({ info: 'Готово', progress: 1 })); - yield put(setRenderer({ info: 'Готово', progress: 1 })); +// return yield canvas.toDataURL('image/jpeg'); +// } - return yield canvas.toDataURL('image/jpeg'); -} +// function* takeAShotSaga() { +// const worker = call(getRenderData); -function* takeAShotSaga() { - const worker = call(getRenderData); +// const { result, timeout } = yield race({ +// result: worker, +// timeout: delay(500), +// }); - const { result, timeout } = yield race({ - result: worker, - timeout: delay(500), - }); +// if (timeout) yield put(setMode(MODES.SHOT_PREFETCH)); - if (timeout) yield put(setMode(MODES.SHOT_PREFETCH)); +// const data = yield result || worker; - const data = yield result || worker; +// yield put(setMode(MODES.NONE)); +// yield put( +// setRenderer({ +// data, +// renderer_active: true, +// width: window.innerWidth, +// height: window.innerHeight, +// }) +// ); +// } - yield put(setMode(MODES.NONE)); - yield put( - setRenderer({ - data, - renderer_active: true, - width: window.innerWidth, - height: window.innerHeight, - }) - ); -} +// function* getCropData({ x, y, width, height }) { +// const { +// logo, +// renderer: { data }, +// } = yield select(selectUser); +// const canvas = document.getElementById('renderer'); +// canvas.width = width; +// canvas.height = height; +// const ctx = canvas.getContext('2d'); +// const image = yield imageFetcher(data); -function* getCropData({ x, y, width, height }) { - const { - logo, - renderer: { data }, - } = yield select(selectUser); - const canvas = document.getElementById('renderer'); - canvas.width = width; - canvas.height = height; - const ctx = canvas.getContext('2d'); - const image = yield imageFetcher(data); +// ctx.drawImage(image, -x, -y); - ctx.drawImage(image, -x, -y); +// if (logo && LOGOS[logo][1]) { +// const logoImage = yield imageFetcher(LOGOS[logo][1]); +// ctx.drawImage(logoImage, width - logoImage.width, height - logoImage.height); +// } - if (logo && LOGOS[logo][1]) { - const logoImage = yield imageFetcher(LOGOS[logo][1]); - ctx.drawImage(logoImage, width - logoImage.width, height - logoImage.height); - } +// return yield canvas.toDataURL('image/jpeg'); +// } - return yield canvas.toDataURL('image/jpeg'); -} +// function* cropAShotSaga(params) { +// const { title, address } = yield select(selectUser); +// yield call(getCropData, params); +// const canvas = document.getElementById('renderer') as HTMLCanvasElement; -function* cropAShotSaga(params) { - const { title, address } = yield select(selectUser); - yield call(getCropData, params); - const canvas = document.getElementById('renderer') as HTMLCanvasElement; +// downloadCanvas(canvas, (title || address).replace(/\./gi, ' ')); - downloadCanvas(canvas, (title || address).replace(/\./gi, ' ')); +// return yield put(hideRenderer()); +// } - return yield put(hideRenderer()); -} +// function* locationChangeSaga({ location }: ReturnType) { +// const { +// address, +// ready, +// user: { id, random_url }, +// } = yield select(selectUser); -function* locationChangeSaga({ location }: ReturnType) { - const { - address, - ready, - user: { id, random_url }, - } = yield select(selectUser); +// const { owner }: ReturnType = yield select(selectMap) - const { owner }: ReturnType = yield select(selectMap) +// if (!ready) return; - if (!ready) return; +// const { path, mode } = getUrlData(location); - const { path, mode } = getUrlData(location); +// if (address !== path) { +// const map = yield call(loadMapSaga, path); - if (address !== path) { - const map = yield call(loadMapSaga, path); +// if (map && map.route && map.route.owner && mode === 'edit' && map.route.owner !== id) { +// return yield call(replaceAddressIfItsBusy, map.random_url, map.address); +// } +// } else if (mode === 'edit' && owner.id !== id) { +// return yield call(replaceAddressIfItsBusy, random_url, address); +// } else { +// yield put(setAddressOrigin('')); +// } - if (map && map.route && map.route.owner && mode === 'edit' && map.route.owner !== id) { - return yield call(replaceAddressIfItsBusy, map.random_url, map.address); - } - } else if (mode === 'edit' && owner.id !== id) { - return yield call(replaceAddressIfItsBusy, random_url, address); - } else { - yield put(setAddressOrigin('')); - } - - if (mode !== 'edit') { - yield put(setEditing(false)); - // editor.stopEditing(); - } else { - yield put(setEditing(true)); - // editor.startEditing(); - } -} +// if (mode !== 'edit') { +// yield put(setEditing(false)); +// // editor.stopEditing(); +// } else { +// yield put(setEditing(true)); +// // editor.startEditing(); +// } +// } function* gotVkUserSaga({ user: u }: ReturnType) { const { @@ -346,40 +315,40 @@ function* gotVkUserSaga({ user: u }: ReturnType yield put(setUser({ ...user, random_url })); } -function* keyPressedSaga({ key, target }: ReturnType): any { - if (target === 'INPUT' || target === 'TEXTAREA') { - return; - } +// function* keyPressedSaga({ key, target }: ReturnType): any { +// if (target === 'INPUT' || target === 'TEXTAREA') { +// return; +// } - if (key === 'Escape') { - const { - dialog_active, - mode, - renderer: { renderer_active }, - } = yield select(selectUser); +// if (key === 'Escape') { +// const { +// dialog_active, +// mode, +// renderer: { renderer_active }, +// } = yield select(selectUser); - if (renderer_active) return yield put(hideRenderer()); - if (dialog_active) return yield put(setDialogActive(false)); - if (mode !== MODES.NONE) return yield put(setMode(MODES.NONE)); - } else if (key === 'Delete') { - const { - user: { editing }, - } = yield select(); +// if (renderer_active) return yield put(hideRenderer()); +// if (dialog_active) return yield put(setDialogActive(false)); +// if (mode !== MODES.NONE) return yield put(setMode(MODES.NONE)); +// } else if (key === 'Delete') { +// const { +// user: { editing }, +// } = yield select(); - if (!editing) return; +// if (!editing) return; - const { mode } = yield select(selectUser); +// const { mode } = yield select(selectUser); - if (mode === MODES.TRASH) { - yield put(clearAll()); - } else { - yield put(setMode(MODES.TRASH)); - } - } -} +// if (mode === MODES.TRASH) { +// yield put(clearAll()); +// } else { +// yield put(setMode(MODES.TRASH)); +// } +// } +// } function* searchGetRoutes() { - const { token } = yield select(selectUserUser); + const { token }: ReturnType = yield select(selectUserUser); const { routes: { @@ -387,7 +356,7 @@ function* searchGetRoutes() { shift, filter: { title, distance, tab }, }, - } = yield select(selectUser); + }: ReturnType = yield select(selectUser); const result: Unwrap = yield getRouteList({ token, @@ -442,14 +411,17 @@ function* searchSetSaga() { function* openMapDialogSaga({ tab }: ReturnType) { const { - dialog_active, routes: { filter: { tab: current }, }, - } = yield select(selectUser); + }: ReturnType = yield select(selectUser); + + const { + dialog_active, + }: ReturnType = yield select(selectEditor); if (dialog_active && tab === current) { - return yield put(setDialogActive(false)); + return yield put(editorSetDialogActive(false)); } if (tab !== current) { @@ -457,8 +429,8 @@ function* openMapDialogSaga({ tab }: ReturnType = yield select(selectEditor); if (dialog_active) yield call(searchSetSagaWorker); return true; } -function* getGPXTrackSaga(): SagaIterator { - const { route, stickers }: ReturnType = yield select(selectMap); - const { title, address } = yield select(selectUser); +// function* getGPXTrackSaga(): SagaIterator { +// const { route, stickers }: ReturnType = yield select(selectMap); +// const { title, address } = yield select(selectUser); - if (!route || route.length <= 0) return; +// if (!route || route.length <= 0) return; - const track = getGPXString({ route, stickers, title: title || address }); +// const track = getGPXString({ route, stickers, title: title || address }); - return downloadGPXTrack({ track, title }); -} +// return downloadGPXTrack({ track, title }); +// } function* mapsLoadMoreSaga() { const { routes: { limit, list, shift, step, loading, filter }, - } = yield select(selectUser); + }: ReturnType = yield select(selectUser); if (loading || list.length >= limit || limit === 0) return; @@ -540,7 +512,7 @@ function* mapsLoadMoreSaga() { } function* dropRouteSaga({ address }: ReturnType): SagaIterator { - const { token } = yield select(selectUserUser); + const { token }: ReturnType = yield select(selectUserUser); const { routes: { list, @@ -549,7 +521,7 @@ function* dropRouteSaga({ address }: ReturnType limit, filter: { min, max }, }, - } = yield select(selectUser); + }: ReturnType = yield select(selectUser); const index = list.findIndex(el => el.address === address); @@ -574,7 +546,7 @@ function* modifyRouteSaga({ title, is_public, }: ReturnType): SagaIterator { - const { token } = yield select(selectUserUser); + const { token }: ReturnType = yield select(selectUserUser); const { routes: { list, @@ -606,12 +578,12 @@ function* modifyRouteSaga({ function* toggleRouteStarredSaga({ address, }: ReturnType) { + const { token }: ReturnType = yield select(selectUserUser); const { routes: { list }, - }: IState['user'] = yield select(selectUser); + }: ReturnType = yield select(selectUser); const route = list.find(el => el.address === address); - const { token } = yield select(selectUserUser); yield put(setRouteStarred(address, !route.is_published)); const result = yield sendRouteStarred({ @@ -624,24 +596,24 @@ function* toggleRouteStarredSaga({ } export function* userSaga() { + // yield takeEvery(USER_ACTIONS.STOP_EDITING, stopEditingSaga); + // yield takeLatest(USER_ACTIONS.TAKE_A_SHOT, takeAShotSaga); + // yield takeLatest(USER_ACTIONS.CROP_A_SHOT, cropAShotSaga); + // yield takeLatest(USER_ACTIONS.LOCATION_CHANGED, locationChangeSaga); + // yield takeLatest(USER_ACTIONS.KEY_PRESSED, keyPressedSaga); + // yield takeLatest(USER_ACTIONS.GET_GPX_TRACK, getGPXTrackSaga); + yield takeLatest(REHYDRATE, authCheckSaga); - yield takeEvery(USER_ACTIONS.START_EDITING, startEditingSaga); - yield takeEvery(USER_ACTIONS.STOP_EDITING, stopEditingSaga); - yield takeEvery(USER_ACTIONS.USER_LOGOUT, userLogoutSaga); // yield takeEvery(USER_ACTIONS.ROUTER_CANCEL, routerCancelSaga); // yield takeEvery(USER_ACTIONS.ROUTER_SUBMIT, routerSubmitSaga); - yield takeLatest(USER_ACTIONS.TAKE_A_SHOT, takeAShotSaga); - yield takeLatest(USER_ACTIONS.CROP_A_SHOT, cropAShotSaga); // yield takeEvery(USER_ACTIONS.CHANGE_PROVIDER, changeProviderSaga); - yield takeLatest(USER_ACTIONS.LOCATION_CHANGED, locationChangeSaga); yield takeLatest(USER_ACTIONS.GOT_VK_USER, gotVkUserSaga); - yield takeLatest(USER_ACTIONS.KEY_PRESSED, keyPressedSaga); // yield takeLatest(USER_ACTIONS.SET_TITLE, setTitleSaga); @@ -654,7 +626,6 @@ export function* userSaga() { yield takeLatest(USER_ACTIONS.SEARCH_SET_TAB, searchSetTabSaga); yield takeLatest(USER_ACTIONS.SET_USER, setUserSaga); - yield takeLatest(USER_ACTIONS.GET_GPX_TRACK, getGPXTrackSaga); yield takeLatest(USER_ACTIONS.MAPS_LOAD_MORE, mapsLoadMoreSaga); yield takeLatest(USER_ACTIONS.DROP_ROUTE, dropRouteSaga); diff --git a/src/redux/user/selectors.ts b/src/redux/user/selectors.ts index 4d9e268..6de1196 100644 --- a/src/redux/user/selectors.ts +++ b/src/redux/user/selectors.ts @@ -1,8 +1,4 @@ import { IState } from '~/redux/store' export const selectUser = (state: IState) => state.user; -export const selectUserUser = (state: IState) => state.user.user; -export const selectUserEditing = (state: IState) => state.user.editing; -export const selectUserMode = (state: IState) => state.user.mode; -export const selectUserActiveSticker = (state: IState) => state.user.activeSticker; -export const selectUserRenderer = (state: IState) => state.user.renderer; \ No newline at end of file +export const selectUserUser = (state: IState) => state.user.user; \ No newline at end of file diff --git a/src/utils/format.ts b/src/utils/format.ts index f086504..12452e7 100644 --- a/src/utils/format.ts +++ b/src/utils/format.ts @@ -164,4 +164,7 @@ export const toTranslit = (string: string = ''): string => export const parseDesc = (text: string = ''): string => text.replace(/(\n{2,})/gi, '\n\n'); -// export const colorizeTitle = (text: string): string => text.replace(/(\[[^\]^]+\])/, ``) +export const getEstimated = (distance: number, speed: number = 15): number => { + const time = (distance && (distance / speed)) || 0; + return (time && parseFloat(time.toFixed(1))); +}; \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json index 48ad582..466a91b 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -23,5 +23,8 @@ "./src/index.tsx", "./backend/**/*", "./custom.d.ts" + ], + "exclude": [ + "./src/_modules/**/*" ] }