diff --git a/src/components/Cursor.tsx b/src/components/Cursor.tsx index ebc392f..52e8e93 100644 --- a/src/components/Cursor.tsx +++ b/src/components/Cursor.tsx @@ -16,7 +16,7 @@ export class Cursor extends React.PureComponent { } moveCursor = e => { - if (!e.clientX || !e.clientY) return; + if (!e.clientX || !e.clientY || !this.cursor || !this.cursor.style) return; const { clientX, clientY } = e; diff --git a/src/containers/App.tsx b/src/containers/App.tsx index 54a490d..bd1aa38 100644 --- a/src/containers/App.tsx +++ b/src/containers/App.tsx @@ -19,6 +19,9 @@ import { IStickerPack } from "$constants/stickers"; import { IDialogs } from "$constants/dialogs"; import { IModes } from "$constants/modes"; +import { Map } from "$containers/map/Map" +import { TileLayer } from '$containers/map/TileLayer'; + type Props = { sticker: string, renderer_active: boolean, @@ -49,6 +52,8 @@ const Component = (props: Props) => ( setDialogActive={props.setDialogActive} /> + + { props.renderer_active && } diff --git a/src/containers/map/Map/index.tsx b/src/containers/map/Map/index.tsx new file mode 100644 index 0000000..2f76a56 --- /dev/null +++ b/src/containers/map/Map/index.tsx @@ -0,0 +1,46 @@ +import { Map as MapInterface, map } from "leaflet"; +import * as React from "react"; +import { createPortal } from "react-dom"; +import { MapContext } from "$utils/context.ts"; +import { TileLayer } from "$containers/map/TileLayer"; +import { Route } from "$containers/map/Route"; +import { selectMapProvider, selectMapRoute } from "$redux/map/selectors"; +import { connect } from "react-redux"; +import * as MAP_ACTIONS from "$redux/map/actions"; + +const mapStateToProps = state => ({ + provider: selectMapProvider(state), + route: selectMapRoute(state) +}); + +const mapDispatchToProps = { + mapSetRoute: MAP_ACTIONS.mapSetRoute +}; + +type IProps = React.HTMLAttributes & + ReturnType & + typeof mapDispatchToProps & {}; + +const MapUnconnected: React.FC = ({ provider, route, mapSetRoute }) => { + const ref = React.useRef(null); + const [maps, setMaps] = React.useState(null); + + React.useEffect(() => { + if (!ref.current) return; + + setMaps(map(ref.current).setView([55.0153275, 82.9071235], 13)); + }, [ref]); + + return createPortal( +
+ + + + +
, + document.getElementById("canvas") + ); +}; + +const Map = connect(mapStateToProps, mapDispatchToProps)(MapUnconnected); +export { Map }; diff --git a/src/containers/map/Route/index.tsx b/src/containers/map/Route/index.tsx new file mode 100644 index 0000000..4eb2936 --- /dev/null +++ b/src/containers/map/Route/index.tsx @@ -0,0 +1,80 @@ +import React, { + FC, + useEffect, + memo, + useContext, + useState, + useCallback +} from "react"; +import { IMapRoute, ILatLng } from "../../../redux/map/types"; +import { MapContext } from "$utils/context"; +import { InteractivePoly } from "$modules/InteractivePoly"; +import { isMobile } from "$utils/window"; +import { LatLng } from "leaflet"; + +interface IProps { + route: IMapRoute; + is_editing: boolean; + mapSetRoute: (latlngs: ILatLng[]) => void; +} + +const Route: FC = memo(({ route, is_editing, mapSetRoute }) => { + const [layer, setLayer] = useState(null); + const map = useContext(MapContext); + + const onRouteChanged = useCallback( + ({ latlngs }) => { + // mapSetRoute(latlngs) + }, + [mapSetRoute] + ); + + useEffect(() => { + if (!layer) return; + + layer.on("latlngschange", onRouteChanged); + + return () => layer.off("latlngschange", onRouteChanged); + }, [layer, onRouteChanged]); + + useEffect(() => { + if (!map) return; + + setLayer( + new InteractivePoly([], { + color: "url(#activePathGradient)", + weight: 6, + maxMarkers: isMobile() ? 20 : 100, + smoothFactor: 3, + updateself: false, + }) + .addTo(map) + .on("distancechange", console.log) + .on("allvertexhide", console.log) + .on("allvertexshow", console.log) + // .on("latlngschange", console.log) + ); + }, [map]); + + useEffect(() => { + if (!layer) return; + + const points = (route && route.length > 0 && route) || []; + + layer.setPoints(points as LatLng[]); // TODO: refactor this + }, [route, layer]); + + useEffect(() => { + if (!layer) return; + + if (is_editing) { + layer.editor.enable(); + } else { + layer.editor.disable(); + } + }, [is_editing, layer]); + + return null; +}); + +export { Route }; diff --git a/src/containers/map/TileLayer/index.tsx b/src/containers/map/TileLayer/index.tsx new file mode 100644 index 0000000..fdddeb6 --- /dev/null +++ b/src/containers/map/TileLayer/index.tsx @@ -0,0 +1,40 @@ +import * as React from "react"; +import { MapContext, TileContext } from "../../../utils/context"; +import { TileLayer as TileLayerInterface, tileLayer } from "leaflet"; +import { DEFAULT_PROVIDER, PROVIDERS } from "$constants/providers"; +import { IMapReducer } from "$redux/map"; + +type IProps = React.HTMLAttributes & { + provider: IMapReducer['provider'], +}; + +const TileLayer: React.FC = ({ children, provider }) => { + const [layer, setLayer] = React.useState(null); + const map = React.useContext(MapContext); + + React.useEffect(() => { + if (!map) return; + + setLayer( + tileLayer(PROVIDERS[DEFAULT_PROVIDER].url, { + attribution: "Независимое Велосообщество", + maxNativeZoom: 18, + maxZoom: 18 + }).addTo(map) + ); + }, [map]); + + React.useEffect(() => { + if (!layer || !provider) return; + + const { url } = + (provider && PROVIDERS[provider] && PROVIDERS[provider]) || + PROVIDERS[DEFAULT_PROVIDER]; + + layer.setUrl(url); + }, [layer, provider]); + + return {children}; +}; + +export { TileLayer }; diff --git a/src/index.html b/src/index.html index 7c1707f..3acea9a 100644 --- a/src/index.html +++ b/src/index.html @@ -130,7 +130,10 @@ +
+
+
ЗАГРУЗКА
diff --git a/src/modules/InteractivePoly.ts b/src/modules/InteractivePoly.ts index cfbef0a..4f5c324 100644 --- a/src/modules/InteractivePoly.ts +++ b/src/modules/InteractivePoly.ts @@ -27,7 +27,7 @@ interface InteractivePolylineOptions extends PolylineOptions { kmMarksStep?: number, } -export class Component extends Polyline { +export class InteractivePoly extends Polyline { constructor(latlngs: LatLngExpression[] | LatLngExpression[][], options?: InteractivePolylineOptions) { super(latlngs, options); @@ -47,7 +47,6 @@ export class Component extends Polyline { this.setLatLngs(latlngs); this.recreateMarkers(); this.recalcDistance(); - // this.recalcKmMarks(); this.touchHinter.setLatLngs(latlngs); this.fire('latlngschange', { latlngs }); }; @@ -101,7 +100,6 @@ export class Component extends Polyline { this._map.addLayer(this.markerLayer); this.fire('allvertexshow'); - console.log(); }; hideAllMarkers = (): void => { @@ -516,7 +514,7 @@ export class Component extends Polyline { distance: number = 0; } -Component.addInitHook(function () { +InteractivePoly.addInitHook(function () { this.once('add', (event) => { if (event.target instanceof InteractivePoly) { this.map = event.target._map; @@ -550,7 +548,7 @@ Component.addInitHook(function () { }); }); -export const InteractivePoly = Component; +// export const InteractivePoly = Component; /* events: vertexdragstart, diff --git a/src/redux/map/actions.ts b/src/redux/map/actions.ts new file mode 100644 index 0000000..ec8a222 --- /dev/null +++ b/src/redux/map/actions.ts @@ -0,0 +1,17 @@ +import { MAP_ACTIONS } from "./constants"; +import { IMapReducer } from "./"; + +export const mapSet = (map: Partial) => ({ + type: MAP_ACTIONS.SET_MAP, + map +}); + +export const mapSetProvider = (provider: IMapReducer['provider']) => ({ + type: MAP_ACTIONS.SET_PROVIDER, + provider +}); + +export const mapSetRoute = (route: IMapReducer['route']) => ({ + type: MAP_ACTIONS.SET_ROUTE, + route, +}); diff --git a/src/redux/map/constants.ts b/src/redux/map/constants.ts new file mode 100644 index 0000000..4e363d9 --- /dev/null +++ b/src/redux/map/constants.ts @@ -0,0 +1,7 @@ +const P = 'MAP' + +export const MAP_ACTIONS = { + SET_MAP: `${P}-SET_MAP`, + SET_PROVIDER: `${P}-SET_PROVIDER`, + SET_ROUTE: `${P}-SET_ROUTE`, +} \ No newline at end of file diff --git a/src/redux/map/handlers.ts b/src/redux/map/handlers.ts index a5d12a5..912bd2a 100644 --- a/src/redux/map/handlers.ts +++ b/src/redux/map/handlers.ts @@ -1,3 +1,33 @@ +import { MAP_ACTIONS } from "./constants"; +import { IMapReducer } from "."; +import { mapSet, mapSetProvider, mapSetRoute } from "./actions"; + +const setMap = ( + state: IMapReducer, + { map }: ReturnType +): IMapReducer => ({ + ...state, + ...map +}); + +const setProvider = ( + state: IMapReducer, + { provider }: ReturnType +): IMapReducer => ({ + ...state, + provider +}); + +const setRoute = ( + state: IMapReducer, + { route }: ReturnType +): IMapReducer => ({ + ...state, + route +}); + export const MAP_HANDLERS = { - -} \ No newline at end of file + [MAP_ACTIONS.SET_MAP]: setMap, + [MAP_ACTIONS.SET_PROVIDER]: setProvider, + [MAP_ACTIONS.SET_ROUTE]: setRoute +}; diff --git a/src/redux/map/index.ts b/src/redux/map/index.ts index ce8f294..ba5bb99 100644 --- a/src/redux/map/index.ts +++ b/src/redux/map/index.ts @@ -1,11 +1,15 @@ import { createReducer } from 'reduxsauce'; import { MAP_HANDLERS } from './handlers'; +import { DEFAULT_PROVIDER } from '$constants/providers'; +import { IMapRoute } from './types'; export interface IMapReducer { - + provider: string; + route: IMapRoute; } export const MAP_INITIAL_STATE = { - + provider: DEFAULT_PROVIDER, } + export const map = createReducer(MAP_INITIAL_STATE, MAP_HANDLERS) \ No newline at end of file diff --git a/src/redux/map/selectors.ts b/src/redux/map/selectors.ts new file mode 100644 index 0000000..5b169a8 --- /dev/null +++ b/src/redux/map/selectors.ts @@ -0,0 +1,2 @@ +export const selectMapProvider = state => state.map.provider; +export const selectMapRoute= state => state.map.route; \ No newline at end of file diff --git a/src/redux/map/types.ts b/src/redux/map/types.ts new file mode 100644 index 0000000..3d59e9f --- /dev/null +++ b/src/redux/map/types.ts @@ -0,0 +1,8 @@ +import { LatLng } from "leaflet"; + +export type ILatLng = { + lat: number, + lng: number, +} + +export type IMapRoute = ILatLng[]; \ No newline at end of file diff --git a/src/redux/store.ts b/src/redux/store.ts index 4687051..5774cd6 100644 --- a/src/redux/store.ts +++ b/src/redux/store.ts @@ -9,6 +9,7 @@ import { userSaga } from '$redux/user/sagas'; import { createBrowserHistory } from 'history'; import { locationChanged } from '$redux/user/actions'; import { PersistConfig, Persistor } from "redux-persist/es/types"; +import { map, IMapReducer } from '$redux/map'; const userPersistConfig: PersistConfig = { key: 'user', @@ -18,6 +19,7 @@ const userPersistConfig: PersistConfig = { export interface IState { user: IRootReducer + map: IMapReducer, } // create the saga middleware export const sagaMiddleware = createSagaMiddleware(); @@ -32,6 +34,7 @@ const composeEnhancers = export const store = createStore( combineReducers({ user: persistReducer(userPersistConfig, userReducer), + map, // routing: routerReducer }), composeEnhancers(applyMiddleware(/* routerMiddleware(history), */ sagaMiddleware)) diff --git a/src/redux/user/sagas.ts b/src/redux/user/sagas.ts index bb7f70a..9ba905b 100644 --- a/src/redux/user/sagas.ts +++ b/src/redux/user/sagas.ts @@ -89,6 +89,7 @@ import { IRootState } from "$redux/user"; import { downloadGPXTrack, getGPXString } from "$utils/gpx"; import { Unwrap } from "$utils/middleware"; import { IState } from "$redux/store"; +import { mapSetProvider, mapSet } from "$redux/map/actions"; const getUser = (state: IState) => state.user.user; const getState = (state: IState) => state.user; @@ -156,6 +157,8 @@ function* loadMapSaga(path) { data: { route, error, random_url } }: Unwrap = yield call(getStoredMap, { name: path }); + console.log({ route }); + if (route && !error) { yield editor.clearAll(); yield editor.setData(route); @@ -164,6 +167,13 @@ function* loadMapSaga(path) { yield put(setChanged(false)); + // TODO: REACTIVE BRANCH: + // yield put(mapSetProvider(route.provider)); + yield put(mapSet({ + provider: route.provider, + route: route.route, + })) + return { route, random_url }; } @@ -529,6 +539,9 @@ function* changeProviderSaga({ yield put(setProvider(provider)); + // TODO: REACTIVE BRANCH + yield put(mapSetProvider(provider)) + if (current_provider === provider) return; yield put(setChanged(true)); diff --git a/src/styles/map.less b/src/styles/map.less index bffc38f..dc7e580 100644 --- a/src/styles/map.less +++ b/src/styles/map.less @@ -1,5 +1,5 @@ #map { - width: 100%; + width: 50% !important; height: 100%; position: absolute; z-index: 1; @@ -283,3 +283,13 @@ font-size: 11px; } } + +#canvas { + background: #ff3344; + z-index: 0; + + > div { + width: 100%; + height: 100%; + } +} \ No newline at end of file diff --git a/src/utils/context.ts b/src/utils/context.ts new file mode 100644 index 0000000..3f25d78 --- /dev/null +++ b/src/utils/context.ts @@ -0,0 +1,5 @@ +import * as 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