From fe311e7839763e711fb91084aed4c8cc43f39b54 Mon Sep 17 00:00:00 2001 From: Fedor Katurov Date: Thu, 8 Apr 2021 16:25:25 +0700 Subject: [PATCH 01/11] fixed typescript errors --- src/components/StickerDesc.tsx | 7 +- src/components/dialogs/MapListDialog.tsx | 13 +- src/components/dialogs/ProviderDialog.tsx | 4 +- src/components/dialogs/TitleDialog.tsx | 2 +- src/components/maps/RouteRowView.tsx | 2 +- src/components/panels/EditorPanel.tsx | 12 +- src/components/renderer/Renderer.tsx | 14 +- src/components/search/MapListDialogHead.tsx | 2 +- src/constants/api.ts | 20 +- src/constants/auth.ts | 12 +- src/constants/providers.ts | 17 +- src/map/Arrows/index.tsx | 6 +- src/map/CurrentLocation/index.tsx | 6 +- src/map/GpxPolyline/index.tsx | 4 +- src/map/KmMarks/index.tsx | 6 +- src/map/Map/index.tsx | 2 +- src/map/Route/index.tsx | 6 +- src/map/Router/index.tsx | 12 +- src/map/Sticker/index.tsx | 39 +- src/map/Stickers/index.tsx | 2 +- src/map/TileLayer/index.tsx | 8 +- src/redux/editor/index.ts | 4 +- src/redux/editor/sagas.ts | 21 +- src/redux/map/index.ts | 4 +- src/redux/map/sagas.ts | 271 ++++++------- src/redux/store.ts | 29 +- src/redux/user/index.ts | 4 +- src/redux/user/sagas.ts | 428 +++++++++++--------- src/utils/api.ts | 258 ------------ src/utils/api/index.ts | 214 ++++++++++ src/utils/api/instance.ts | 6 + src/utils/context.ts | 4 +- src/utils/gpx.ts | 6 +- src/utils/history.ts | 10 +- src/utils/map/ArrowsLayer.ts | 9 +- src/utils/map/InteractivePoly.ts | 42 +- src/utils/marks.ts | 8 +- src/utils/middleware.ts | 27 +- src/utils/renderer.ts | 10 +- src/utils/window.ts | 10 +- tsconfig.json | 2 +- 41 files changed, 786 insertions(+), 777 deletions(-) delete mode 100644 src/utils/api.ts create mode 100644 src/utils/api/index.ts create mode 100644 src/utils/api/instance.ts diff --git a/src/components/StickerDesc.tsx b/src/components/StickerDesc.tsx index ab20af4..40477bb 100644 --- a/src/components/StickerDesc.tsx +++ b/src/components/StickerDesc.tsx @@ -23,10 +23,15 @@ class StickerDesc extends React.PureComponent { blockMouse = e => { e.preventDefault(); e.stopPropagation(); + + if (!this.input) { + return + } + this.input.focus(); }; - input: HTMLTextAreaElement; + input: HTMLTextAreaElement | null = null; render() { const { value: text } = this.props; diff --git a/src/components/dialogs/MapListDialog.tsx b/src/components/dialogs/MapListDialog.tsx index 557f10f..48935b7 100644 --- a/src/components/dialogs/MapListDialog.tsx +++ b/src/components/dialogs/MapListDialog.tsx @@ -64,9 +64,8 @@ export interface State { class MapListDialogUnconnected extends PureComponent { state = { - menu_target: null, - editor_target: null, - + menu_target: '', + editor_target: '', is_editing: false, is_dropping: false, }; @@ -74,7 +73,7 @@ class MapListDialogUnconnected extends PureComponent { startEditing = (editor_target: IRouteListItem['address']): void => this.setState({ editor_target, - menu_target: null, + menu_target: '', is_editing: true, is_dropping: false, }); @@ -86,19 +85,19 @@ class MapListDialogUnconnected extends PureComponent { hideMenu = (): void => this.setState({ - menu_target: null, + menu_target: '', }); showDropCard = (editor_target: IRouteListItem['address']): void => this.setState({ editor_target, - menu_target: null, + menu_target: '', is_editing: false, is_dropping: true, }); stopEditing = (): void => { - this.setState({ editor_target: null }); + this.setState({ editor_target: '' }); }; setTitle = ({ target: { value } }: { target: { value: string } }): void => { diff --git a/src/components/dialogs/ProviderDialog.tsx b/src/components/dialogs/ProviderDialog.tsx index 29ac463..e564458 100644 --- a/src/components/dialogs/ProviderDialog.tsx +++ b/src/components/dialogs/ProviderDialog.tsx @@ -27,7 +27,7 @@ const ProviderDialogUnconnected = ({ provider, mapSetProvider }: Props) => ( backgroundImage: `url(${replaceProviderUrl(item, { x: 5980, y: 2589, zoom: 13 })})`, }} onMouseDown={() => mapSetProvider(item)} - key={PROVIDERS[item].name} + key={PROVIDERS[item]?.name} > { provider === item && @@ -44,4 +44,4 @@ const ProviderDialogUnconnected = ({ provider, mapSetProvider }: Props) => ( const ProviderDialog = connect(mapStateToProps, mapDispatchToProps)(ProviderDialogUnconnected) -export { ProviderDialog } \ No newline at end of file +export { ProviderDialog } diff --git a/src/components/dialogs/TitleDialog.tsx b/src/components/dialogs/TitleDialog.tsx index 75fd328..d626b06 100644 --- a/src/components/dialogs/TitleDialog.tsx +++ b/src/components/dialogs/TitleDialog.tsx @@ -42,7 +42,7 @@ export class TitleDialogUnconnected extends React.PureComponent { this.setMaxHeight(); } - setMaxHeight = (): number => { + setMaxHeight = () => { if (!this.ref_sizer || !this.ref_title || !this.ref_text) return 0; const { height: sizer_height } = this.ref_sizer.getBoundingClientRect(); diff --git a/src/components/maps/RouteRowView.tsx b/src/components/maps/RouteRowView.tsx index 072c80a..181ec61 100644 --- a/src/components/maps/RouteRowView.tsx +++ b/src/components/maps/RouteRowView.tsx @@ -40,7 +40,7 @@ export const RouteRowView = ({ is_admin, is_published, toggleStarred -}: Props): ReactElement => ( +}: Props): ReactElement => (
{(tab === TABS.PENDING || tab === TABS.STARRED) && is_admin && (
diff --git a/src/components/panels/EditorPanel.tsx b/src/components/panels/EditorPanel.tsx index 5b017ea..61bd25c 100644 --- a/src/components/panels/EditorPanel.tsx +++ b/src/components/panels/EditorPanel.tsx @@ -1,4 +1,4 @@ -import React, { PureComponent, useState, useCallback } from 'react'; +import React, { PureComponent } from 'react'; import { MODES } from '~/constants/modes'; import classnames from 'classnames'; @@ -7,12 +7,12 @@ import { EditorDialog } from '~/components/panels/EditorDialog'; import { connect } from 'react-redux'; import { editorChangeMode, + editorKeyPressed, + editorRedo, editorStartEditing, editorStopEditing, editorTakeAShot, - editorKeyPressed, editorUndo, - editorRedo, } from '~/redux/editor/actions'; import { Tooltip } from '~/components/panels/Tooltip'; import { IState } from '~/redux/store'; @@ -47,6 +47,10 @@ type Props = ReturnType & typeof mapDispatchToProps & {} class EditorPanelUnconnected extends PureComponent { componentDidMount() { + if (!this.panel) { + return; + } + window.addEventListener('keydown', this.onKeyPress as any); const obj = document.getElementById('control-dialog'); @@ -57,7 +61,7 @@ class EditorPanelUnconnected extends PureComponent { obj.style.width = String(width); } - panel: HTMLElement = null; + panel: HTMLDivElement | null = null; componentWillUnmount() { window.removeEventListener('keydown', this.onKeyPress as any); diff --git a/src/components/renderer/Renderer.tsx b/src/components/renderer/Renderer.tsx index a245da5..ea7acf3 100644 --- a/src/components/renderer/Renderer.tsx +++ b/src/components/renderer/Renderer.tsx @@ -31,6 +31,10 @@ class Component extends React.Component { }; onImageLoaded = () => { + if (!this.image) { + return + } + this.croppr = new Croppr(this.image, { onInitialize: this.onCropInit, }); @@ -57,12 +61,12 @@ class Component extends React.Component { regionEl.append(this.logo); }; - croppr: Croppr; - logo: HTMLDivElement; - image: HTMLImageElement; - logoImg: HTMLImageElement; + croppr?: Croppr; + logo: HTMLDivElement | null = null; + image: HTMLImageElement | null = null; + logoImg: HTMLImageElement | null = null; - getImage = () => this.props.editorCropAShot(this.croppr.getValue()); + getImage = () => this.props.editorCropAShot(this.croppr?.getValue()); render() { const { data } = this.props.editor.renderer; diff --git a/src/components/search/MapListDialogHead.tsx b/src/components/search/MapListDialogHead.tsx index cd8edf9..11ce7f2 100644 --- a/src/components/search/MapListDialogHead.tsx +++ b/src/components/search/MapListDialogHead.tsx @@ -7,7 +7,7 @@ interface Props { max: number; search: string; distance: [number, number]; - onDistanceChange: (val: [number, number]) => void; + onDistanceChange: (val: number[]) => void; onSearchChange: ChangeEventHandler; } diff --git a/src/constants/api.ts b/src/constants/api.ts index 5febdcf..dd1f73e 100644 --- a/src/constants/api.ts +++ b/src/constants/api.ts @@ -1,16 +1,14 @@ -import { CLIENT } from '~/config/frontend'; - export const API = { - GET_GUEST: `${CLIENT.API_ADDR}/api/auth/`, - CHECK_TOKEN: `${CLIENT.API_ADDR}/api/auth/`, - IFRAME_LOGIN_VK: `${CLIENT.API_ADDR}/api/auth/vk`, - GET_MAP: `${CLIENT.API_ADDR}/api/route/`, - POST_MAP: `${CLIENT.API_ADDR}/api/route/`, - GET_ROUTE_LIST: tab => `${CLIENT.API_ADDR}/api/route/list/${tab}`, + GET_GUEST: `/api/auth/`, + CHECK_TOKEN: `/api/auth/`, + IFRAME_LOGIN_VK: `/api/auth/vk`, + GET_MAP: `/api/route/`, + POST_MAP: `/api/route/`, + GET_ROUTE_LIST: tab => `/api/route/list/${tab}`, - DROP_ROUTE: `${CLIENT.API_ADDR}/api/route/`, - MODIFY_ROUTE: `${CLIENT.API_ADDR}/api/route/`, - SET_STARRED: `${CLIENT.API_ADDR}/api/route/publish`, + DROP_ROUTE: `/api/route/`, + MODIFY_ROUTE: `/api/route/`, + SET_STARRED: `/api/route/publish`, }; export const API_RETRY_INTERVAL = 10; diff --git a/src/constants/auth.ts b/src/constants/auth.ts index 8e011b8..8b70e58 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?: string; + id: string; uid: string; token?: string; photo: string; @@ -31,9 +31,9 @@ export const DEFAULT_USER: IUser = { role: ROLES.guest, routes: {}, success: false, - id: null, - token: null, - photo: null, - name: null, - uid: null, + id: '', + token: undefined, + photo: '', + name: '', + uid: '', }; diff --git a/src/constants/providers.ts b/src/constants/providers.ts index 17ad1cf..a075539 100644 --- a/src/constants/providers.ts +++ b/src/constants/providers.ts @@ -5,21 +5,6 @@ export interface IProvider { } export type ITileMaps = Record -// { - // WATERCOLOR: IProvider, - // DGIS: IProvider, - // DEFAULT: IProvider, - // DARQ: IProvider, - // BLANK: IProvider, - // HOT: IProvider, - // YSAT: IProvider, - // YMAP: IProvider, - // SAT: IProvider, - // ESAT: IProvider, - // CACHE_OSM: IProvider, - // CACHE_CARTO: IProvider, -// } - // Стили карт const TILEMAPS: ITileMaps = { @@ -53,7 +38,7 @@ const TILEMAPS: ITileMaps = { const ENABLED: Array = ['BLANK', 'DEFAULT', 'DGIS', 'HOT', 'ESAT']; export const DEFAULT_PROVIDER: keyof ITileMaps = ENABLED[1]; -export const PROVIDERS: Partial = ENABLED.reduce((obj, provider) => ({ +export const PROVIDERS: ITileMaps = ENABLED.reduce((obj, provider) => ({ ...obj, [provider]: TILEMAPS[provider], }), {}); diff --git a/src/map/Arrows/index.tsx b/src/map/Arrows/index.tsx index fa4dbb0..1a10263 100644 --- a/src/map/Arrows/index.tsx +++ b/src/map/Arrows/index.tsx @@ -14,17 +14,17 @@ const mapDispatchToProps = {}; type Props = ReturnType & typeof mapDispatchToProps & {}; const ArrowsUnconnected: FC = memo(({ route }) => { - const [layer, setLayer] = useState(null); + const [layer, setLayer] = useState(null); useEffect(() => { const item = new ArrowsLayer({}).addTo(MainMap); - setLayer(item); + setLayer(item); return () => MainMap.removeLayer(item); }, [MainMap]); useEffect(() => { if (!layer) return - + layer.setLatLngs(route); }, [layer, route]) return null; diff --git a/src/map/CurrentLocation/index.tsx b/src/map/CurrentLocation/index.tsx index 745a8aa..61aac29 100644 --- a/src/map/CurrentLocation/index.tsx +++ b/src/map/CurrentLocation/index.tsx @@ -1,9 +1,9 @@ -import React, { FC, useState, useEffect, useCallback } from 'react'; -import { LatLngLiteral, marker, Marker, DivIcon } from 'leaflet'; +import React, { FC, useCallback, useEffect } from 'react'; +import { DivIcon, LatLngLiteral, Marker } from 'leaflet'; import { MainMap } from '~/constants/map'; interface IProps { - location: LatLngLiteral; + location?: LatLngLiteral; } const CurrentLocation: FC = ({ location }) => { diff --git a/src/map/GpxPolyline/index.tsx b/src/map/GpxPolyline/index.tsx index b78bdf3..f59f008 100644 --- a/src/map/GpxPolyline/index.tsx +++ b/src/map/GpxPolyline/index.tsx @@ -8,14 +8,14 @@ interface IProps { } const GpxPolyline: FC = ({ latlngs, color }) => { - const [layer, setLayer] = useState(null); + const [layer, setLayer] = useState(null); useEffect(() => { const item = new Polyline([], { color, stroke: true, opacity: 1, - weight: 7, + weight: 7, // dashArray: [12,12], }).addTo(MainMap); setLayer(item); diff --git a/src/map/KmMarks/index.tsx b/src/map/KmMarks/index.tsx index 2440e96..667d811 100644 --- a/src/map/KmMarks/index.tsx +++ b/src/map/KmMarks/index.tsx @@ -1,4 +1,4 @@ -import React, { FC, useEffect, useState, memo } from 'react'; +import React, { FC, memo, useEffect, useState } from 'react'; import { KmMarksLayer } from '~/utils/marks'; import { MainMap } from '~/constants/map'; import { selectMap } from '~/redux/map/selectors'; @@ -14,14 +14,14 @@ const mapDispatchToProps = {}; type Props = ReturnType & typeof mapDispatchToProps & {}; const KmMarksUnconnected: FC = memo(({ map: { route } }) => { - const [layer, setLayer] = useState(null); + const [layer, setLayer] = useState(null); useEffect(() => { const layer = new KmMarksLayer([]); layer.addTo(MainMap); setLayer(layer); return () => MainMap.removeLayer(layer); - }, [MainMap]); + }, []); useEffect(() => { if (!layer) return; diff --git a/src/map/Map/index.tsx b/src/map/Map/index.tsx index 44cf90b..0836afd 100644 --- a/src/map/Map/index.tsx +++ b/src/map/Map/index.tsx @@ -93,7 +93,7 @@ const MapUnconnected: React.FC = memo( enabled && )}
, - document.getElementById('canvas') + document.getElementById('canvas')! ); } ); diff --git a/src/map/Route/index.tsx b/src/map/Route/index.tsx index 9d769b2..9c7b890 100644 --- a/src/map/Route/index.tsx +++ b/src/map/Route/index.tsx @@ -1,8 +1,8 @@ -import React, { FC, useEffect, memo, useState, useCallback } from 'react'; +import React, { FC, memo, useCallback, useEffect, useState } from 'react'; import { InteractivePoly } from '~/utils/map/InteractivePoly'; import { isMobile } from '~/utils/window'; import { LatLng } from 'leaflet'; -import { selectEditorMode, selectEditorEditing, selectEditorDirection } from '~/redux/editor/selectors'; +import { selectEditorDirection, selectEditorEditing, selectEditorMode } from '~/redux/editor/selectors'; import * as MAP_ACTIONS from '~/redux/map/actions'; import { connect } from 'react-redux'; import { selectMapRoute } from '~/redux/map/selectors'; @@ -28,7 +28,7 @@ type Props = ReturnType & typeof mapDispatchToProps & {} const RouteUnconnected: FC = memo( ({ route, editing, mode, drawing_direction, mapSetRoute, editorSetDistance, editorSetMarkersShown }) => { - const [layer, setLayer] = useState(null); + const [layer, setLayer] = useState(null); const onDistanceChange = useCallback(({ distance }) => editorSetDistance(distance), [ editorSetDistance, diff --git a/src/map/Router/index.tsx b/src/map/Router/index.tsx index de3b693..7e58edb 100644 --- a/src/map/Router/index.tsx +++ b/src/map/Router/index.tsx @@ -1,16 +1,12 @@ -import { FC, useEffect, useCallback, memo, useState } from 'react'; +import { FC, memo, useCallback, useEffect, useState } from 'react'; import { OsrmRouter } from '~/utils/map/OsrmRouter'; import { connect } from 'react-redux'; import { selectMapRoute } from '~/redux/map/selectors'; -import { - selectEditorRouter, - selectEditorMode, - selectEditorDistance, -} from '~/redux/editor/selectors'; +import { selectEditorDistance, selectEditorMode, selectEditorRouter } from '~/redux/editor/selectors'; import { MainMap } from '~/constants/map'; import * as EDITOR_ACTIONS from '~/redux/editor/actions'; import { MODES } from '~/constants/modes'; -import { LatLngLiteral, marker, divIcon } from 'leaflet'; +import { divIcon, LatLngLiteral, marker } from 'leaflet'; import classNames from 'classnames'; import { angleBetweenPoints } from '~/utils/geom'; @@ -30,7 +26,7 @@ type Props = ReturnType & typeof mapDispatchToProps & {} const RouterUnconnected: FC = memo( ({ route, mode, router: { waypoints }, editorSetRouter, distance }) => { const [dist, setDist] = useState(0); - const [end, setEnd] = useState(null); + const [end, setEnd] = useState(null); const [direction, setDirection] = useState(false); const updateWaypoints = useCallback( diff --git a/src/map/Sticker/index.tsx b/src/map/Sticker/index.tsx index 85638a5..a611388 100644 --- a/src/map/Sticker/index.tsx +++ b/src/map/Sticker/index.tsx @@ -20,8 +20,8 @@ interface IProps { mapDropSticker: (index: number) => void; } -export const getLabelDirection = (angle: number): 'left' | 'right' => - angle % Math.PI >= -(Math.PI / 2) && angle % Math.PI <= Math.PI / 2 ? 'left' : 'right'; +export const getLabelDirection = (angle?: number): 'left' | 'right' => + !!angle && angle % Math.PI >= -(Math.PI / 2) && angle % Math.PI <= Math.PI / 2 ? 'left' : 'right'; const getX = e => e.touches && e.touches.length > 0 @@ -36,50 +36,58 @@ const Sticker: React.FC = ({ mapSetSticker, mapDropSticker, }) => { - const [text, setText] = useState(sticker.text); - const [layer, setLayer] = React.useState(null); + const [text, setText] = useState(sticker.text || ''); + const [layer, setLayer] = React.useState(null); const [dragging, setDragging] = React.useState(false); - const wrapper = useRef(null); + const wrapper = useRef(null); let angle = useRef(sticker.angle); const element = React.useMemo(() => document.createElement('div'), []); - const stickerArrow = React.useRef(null); - const stickerImage = React.useRef(null); + const stickerArrow = React.useRef(null); + const stickerImage = React.useRef(null); const onChange = React.useCallback(state => mapSetSticker(index, state), [mapSetSticker, index]); const onDelete = React.useCallback(state => mapDropSticker(index), [mapSetSticker, index]); const updateAngle = useCallback( ang => { - if (!stickerImage.current || !stickerArrow.current) return; - const x = Math.cos(ang + Math.PI) * 56 - 30; const y = Math.sin(ang + Math.PI) * 56 - 30; + if (!stickerImage.current || !stickerArrow.current) { + return; + } + stickerImage.current.style.left = String(6 + x); stickerImage.current.style.top = String(6 + y); stickerArrow.current.style.transform = `rotate(${ang + Math.PI}rad)`; }, - [stickerArrow, stickerImage, angle] + [stickerArrow, stickerImage] ); const onDragStart = React.useCallback(() => { + if (!layer?.dragging) { + return + } + layer.dragging.disable(); MainMap.dragging.disable(); MainMap.disableClicks(); setDragging(true); - }, [setDragging, layer, MainMap]); + }, [setDragging, layer]); const onDragStop = React.useCallback( event => { event.stopPropagation(); event.preventDefault(); - if (!layer) return; + if (!layer?.dragging) { + return; + } setDragging(false); onChange({ @@ -134,7 +142,9 @@ const Sticker: React.FC = ({ }); }, [text, onChange, sticker]); - const direction = React.useMemo(() => getLabelDirection(sticker.angle), [sticker.angle]); + const direction = React.useMemo(() => { + getLabelDirection(sticker?.angle) + }, [sticker.angle]); useEffect(() => { updateAngle(sticker.angle); @@ -148,7 +158,8 @@ const Sticker: React.FC = ({ useEffect(() => { if (!layer) return; - setText(sticker.text); + + setText(sticker.text || ''); }, [layer, sticker.text]); useEffect(() => { diff --git a/src/map/Stickers/index.tsx b/src/map/Stickers/index.tsx index 3cee214..fbd4442 100644 --- a/src/map/Stickers/index.tsx +++ b/src/map/Stickers/index.tsx @@ -14,7 +14,7 @@ interface IProps { } const Stickers: FC = memo(({ stickers, is_editing, mapSetSticker, mapDropSticker }) => { - const [layer, setLayer] = useState(null); + const [layer, setLayer] = useState(null); const [zoom, setZoom] = useState(MainMap.getZoom()); const onZoomChange = useCallback( diff --git a/src/map/TileLayer/index.tsx b/src/map/TileLayer/index.tsx index f01b676..18c9382 100644 --- a/src/map/TileLayer/index.tsx +++ b/src/map/TileLayer/index.tsx @@ -10,7 +10,7 @@ type IProps = React.HTMLAttributes & { }; const TileLayer: React.FC = React.memo(({ children, provider, map }) => { - const [layer, setLayer] = React.useState(null); + const [layer, setLayer] = React.useState(undefined); React.useEffect(() => { if (!map) return; @@ -34,7 +34,11 @@ const TileLayer: React.FC = React.memo(({ children, provider, map }) => layer.setUrl(url); }, [layer, provider]); - return {children}; + return ( + + {children} + + ); }); export { TileLayer }; diff --git a/src/redux/editor/index.ts b/src/redux/editor/index.ts index 383d4ac..5417ecf 100644 --- a/src/redux/editor/index.ts +++ b/src/redux/editor/index.ts @@ -41,7 +41,7 @@ export interface IEditorState { distance: number; estimated: number; speed: number; - activeSticker: { set?: string; sticker?: string }; + activeSticker: { set: string; sticker: string }; is_empty: boolean; is_published: boolean; is_routing: boolean; @@ -134,7 +134,7 @@ export const EDITOR_INITIAL_STATE = { }, save: { - error: null, + error: '', finished: false, overwriting: false, processing: false, diff --git a/src/redux/editor/sagas.ts b/src/redux/editor/sagas.ts index 39275bc..8521125 100644 --- a/src/redux/editor/sagas.ts +++ b/src/redux/editor/sagas.ts @@ -69,8 +69,14 @@ import uuid from 'uuid'; import { getRandomColor, getAdaptiveScale } from '~/utils/dom'; const hideLoader = () => { - document.getElementById('loader').style.opacity = String(0); - document.getElementById('loader').style.pointerEvents = 'none'; + const el = document.getElementById('loader'); + + if (!el) { + return true; + } + + el.style.opacity = String(0); + el.style.pointerEvents = 'none'; return true; }; @@ -125,6 +131,10 @@ function* getRenderData() { canvas.height = window.innerHeight; const ctx = canvas.getContext('2d'); + if (!ctx) { + return + } + const geometry = getTilePlacement(); const points = getPolyPlacement(route); const sticker_points = getStickersPlacement(stickers); @@ -184,6 +194,11 @@ function* getCropData({ x, y, width, height }) { canvas.width = width; canvas.height = height; const ctx = canvas.getContext('2d'); + + if (!ctx) { + return + } + const image = yield imageFetcher(data); ctx.drawImage(image, -x, -y); @@ -281,7 +296,7 @@ function* mapClick({ latlng }: ReturnType) { function* routerSubmit() { const route: ReturnType = yield select(selectMapRoute); - const latlngs: LatLng[] = path(['_routes', 0, 'coordinates'], OsrmRouter); + const latlngs: LatLng[] = path(['_routes', 0, 'coordinates'], OsrmRouter) || []; const coordinates = simplify(latlngs); diff --git a/src/redux/map/index.ts b/src/redux/map/index.ts index 1084456..ffd2ecb 100644 --- a/src/redux/map/index.ts +++ b/src/redux/map/index.ts @@ -28,9 +28,9 @@ export const MAP_INITIAL_STATE: IMapReducer = { address: '', address_origin: '', description: '', - owner: { id: null }, + owner: { id: '' }, is_public: false, zoom: 13, } -export const map = createReducer(MAP_INITIAL_STATE, MAP_HANDLERS) \ No newline at end of file +export const map = createReducer(MAP_INITIAL_STATE, MAP_HANDLERS) diff --git a/src/redux/map/sagas.ts b/src/redux/map/sagas.ts index 718a242..1572bf6 100644 --- a/src/redux/map/sagas.ts +++ b/src/redux/map/sagas.ts @@ -1,40 +1,30 @@ -import { - takeEvery, - select, - put, - call, - TakeEffect, - race, - take, - takeLatest, - delay, -} from 'redux-saga/effects'; +import { call, delay, put, race, select, take, TakeEffect, takeEvery, takeLatest } from 'redux-saga/effects'; import { MAP_ACTIONS } from './constants'; import { - mapClicked, mapAddSticker, - mapSetProvider, + mapClicked, mapSet, - mapSetTitle, mapSetAddressOrigin, + mapSetProvider, mapSetRoute, mapSetStickers, + mapSetTitle, } from './actions'; -import { selectUser, selectUserUser } from '~/redux/user/selectors'; +import { selectUser } from '~/redux/user/selectors'; import { MODES } from '~/constants/modes'; import { + editorCaptureHistory, editorChangeMode, + editorClearAll, + editorSendSaveRequest, + editorSetActiveSticker, editorSetChanged, editorSetEditing, - editorSetReady, - editorSetActiveSticker, - editorSendSaveRequest, - editorSetSave, - editorClearAll, editorSetHistory, - editorCaptureHistory, + editorSetReady, + editorSetSave, } from '~/redux/editor/actions'; -import { pushLoaderState, getUrlData, pushPath } from '~/utils/history'; +import { getUrlData, pushLoaderState, pushPath } from '~/utils/history'; import { getStoredMap, postMap } from '~/utils/api'; import { Unwrap } from '~/utils/middleware'; import { selectMap, selectMapProvider, selectMapRoute, selectMapStickers } from './selectors'; @@ -70,29 +60,36 @@ export function* replaceAddressIfItsBusy(destination, original) { } export function* loadMapSaga(path) { - const { - data: { route, error, random_url }, - }: Unwrap = yield call(getStoredMap, { name: path }); + try { + const { + data: { + route, error, random_url, + }, + }: Unwrap = yield call(getStoredMap, { name: path }); - if (route && !error) { - yield put( - mapSet({ - provider: route.provider, - route: route.route, - stickers: route.stickers, - title: route.title, - address: route.address, - description: route.description, - is_public: route.is_public, - logo: route.logo, - }) - ); + if (route && !error) { + yield put( + mapSet({ + provider: route.provider, + route: route.route, + stickers: route.stickers, + title: route.title, + address: route.address, + description: route.description, + is_public: route.is_public, + logo: route.logo, + }), + ); - yield put(editorSetHistory({ records: [{ route: route.route, stickers: route.stickers }] })); - return { route, random_url }; + yield put(editorSetHistory({ records: [{ route: route.route, stickers: route.stickers }] })); + return { route, random_url }; + } + + return null; + } catch (e) { + console.log(e); + yield call(startEmptyEditorSaga); } - - return null; } export function* startEmptyEditorSaga() { @@ -142,10 +139,10 @@ export function* mapInitSaga() { yield put(mapSetProvider(provider)); if (hash && /^#map/.test(hash)) { - const [, newUrl] = hash.match(/^#map[:/?!](.*)$/); + const matches = hash.match(/^#map[:/?!](.*)$/); - if (newUrl) { - yield pushPath(`/${newUrl}`); + if (matches && matches[1]) { + yield pushPath(`/${matches[1]}`); yield call(setReadySaga); return; } @@ -161,7 +158,7 @@ function* setActiveStickerSaga() { yield put(editorChangeMode(MODES.STICKERS)); } -function* setTitleSaga({ title }: ReturnType) { +function setTitleSaga({ title }: ReturnType) { if (title) { document.title = `${title} | Редактор маршрутов`; } @@ -216,7 +213,7 @@ function* clearSaga({ type }) { const { mode, activeSticker }: ReturnType = yield select(selectEditor); if (activeSticker && activeSticker.set && activeSticker.sticker) { - yield put(editorSetActiveSticker(null)); + yield put(editorSetActiveSticker({ set: '', sticker: '' })); } if (mode !== MODES.NONE) { @@ -231,94 +228,96 @@ function* sendSaveRequestSaga({ is_public, description, }: ReturnType) { - const { route, stickers, provider }: ReturnType = yield select(selectMap); + try { + const { route, stickers, provider }: ReturnType = yield select(selectMap); - if (!route.length && !stickers.length) { - return yield put( - editorSetSave({ error: TIPS.SAVE_EMPTY, loading: false, overwriting: false, finished: false }) + if (!route.length && !stickers.length) { + return yield put( + editorSetSave({ error: TIPS.SAVE_EMPTY, loading: false, overwriting: false, finished: false }), + ); + } + + const { logo }: ReturnType = yield select(selectMap); + const { distance }: ReturnType = yield select(selectEditor); + + yield put(editorSetSave({ loading: true, overwriting: false, finished: false, error: '' })); + + const { + result, + timeout, + cancel, + }: { + result: Unwrap; + timeout: boolean; + cancel: TakeEffect; + } = yield race({ + result: postMap({ + route, + stickers, + title, + force, + address, + logo, + distance, + provider, + is_public, + description, + }), + timeout: delay(10000), + cancel: take(EDITOR_ACTIONS.RESET_SAVE_DIALOG), + }); + + yield put(editorSetSave({ loading: false })); + + if (cancel) return yield put(editorChangeMode(MODES.NONE)); + + if (result && result.data.code === 'already_exist') + return yield put(editorSetSave({ overwriting: true })); + + if (result && result.data.code === 'conflict') + return yield put( + editorSetSave({ + error: TIPS.SAVE_EXISTS, + loading: false, + overwriting: false, + finished: false, + }), + ); + + if (timeout || !result || !result.data.route || !result.data.route.address) + return yield put( + editorSetSave({ + error: TIPS.SAVE_TIMED_OUT, + loading: false, + overwriting: false, + finished: false, + }), + ); + + yield put( + mapSet({ + address: result.data.route.address, + title: result.data.route.title, + is_public: result.data.route.is_public, + description: result.data.route.description, + }), ); + + yield put(editorSetReady(false)); + pushPath(`/${address}/edit`); + yield put(editorSetReady(true)); + + yield put( + editorSetSave({ + error: TIPS.SAVE_SUCCESS, + loading: false, + overwriting: false, + finished: true, + }), + ); + } catch (e) { + console.log(e); } - - const { logo }: ReturnType = yield select(selectMap); - const { distance }: ReturnType = yield select(selectEditor); - const { token }: ReturnType = yield select(selectUserUser); - - yield put(editorSetSave({ loading: true, overwriting: false, finished: false, error: null })); - - const { - result, - timeout, - cancel, - }: { - result: Unwrap; - timeout: boolean; - cancel: TakeEffect; - } = yield race({ - result: postMap({ - token, - route, - stickers, - title, - force, - address, - logo, - distance, - provider, - is_public, - description, - }), - timeout: delay(10000), - cancel: take(EDITOR_ACTIONS.RESET_SAVE_DIALOG), - }); - - yield put(editorSetSave({ loading: false })); - - if (cancel) return yield put(editorChangeMode(MODES.NONE)); - - if (result && result.data.code === 'already_exist') - return yield put(editorSetSave({ overwriting: true })); - - if (result && result.data.code === 'conflict') - return yield put( - editorSetSave({ - error: TIPS.SAVE_EXISTS, - loading: false, - overwriting: false, - finished: false, - }) - ); - - if (timeout || !result || !result.data.route || !result.data.route.address) - return yield put( - editorSetSave({ - error: TIPS.SAVE_TIMED_OUT, - loading: false, - overwriting: false, - finished: false, - }) - ); - - yield put( - mapSet({ - address: result.data.route.address, - title: result.data.route.title, - is_public: result.data.route.is_public, - description: result.data.route.description, - }) - ); - - yield put(editorSetReady(false)); - pushPath(`/${address}/edit`); - yield put(editorSetReady(true)); - - yield put( - editorSetSave({ - error: TIPS.SAVE_SUCCESS, - loading: false, - overwriting: false, - finished: true, - }) - ); } function* setChanged() { @@ -328,14 +327,10 @@ function* setChanged() { yield put(editorSetChanged(true)); } -function* onZoomChange() { - -} - export function* mapSaga() { yield takeEvery( [MAP_ACTIONS.SET_ROUTE, MAP_ACTIONS.SET_STICKER, MAP_ACTIONS.SET_STICKERS], - setChanged + setChanged, ); yield takeEvery(EDITOR_ACTIONS.START_EDITING, startEditingSaga); @@ -351,6 +346,6 @@ export function* mapSaga() { EDITOR_ACTIONS.CLEAR_ALL, EDITOR_ACTIONS.CLEAR_CANCEL, ], - clearSaga + clearSaga, ); } diff --git a/src/redux/store.ts b/src/redux/store.ts index ccdee48..e8212ab 100644 --- a/src/redux/store.ts +++ b/src/redux/store.ts @@ -18,9 +18,12 @@ import { map, IMapReducer } from '~/redux/map'; import { mapSaga } from '~/redux/map/sagas'; import { watchLocation, getLocation } from '~/utils/window'; import { LatLngLiteral } from 'leaflet'; -import { setUserLocation } from './user/actions'; +import { setUserLocation, userLogout } from './user/actions'; import { MainMap } from '~/constants/map'; import { mapZoomChange } from './map/actions'; +import { assocPath } from 'ramda'; +import { AxiosError } from 'axios'; +import { api } from '~/utils/api/instance'; const userPersistConfig: PersistConfig = { key: 'user', @@ -64,6 +67,28 @@ export function configureStore(): { store: Store; persistor: Persistor } { const persistor = persistStore(store); + // Pass token to axios + api.interceptors.request.use(options => { + const token = store.getState().user.token; + + if (!token) { + return options; + } + + return assocPath(['headers', 'authorization'], `Bearer ${token}`, options); + }); + + // Logout on 401 + api.interceptors.response.use(undefined, (error: AxiosError<{ error: string }>) => { + if (error.response?.status === 401) { + store.dispatch(userLogout()); + } + + error.message = error?.response?.data?.error || error?.response?.statusText || error.message; + + throw error; + }); + return { store, persistor }; } @@ -74,5 +99,5 @@ history.listen((location, action) => { store.dispatch(editorLocationChanged(location.pathname)); }); -watchLocation((location: LatLngLiteral) => store.dispatch(setUserLocation(location))); +watchLocation((location: LatLngLiteral | undefined) => store.dispatch(setUserLocation(location))); MainMap.on('zoomend', event => store.dispatch(mapZoomChange(event.target._zoom))) diff --git a/src/redux/user/index.ts b/src/redux/user/index.ts index 162d8d0..51ef671 100644 --- a/src/redux/user/index.ts +++ b/src/redux/user/index.ts @@ -15,7 +15,7 @@ export interface IRouteListItem { export interface IRootReducer { // ready: boolean, user: IUser; - location: LatLngLiteral; + location?: LatLngLiteral; routes: { limit: 0; loading: boolean; @@ -38,7 +38,7 @@ export type IRootState = Readonly; export const INITIAL_STATE: IRootReducer = { user: { ...DEFAULT_USER }, - location: null, + location: undefined, routes: { limit: 0, loading: false, // <-- maybe delete this diff --git a/src/redux/user/sagas.ts b/src/redux/user/sagas.ts index e366860..f9ee41a 100644 --- a/src/redux/user/sagas.ts +++ b/src/redux/user/sagas.ts @@ -1,5 +1,5 @@ import { REHYDRATE, RehydrateAction } from 'redux-persist'; -import { takeLatest, select, call, put, takeEvery, delay } from 'redux-saga/effects'; +import { call, delay, put, select, takeEvery, takeLatest } from 'redux-saga/effects'; import { checkIframeToken, checkUserToken, @@ -9,15 +9,16 @@ import { modifyRoute, sendRouteStarred, } from '~/utils/api'; +import * as ActionCreators from '~/redux/user/actions'; import { - searchSetTab, - setUser, mapsSetShift, searchChangeDistance, searchPutRoutes, searchSetLoading, + searchSetTab, searchSetTitle, setRouteStarred, + setUser, userLogin, } from '~/redux/user/actions'; @@ -26,8 +27,6 @@ import { USER_ACTIONS } from '~/redux/user/constants'; import { DEFAULT_USER } from '~/constants/auth'; import { DIALOGS, TABS } from '~/constants/dialogs'; - -import * as ActionCreators from '~/redux/user/actions'; import { Unwrap } from '~/utils/middleware'; import { selectUser, selectUserUser } from './selectors'; import { mapInitSaga } from '~/redux/map/sagas'; @@ -35,64 +34,71 @@ import { editorSetDialog, editorSetDialogActive } from '../editor/actions'; import { selectEditor } from '../editor/selectors'; function* generateGuestSaga() { - const { - data: { user, random_url }, - }: Unwrap = yield call(getGuestToken); + try { + const { + data: { user, random_url }, + }: Unwrap = yield call(getGuestToken); - yield put(setUser({ ...user, random_url })); + yield put(setUser({ ...user, random_url })); - return { ...user, random_url }; + return { ...user, random_url }; + } catch(e) { + console.log(e) + } } function* authCheckSaga({ key }: RehydrateAction) { - if (key !== 'user') return; + try { + if (key !== 'user') return; - pushLoaderState(70); + pushLoaderState(70); - const { id, token }: ReturnType = yield select(selectUserUser); - const { ready }: ReturnType = yield select(selectEditor); + 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); + if (window.location.search || true) { + const { viewer_id, auth_key } = yield parseQuery(window.location.search); - if (viewer_id && auth_key && id !== `vk:${viewer_id}`) { - const user = yield call(checkIframeToken, { viewer_id, auth_key }); + if (viewer_id && auth_key && id !== `vk:${viewer_id}`) { + const user = yield call(checkIframeToken, { viewer_id, auth_key }); + + if (user) { + yield put(setUser(user)); + + pushLoaderState(99); + + return yield call(mapInitSaga); + } + } + } + + if (id && token) { + const { + data: { user, random_url }, + }: Unwrap = yield call(checkUserToken, { + id, + }); if (user) { - yield put(setUser(user)); + yield put(setUser({ ...user, random_url })); pushLoaderState(99); return yield call(mapInitSaga); + } else if (!ready) { + pushNetworkInitError(); + return; } } + + yield call(generateGuestSaga); + + pushLoaderState(80); + + return yield call(mapInitSaga); + } catch (e) { + console.log(e); } - - if (id && token) { - const { - data: { user, random_url }, - }: Unwrap = yield call(checkUserToken, { - id, - token, - }); - - if (user) { - yield put(setUser({ ...user, random_url })); - - pushLoaderState(99); - - return yield call(mapInitSaga); - } else if (!ready) { - pushNetworkInitError(); - return; - } - } - - yield call(generateGuestSaga); - - pushLoaderState(80); - - return yield call(mapInitSaga); } function* gotVkUserSaga({ user: u }: ReturnType) { @@ -105,58 +111,63 @@ function* gotVkUserSaga({ user: u }: ReturnType } function* searchGetRoutes() { - const { token }: ReturnType = yield select(selectUserUser); + try { + const { + routes: { + step, + shift, + filter: { title, distance, tab }, + }, + }: ReturnType = yield select(selectUser); - const { - routes: { + const result: Unwrap = yield getRouteList({ + search: title, + min: distance[0], + max: distance[1], step, shift, - filter: { title, distance, tab }, - }, - }: ReturnType = yield select(selectUser); + tab, + }); - const result: Unwrap = yield getRouteList({ - token, - search: title, - min: distance[0], - max: distance[1], - step, - shift, - tab, - }); - - return result; + return result; + } catch (e) { + console.log(e); + } } export function* searchSetSagaWorker() { - const { - routes: { filter }, - }: ReturnType = yield select(selectUser); + try { + const { + routes: { filter }, + }: ReturnType = yield select(selectUser); - const { - data: { - routes, - limits: { min, max, count: limit }, - filter: { shift, step }, - }, - }: Unwrap = yield call(searchGetRoutes); + const { + data: { + routes, + limits: { min, max, count: limit }, + filter: { shift, step }, + }, + }: Unwrap = yield call(searchGetRoutes); - yield put(searchPutRoutes({ list: routes, min, max, limit, shift, step })); + yield put(searchPutRoutes({ list: routes, min, max, limit, shift, step })); - // change distange range if needed and load additional data - if ( - (filter.min > min && filter.distance[0] <= filter.min) || - (filter.max < max && filter.distance[1] >= filter.max) - ) { - yield put( - searchChangeDistance([ - filter.min > min && filter.distance[0] <= filter.min ? min : filter.distance[0], - filter.max < max && filter.distance[1] >= filter.max ? max : filter.distance[1], - ]) - ); + // change distange range if needed and load additional data + if ( + (filter.min > min && filter.distance[0] <= filter.min) || + (filter.max < max && filter.distance[1] >= filter.max) + ) { + yield put( + searchChangeDistance([ + filter.min > min && filter.distance[0] <= filter.min ? min : filter.distance[0], + filter.max < max && filter.distance[1] >= filter.max ? max : filter.distance[1], + ]), + ); + } + + return yield put(searchSetLoading(false)); + } catch (e) { + console.log(e); } - - return yield put(searchSetLoading(false)); } function* searchSetSaga() { @@ -167,26 +178,30 @@ function* searchSetSaga() { } function* openMapDialogSaga({ tab }: ReturnType) { - const { - routes: { - filter: { tab: current }, - }, - }: ReturnType = yield select(selectUser); + try { + const { + routes: { + filter: { tab: current }, + }, + }: ReturnType = yield select(selectUser); - const { dialog_active }: ReturnType = yield select(selectEditor); + const { dialog_active }: ReturnType = yield select(selectEditor); - if (dialog_active && tab === current) { - return yield put(editorSetDialogActive(false)); + if (dialog_active && tab === current) { + return yield put(editorSetDialogActive(false)); + } + + if (tab !== current) { + yield put(searchSetTab(tab)); + } + + yield put(editorSetDialog(DIALOGS.MAP_LIST)); + yield put(editorSetDialogActive(true)); + + return tab; + } catch (e) { + console.log(e); } - - if (tab !== current) { - yield put(searchSetTab(tab)); - } - - yield put(editorSetDialog(DIALOGS.MAP_LIST)); - yield put(editorSetDialogActive(true)); - - return tab; } function* searchSetTabSaga() { @@ -210,78 +225,85 @@ function* setUserSaga() { } function* mapsLoadMoreSaga() { - const { - routes: { limit, list, shift, step, loading, filter }, - }: ReturnType = yield select(selectUser); + try { + const { + routes: { limit, list, shift, step, loading, filter }, + }: ReturnType = yield select(selectUser); - if (loading || list.length >= limit || limit === 0) return; + if (loading || list.length >= limit || limit === 0) return; - yield delay(50); + yield delay(50); - yield put(searchSetLoading(true)); - yield put(mapsSetShift(shift + step)); + yield put(searchSetLoading(true)); + yield put(mapsSetShift(shift + step)); - const { - data: { - limits: { min, max, count }, - filter: { shift: resp_shift, step: resp_step }, - routes, - }, - }: Unwrap = yield call(searchGetRoutes); + const { + data: { + limits: { min, max, count }, + filter: { shift: resp_shift, step: resp_step }, + routes, + }, + }: Unwrap = yield call(searchGetRoutes); + + if ( + (filter.min > min && filter.distance[0] <= filter.min) || + (filter.max < max && filter.distance[1] >= filter.max) + ) { + yield put( + searchChangeDistance([ + filter.min > min && filter.distance[0] <= filter.min ? min : filter.distance[0], + filter.max < max && filter.distance[1] >= filter.max ? max : filter.distance[1], + ]), + ); + } - if ( - (filter.min > min && filter.distance[0] <= filter.min) || - (filter.max < max && filter.distance[1] >= filter.max) - ) { yield put( - searchChangeDistance([ - filter.min > min && filter.distance[0] <= filter.min ? min : filter.distance[0], - filter.max < max && filter.distance[1] >= filter.max ? max : filter.distance[1], - ]) + searchPutRoutes({ + min, + max, + limit: count, + shift: resp_shift, + step: resp_step, + list: [...list, ...routes], + }), ); + yield put(searchSetLoading(false)); + } catch (e) { + console.log(e); } - - yield put( - searchPutRoutes({ - min, - max, - limit: count, - shift: resp_shift, - step: resp_step, - list: [...list, ...routes], - }) - ); - yield put(searchSetLoading(false)); } function* dropRouteSaga({ address }: ReturnType) { - const { token }: ReturnType = yield select(selectUserUser); - const { - routes: { - list, - step, - shift, - limit, - filter: { min, max }, - }, - }: ReturnType = yield select(selectUser); - - const index = list.findIndex(el => el.address === address); - - if (index >= 0) { - yield put( - searchPutRoutes({ - list: list.filter(el => el.address !== address), - min, - max, + try { + const { + routes: { + list, step, - shift: shift > 0 ? shift - 1 : 0, - limit: limit > 0 ? limit - 1 : limit, - }) - ); - } + shift, + limit, + filter: { min, max }, + }, + }: ReturnType = yield select(selectUser); - return yield call(dropRoute, { address, token }); + const index = list.findIndex(el => el.address === address); + + if (index >= 0) { + yield put( + searchPutRoutes({ + list: list.filter(el => el.address !== address), + min, + max, + step, + shift: shift > 0 ? shift - 1 : 0, + limit: limit > 0 ? limit - 1 : limit, + }), + ); + } + + return yield call(dropRoute, { address }); + } catch (e) { + console.log(e); + } } function* modifyRouteSaga({ @@ -289,53 +311,59 @@ function* modifyRouteSaga({ title, is_public, }: ReturnType) { - const { token }: ReturnType = yield select(selectUserUser); - const { - routes: { - list, - step, - shift, - limit, - filter: { min, max }, - }, - }: ReturnType = yield select(selectUser); - - const index = list.findIndex(el => el.address === address); - - if (index >= 0) { - yield put( - searchPutRoutes({ - list: list.map(el => (el.address !== address ? el : { ...el, title, is_public })), - min, - max, + try { + const { + routes: { + list, step, - shift: shift > 0 ? shift - 1 : 0, - limit: limit > 0 ? limit - 1 : limit, - }) - ); - } + shift, + limit, + filter: { min, max }, + }, + }: ReturnType = yield select(selectUser); - return yield call(modifyRoute, { address, token, title, is_public }); + const index = list.findIndex(el => el.address === address); + + if (index >= 0) { + yield put( + searchPutRoutes({ + list: list.map(el => (el.address !== address ? el : { ...el, title, is_public })), + min, + max, + step, + shift: shift > 0 ? shift - 1 : 0, + limit: limit > 0 ? limit - 1 : limit, + }), + ); + } + + return yield call(modifyRoute, { address, title, is_public }); + } catch (e) { + console.log(e); + } } function* toggleRouteStarredSaga({ address, }: ReturnType) { - const { token }: ReturnType = yield select(selectUserUser); - const { - routes: { list }, - }: ReturnType = yield select(selectUser); + try { + const { + routes: { list }, + }: ReturnType = yield select(selectUser); - const route = list.find(el => el.address === address); + const route = list.find(el => el.address === address); - yield put(setRouteStarred(address, !route.is_published)); - const result = yield sendRouteStarred({ - token, - address, - is_published: !route.is_published, - }); + yield put(setRouteStarred(address, !route?.is_published)); - if (!result) return yield put(setRouteStarred(address, route.is_published)); + const result = yield sendRouteStarred({ + address, + is_published: !route?.is_published, + }); + + if (!result) return yield put(setRouteStarred(address, !!route?.is_published)); + } catch (e) { + console.log(e); + } } export function* updateUserRoutes() { @@ -350,7 +378,7 @@ export function* userSaga() { yield takeLatest( [USER_ACTIONS.SEARCH_SET_TITLE, USER_ACTIONS.SEARCH_SET_DISTANCE], - searchSetSaga + searchSetSaga, ); yield takeLatest(USER_ACTIONS.OPEN_MAP_DIALOG, openMapDialogSaga); diff --git a/src/utils/api.ts b/src/utils/api.ts deleted file mode 100644 index b6d8c45..0000000 --- a/src/utils/api.ts +++ /dev/null @@ -1,258 +0,0 @@ -import axios from 'axios/index'; -import { API } from '~/constants/api'; -import { IRootState, IRouteListItem } from '~/redux/user'; -import { IUser } from '~/constants/auth'; -import { CLIENT } from '~/config/frontend'; -import { LatLngLiteral } from 'leaflet'; -import { - resultMiddleware, - errorMiddleware, - IResultWithStatus, - configWithToken, -} from './middleware'; -import { IRoute } from '~/redux/map/types'; -import { INominatimResult } from '~/redux/types'; -import { MainMap } from '~/constants/map'; - -const arrayToObject = (array: any[], key: string): {} => - array.reduce((obj, el) => ({ ...obj, [el[key]]: el }), {}); - -interface IGetRouteList { - min: number; - max: number; - tab: string; - search: string; - step: IRootState['routes']['step']; - shift: IRootState['routes']['step']; - token: IRootState['user']['token']; -} - -interface IGetRouteListResult { - min: IRootState['routes']['filter']['min']; - max: IRootState['routes']['filter']['max']; - limit: IRootState['routes']['limit']; - step: IRootState['routes']['step']; - shift: IRootState['routes']['shift']; - list: IRootState['routes']['list']; -} - -export const checkUserToken = ({ - id, - token, -}: { - id: IRootState['user']['id']; - token: IRootState['user']['token']; -}): Promise> => - axios - .get(API.CHECK_TOKEN, { - params: { id, token }, - }) - .then(resultMiddleware) - .catch(errorMiddleware); - -export const getGuestToken = (): Promise> => - axios - .get(API.GET_GUEST) - .then(resultMiddleware) - .catch(errorMiddleware); - -export const getStoredMap = ({ - name, -}: { - name: IRoute['address']; -}): Promise> => - axios - .get(API.GET_MAP, { - params: { name }, - }) - .then(resultMiddleware) - .catch(errorMiddleware); - -export const postMap = ({ - title, - address, - route, - stickers, - force, - logo, - distance, - provider, - is_public, - description, - token, -}: Partial & { - force: boolean; - token: string; -}): Promise> => - axios - .post( - API.POST_MAP, - { - route: { - title, - address, - route, - stickers, - logo, - distance, - provider, - is_public, - description, - }, - force, - }, - configWithToken(token) - ) - .then(resultMiddleware) - .catch(errorMiddleware); - -export const checkIframeToken = ({ - viewer_id, - auth_key, -}: { - viewer_id: string; - auth_key: string; -}) => - axios - .get(API.IFRAME_LOGIN_VK, { - params: { viewer_id, auth_key }, - }) - .then(result => result && result.data && result.data.success && result.data.user) - .catch(() => false); - -export const getRouteList = ({ - search, - min, - max, - tab, - token, - step, - shift, -}: IGetRouteList): Promise> => - axios - .get( - API.GET_ROUTE_LIST(tab), - configWithToken(token, { - params: { - search, - min, - max, - token, - step, - shift, - }, - }) - ) - .then(resultMiddleware) - .catch(errorMiddleware); - -export const checkOSRMService = (bounds: LatLngLiteral[]): Promise => - CLIENT && - CLIENT.OSRM_URL && - axios - .get(CLIENT.OSRM_TEST_URL(bounds)) - .then(() => true) - .catch(() => false); - -export const checkNominatimService = (): Promise => - CLIENT && - CLIENT.NOMINATIM_TEST_URL && - axios - .get(CLIENT.NOMINATIM_TEST_URL) - .then(() => true) - .catch(() => false); - -export const searchNominatim = (query: string) => - CLIENT && - CLIENT.NOMINATIM_URL && - axios - .get(`${CLIENT.NOMINATIM_URL} ${query}`, { - params: { - format: 'json', - country_code: 'ru', - 'accept-language': 'ru_RU', - dedupe: 1, - }, - }) - .then( - data => - data && - data.data && - data.data.map( - (item): INominatimResult => ({ - id: item.place_id, - latlng: { - lat: item.lat, - lng: item.lon, - }, - title: item.display_name, - }) - ) - ) - .catch(() => []); - -export const dropRoute = ({ address, token }: { address: string; token: string }): Promise => - axios - .delete(API.DROP_ROUTE, configWithToken(token, { data: { address } })) - .then(resultMiddleware) - .catch(errorMiddleware); - -export const modifyRoute = ({ - address, - token, - title, - is_public, -}: { - address: string; - token: string; - title: string; - is_public: boolean; -}): Promise> => - axios - .patch(API.MODIFY_ROUTE, { address, token, is_public, title }, configWithToken(token)) - .then(resultMiddleware) - .catch(errorMiddleware); - -export const sendRouteStarred = ({ - token, - address, - is_published, -}: { - token: string; - address: string; - is_published: boolean; -}): Promise> => - axios - .post(API.SET_STARRED, { address, is_published }, configWithToken(token)) - .then(resultMiddleware) - .catch(errorMiddleware); diff --git a/src/utils/api/index.ts b/src/utils/api/index.ts new file mode 100644 index 0000000..fae0f68 --- /dev/null +++ b/src/utils/api/index.ts @@ -0,0 +1,214 @@ +import { API } from '~/constants/api'; +import { IRootState, IRouteListItem } from '~/redux/user'; +import { IUser } from '~/constants/auth'; +import { CLIENT } from '~/config/frontend'; +import { LatLngLiteral } from 'leaflet'; +import { IRoute } from '~/redux/map/types'; +import { INominatimResult } from '~/redux/types'; +import { api } from './instance'; + +interface IGetRouteList { + min: number; + max: number; + tab: string; + search: string; + step: IRootState['routes']['step']; + shift: IRootState['routes']['step']; +} + +export const checkUserToken = ({ + id, +}: { + id: IRootState['user']['id']; +}) => + api + .get<{ + user: IUser; + random_url: string; + routes: IRouteListItem[]; + }>(API.CHECK_TOKEN, { + params: { id }, + }); + +export const getGuestToken = () => + api + .get<{ + user: IUser; + random_url: string; + }>(API.GET_GUEST); + +export const getStoredMap = ({ + name, +}: { + name: IRoute['address']; +}) => + api + .get<{ + route: IRoute; + error?: string; + random_url: string; + }>(API.GET_MAP, { + params: { name }, + }); + +export const postMap = ({ + title, + address, + route, + stickers, + force, + logo, + distance, + provider, + is_public, + description, +}: Partial & { + force: boolean; +}) => + api + .post<{ + route: IRoute; + error?: string; + code?: string; + }>( + API.POST_MAP, + { + route: { + title, + address, + route, + stickers, + logo, + distance, + provider, + is_public, + description, + }, + force, + }, + ); + +export const checkIframeToken = ({ + viewer_id, + auth_key, +}: { + viewer_id: string; + auth_key: string; +}) => + api + .get<{ + success: boolean, + user: IUser, + }>(API.IFRAME_LOGIN_VK, { + params: { viewer_id, auth_key }, + }) + .then(result => !!result.data.success && !!result.data.user) + .catch(() => false); + +export const getRouteList = ({ + search, + min, + max, + tab, + step, + shift, +}: IGetRouteList) => + api + .get<{ + routes: IRoute[]; + limits: { + min: number; + max: number; + count: number; + }; + filter: { + min: number; + max: number; + shift: number; + step: number; + }; + }>( + API.GET_ROUTE_LIST(tab), + { + params: { + search, + min, + max, + step, + shift, + }, + }, + ); + +export const checkOSRMService = (bounds: LatLngLiteral[]) => + !!CLIENT && + !!CLIENT.OSRM_URL && + api + .get(CLIENT.OSRM_TEST_URL(bounds)) + .then(() => true) + .catch(() => false); + +export const checkNominatimService = () => + !!CLIENT && + !!CLIENT.NOMINATIM_TEST_URL && + api + .get(CLIENT.NOMINATIM_TEST_URL) + .then(() => true) + .catch(() => false); + +export const searchNominatim = (query: string) => + CLIENT && + CLIENT.NOMINATIM_URL && + api + .get(`${CLIENT.NOMINATIM_URL} ${query}`, { + params: { + format: 'json', + country_code: 'ru', + 'accept-language': 'ru_RU', + dedupe: 1, + }, + }) + .then( + data => + data && + data.data && + data.data.map( + (item): INominatimResult => ({ + id: item.place_id, + latlng: { + lat: item.lat, + lng: item.lon, + }, + title: item.display_name, + }), + ), + ) + .catch(() => []); + +export const dropRoute = ({ address }: { address: string }) => + api + .delete(API.DROP_ROUTE, { data: { address } }); + +export const modifyRoute = ({ + address, + title, + is_public, +}: { + address: string; + title: string; + is_public: boolean; +}) => + api + .patch<{ + route: IRoute; + }>(API.MODIFY_ROUTE, { address, is_public, title }); + +export const sendRouteStarred = ({ + address, + is_published, +}: { + address: string; + is_published: boolean; +}) => + api + .post<{ route: IRoute }>(API.SET_STARRED, { address, is_published }); diff --git a/src/utils/api/instance.ts b/src/utils/api/instance.ts new file mode 100644 index 0000000..ee2bb46 --- /dev/null +++ b/src/utils/api/instance.ts @@ -0,0 +1,6 @@ +import axios from 'axios'; +import { CLIENT } from '~/config/frontend'; + +export const api = axios.create({ + baseURL: CLIENT.API_ADDR, +}) diff --git a/src/utils/context.ts b/src/utils/context.ts index 6631da9..2310b3a 100644 --- a/src/utils/context.ts +++ b/src/utils/context.ts @@ -1,5 +1,5 @@ import React from 'react'; import { Map, TileLayer } from 'leaflet'; -export const MapContext = React.createContext(null); -export const TileContext = React.createContext(null) \ No newline at end of file +export const MapContext = React.createContext(undefined); +export const TileContext = React.createContext(undefined) diff --git a/src/utils/gpx.ts b/src/utils/gpx.ts index 67e3560..240b806 100644 --- a/src/utils/gpx.ts +++ b/src/utils/gpx.ts @@ -39,7 +39,7 @@ export const getGPXString = ({ ${title || 'GPX Track'} - ${stickers.reduce( + ${(stickers || []).reduce( (cat, { latlng: { lat, lng }, text }) => `${cat} @@ -93,12 +93,12 @@ export const importGpxTrack = async (file: File) => { return trkseg.trkpt ? [ ...trkseg_res, - ...trkseg.trkpt.map(pnt => ({ lat: pnt['$'].lat, lng: pnt['$'].lon })), + ...trkseg.trkpt.map(pnt => new LatLng(pnt['$'].lat, pnt['$'].lon)), ] : trkseg_res; }, trk_res) : trk_res; - }, []); + }, [] as LatLng[]); return [ { diff --git a/src/utils/history.ts b/src/utils/history.ts index c23be92..c2435c9 100644 --- a/src/utils/history.ts +++ b/src/utils/history.ts @@ -36,20 +36,20 @@ export const parseQuery = (queryString: string) => { }; export const pushLoaderState = (state: number) => { - document.getElementById('loader-bar').style.width = `${state}%`; + document.getElementById('loader-bar')!.style.width = `${state}%`; }; export const countDownToRefresh = (left: number = API_RETRY_INTERVAL): void => { if (left <= 0) return document.location.reload(); - document.getElementById('loader-bar').style.width = `${(left / API_RETRY_INTERVAL) * 100}%`; + document.getElementById('loader-bar')!.style.width = `${(left / API_RETRY_INTERVAL) * 100}%`; setTimeout(() => countDownToRefresh(left - 0.25), 1000); }; export const pushNetworkInitError = () => { - document.getElementById('loader-bar').classList.add('is_failed'); - document.getElementById('loader-bar').style.width = '100%'; - document.getElementById('loader-error').style.opacity = String(1); + document.getElementById('loader-bar')!.classList.add('is_failed'); + document.getElementById('loader-bar')!.style.width = '100%'; + document.getElementById('loader-error')!.style.opacity = String(1); countDownToRefresh(); }; diff --git a/src/utils/map/ArrowsLayer.ts b/src/utils/map/ArrowsLayer.ts index ef46715..435e8c2 100644 --- a/src/utils/map/ArrowsLayer.ts +++ b/src/utils/map/ArrowsLayer.ts @@ -9,6 +9,7 @@ import { LatLng, LatLngLiteral, LayerGroup, Map, Marker } from 'leaflet'; import { arrowClusterIcon, createArrow } from '~/utils/arrow'; import { MarkerClusterGroup } from 'leaflet.markercluster/dist/leaflet.markercluster-src.js'; import { angleBetweenPoints, dist2, middleCoord } from '~/utils/geom'; +import { MainMap } from '~/constants/map'; class ArrowsLayer extends LayerGroup { constructor(props) { @@ -46,13 +47,13 @@ class ArrowsLayer extends LayerGroup { ), ] : res, - [] + [] as Marker[] ); this.arrowLayer.addLayers(midpoints); }; - map: Map; + map: Map = MainMap; arrowLayer = new MarkerClusterGroup({ spiderfyOnMaxZoom: false, showCoverageOnHover: false, @@ -62,10 +63,10 @@ class ArrowsLayer extends LayerGroup { iconCreateFunction: arrowClusterIcon, }); - layers: Marker[] = []; + layers: Marker[] = []; } -ArrowsLayer.addInitHook(function() { +ArrowsLayer.addInitHook(function(this: ArrowsLayer) { this.once('add', event => { if (event.target instanceof ArrowsLayer) { this.map = event.target._map; diff --git a/src/utils/map/InteractivePoly.ts b/src/utils/map/InteractivePoly.ts index de5b064..fa445bc 100644 --- a/src/utils/map/InteractivePoly.ts +++ b/src/utils/map/InteractivePoly.ts @@ -36,9 +36,9 @@ class InteractivePoly extends Polyline { this.constraintsStyle = { ...this.constraintsStyle, - ...options.constraintsStyle, + ...(options?.constraintsStyle || {}), }; - this.maxMarkers = options.maxMarkers || this.maxMarkers; + this.maxMarkers = options?.maxMarkers || this.maxMarkers; this.constrLine = new Polyline([], this.constraintsStyle); @@ -162,10 +162,12 @@ class InteractivePoly extends Polyline { ? { ...obj, hidden: [...obj.hidden, marker] } : { ...obj, visible: [...obj.visible, marker] }; }, - { visible: [], hidden: [] } + { visible: [], hidden: [] } as Record<'visible' | 'hidden', Marker[]> ); - if (visible.length > this.maxMarkers) return this.hideAllMarkers(); + if (visible.length > (this.maxMarkers || 2)) { + return this.hideAllMarkers(); + } this.showAllMarkers(); @@ -337,11 +339,11 @@ class InteractivePoly extends Polyline { onMarkerDrag = ({ target }: { target: Marker }) => { const coords = new Array(0) - .concat((this.vertex_index > 0 && this.markers[this.vertex_index - 1].getLatLng()) || []) + .concat((this.vertex_index! > 0 && this.markers[this.vertex_index! - 1].getLatLng()) || []) .concat(target.getLatLng()) .concat( - (this.vertex_index < this.markers.length - 1 && - this.markers[this.vertex_index + 1].getLatLng()) || + (this.vertex_index! < this.markers.length - 1 && + this.markers[this.vertex_index! + 1].getLatLng()) || [] ); @@ -369,17 +371,17 @@ class InteractivePoly extends Polyline { onMarkerDragEnd = ({ target }: { target: Marker }): void => { const latlngs = this.getLatLngs() as LatLngLiteral[]; this.markerDragChangeDistance( - this.vertex_index, - latlngs[this.vertex_index], + this.vertex_index!, + latlngs[this.vertex_index!], target.getLatLng() ); - this.replaceLatlng(target.getLatLng(), this.vertex_index); + this.replaceLatlng(target.getLatLng(), this.vertex_index!); this.is_dragging = false; this.constrLine.removeFrom(this._map); - this.vertex_index = null; + this.vertex_index = 0; if (this.is_drawing) this.startDrawing(); @@ -496,7 +498,7 @@ class InteractivePoly extends Polyline { this.constrLine.setLatLngs(coords); }; - setDirection = (direction: 'forward' | 'backward') => { + setDirection = (direction: 'forward' | 'backward') => { this.drawing_direction = direction; this.updateConstraintsToLatLngs(this.getLatLngs() as LatLngExpression[]); } @@ -568,27 +570,27 @@ class InteractivePoly extends Polyline { is_drawing: boolean = false; drawing_direction: 'forward' | 'backward' = 'forward'; - vertex_index?: number = null; + vertex_index: number = 0; - hint_prev_marker: number = null; + hint_prev_marker: number = 0; distance: number = 0; } -InteractivePoly.addInitHook(function() { +InteractivePoly.addInitHook(function(this: InteractivePoly) { this.once('add', event => { if (event.target instanceof InteractivePoly) { - this.map = event.target._map; + this._map = event.target._map; - this.map.on('touch', console.log); + this._map.on('touch', console.log); this.markerLayer.addTo(event.target._map); this.hintMarker.addTo(event.target._map); this.constrLine.addTo(event.target._map); this.touchHinter.addTo(event.target._map); - this.map.on('moveend', this.updateMarkers); + this._map.on('moveend', this.updateMarkers); - this.on('latlngschange', this.updateTouchHinter); + this.on('latlngschange' as any, this.updateTouchHinter as any); if (this.touchHinter && window.innerWidth < 768) { try { @@ -605,7 +607,7 @@ InteractivePoly.addInitHook(function() { this.constrLine.removeFrom(this._map); this.touchHinter.removeFrom(this._map); - this.map.off('moveend', this.updateMarkers); + this._map.off('moveend', this.updateMarkers); } }); }); diff --git a/src/utils/marks.ts b/src/utils/marks.ts index f3df6cf..961e7fe 100644 --- a/src/utils/marks.ts +++ b/src/utils/marks.ts @@ -3,6 +3,7 @@ import { arrowClusterIcon } from '~/utils/arrow'; import { MarkerClusterGroup } from 'leaflet.markercluster/dist/leaflet.markercluster-src.js'; import { allwaysPositiveAngleDeg, angleBetweenPoints, distKmHaversine } from '~/utils/geom'; import classNames from 'classnames'; +import { MainMap } from '~/constants/map'; const arrow_image = '/images/arrow.svg'; @@ -43,8 +44,7 @@ class KmMarksLayer extends LayerGroup { }; drawMiddleMarkers = (latlngs: LatLngLiteral[]) => { - const marks = []; - const arrows = []; + const marks: Marker[] = []; let last_km_mark = 0; this.distance = latlngs.reduce((dist, current, index) => { @@ -160,7 +160,7 @@ class KmMarksLayer extends LayerGroup { }; options: KmMarksOptions; - map: Map; + map: Map = MainMap; marksLayer: MarkerClusterGroup = new MarkerClusterGroup({ spiderfyOnMaxZoom: false, showCoverageOnHover: false, @@ -173,7 +173,7 @@ class KmMarksLayer extends LayerGroup { distance: number = 0; } -KmMarksLayer.addInitHook(function() { +KmMarksLayer.addInitHook(function(this: KmMarksLayer) { this.once('add', event => { if (event.target instanceof KmMarksLayer) { this.map = event.target._map; diff --git a/src/utils/middleware.ts b/src/utils/middleware.ts index 1a2fa1f..7504f3e 100644 --- a/src/utils/middleware.ts +++ b/src/utils/middleware.ts @@ -1,4 +1,4 @@ -import { AxiosRequestConfig } from "axios"; +import { AxiosRequestConfig, AxiosResponse } from 'axios'; export type Unwrap = T extends (...args: any[]) => Promise ? U : T; @@ -23,28 +23,3 @@ export const HTTP_RESPONSES = { NOT_FOUND: 404, TOO_MANY_REQUESTS: 429, }; - -export const resultMiddleware = (({ - status, - data, -}: { - status: number; - data: T; -}): { status: number; data: T } => ({ status, data })); - -export const errorMiddleware = (debug): IResultWithStatus => (debug && debug.response - ? debug.response - : { - status: HTTP_RESPONSES.CONNECTION_REFUSED, - data: {}, - debug, - error: 'Ошибка сети', - }); - -export const configWithToken = ( - token: string, - config: AxiosRequestConfig = {}, -): AxiosRequestConfig => ({ - ...config, - headers: { ...(config.headers || {}), Authorization: `${token}` }, -}); \ No newline at end of file diff --git a/src/utils/renderer.ts b/src/utils/renderer.ts index 4c6ffb0..f76f0bc 100644 --- a/src/utils/renderer.ts +++ b/src/utils/renderer.ts @@ -133,7 +133,7 @@ export const fetchImages = ( ): Promise<{ x: number; y: number; image: HTMLImageElement }[]> => { const { minX, maxX, minY, maxY, zoom } = geometry; - const images = []; + const images: { x: number; y: number; source: string }[] = []; for (let x = minX; x <= maxX; x += 1) { for (let y = minY; y <= maxY; y += 1) { images.push({ x, y, source: getImageSource({ x, y, zoom }, provider) }); @@ -173,7 +173,7 @@ export const composePoly = ({ ctx, color = 'gradient', weight = CLIENT.STROKE_WIDTH, - dash = null, + dash = [], }: { points: Point[]; ctx: CanvasRenderingContext2D; @@ -472,14 +472,14 @@ export const composeStickers = async ({ if (!stickers || stickers.length < 0) return; stickers.map(({ x, y, angle, text }) => { - composeStickerArrow(ctx, x, y, angle, zoom); + composeStickerArrow(ctx, x, y, angle || 0, zoom); - if (text) composeStickerText(ctx, x, y, angle, text, zoom); + if (text) composeStickerText(ctx, x, y, angle || 0, text, zoom); }); await Promise.all( stickers.map(({ x, y, angle, set, sticker }) => - composeStickerImage(ctx, x, y, angle, set, sticker, zoom) + composeStickerImage(ctx, x, y, angle || 0, set, sticker, zoom) ) ); }; diff --git a/src/utils/window.ts b/src/utils/window.ts index aef72df..96aa295 100644 --- a/src/utils/window.ts +++ b/src/utils/window.ts @@ -3,10 +3,10 @@ import { LatLngLiteral } from 'leaflet'; export const isMobile = (): boolean => window.innerWidth <= MOBILE_BREAKPOINT; -export const getLocation = (callback: (pos: LatLngLiteral) => void) => { +export const getLocation = (callback: (pos: LatLngLiteral | undefined) => void) => { window.navigator.geolocation.getCurrentPosition(position => { if (!position || !position.coords || !position.coords.latitude || !position.coords.longitude) - return callback(null); + return callback(undefined); const { latitude: lat, longitude: lng } = position.coords; @@ -15,18 +15,18 @@ export const getLocation = (callback: (pos: LatLngLiteral) => void) => { }); }; -export const watchLocation = (callback: (pos: LatLngLiteral) => void): number => { +export const watchLocation = (callback: (pos: LatLngLiteral | undefined) => void): number => { return window.navigator.geolocation.watchPosition( position => { if (!position || !position.coords || !position.coords.latitude || !position.coords.longitude) - return callback(null); + return callback(undefined); const { latitude: lat, longitude: lng } = position.coords; callback({ lat, lng }); return; }, - () => callback(null), + () => callback(undefined), { timeout: 30, } diff --git a/tsconfig.json b/tsconfig.json index 5e8c822..9660a61 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -11,7 +11,7 @@ "skipLibCheck": true, "esModuleInterop": true, "allowSyntheticDefaultImports": true, - "strict": false, + "strict": true, "forceConsistentCasingInFileNames": true, "noFallthroughCasesInSwitch": true, "module": "esnext", From 9c436d348cf2374907452ae686a67bf8c6c54375 Mon Sep 17 00:00:00 2001 From: Fedor Katurov Date: Tue, 20 Apr 2021 16:39:51 +0700 Subject: [PATCH 02/11] added post map interceptor --- .drone.yml | 19 +------------------ src/redux/store.ts | 4 ++-- src/utils/api/index.ts | 14 +++++--------- src/utils/api/interceptors.ts | 14 ++++++++++++++ src/utils/api/types.ts | 11 +++++++++++ 5 files changed, 33 insertions(+), 29 deletions(-) create mode 100644 src/utils/api/interceptors.ts create mode 100644 src/utils/api/types.ts diff --git a/.drone.yml b/.drone.yml index 381f7d0..5fde9cb 100644 --- a/.drone.yml +++ b/.drone.yml @@ -64,21 +64,4 @@ steps: - cp -a $${ENV_PATH}/${DRONE_BRANCH}/. $${BUILD_PATH}/${DRONE_BRANCH} - docker-compose build - docker-compose up -d -# - name: telgram_notify -# image: appleboy/drone-telegram -# when: -# status: -# - success -# - failure -# settings: -# token: -# from_secret: telegram_token -# to: -# from_secret: telegram_chat_id -# format: markdown -# message: > -# {{#success build.status}}🤓{{else}}😨{{/success}} -# [{{repo.name}} / {{commit.branch}}]({{ build.link }}) -# ``` -# {{ commit.message }} -# ``` + diff --git a/src/redux/store.ts b/src/redux/store.ts index e8212ab..48328a5 100644 --- a/src/redux/store.ts +++ b/src/redux/store.ts @@ -69,13 +69,13 @@ export function configureStore(): { store: Store; persistor: Persistor } { // Pass token to axios api.interceptors.request.use(options => { - const token = store.getState().user.token; + const token = store.getState().user.user.token; if (!token) { return options; } - return assocPath(['headers', 'authorization'], `Bearer ${token}`, options); + return assocPath(['headers', 'authorization'], token, options); }); // Logout on 401 diff --git a/src/utils/api/index.ts b/src/utils/api/index.ts index fae0f68..38d0a38 100644 --- a/src/utils/api/index.ts +++ b/src/utils/api/index.ts @@ -6,6 +6,8 @@ import { LatLngLiteral } from 'leaflet'; import { IRoute } from '~/redux/map/types'; import { INominatimResult } from '~/redux/types'; import { api } from './instance'; +import { postMapInterceptor } from '~/utils/api/interceptors'; +import { PostMapRequest, PostMapResponse } from '~/utils/api/types'; interface IGetRouteList { min: number; @@ -62,15 +64,9 @@ export const postMap = ({ provider, is_public, description, -}: Partial & { - force: boolean; -}) => +}: PostMapRequest) => api - .post<{ - route: IRoute; - error?: string; - code?: string; - }>( + .post( API.POST_MAP, { route: { @@ -86,7 +82,7 @@ export const postMap = ({ }, force, }, - ); + ).catch(postMapInterceptor); export const checkIframeToken = ({ viewer_id, diff --git a/src/utils/api/interceptors.ts b/src/utils/api/interceptors.ts new file mode 100644 index 0000000..a4b52d8 --- /dev/null +++ b/src/utils/api/interceptors.ts @@ -0,0 +1,14 @@ +import { AxiosError } from 'axios'; +import { PostMapResponse } from '~/utils/api/types'; + +export const postMapInterceptor = (res: AxiosError) => { + if (res.response?.data.code) { + return res.response; + } + + if (res.response?.data.error) { + throw new Error(res.response?.data.error); + } + + throw res; +}; diff --git a/src/utils/api/types.ts b/src/utils/api/types.ts new file mode 100644 index 0000000..2fdf3a1 --- /dev/null +++ b/src/utils/api/types.ts @@ -0,0 +1,11 @@ +import { IRoute } from '~/redux/map/types'; + +export interface PostMapResponse { + route: IRoute; + error?: string; + code?: string; +} + +export type PostMapRequest = Partial & { + force: boolean; +} From 48cf0b93eea43833a47dee9fc7642ba26d355851 Mon Sep 17 00:00:00 2001 From: Fedor Katurov Date: Tue, 20 Apr 2021 17:21:05 +0700 Subject: [PATCH 03/11] fixed token checking --- src/redux/store.ts | 10 +++++----- src/redux/user/sagas.ts | 1 + src/utils/api/index.ts | 17 ++++++----------- src/utils/api/types.ts | 13 +++++++++++++ 4 files changed, 25 insertions(+), 16 deletions(-) diff --git a/src/redux/store.ts b/src/redux/store.ts index 48328a5..f3919fb 100644 --- a/src/redux/store.ts +++ b/src/redux/store.ts @@ -1,6 +1,6 @@ -import { createStore, applyMiddleware, combineReducers, compose, Store } from 'redux'; +import { applyMiddleware, combineReducers, compose, createStore, Store } from 'redux'; -import { persistStore, persistReducer } from 'redux-persist'; +import { persistReducer, persistStore } from 'redux-persist'; import storage from 'redux-persist/lib/storage'; import createSagaMiddleware from 'redux-saga'; @@ -8,15 +8,15 @@ 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 { IRootReducer, userReducer } from '~/redux/user'; import { userSaga } from '~/redux/user/sagas'; import { editor, IEditorState } from '~/redux/editor'; import { editorSaga } from '~/redux/editor/sagas'; -import { map, IMapReducer } from '~/redux/map'; +import { IMapReducer, map } from '~/redux/map'; import { mapSaga } from '~/redux/map/sagas'; -import { watchLocation, getLocation } from '~/utils/window'; +import { watchLocation } from '~/utils/window'; import { LatLngLiteral } from 'leaflet'; import { setUserLocation, userLogout } from './user/actions'; import { MainMap } from '~/constants/map'; diff --git a/src/redux/user/sagas.ts b/src/redux/user/sagas.ts index f9ee41a..a1c9faa 100644 --- a/src/redux/user/sagas.ts +++ b/src/redux/user/sagas.ts @@ -77,6 +77,7 @@ function* authCheckSaga({ key }: RehydrateAction) { data: { user, random_url }, }: Unwrap = yield call(checkUserToken, { id, + token, }); if (user) { diff --git a/src/utils/api/index.ts b/src/utils/api/index.ts index 38d0a38..d89e12d 100644 --- a/src/utils/api/index.ts +++ b/src/utils/api/index.ts @@ -1,5 +1,5 @@ import { API } from '~/constants/api'; -import { IRootState, IRouteListItem } from '~/redux/user'; +import { IRootState } from '~/redux/user'; import { IUser } from '~/constants/auth'; import { CLIENT } from '~/config/frontend'; import { LatLngLiteral } from 'leaflet'; @@ -7,7 +7,7 @@ import { IRoute } from '~/redux/map/types'; import { INominatimResult } from '~/redux/types'; import { api } from './instance'; import { postMapInterceptor } from '~/utils/api/interceptors'; -import { PostMapRequest, PostMapResponse } from '~/utils/api/types'; +import { CheckTokenRequest, CheckTokenResult, PostMapRequest, PostMapResponse } from '~/utils/api/types'; interface IGetRouteList { min: number; @@ -20,16 +20,11 @@ interface IGetRouteList { export const checkUserToken = ({ id, -}: { - id: IRootState['user']['id']; -}) => + token, +}: CheckTokenRequest) => api - .get<{ - user: IUser; - random_url: string; - routes: IRouteListItem[]; - }>(API.CHECK_TOKEN, { - params: { id }, + .get(API.CHECK_TOKEN, { + params: { id, token }, }); export const getGuestToken = () => diff --git a/src/utils/api/types.ts b/src/utils/api/types.ts index 2fdf3a1..b0ded9a 100644 --- a/src/utils/api/types.ts +++ b/src/utils/api/types.ts @@ -1,4 +1,6 @@ import { IRoute } from '~/redux/map/types'; +import { IUser } from '~/constants/auth'; +import { IRootState, IRouteListItem } from '~/redux/user'; export interface PostMapResponse { route: IRoute; @@ -9,3 +11,14 @@ export interface PostMapResponse { export type PostMapRequest = Partial & { force: boolean; } + +export interface CheckTokenResult { + user: IUser; + random_url: string; + routes: IRouteListItem[]; +} + +export interface CheckTokenRequest { + id: IRootState['user']['id']; + token: string, +} From 0018465f1585381b7e99f5c6e8f584366de76cf4 Mon Sep 17 00:00:00 2001 From: Fedor Katurov Date: Tue, 20 Apr 2021 17:26:36 +0700 Subject: [PATCH 04/11] fixed token checking and some types --- src/utils/api/index.ts | 49 ++++++++------------------------ src/utils/api/types.ts | 39 +++++++++++++++++++++++++ src/utils/geom.ts | 15 ++++------ src/utils/history.ts | 10 +------ src/utils/map/InteractivePoly.ts | 2 +- src/utils/map/OsrmRouter.ts | 6 ++-- src/utils/reducer.ts | 7 ----- src/utils/renderer.ts | 16 +++-------- src/utils/simplify.ts | 2 +- 9 files changed, 65 insertions(+), 81 deletions(-) diff --git a/src/utils/api/index.ts b/src/utils/api/index.ts index d89e12d..1c6776d 100644 --- a/src/utils/api/index.ts +++ b/src/utils/api/index.ts @@ -7,16 +7,13 @@ import { IRoute } from '~/redux/map/types'; import { INominatimResult } from '~/redux/types'; import { api } from './instance'; import { postMapInterceptor } from '~/utils/api/interceptors'; -import { CheckTokenRequest, CheckTokenResult, PostMapRequest, PostMapResponse } from '~/utils/api/types'; - -interface IGetRouteList { - min: number; - max: number; - tab: string; - search: string; - step: IRootState['routes']['step']; - shift: IRootState['routes']['step']; -} +import { + CheckTokenRequest, + CheckTokenResult, + GetGuestTokenResult, GetRouteListRequest, GetRouteListResponse, GetStoredMapRequest, GetStoredMapResult, + PostMapRequest, + PostMapResponse, +} from '~/utils/api/types'; export const checkUserToken = ({ id, @@ -29,22 +26,13 @@ export const checkUserToken = ({ export const getGuestToken = () => api - .get<{ - user: IUser; - random_url: string; - }>(API.GET_GUEST); + .get(API.GET_GUEST); export const getStoredMap = ({ name, -}: { - name: IRoute['address']; -}) => +}: GetStoredMapRequest) => api - .get<{ - route: IRoute; - error?: string; - random_url: string; - }>(API.GET_MAP, { + .get(API.GET_MAP, { params: { name }, }); @@ -103,22 +91,9 @@ export const getRouteList = ({ tab, step, shift, -}: IGetRouteList) => +}: GetRouteListRequest) => api - .get<{ - routes: IRoute[]; - limits: { - min: number; - max: number; - count: number; - }; - filter: { - min: number; - max: number; - shift: number; - step: number; - }; - }>( + .get( API.GET_ROUTE_LIST(tab), { params: { diff --git a/src/utils/api/types.ts b/src/utils/api/types.ts index b0ded9a..48dbf1e 100644 --- a/src/utils/api/types.ts +++ b/src/utils/api/types.ts @@ -22,3 +22,42 @@ export interface CheckTokenRequest { id: IRootState['user']['id']; token: string, } + +export interface GetGuestTokenResult { + user: IUser; + random_url: string; +} + +export interface GetStoredMapResult { + route: IRoute; + error?: string; + random_url: string; +} + +export interface GetStoredMapRequest { + name: IRoute['address']; +} + +export interface GetRouteListRequest { + min: number; + max: number; + tab: string; + search: string; + step: IRootState['routes']['step']; + shift: IRootState['routes']['step']; +} + +export interface GetRouteListResponse { + routes: IRoute[]; + limits: { + min: number; + max: number; + count: number; + }; + filter: { + min: number; + max: number; + shift: number; + step: number; + }; +} diff --git a/src/utils/geom.ts b/src/utils/geom.ts index bfc3341..02afe02 100644 --- a/src/utils/geom.ts +++ b/src/utils/geom.ts @@ -1,9 +1,4 @@ -import { LatLng, LatLngLiteral, point, Point, PointExpression, latLng } from 'leaflet'; - -// interface LatLng { -// lat: number; -// lng: number; -// } +import { LatLng, latLng, LatLngLiteral, Point, point } from 'leaflet'; export const middleCoord = (l1: LatLng, l2: LatLng): LatLng => latLng({ lat: l2.lat + (l1.lat - l2.lat) / 2, @@ -46,14 +41,14 @@ export const findDistance = (t1: number, n1: number, t2: number, n2: number): nu export const findDistanceHaversine = (t1: number, n1: number, t2: number, n2: number): number => { const R = 6371; // km const dLat = ((t2 - t1) * Math.PI) / 180; - var dLon = ((n2 - n1) * Math.PI) / 180; - var a = + const dLon = ((n2 - n1) * Math.PI) / 180; + const a = Math.sin(dLat / 2) * Math.sin(dLat / 2) + Math.cos((t1 * Math.PI) / 180) * Math.cos((t2 * Math.PI) / 180) * Math.sin(dLon / 2) * Math.sin(dLon / 2); - var c = 2 * Math.asin(Math.sqrt(a)); + const c = 2 * Math.asin(Math.sqrt(a)); return R * c; }; @@ -88,7 +83,7 @@ export const dist2 = (A: LatLngLiteral, B: LatLngLiteral): number => const distToSegmentSquared = (A: LatLng, B: LatLng, C: LatLng): number => { const l2 = dist2(A, B); - if (l2 == 0) return dist2(C, A); + if (l2 === 0) return dist2(C, A); const t = Math.max( 0, diff --git a/src/utils/history.ts b/src/utils/history.ts index c2435c9..e203911 100644 --- a/src/utils/history.ts +++ b/src/utils/history.ts @@ -1,13 +1,5 @@ import { history } from '~/redux/store'; -import {API_RETRY_INTERVAL} from "~/constants/api"; - -interface IUrlData { - path: string, - mode: 'edit' | '', - host: string, - hash: string, - protocol: 'http' | 'https', -} +import { API_RETRY_INTERVAL } from '~/constants/api'; export const getPath = (): string => (window.location && window.location.pathname); export const pushPath = (url: string): string => history.push(url); diff --git a/src/utils/map/InteractivePoly.ts b/src/utils/map/InteractivePoly.ts index fa445bc..70aac65 100644 --- a/src/utils/map/InteractivePoly.ts +++ b/src/utils/map/InteractivePoly.ts @@ -507,7 +507,7 @@ class InteractivePoly extends Polyline { const index = this.markers.indexOf(target); const latlngs = this.getLatLngs(); - if (typeof index === 'undefined' || latlngs.length == 0) return; + if (typeof index === 'undefined' || latlngs.length === 0) return; this.dropMarkerDistanceChange(index); this._map.removeLayer(this.markers[index]); diff --git a/src/utils/map/OsrmRouter.ts b/src/utils/map/OsrmRouter.ts index 1b3a461..6da8fd8 100644 --- a/src/utils/map/OsrmRouter.ts +++ b/src/utils/map/OsrmRouter.ts @@ -35,7 +35,7 @@ export const OsrmRouter = Routing.control({ show: false, plan: Routing.plan([], { createMarker: (_, wp) => { - const marker = new Marker(wp.latLng, { + return new Marker(wp.latLng, { draggable: true, icon: createWaypointMarker(), }) @@ -45,12 +45,10 @@ export const OsrmRouter = Routing.control({ OsrmRouter.setWaypoints( OsrmRouter.getWaypoints().filter( point => - !point.latLng || (point.latLng.lat != latlng.lat && point.latLng.lng != latlng.lng) + !point.latLng || (point.latLng.lat !== latlng.lat && point.latLng.lng !== latlng.lng) ) ); }); - - return marker; }, routeWhileDragging: false, }), diff --git a/src/utils/reducer.ts b/src/utils/reducer.ts index 1121914..46e5a2d 100644 --- a/src/utils/reducer.ts +++ b/src/utils/reducer.ts @@ -1,10 +1,3 @@ -// create-reducer.ts -import { Action } from 'redux'; - -type Handlers> = { - readonly [Type in Types]: (state: State, action: Actions) => State -} - export const createReducer = ( initialState, handlers, diff --git a/src/utils/renderer.ts b/src/utils/renderer.ts index f76f0bc..1620790 100644 --- a/src/utils/renderer.ts +++ b/src/utils/renderer.ts @@ -1,18 +1,10 @@ -// import { editor } from '~/modules/Editor'; -import { COLORS, CLIENT } from '~/config/frontend'; +import { CLIENT, COLORS } from '~/config/frontend'; import saveAs from 'file-saver'; import { replaceProviderUrl } from '~/constants/providers'; import { STICKERS } from '~/constants/stickers'; -import { IRoute } from '~/redux/map/types'; -import { IStickerDump } from '~/redux/map/types'; -import { IRootState } from '~/redux/user'; -import { - angleBetweenPoints, - angleBetweenPointsRad, - findDistancePx, - middleCoordPx, -} from '~/utils/geom'; -import { Point, LatLng, latLng } from 'leaflet'; +import { IRoute, IStickerDump } from '~/redux/map/types'; +import { angleBetweenPoints, angleBetweenPointsRad, findDistancePx, middleCoordPx } from '~/utils/geom'; +import { LatLng, latLng, Point } from 'leaflet'; import { MainMap } from '~/constants/map'; export interface ITilePlacement { diff --git a/src/utils/simplify.ts b/src/utils/simplify.ts index 4c20705..1ed303d 100644 --- a/src/utils/simplify.ts +++ b/src/utils/simplify.ts @@ -1,4 +1,4 @@ -import { Map, LineUtil, LatLng } from 'leaflet'; +import { LatLng, LineUtil } from 'leaflet'; import { MainMap } from '~/constants/map'; export const simplify = (latlngs: LatLng[]): LatLng[] => { From 8346c2533f7c042c29f7552ecccfad484ec25245 Mon Sep 17 00:00:00 2001 From: Fedor Katurov Date: Tue, 20 Apr 2021 17:34:53 +0700 Subject: [PATCH 05/11] fixed sticker deletion bug --- package.json | 1 - src/map/Sticker/index.tsx | 20 ++++++++++---------- yarn.lock | 12 ------------ 3 files changed, 10 insertions(+), 23 deletions(-) diff --git a/package.json b/package.json index f29bbf5..31dfab0 100644 --- a/package.json +++ b/package.json @@ -32,7 +32,6 @@ "gpx-parser-builder": "^1.0.2", "leaflet": "1.6.0", "leaflet-editable": "^1.1.0", - "leaflet-geometryutil": "^0.9.0", "leaflet-routing-machine": "^3.2.12", "leaflet.markercluster": "^1.4.1", "node-sass": "^5.0.0", diff --git a/src/map/Sticker/index.tsx b/src/map/Sticker/index.tsx index a611388..900df90 100644 --- a/src/map/Sticker/index.tsx +++ b/src/map/Sticker/index.tsx @@ -49,7 +49,7 @@ const Sticker: React.FC = ({ const stickerImage = React.useRef(null); const onChange = React.useCallback(state => mapSetSticker(index, state), [mapSetSticker, index]); - const onDelete = React.useCallback(state => mapDropSticker(index), [mapSetSticker, index]); + const onDelete = React.useCallback(() => setTimeout(() => mapDropSticker(index), 0), [mapDropSticker, index]); const updateAngle = useCallback( ang => { @@ -65,12 +65,12 @@ const Sticker: React.FC = ({ stickerArrow.current.style.transform = `rotate(${ang + Math.PI}rad)`; }, - [stickerArrow, stickerImage] + [stickerArrow, stickerImage], ); const onDragStart = React.useCallback(() => { if (!layer?.dragging) { - return + return; } layer.dragging.disable(); @@ -100,7 +100,7 @@ const Sticker: React.FC = ({ setTimeout(MainMap.enableClicks, 100); }, - [setDragging, layer, MainMap, sticker, angle] + [setDragging, layer, MainMap, sticker, angle], ); const onMoveStarted = React.useCallback(() => { @@ -118,7 +118,7 @@ const Sticker: React.FC = ({ MainMap.enableClicks(); }, - [onChange, sticker] + [onChange, sticker], ); const onDrag = React.useCallback( @@ -130,7 +130,7 @@ const Sticker: React.FC = ({ angle.current = parseFloat(Math.atan2(y - pageY, x - pageX).toFixed(2)); updateAngle(angle.current); }, - [element, updateAngle, angle] + [element, updateAngle, angle], ); const onTextChange = React.useCallback(text => setText(text), [sticker, onChange]); @@ -143,7 +143,7 @@ const Sticker: React.FC = ({ }, [text, onChange, sticker]); const direction = React.useMemo(() => { - getLabelDirection(sticker?.angle) + getLabelDirection(sticker?.angle); }, [sticker.angle]); useEffect(() => { @@ -165,9 +165,9 @@ const Sticker: React.FC = ({ useEffect(() => { if (!wrapper || !wrapper.current) return; - const scale = getAdaptiveScale(zoom) // adaptive zoom :-) + const scale = getAdaptiveScale(zoom); // adaptive zoom :-) - wrapper.current.style.transform = `scale(${scale}) perspective(1px)` + wrapper.current.style.transform = `scale(${scale}) perspective(1px)`; }, [zoom, wrapper]); // Attaches onMoveFinished event to item @@ -246,7 +246,7 @@ const Sticker: React.FC = ({
, - element + element, ); }; diff --git a/yarn.lock b/yarn.lock index c754c94..f0cac18 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7197,13 +7197,6 @@ leaflet-editable@^1.1.0: resolved "https://registry.yarnpkg.com/leaflet-editable/-/leaflet-editable-1.2.0.tgz#a3a01001764ba58ea923381ee6a1c814708a0b84" integrity sha512-wG11JwpL8zqIbypTop6xCRGagMuWw68ihYu4uqrqc5Ep0wnEJeyob7NB2Rt5t74Oih4rwJ3OfwaGbzdowOGfYQ== -leaflet-geometryutil@^0.9.0: - version "0.9.3" - resolved "https://registry.yarnpkg.com/leaflet-geometryutil/-/leaflet-geometryutil-0.9.3.tgz#e10fa302d99d4b1d3c6365a1f39298635a2704cd" - integrity sha512-Wi6YvfNx/Xu9q35AEfXpsUXmIFLen/MO+C2qimxHRnjyeyOxBhdcZa6kSiReaOX0cGK7yQInqrzz0dkIqZ8Dpg== - dependencies: - leaflet ">=0.7.0" - leaflet-routing-machine@^3.2.12: version "3.2.12" resolved "https://registry.yarnpkg.com/leaflet-routing-machine/-/leaflet-routing-machine-3.2.12.tgz#9e4aef008321b0227cf894d829c3b4c1f13e4e13" @@ -7223,11 +7216,6 @@ leaflet@1.6.0: resolved "https://registry.yarnpkg.com/leaflet/-/leaflet-1.6.0.tgz#aecbb044b949ec29469eeb31c77a88e2f448f308" integrity sha512-CPkhyqWUKZKFJ6K8umN5/D2wrJ2+/8UIpXppY7QDnUZW5bZL5+SEI2J7GBpwh4LIupOKqbNSQXgqmrEJopHVNQ== -leaflet@>=0.7.0: - version "1.7.1" - resolved "https://registry.yarnpkg.com/leaflet/-/leaflet-1.7.1.tgz#10d684916edfe1bf41d688a3b97127c0322a2a19" - integrity sha512-/xwPEBidtg69Q3HlqPdU3DnrXQOvQU/CCHA1tcDQVzOwm91YMYaILjNp7L4Eaw5Z4sOYdbBz6koWyibppd8Zqw== - leven@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/leven/-/leven-3.1.0.tgz#77891de834064cccba82ae7842bb6b14a13ed7f2" From 7b24499f49fe9ea3d6fcbf4c3171a6bcfe172369 Mon Sep 17 00:00:00 2001 From: Fedor Katurov Date: Tue, 20 Apr 2021 17:36:49 +0700 Subject: [PATCH 06/11] fixed svg colors --- src/styles/colors.scss | 2 +- src/styles/dialogs.scss | 24 ++++++++++++------------ src/styles/map.scss | 4 ++-- src/styles/panel.scss | 4 ++-- src/styles/save.scss | 2 +- 5 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/styles/colors.scss b/src/styles/colors.scss index c6b9fea..8bb6fdd 100644 --- a/src/styles/colors.scss +++ b/src/styles/colors.scss @@ -32,4 +32,4 @@ $tooltip_background: #123740; $loading_shade: darken($blue_secondary, 20%); $cluster_small: #0069a7; -$title_dialog_color: fade(#111111, 85%); +$title_dialog_color: darken(#111111, 85%); diff --git a/src/styles/dialogs.scss b/src/styles/dialogs.scss index caff976..b0cf936 100644 --- a/src/styles/dialogs.scss +++ b/src/styles/dialogs.scss @@ -178,7 +178,7 @@ left: 0; width: 100%; z-index: 10; - background: linear-gradient(fade($loading_shade, 0%), $loading_shade 70%); + background: linear-gradient(darken($loading_shade, 0%), $loading_shade 70%); height: 100px; pointer-events: none; transition: opacity 100ms; @@ -240,7 +240,7 @@ &.has_edit { //transform: translateY(-2px); .route-row { - background: fade($green_secondary, 30%); + background: darken($green_secondary, 30%); } } @@ -280,11 +280,11 @@ } .route-row-edit { - background: fade($green_secondary, 30%); + background: darken($green_secondary, 30%); } .route-row-drop { - background: fade($red_secondary, 20%); + background: darken($red_secondary, 20%); .route-row { align-items: center; @@ -341,13 +341,13 @@ display: flex; align-items: center; justify-content: center; - fill: fade(white, 30%); - background: fade(white, 8%); + fill: darken(white, 30%); + background: darken(white, 8%); cursor: pointer; transition: background 250ms, transform 500ms; &:hover { - background: fade(white, 10%); + background: darken(white, 10%); } } @@ -361,7 +361,7 @@ overflow: hidden; transition: all 500ms; display: flex; - fill: fade(white, 30%); + fill: darken(white, 30%); div { width: 60px; @@ -371,16 +371,16 @@ align-items: center; &:first-child { - box-shadow: fade(black, 30%) 1px 0; + box-shadow: darken(black, 30%) 1px 0; } &:hover { - background: fade($red_secondary, 30%); + background: darken($red_secondary, 30%); } &.modify-button { &:hover { - background: fade($green_secondary, 30%); + background: darken($green_secondary, 30%); } } } @@ -399,7 +399,7 @@ .route-row-corner { svg { - fill: fade(white, 50%); + fill: darken(white, 50%); margin-right: 2px; flex-shrink: 0; } diff --git a/src/styles/map.scss b/src/styles/map.scss index a1b7981..38d93f8 100644 --- a/src/styles/map.scss +++ b/src/styles/map.scss @@ -308,7 +308,7 @@ align-items: center; justify-content: center; color: white; - box-shadow: fade($cluster_small, 70%) 0 0 0 5px; + box-shadow: darken($cluster_small, 70%) 0 0 0 5px; font-weight: bold; font-size: 13px; transform: translate(-12px, -12px); @@ -317,7 +317,7 @@ outline: none; &:hover { - box-shadow: fade($cluster_small, 70%) 0 0 0 7px; + box-shadow: darken($cluster_small, 70%) 0 0 0 7px; } span { diff --git a/src/styles/panel.scss b/src/styles/panel.scss index 6bd3a1d..4854214 100644 --- a/src/styles/panel.scss +++ b/src/styles/panel.scss @@ -729,7 +729,7 @@ margin-bottom: 10px; padding: 10px; background: $title_dialog_color; - color: fade(white, 50%); + color: darken(white, 50%); font-size: 13px; box-sizing: border-box; border-radius: $panel_radius; @@ -761,7 +761,7 @@ content: ' '; width: 100%; height: 40px; - background: linear-gradient(fade($title_dialog_color, 0), $title_dialog_color); + background: linear-gradient(darken($title_dialog_color, 0), $title_dialog_color); position: absolute; bottom: 0; left: 0; diff --git a/src/styles/save.scss b/src/styles/save.scss index 8edf45e..f85ab5d 100644 --- a/src/styles/save.scss +++ b/src/styles/save.scss @@ -41,7 +41,7 @@ pointer-events: none; text-transform: uppercase; font-size: 1.2em; - color: fade(white, 70%); + color: darken(white, 70%); svg { fill: white; From 10d5fa1b9843517b060be20ee009f72f8b25ffec Mon Sep 17 00:00:00 2001 From: Fedor Katurov Date: Tue, 20 Apr 2021 17:49:35 +0700 Subject: [PATCH 07/11] fixed dash on renderer --- src/utils/renderer.ts | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/utils/renderer.ts b/src/utils/renderer.ts index 1620790..357c58e 100644 --- a/src/utils/renderer.ts +++ b/src/utils/renderer.ts @@ -29,8 +29,7 @@ const latLngToTile = (latlng: { lat: number; lng: number; }): { x: number; y: number; z: number } => { - const map = MainMap; - const zoom = map.getZoom(); + const zoom = MainMap.getZoom(); const xtile = Number(Math.floor(((latlng.lng + 180) / 360) * (1 << zoom))); const ytile = Number( Math.floor( @@ -48,8 +47,7 @@ const latLngToTile = (latlng: { }; const tileToLatLng = (point: { x: number; y: number }): LatLng => { - const map = MainMap; - const z = map.getZoom(); + const z = MainMap.getZoom(); const lng = (point.x / Math.pow(2, z)) * 360 - 180; const n = Math.PI - (2 * Math.PI * point.y) / Math.pow(2, z); const lat = (180 / Math.PI) * Math.atan(0.5 * (Math.exp(n) - Math.exp(-n))); @@ -209,7 +207,7 @@ export const composePoly = ({ } if (dash) { - ctx.setLineDash([12, 12]); + ctx.setLineDash(dash); } ctx.stroke(); From b421005b7d518c9db832e1fd04086e28d1ea9e8a Mon Sep 17 00:00:00 2001 From: Fedor Katurov Date: Tue, 20 Apr 2021 17:54:18 +0700 Subject: [PATCH 08/11] fixed change detection by stickers --- src/redux/map/sagas.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/redux/map/sagas.ts b/src/redux/map/sagas.ts index 1572bf6..0441d31 100644 --- a/src/redux/map/sagas.ts +++ b/src/redux/map/sagas.ts @@ -329,7 +329,7 @@ function* setChanged() { export function* mapSaga() { yield takeEvery( - [MAP_ACTIONS.SET_ROUTE, MAP_ACTIONS.SET_STICKER, MAP_ACTIONS.SET_STICKERS], + [MAP_ACTIONS.SET_ROUTE, MAP_ACTIONS.SET_STICKER, MAP_ACTIONS.SET_STICKERS, MAP_ACTIONS.ADD_STICKER], setChanged, ); From a3982eddae18ae09e4666bf7fac0597215eaa81b Mon Sep 17 00:00:00 2001 From: Fedor Katurov Date: Tue, 20 Apr 2021 18:01:28 +0700 Subject: [PATCH 09/11] fixed provider and logo persistence --- src/redux/store.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/redux/store.ts b/src/redux/store.ts index f3919fb..19369b5 100644 --- a/src/redux/store.ts +++ b/src/redux/store.ts @@ -25,6 +25,12 @@ import { assocPath } from 'ramda'; import { AxiosError } from 'axios'; import { api } from '~/utils/api/instance'; +const mapPersistConfig: PersistConfig = { + key: 'map', + whitelist: ['logo', 'provider'], + storage, +}; + const userPersistConfig: PersistConfig = { key: 'user', whitelist: ['user', 'logo', 'provider', 'speed'], @@ -55,7 +61,7 @@ export const store = createStore( combineReducers({ user: persistReducer(userPersistConfig, userReducer), editor: persistReducer(editorPersistConfig, editor), - map, + map: persistReducer(mapPersistConfig, map), }), composeEnhancers(applyMiddleware(sagaMiddleware)) ); From afc1b084e186470356608c2b6c8586b03c0aaa1c Mon Sep 17 00:00:00 2001 From: Fedor Katurov Date: Fri, 6 Aug 2021 11:33:34 +0700 Subject: [PATCH 10/11] fixed styles --- .env | 2 +- src/styles/dialogs.scss | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.env b/.env index 3dd4857..d8d2c6b 100644 --- a/.env +++ b/.env @@ -1,4 +1,4 @@ REACT_APP_PUBLIC_PATH = https://localhost:3000/ -REACT_APP_API_ADDR = https://backend.alpha-map.vault48.org/ +REACT_APP_API_ADDR = https://backend.map.vault48.org REACT_APP_OSRM_URL = https://vault48.org:5001/route/v1 REACT_APP_OSRM_PROFILE = bike diff --git a/src/styles/dialogs.scss b/src/styles/dialogs.scss index b0cf936..20e86e7 100644 --- a/src/styles/dialogs.scss +++ b/src/styles/dialogs.scss @@ -342,12 +342,12 @@ align-items: center; justify-content: center; fill: darken(white, 30%); - background: darken(white, 8%); + background: transparentize(white, 0.9); cursor: pointer; transition: background 250ms, transform 500ms; &:hover { - background: darken(white, 10%); + background: transparentize(white, 0.95); } } From 91fc203b0a35bd4de45ce9f99751f7e8b0296bbc Mon Sep 17 00:00:00 2001 From: Fedor Katurov Date: Fri, 6 Aug 2021 11:37:45 +0700 Subject: [PATCH 11/11] added zoom icons --- src/styles/map.scss | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/src/styles/map.scss b/src/styles/map.scss index 38d93f8..a541f8f 100644 --- a/src/styles/map.scss +++ b/src/styles/map.scss @@ -12,7 +12,28 @@ } .leaflet-control-zoom { - display: none; + width: 32px; + opacity: 0.5; + transition: opacity 0.1s; + + &:hover { + opacity: 1; + } +} + +a.leaflet-control-zoom-in, a.leaflet-control-zoom-out { + width: 32px; + height: 32px; + display: flex; + align-items: center; + justify-content: center; + + &:hover { + width: 32px; + height: 32px; + display: flex; + opacity: 1; + } } .leaflet-touch .leaflet-bar a {