diff --git a/package-lock.json b/package-lock.json
index 67bd187..23cd49a 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -778,17 +778,20 @@
     "@types/classnames": {
       "version": "2.2.7",
       "resolved": "https://registry.npmjs.org/@types/classnames/-/classnames-2.2.7.tgz",
-      "integrity": "sha512-rzOhiQ55WzAiFgXRtitP/ZUT8iVNyllEpylJ5zHzR4vArUvMB39GTk+Zon/uAM0JxEFAWnwsxC2gH8s+tZ3Myg=="
+      "integrity": "sha512-rzOhiQ55WzAiFgXRtitP/ZUT8iVNyllEpylJ5zHzR4vArUvMB39GTk+Zon/uAM0JxEFAWnwsxC2gH8s+tZ3Myg==",
+      "dev": true
     },
     "@types/geojson": {
       "version": "7946.0.6",
       "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.6.tgz",
-      "integrity": "sha512-f6qai3iR62QuMPPdgyH+LyiXTL2n9Rf62UniJjV7KHrbiwzLTZUKsdq0mFSTxAHbO7JvwxwC4tH0m1UnweuLrA=="
+      "integrity": "sha512-f6qai3iR62QuMPPdgyH+LyiXTL2n9Rf62UniJjV7KHrbiwzLTZUKsdq0mFSTxAHbO7JvwxwC4tH0m1UnweuLrA==",
+      "dev": true
     },
     "@types/leaflet": {
       "version": "1.4.3",
       "resolved": "https://registry.npmjs.org/@types/leaflet/-/leaflet-1.4.3.tgz",
       "integrity": "sha512-jFRBSsPHi1EwQSwrN0cOJLdPhwOZsRl4IMxvm/2ShLh0YM5GfCtQXCzsrv8RE7DWL+AykXdYSAd9bFLWbZT4CQ==",
+      "dev": true,
       "requires": {
         "@types/geojson": "7946.0.6"
       }
@@ -796,22 +799,34 @@
     "@types/node": {
       "version": "11.9.0",
       "resolved": "https://registry.npmjs.org/@types/node/-/node-11.9.0.tgz",
-      "integrity": "sha512-ry4DOrC+xenhQbzk1iIPzCZGhhPGEFv7ia7Iu6XXSLVluiJIe9FfG7Iu3mObH9mpxEXCWLCMU4JWbCCR9Oy1Zg=="
+      "integrity": "sha512-ry4DOrC+xenhQbzk1iIPzCZGhhPGEFv7ia7Iu6XXSLVluiJIe9FfG7Iu3mObH9mpxEXCWLCMU4JWbCCR9Oy1Zg==",
+      "dev": true
     },
     "@types/prop-types": {
       "version": "15.5.8",
       "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.5.8.tgz",
-      "integrity": "sha512-3AQoUxQcQtLHsK25wtTWIoIpgYjH3vSDroZOUr7PpCHw/jLY1RB9z9E8dBT/OSmwStVgkRNvdh+ZHNiomRieaw=="
+      "integrity": "sha512-3AQoUxQcQtLHsK25wtTWIoIpgYjH3vSDroZOUr7PpCHw/jLY1RB9z9E8dBT/OSmwStVgkRNvdh+ZHNiomRieaw==",
+      "dev": true
     },
     "@types/q": {
       "version": "1.5.1",
       "resolved": "https://registry.npmjs.org/@types/q/-/q-1.5.1.tgz",
       "integrity": "sha512-eqz8c/0kwNi/OEHQfvIuJVLTst3in0e7uTKeuY+WL/zfKn0xVujOTp42bS/vUUokhK5P2BppLd9JXMOMHcgbjA=="
     },
+    "@types/ramda": {
+      "version": "0.26.39",
+      "resolved": "https://registry.npmjs.org/@types/ramda/-/ramda-0.26.39.tgz",
+      "integrity": "sha512-3bu32X02VpjJhsYPUWkdOQGoBXjb/UveZgGg4IYMm+SPAXio96BOYrRhVELfM4AoP00sxoi/f2tqrXdwtR4jjg==",
+      "dev": true,
+      "requires": {
+        "ts-toolbelt": "4.14.6"
+      }
+    },
     "@types/react": {
       "version": "16.8.1",
       "resolved": "https://registry.npmjs.org/@types/react/-/react-16.8.1.tgz",
       "integrity": "sha512-tD1ETKJcuhANOejRc/p7OgQ16DKnbGi0M3LccelKlPnUCDp2a5koVxZFoRN9HN+A+m84HB5VGN7I+r3nNhS3PA==",
+      "dev": true,
       "requires": {
         "@types/prop-types": "15.5.8",
         "csstype": "2.6.2"
@@ -4464,7 +4479,8 @@
     "csstype": {
       "version": "2.6.2",
       "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.2.tgz",
-      "integrity": "sha512-Rl7PvTae0pflc1YtxtKbiSqq20Ts6vpIYOD5WBafl4y123DyHUeLrRdQP66sQW8/6gmX8jrYJLXwNeMqYVJcow=="
+      "integrity": "sha512-Rl7PvTae0pflc1YtxtKbiSqq20Ts6vpIYOD5WBafl4y123DyHUeLrRdQP66sQW8/6gmX8jrYJLXwNeMqYVJcow==",
+      "dev": true
     },
     "currently-unhandled": {
       "version": "0.4.1",
@@ -13015,6 +13031,12 @@
         "yn": "3.0.0"
       }
     },
+    "ts-toolbelt": {
+      "version": "4.14.6",
+      "resolved": "https://registry.npmjs.org/ts-toolbelt/-/ts-toolbelt-4.14.6.tgz",
+      "integrity": "sha512-SONcnRd93+LuYGfn/CZg5A5qhCODohZslAVZKHHu5bnwUxoXLqd2k2VIdwRUXYfKnY+UCeNbI2pTPz+Dno6Mpg==",
+      "dev": true
+    },
     "tslib": {
       "version": "1.9.3",
       "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz",
@@ -13085,9 +13107,9 @@
       "integrity": "sha512-NLpRc/FY+jPfWL0aUXQzjxPyF0Xug2om6akaoRLQ18KGwP2yYNBJu9vkv2q1q+Cx/+edy2Qf6O8xXnYY/xwz1A=="
     },
     "typescript": {
-      "version": "3.3.3",
-      "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.3.3.tgz",
-      "integrity": "sha512-Y21Xqe54TBVp+VDSNbuDYdGw0BpoR/Q6wo/+35M8PAU0vipahnyduJWirxxdxjsAkS7hue53x2zp8gz7F05u0A==",
+      "version": "3.7.4",
+      "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.7.4.tgz",
+      "integrity": "sha512-A25xv5XCtarLwXpcDNZzCGvW2D1S3/bACratYBx2sax8PefsFhlYmkQicKHvpYflFS8if4zne5zT5kpJ7pzuvw==",
       "dev": true
     },
     "uglifyjs-webpack-plugin": {
diff --git a/package.json b/package.json
index 0bd8aea..5c75fc7 100644
--- a/package.json
+++ b/package.json
@@ -15,6 +15,11 @@
   "devDependencies": {
     "@babel/cli": "^7.0.0-rc.1",
     "@babel/preset-env": "^7.0.0-rc.1",
+    "@types/ramda": "^0.26.39",
+    "@types/classnames": "^2.2.7",
+    "@types/leaflet": "^1.4.3",
+    "@types/node": "^11.9.0",
+    "@types/react": "16.8.1",
     "awesome-typescript-loader": "^5.2.1",
     "babel-core": "^6.26.0",
     "babel-eslint": "^8.2.3",
@@ -43,7 +48,7 @@
     "style-loader": "^0.21.0",
     "sw-precache-webpack-plugin": "^0.11.5",
     "ts-node": "^8.0.1",
-    "typescript": "^3.2.4",
+    "typescript": "^3.7.4",
     "uglifyjs-webpack-plugin": "^1.3.0",
     "webpack": "^4.6.0",
     "webpack-cli": "^3.2.3",
@@ -52,10 +57,6 @@
     "webpack-pwa-manifest": "^4.0.0"
   },
   "dependencies": {
-    "@types/classnames": "^2.2.7",
-    "@types/leaflet": "^1.4.3",
-    "@types/node": "^11.9.0",
-    "@types/react": "16.8.1",
     "axios": "^0.18.0",
     "babel-runtime": "^6.26.0",
     "body-parser": "^1.18.3",
diff --git a/src/_modules/Editor.ts b/src/_modules/Editor.ts
deleted file mode 100644
index 79bd824..0000000
--- a/src/_modules/Editor.ts
+++ /dev/null
@@ -1,462 +0,0 @@
-import { Map } from '~/modules/Map';
-import { Poly } from '~/modules/Poly';
-import { MODES } from '~/constants/modes';
-import { ILatLng, Stickers } from '~/modules/Stickers';
-import { Router } from '~/modules/Router';
-import { DEFAULT_LOGO, ILogos, LOGOS } from '~/constants/logos';
-
-import { getUrlData } from '~/utils/history';
-import { store } from '~/redux/store';
-import {
-  resetSaveDialog,
-  setActiveSticker,
-  setChanged, 
-  setDistance,
-  setIsEmpty, setIsRouting,
-  setMarkersShown,
-  setMode,
-  setRouterPoints, setStarred,
-} from '~/redux/user/actions';
-import {
-  mapSetAddress,
-  mapSetDescription,
-  mapSetLogo,
-  mapSetPublic,
-  mapSetTitle,
-  mapSetProvider,
-} from '~/redux/map/actions';
-import { DEFAULT_PROVIDER, IProvider, PROVIDERS } from '~/constants/providers';
-import { STICKERS } from '~/constants/stickers';
-import { IRootState } from "~/redux/user";
-import { DEFAULT_USER, IUser } from "~/constants/auth";
-
-interface IEditor {
-  map: Map;
-  poly: Poly;
-  stickers: Stickers;
-  router: Router;
-
-  logo: keyof ILogos;
-  owner: number;
-  initialData: {
-    version: number,
-    title: IRootState['title'],
-    owner: number,
-    address: IRootState['address'],
-    path: any,
-    route: any,
-    stickers: any,
-    provider: IRootState['provider'],
-    is_public: IRootState['is_public'],
-    is_published: IRootState['is_published'],
-    description: IRootState['description'],
-    logo: IRootState['logo'],
-  };
-  activeSticker: IRootState['activeSticker'];
-  mode: IRootState['mode'];
-  provider: IProvider;
-  switches: {
-    [x: string]: {
-      start?: () => any,
-      stop?: () => any,
-      toggle?: () => any,
-    }
-  };
-  clickHandlers: {
-    [x: string]: (event) => void
-  };
-  user: IUser;
-}
-
-export class Editor {
-  constructor() {
-    this.logo = DEFAULT_LOGO;
-    this.owner = null;
-    this.map = new Map({ container: 'map' });
-    this.activeSticker = {};
-    this.mode = MODES.NONE;
-    this.provider = PROVIDERS[DEFAULT_PROVIDER];
-
-    const {
-      triggerOnChange, lockMapClicks, routerMoveStart, pushPolyPoints,
-      map: { map }
-    } = this;
-
-    this.poly = new Poly({
-      map, routerMoveStart, lockMapClicks, setDistance: this.setDistance, triggerOnChange, editor: this,
-    });
-
-    this.stickers = new Stickers({
-      map,
-      lockMapClicks,
-      triggerOnChange,
-      editor: this
-    });
-
-    this.router = new Router({
-      map,
-      lockMapClicks,
-      pushPolyPoints,
-      setRouterPoints: this.setRouterPoints,
-      setIsRouting: this.setIsRouting,
-    });
-
-    this.switches = {
-      [MODES.POLY]: {
-        start: this.startPoly,
-        stop: this.poly.stop,
-        toggle: this.clearMode,
-      },
-      [MODES.ROUTER]: {
-        toggle: this.clearMode,
-        start: this.routerSetStart,
-      },
-      [MODES.STICKERS]: {
-        toggle: this.clearSticker,
-      },
-      [MODES.STICKERS_SELECT]: {
-        toggle: this.clearSticker,
-      },
-      [MODES.TRASH]: {
-        toggle: this.clearMode,
-      },
-      [MODES.CONFIRM_CANCEL]: {
-        toggle: this.cancelEditing,
-      },
-      [MODES.LOGO]: {
-        toggle: this.clearMode,
-      },
-      [MODES.SAVE]: {
-        toggle: this.clearMode,
-        stop: this.resetSaveDialog,
-      },
-      [MODES.PROVIDER]: {
-        toggle: this.clearMode,
-      }
-    };
-
-    this.clickHandlers = {
-      [MODES.STICKERS]: this.createStickerOnClick,
-      [MODES.ROUTER]: this.router.pushWaypointOnClick,
-    };
-
-    map.addEventListener('mouseup', this.onClick);
-    // map.addEventListener('touchend', this.onClick);
-    map.addEventListener('dragstart', () => lockMapClicks(true));
-    map.addEventListener('dragstop', () => lockMapClicks(false));
-  }
-
-  map: IEditor['map'];
-  poly: IEditor['poly'];
-  stickers: IEditor['stickers'];
-  router: IEditor['router'];
-
-  logo: IEditor['logo'] = DEFAULT_LOGO;
-  owner: IEditor['owner'] = null;
-  initialData: IEditor['initialData'] = {
-    version: null,
-    title: null,
-    owner: null,
-    address: null,
-    path: null,
-    route: null,
-    stickers: null,
-    provider: null,
-    is_public: false,
-    is_published: false,
-    description: '',
-    logo: null,
-  };
-  activeSticker: IEditor['activeSticker'];
-  mode: IEditor['mode'];
-  provider: IEditor['provider'];
-  switches: IEditor['switches'];
-  clickHandlers: IEditor['clickHandlers'];
-  user: IEditor['user'] = DEFAULT_USER;
-
-  getState = (): IRootState => <any>store.getState().user;
-
-  getUser = () => this.getState().user;
-  getMode = () => this.getState().mode;
-  getProvider = () => this.getState().provider;
-  getTitle = () => this.getState().title;
-  getEditing = () => this.getState().editing;
-  getChanged = () => this.getState().changed;
-  getRouterPoints = () => this.getState().routerPoints;
-  getDistance = () => this.getState().distance;
-  getIsEmpty = () => this.getState().is_empty;
-
-  mapSetLogo: typeof mapSetLogo = logo => store.dispatch(mapSetLogo(logo));
-  setMode: typeof setMode = value => store.dispatch(setMode(value));
-  setRouterPoints: typeof setRouterPoints = value => store.dispatch(setRouterPoints(value));
-  setActiveSticker: typeof setActiveSticker = value => store.dispatch(setActiveSticker(value));
-  mapSetTitle: typeof mapSetTitle = value => store.dispatch(mapSetTitle(value));
-  mapSetDescription: typeof mapSetDescription = value => store.dispatch(mapSetDescription(value));
-  mapSetAddress: typeof mapSetAddress = value => store.dispatch(mapSetAddress(value));
-  mapSetPublic: typeof mapSetPublic = value => store.dispatch(mapSetPublic(value));
-  setStarred: typeof setStarred = value => store.dispatch(setStarred(value));
-  setIsEmpty: typeof setIsEmpty = value => store.dispatch(setIsEmpty(value));
-  setIsRouting: typeof setIsRouting = value => store.dispatch(setIsRouting(value));
-
-  setMarkersShown = (value: boolean): void => {
-    if (this.getState().markers_shown !== value) store.dispatch(setMarkersShown(value));
-  };
-
-  resetSaveDialog = (): void => { store.dispatch(resetSaveDialog()); };
-
-  setDistance = (value: number): void => {
-    if (this.getDistance() !== value) store.dispatch(setDistance(value));
-  };
-
-  setChanged = (value: boolean): void => {
-    if (this.getChanged() !== value) store.dispatch(setChanged(value));
-  };
-
-  clearMode = (): void => { this.setMode(MODES.NONE); };
-  clearChanged = (): void => { store.dispatch(setChanged(false)); };
-
-  startPoly = (): void => {
-    if (this.getRouterPoints()) this.router.clearAll();
-    this.poly.continue();
-  };
-
-  triggerOnChange = (): void => {
-    if (this.isEmpty !== this.getIsEmpty()) this.setIsEmpty(this.isEmpty);
-    if (this.getEditing() && !this.getChanged()) this.setChanged(true);
-  };
-
-  createStickerOnClick = (e): void => {
-    if (!e || !e.latlng || !this.activeSticker) return;
-    const { latlng }: { latlng: ILatLng } = e;
-
-    this.stickers.createSticker({ latlng, sticker: this.activeSticker.sticker, set: this.activeSticker.set });
-    this.setActiveSticker(null);
-    this.setChanged(true);
-    // this.setMode(MODES.STICKERS_SELECT);
-  };
-
-  changeMode = (mode: IRootState['mode']): void => {
-    if (this.mode === mode) {
-      if (this.switches[mode] && this.switches[mode].toggle) {
-        // if we have special function on mode when it clicked again
-        this.switches[mode].toggle();
-      } else {
-        this.disableMode(mode);
-        // this.setMode(MODES.NONE);
-        this.mode = MODES.NONE;
-      }
-    } else {
-      this.disableMode(this.mode);
-      // this.setMode(mode);
-      this.mode = mode;
-      this.enableMode(mode);
-    }
-  };
-
-  enableMode = (mode: IRootState['mode']): void => {
-    if (this.switches[mode] && this.switches[mode].start) this.switches[mode].start();
-  };
-
-  disableMode = (mode: IRootState['mode']): void => {
-    if (this.switches[mode] && this.switches[mode].stop) this.switches[mode].stop();
-  };
-
-  onClick = (e): void => {
-    if (e.originalEvent.which === 3) return; // skip right / middle click
-    if (this.clickHandlers[this.mode]) this.clickHandlers[this.mode](e);
-  };
-
-  lockMapClicks = (lock: boolean): void => {
-    if (lock) {
-      this.map.map.removeEventListener('mouseup', this.onClick);
-      this.map.map.addEventListener('mouseup', this.unlockMapClicks);
-    } else {
-      this.map.map.removeEventListener('mouseup', this.unlockMapClicks);
-      this.map.map.addEventListener('mouseup', this.onClick);
-    }
-  };
-
-  unlockMapClicks = (): void => {
-    this.lockMapClicks(false);
-  };
-
-  routerSetStart = (): void => {
-    const { latlngs } = this.poly;
-
-    if (!latlngs || !latlngs.length) return;
-
-    this.router.startFrom(latlngs[latlngs.length - 1]);
-  };
-
-  routerMoveStart = (): void => {
-    const { _latlngs } = this.poly.poly;
-
-    if (_latlngs) this.router.moveStart(_latlngs[_latlngs.length - 1]);
-  };
-
-  pushPolyPoints = (latlngs: Array<ILatLng>): void => {
-    this.poly.pushPoints(latlngs);
-  };
-
-  clearSticker = (): void => {
-    if (this.activeSticker) {
-      this.setActiveSticker(null);
-    } else {
-      this.setMode(MODES.NONE);
-    }
-  };
-
-  clearAll = (): void => {
-    this.poly.clearAll();
-    this.router.clearAll();
-    this.stickers.clearAll();
-
-    this.setIsEmpty(true);
-  };
-
-  setData = ({
-    route = [],
-    stickers = [],
-    owner,
-    title,
-    address,
-    provider = DEFAULT_PROVIDER,
-    logo = DEFAULT_LOGO,
-    is_public,
-    is_published,
-    description,
-  }: Partial<IEditor['initialData']>): void => {
-    this.mapSetTitle(title || '');
-    const { id } = this.getUser();
-
-    if (address && id && owner && id === owner) {
-      this.mapSetAddress(address);
-    }
-
-    if (route) this.poly.setPoints(route);
-
-    this.stickers.clearAll();
-
-    if (stickers) {
-      stickers.map(sticker =>
-        sticker.set && STICKERS[sticker.set].url &&
-          this.stickers.createSticker({
-            latlng: sticker.latlng,
-            angle: sticker.angle,
-            sticker: sticker.sticker,
-            set: sticker.set,
-            text: sticker.text,
-          })
-      );
-    }
-
-    this.mapSetPublic(is_public);
-    this.setStarred(is_published);
-    this.mapSetDescription(description);
-
-    this.mapSetLogo((logo && LOGOS[DEFAULT_LOGO] && logo) || DEFAULT_LOGO);
-    this.setProvider((provider && PROVIDERS[provider] && provider) || DEFAULT_PROVIDER);
-
-    if (owner) this.owner = owner;
-  };
-
-  fitDrawing = (): void => {
-    if (!this.poly.isEmpty) {
-      const poly_bounds = this.poly.poly.getBounds();
-
-      if (poly_bounds && Object.values(poly_bounds).length) {
-        this.map.map.fitBounds(poly_bounds);
-        return;
-      }
-    }
-
-    if (!this.stickers.isEmpty) {
-      const stickers_bounds = this.stickers.layer.getBounds();
-
-      if (stickers_bounds && Object.values(stickers_bounds).length) {
-        this.map.map.fitBounds(stickers_bounds);
-        return;
-      }
-    }
-
-    // no bounds to fit. better do something later
-  };
-
-  setInitialData = (): void => {
-    const { path } = getUrlData();
-    const { id } = this.getUser();
-    const { is_public, logo, is_published , description} = this.getState();
-    const { route, stickers, provider } = this.dumpData();
-
-    this.initialData = {
-      version: 2,
-      title: this.getTitle(),
-      owner: this.owner,
-      address: (this.owner === id ? path : null),
-      path,
-      route,
-      stickers,
-      provider,
-      is_public,
-      logo,
-      is_published,
-      description,
-    };
-  };
-
-  startEditing = (): void => {
-    const { id } = this.getUser();
-
-    this.setInitialData();
-    this.owner = id;
-
-    this.poly.enableEditor();
-    this.stickers.startEditing();
-  };
-
-  stopEditing = (): void => {
-    this.poly.poly.editor.disable();
-    this.stickers.stopEditing();
-    this.router.clearAll();
-  };
-
-  cancelEditing = (): void => {
-    if (this.hasEmptyHistory) {
-      this.clearAll();
-      this.startEditing();
-      this.clearChanged();
-      return;
-    } else {
-      this.setData(this.initialData);
-    }
-
-    this.stopEditing();
-    this.clearChanged();
-  };
-
-  dumpData = () => ({
-    route: this.poly.dumpData(),
-    stickers: this.stickers.dumpData(),
-    provider: this.getProvider(),
-  });
-
-  setProvider: typeof mapSetProvider = provider => store.dispatch(mapSetProvider(provider));
-
-  get isEmpty(): boolean {
-    const { route, stickers } = this.dumpData();
-
-    return (!route || route.length <= 1) && (!stickers || stickers.length <= 0);
-  }
-
-  get hasEmptyHistory(): boolean {
-    const { route, stickers } = this.initialData;
-
-    return (!route || route.length < 1) && (!stickers || stickers.length <= 0);
-  };
-}
-
-export const editor = new Editor();
-
-// for debug purposes
-declare let window: any;
-window.editor = editor;
diff --git a/src/_modules/InteractivePoly.ts b/src/_modules/InteractivePoly.ts
deleted file mode 100644
index 2d021ab..0000000
--- a/src/_modules/InteractivePoly.ts
+++ /dev/null
@@ -1,606 +0,0 @@
-/*
-  done IMPORTANT: select closest point on drag instead of first
-  done add touch hint poly
-  done approx radius for dragFindNearest
-*/
-
-import {
-  LatLngExpression,
-  Marker,
-  Polyline,
-  PolylineOptions,
-  marker,
-  divIcon,
-  LayerGroup,
-  LatLng,
-  LeafletMouseEvent,
-  latLng,
-  LatLngLiteral
-} from "leaflet";
-
-import { distKmHaversine, distToSegment, getPolyLength, pointInArea } from "~/utils/geom";
-
-interface InteractivePolylineOptions extends PolylineOptions {
-  maxMarkers?: number;
-  constraintsStyle?: PolylineOptions;
-  kmMarksEnabled?: boolean;
-  kmMarksStep?: number;
-}
-
-export class InteractivePoly extends Polyline {
-  constructor(
-    latlngs: LatLngExpression[] | LatLngExpression[][],
-    options?: InteractivePolylineOptions
-  ) {
-    super(latlngs, options);
-
-    this.constraintsStyle = {
-      ...this.constraintsStyle,
-      ...options.constraintsStyle
-    };
-    this.maxMarkers = options.maxMarkers || this.maxMarkers;
-
-    this.constrLine = new Polyline([], this.constraintsStyle);
-
-    this.startDragHinting();
-  }
-
-  updateTouchHinter = ({ latlngs }: { latlngs: LatLngLiteral[] }): void => {
-    this.touchHinter.setLatLngs(latlngs);
-  };
-
-  setPoints = (latlngs: LatLng[], emitEvent = false) => {
-    this.setLatLngs(latlngs);
-    this.recreateMarkers();
-    this.recalcDistance();
-    this.touchHinter.setLatLngs(latlngs);
-
-    if (emitEvent) {
-      this.fire("latlngschange", { latlngs });
-    }
-  };
-
-  createHintMarker = (latlng: LatLng): Marker =>
-    marker(latlng, {
-      draggable: false,
-      icon: divIcon({
-        className: "leaflet-vertex-drag-helper",
-        iconSize: [11, 11],
-        iconAnchor: [6, 6]
-      })
-    });
-
-  createMarker = (latlng: LatLng): Marker =>
-    marker(latlng, {
-      draggable: true,
-      icon: divIcon({
-        className: "leaflet-vertex-icon",
-        iconSize: [11, 11],
-        iconAnchor: [6, 6]
-      })
-    })
-      .on("contextmenu", this.dropMarker)
-      .on("drag", this.onMarkerDrag)
-      .on("dragstart", this.onMarkerDragStart)
-      .on("dragend", this.onMarkerDragEnd)
-      .addTo(this.markerLayer);
-
-  recreateMarkers = () => {
-    this.clearAllMarkers();
-    const latlngs = this.getLatLngs();
-
-    if (!latlngs || latlngs.length === 0) return;
-
-    latlngs.forEach(latlng => this.markers.push(this.createMarker(latlng)));
-
-    this.updateMarkers();
-  };
-
-  clearAllMarkers = (): void => {
-    this.markerLayer.clearLayers();
-    this.markers = [];
-  };
-
-  updateMarkers = (): void => {
-    this.showVisibleMarkers();
-  };
-
-  showAllMarkers = (): void => {
-    if (!this.is_editing) return;
-    if (this._map.hasLayer(this.markerLayer)) return;
-
-    this._map.addLayer(this.markerLayer);
-    this.fire("allvertexshow");
-  };
-
-  hideAllMarkers = (): void => {
-    if (!this._map.hasLayer(this.markerLayer)) return;
-
-    this._map.removeLayer(this.markerLayer);
-    this.fire("allvertexhide");
-  };
-
-  showVisibleMarkers = (): void => {
-    if (!this.is_editing) return;
-
-    const northEast = this._map.getBounds().getNorthEast();
-    const southWest = this._map.getBounds().getSouthWest();
-
-    const { visible, hidden } = this.markers.reduce(
-      (obj, marker) => {
-        const { lat, lng } = marker.getLatLng();
-        const is_hidden =
-          lat > northEast.lat ||
-          lng > northEast.lng ||
-          lat < southWest.lat ||
-          lng < southWest.lng;
-
-        return is_hidden
-          ? { ...obj, hidden: [...obj.hidden, marker] }
-          : { ...obj, visible: [...obj.visible, marker] };
-      },
-      { visible: [], hidden: [] }
-    );
-
-    if (visible.length > this.maxMarkers) return this.hideAllMarkers();
-
-    this.showAllMarkers();
-
-    visible.forEach(marker => {
-      if (!this.markerLayer.hasLayer(marker)) this.markerLayer.addLayer(marker);
-    });
-
-    hidden.forEach(marker => {
-      if (this.markerLayer.hasLayer(marker))
-        this.markerLayer.removeLayer(marker);
-    });
-  };
-
-  editor = {
-    disable: () => {
-      this.hideAllMarkers();
-      this.is_editing = false;
-      this.stopDragHinting();
-      this.stopDrawing();
-      this.touchHinter.removeFrom(this._map);
-      this.fire("editordisable");
-    },
-    enable: () => {
-      this.is_editing = true;
-      this.showVisibleMarkers();
-      this.startDragHinting();
-      this.touchHinter.addTo(this._map);
-
-      this.fire("editorenable");
-    },
-    continue: () => {
-      this.is_drawing = true;
-      this.drawing_direction = "forward";
-      this.startDrawing();
-    },
-    prepend: () => {
-      this.is_drawing = true;
-      this.drawing_direction = "backward";
-      this.startDrawing();
-    }
-  };
-
-  moveDragHint = ({ latlng }: LeafletMouseEvent): void => {
-    this.hintMarker.setLatLng(latlng);
-  };
-
-  hideDragHint = (): void => {
-    if (this._map.hasLayer(this.hintMarker))
-      this._map.removeLayer(this.hintMarker);
-  };
-
-  showDragHint = (): void => {
-    this._map.addLayer(this.hintMarker);
-  };
-
-  startDragHinting = (): void => {
-    this.touchHinter.on("mousemove", this.moveDragHint);
-    this.touchHinter.on("mousedown", this.startDragHintMove);
-    this.touchHinter.on("mouseover", this.showDragHint);
-    this.touchHinter.on("mouseout", this.hideDragHint);
-  };
-
-  stopDragHinting = (): void => {
-    this.touchHinter.off("mousemove", this.moveDragHint);
-    this.touchHinter.off("mousedown", this.startDragHintMove);
-    this.touchHinter.off("mouseover", this.showDragHint);
-    this.touchHinter.off("mouseout", this.hideDragHint);
-  };
-
-  startDragHintMove = (event: LeafletMouseEvent): void => {
-    event.originalEvent.stopPropagation();
-    event.originalEvent.preventDefault();
-
-    if (this.is_drawing) {
-      this.stopDrawing();
-      this.is_drawing = true;
-    }
-
-    const prev = this.dragHintFindNearest(event.latlng);
-
-    if (prev < 0) return;
-
-    this.hint_prev_marker = prev;
-
-    this.constrLine.setLatLngs([]).addTo(this._map);
-
-    this._map.dragging.disable();
-
-    this.is_dragging = true;
-
-    this._map.on("mousemove", this.dragHintMove);
-    this._map.on("mouseup", this.dragHintAddMarker);
-    this._map.on("mouseout", this.stopDragHintMove);
-  };
-
-  stopDragHintMove = (): void => {
-    this._map.dragging.enable();
-
-    this.constrLine.removeFrom(this._map);
-
-    this._map.off("mousemove", this.dragHintMove);
-    this._map.off("mouseup", this.dragHintAddMarker);
-    this._map.off("mouseout", this.stopDragHintMove);
-
-    if (this.is_drawing) this.startDrawing();
-
-    setTimeout(() => {
-      this.is_dragging = false;
-    }, 0);
-  };
-
-  dragHintAddMarker = ({ latlng }: LeafletMouseEvent): void => {
-    this.dragHintChangeDistance(this.hint_prev_marker, latlng);
-
-    this.markers.splice(
-      this.hint_prev_marker + 1,
-      0,
-      this.createMarker(latlng)
-    );
-    this.insertLatLng(latlng, this.hint_prev_marker + 1);
-    this.hideDragHint();
-    this.stopDragHintMove();
-  };
-
-  dragHintChangeDistance = (index: number, current: LatLngLiteral): void => {
-    const prev = this.markers[index];
-    const next = this.markers[index + 1];
-
-    const initial_distance = distKmHaversine(prev.getLatLng(), next.getLatLng());
-
-    const current_distance =
-      ((prev && distKmHaversine(prev.getLatLng(), current)) || 0) +
-      ((next && distKmHaversine(next.getLatLng(), current)) || 0);
-
-    this.distance += current_distance - initial_distance;
-
-    this.fire("distancechange", { distance: this.distance });
-  };
-
-  dragHintFindNearest = (latlng: LatLng): any => {
-    const latlngs = this.getLatLngs() as LatLng[];
-
-    const neighbours = latlngs
-      .filter((current, index) => {
-        const next = latlngs[index + 1] as LatLng;
-
-        return next && pointInArea(current, next, latlng);
-      })
-      .map(el => latlngs.indexOf(el))
-      .sort(
-        (a, b) =>
-          distToSegment(latlngs[a], latlngs[a + 1], latlng) -
-          distToSegment(latlngs[b], latlngs[b + 1], latlng)
-      );
-
-    return neighbours.length > 0 ? neighbours[0] : -1;
-  };
-
-  dragHintMove = (event: LeafletMouseEvent): void => {
-    event.originalEvent.stopPropagation();
-    event.originalEvent.preventDefault();
-
-    this.setConstraints([
-      this.markers[this.hint_prev_marker].getLatLng(),
-      event.latlng,
-      this.markers[this.hint_prev_marker + 1].getLatLng()
-    ]);
-  };
-
-  onMarkerDrag = ({ target }: { target: Marker }) => {
-    const coords = new Array(0)
-      .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.setConstraints(coords);
-
-    this.fire("vertexdrag", { index: this.vertex_index, vertex: target });
-  };
-
-  onMarkerDragStart = ({ target }: { target: Marker }) => {
-    if (this.is_drawing) {
-      this.stopDrawing();
-      this.is_drawing = true;
-    }
-
-    if (this.is_dragging) this.stopDragHintMove();
-
-    this.vertex_index = this.markers.indexOf(target);
-
-    this.is_dragging = true;
-    this.constrLine.addTo(this._map);
-
-    this.fire("vertexdragstart", { index: this.vertex_index, vertex: target });
-  };
-
-  onMarkerDragEnd = ({ target }: { target: Marker }): void => {
-    const latlngs = this.getLatLngs() as LatLngLiteral[];
-    this.markerDragChangeDistance(
-      this.vertex_index,
-      latlngs[this.vertex_index],
-      target.getLatLng()
-    );
-
-    this.replaceLatlng(target.getLatLng(), this.vertex_index);
-
-    this.is_dragging = false;
-    this.constrLine.removeFrom(this._map);
-
-    this.vertex_index = null;
-
-    if (this.is_drawing) this.startDrawing();
-
-    this.fire("vertexdragend", { index: this.vertex_index, vertex: target });
-  };
-
-  markerDragChangeDistance = (
-    index: number,
-    initial: LatLngLiteral,
-    current: LatLngLiteral
-  ): void => {
-    const prev = index > 0 ? this.markers[index - 1] : null;
-    const next =
-      index <= this.markers.length + 1 ? this.markers[index + 1] : null;
-
-    const initial_distance =
-      ((prev && distKmHaversine(prev.getLatLng(), initial)) || 0) +
-      ((next && distKmHaversine(next.getLatLng(), initial)) || 0);
-
-    const current_distance =
-      ((prev && distKmHaversine(prev.getLatLng(), current)) || 0) +
-      ((next && distKmHaversine(next.getLatLng(), current)) || 0);
-
-    this.distance += current_distance - initial_distance;
-
-    this.fire("distancechange", { distance: this.distance });
-  };
-
-  startDrawing = (): void => {
-    this.is_drawing = true;
-    this.setConstraints([]);
-    this.constrLine.addTo(this._map);
-    this._map.on("mousemove", this.onDrawingMove);
-    this._map.on("click", this.onDrawingClick);
-  };
-
-  stopDrawing = (): void => {
-    this.constrLine.removeFrom(this._map);
-    this._map.off("mousemove", this.onDrawingMove);
-    this._map.off("click", this.onDrawingClick);
-    this.is_drawing = false;
-  };
-
-  onDrawingMove = ({ latlng }: LeafletMouseEvent): void => {
-    if (this.markers.length === 0) {
-      this.setConstraints([]);
-      return;
-    }
-
-    if (!this._map.hasLayer(this.constrLine))
-      this._map.addLayer(this.constrLine);
-
-    const marker =
-      this.drawing_direction === "forward"
-        ? this.markers[this.markers.length - 1]
-        : this.markers[0];
-
-    this.setConstraints([latlng, marker.getLatLng()]);
-  };
-
-  onDrawingClick = (event: LeafletMouseEvent): void => {
-    if (this.is_dragging) return;
-
-    const { latlng } = event;
-
-    this.stopDrawing();
-
-    const latlngs = this.getLatLngs() as any[];
-
-    this.drawingChangeDistance(latlng);
-
-    if (this.drawing_direction === "forward") {
-      latlngs.push(latlng);
-      this.markers.push(this.createMarker(latlng));
-    } else {
-      latlngs.unshift(latlng);
-      this.markers.unshift(this.createMarker(latlng));
-    }
-
-    this.setLatLngs(latlngs);
-    this.fire("latlngschange", { latlngs });
-    this.showVisibleMarkers();
-    this.startDrawing();
-  };
-
-  drawingChangeDistance = (latlng: LatLngLiteral): void => {
-    const latlngs = this.getLatLngs() as LatLngLiteral[];
-
-    if (latlngs.length < 1) {
-      this.distance = 0;
-      this.fire("distancechange", { distance: this.distance });
-      return;
-    }
-
-    const point =
-      this.drawing_direction === "forward"
-        ? latlngs[latlngs.length - 1]
-        : latlngs[0];
-
-    this.distance += distKmHaversine(point, latlng);
-    this.fire("distancechange", { distance: this.distance });
-  };
-
-  replaceLatlng = (latlng: LatLng, index: number): void => {
-    const latlngs = this.getLatLngs() as LatLngLiteral[];
-    latlngs.splice(index, 1, latlng);
-    this.setLatLngs(latlngs);
-    this.fire("latlngschange", { latlngs });
-  };
-
-  insertLatLng = (latlng, index): void => {
-    const latlngs = this.getLatLngs();
-    latlngs.splice(index, 0, latlng);
-    this.setLatLngs(latlngs);
-    this.fire("latlngschange", { latlngs });
-  };
-
-  setConstraints = (coords: LatLng[]) => {
-    this.constrLine.setLatLngs(coords);
-  };
-
-  dropMarker = ({ target }: LeafletMouseEvent): void => {
-    const index = this.markers.indexOf(target);
-    const latlngs = this.getLatLngs();
-
-    if (typeof index === "undefined" || latlngs.length <= 2) return;
-
-    this.dropMarkerDistanceChange(index);
-    this._map.removeLayer(this.markers[index]);
-    this.markers.splice(index, 1);
-    latlngs.splice(index, 1);
-
-    this.setLatLngs(latlngs);
-    this.fire("latlngschange", { latlngs });
-  };
-
-  dropMarkerDistanceChange = (index: number): void => {
-    const latlngs = this.getLatLngs() as LatLngLiteral[];
-
-    const prev = index > 0 ? latlngs[index - 1] : null;
-    const current = latlngs[index];
-    const next = index <= latlngs.length + 1 ? latlngs[index + 1] : null;
-
-    const initial_distance =
-      ((prev && distKmHaversine(prev, current)) || 0) +
-      ((next && distKmHaversine(next, current)) || 0);
-
-    const current_distance = (prev && next && distKmHaversine(prev, next)) || 0;
-
-    this.distance += current_distance - initial_distance;
-
-    this.fire("distancechange", { distance: this.distance });
-  };
-
-  recalcDistance = () => {
-    const latlngs = this.getLatLngs() as LatLngLiteral[];
-    this.distance = getPolyLength(latlngs);
-
-    this.fire("distancechange", { distance: this.distance });
-  };
-
-  markers: Marker[] = [];
-  maxMarkers: InteractivePolylineOptions["maxMarkers"] = 2;
-  markerLayer: LayerGroup = new LayerGroup();
-
-  constraintsStyle: InteractivePolylineOptions["constraintsStyle"] = {
-    weight: 6,
-    color: "red",
-    dashArray: "10, 12",
-    opacity: 0.5,
-    interactive: false
-  };
-
-  touchHinter: Polyline = new Polyline([], {
-    weight: 24,
-    smoothFactor: 3,
-    className: "touch-hinter-poly"
-  });
-
-  hintMarker: Marker = this.createHintMarker(latLng({ lat: 0, lng: 0 }));
-
-  constrLine: Polyline;
-
-  is_editing: boolean = true;
-  is_dragging: boolean = false;
-  is_drawing: boolean = false;
-
-  drawing_direction: "forward" | "backward" = "forward";
-  vertex_index?: number = null;
-
-  hint_prev_marker: number = null;
-  distance: number = 0;
-}
-
-InteractivePoly.addInitHook(function() {
-  this.once("add", event => {
-    if (event.target instanceof InteractivePoly) {
-      this.map = event.target._map;
-
-      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.on("latlngschange", this.updateTouchHinter);
-
-      if (window.innerWidth < 768) {
-        this.touchHinter.setStyle({ weight: 32 });
-      }
-    }
-  });
-
-  this.once("remove", event => {
-    if (event.target instanceof InteractivePoly) {
-      this.markerLayer.removeFrom(this._map);
-      this.hintMarker.removeFrom(this._map);
-      this.constrLine.removeFrom(this._map);
-      this.touchHinter.removeFrom(this._map);
-
-      this.map.off("moveend", this.updateMarkers);
-    }
-  });
-});
-
-// export const InteractivePoly = Component;
-/*
-  events:
-  vertexdragstart,
-  vertexdragend,
-  vertexdrag,
-
-  allvertexhide
-  allvertexshow
-
-  editordisable
-  editorenable
-
-  distancechange
-
-  latlngschange
- */
diff --git a/src/_modules/KmMarks.ts b/src/_modules/KmMarks.ts
deleted file mode 100644
index d44aa70..0000000
--- a/src/_modules/KmMarks.ts
+++ /dev/null
@@ -1,156 +0,0 @@
-import { divIcon, LatLngLiteral, Layer, LayerGroup, Map, marker, Marker } from "leaflet";
-import { arrowClusterIcon, createArrow } from "~/utils/arrow";
-import { MarkerClusterGroup } from 'leaflet.markercluster/dist/leaflet.markercluster-src.js';
-import { allwaysPositiveAngleDeg, angleBetweenPoints, distKmHaversine } from "~/utils/geom";
-import classNames from 'classnames';
-
-interface KmMarksOptions {
-  showMiddleMarkers: boolean,
-  showEndMarker: boolean,
-  kmMarksStep: number,
-}
-
-class Component extends LayerGroup {
-  constructor(latlngs?: LatLngLiteral[], options?: KmMarksOptions){
-    super();
-
-    this.options = {
-      showMiddleMarkers: true,
-      showEndMarker: true,
-      kmMarksStep: 10,
-      ...(options || {}),
-    } as KmMarksOptions;
-  }
-
-  setLatLngs = (latlngs: LatLngLiteral[]): void => {
-    if (!this.map) return;
-    this.marksLayer.clearLayers();
-    this.endMarker.clearLayers();
-
-    this.distance = 0;
-
-    if (latlngs.length <= 1) return;
-
-    if (this.options.showMiddleMarkers) this.drawMiddleMarkers(latlngs);
-    if (this.options.showEndMarker) this.drawEndMarker(latlngs);
-  };
-
-  drawMiddleMarkers = (latlngs: LatLngLiteral[]) => {
-    const kmMarks = {};
-    let last_km_mark = 0;
-
-    this.distance = latlngs.reduce((dist, current, index) => {
-      if (index >= latlngs.length - 1) return dist;
-
-      const next = latlngs[index + 1];
-      const diff = distKmHaversine(current, next);
-      const sum = dist + diff;
-      const rounded = Math.floor(sum / this.options.kmMarksStep) * this.options.kmMarksStep;
-      const count = Math.floor((rounded - last_km_mark) / this.options.kmMarksStep);
-
-      if (rounded > last_km_mark) {
-        const angle = angleBetweenPoints(
-          this.map.latLngToContainerPoint(current),
-          this.map.latLngToContainerPoint(next),
-        );
-
-        for (let i = 1; i <= count; i += 1) {
-          const step = last_km_mark + (i * this.options.kmMarksStep);
-          const shift = (step - dist) / diff;
-
-          const coords = {
-            lat: current.lat - ((current.lat - next.lat) * shift),
-            lng: current.lng - ((current.lng - next.lng) * shift),
-          };
-
-          kmMarks[step] = { ...coords, angle };
-          this.marksLayer.addLayer(this.createMiddleMarker(coords, angle, step));
-        }
-
-        last_km_mark = rounded;
-      }
-
-      return sum;
-    }, 0);
-
-  };
-
-  createMiddleMarker = (latlng: LatLngLiteral, angle: number, distance: number): Marker => marker(latlng, {
-    draggable: false,
-    interactive: false,
-    icon: divIcon({
-      html: `
-        <div class="leaflet-km-dist" style="transform: translate(-50%, -50%) rotate(${allwaysPositiveAngleDeg(angle)}deg);">
-          ${distance}
-        </div>
-      `,
-      className: 'leaflet-km-marker',
-      iconSize: [11, 11],
-      iconAnchor: [6, 6]
-    })
-  });
-
-  createEndMarker = (latlng: LatLngLiteral, angle: number, distance: number): Marker => marker(latlng, {
-    draggable: false,
-    interactive: false,
-    icon: divIcon({
-      html: `
-        <div class="leaflet-km-dist">
-          ${parseFloat(distance.toFixed(1))}
-        </div>
-      `,
-      className: classNames('leaflet-km-marker end-marker', { right: (angle > -90 && angle < 90) }),
-      iconSize: [11, 11],
-      iconAnchor: [6, 6]
-    }),
-    zIndexOffset: -100,
-  });
-
-  drawEndMarker = (latlngs: LatLngLiteral[]): void => {
-    this.endMarker.clearLayers();
-
-    const current = latlngs[latlngs.length - 2];
-    const next = latlngs[latlngs.length - 1
-      ];
-
-    const angle = angleBetweenPoints(
-      this.map.latLngToContainerPoint(current),
-      this.map.latLngToContainerPoint(next),
-    );
-
-    this.endMarker.addLayer(this.createEndMarker(next, angle, this.distance));
-  };
-
-  options: KmMarksOptions;
-  map: Map;
-  marksLayer: MarkerClusterGroup = new MarkerClusterGroup({
-    spiderfyOnMaxZoom: false,
-    showCoverageOnHover: false,
-    zoomToBoundsOnClick: false,
-    animate: false,
-    maxClusterRadius: 120,
-    iconCreateFunction: arrowClusterIcon,
-  });
-  endMarker: LayerGroup = new LayerGroup();
-  distance: number = 0;
-}
-
-
-Component.addInitHook(function () {
-  this.once('add', (event) => {
-    if (event.target instanceof KmMarks) {
-      this.map = event.target._map;
-      this.marksLayer.addTo(this.map);
-      this.endMarker.addTo(this.map);
-    }
-  });
-
-  this.once('remove', (event) => {
-    if (event.target instanceof KmMarks) {
-      this.marksLayer.removeFrom(this.map);
-      this.endMarker.removeFrom(this.map);
-    }
-  });
-});
-
-export const KmMarks = Component;
diff --git a/src/_modules/Map.ts b/src/_modules/Map.ts
deleted file mode 100644
index 25ceece..0000000
--- a/src/_modules/Map.ts
+++ /dev/null
@@ -1,36 +0,0 @@
-import {
-  Map as MapInterface,
-  map,
-  tileLayer,
-  TileLayer,
-} from 'leaflet';
-
-import 'leaflet/dist/leaflet.css';
-import { PROVIDER } from '~/config/frontend';
-import { DEFAULT_PROVIDER, PROVIDERS } from '~/constants/providers';
-
-interface Props {
-  container: string
-}
-
-export class Map {
-  constructor({ container }: Props) {
-    this.map = map(container).setView([55.0153275, 82.9071235], 13);
-    // todo: change coords?
-
-    this.tileLayer.addTo(this.map);
-  }
-
-  map: MapInterface;
-  tileLayer: TileLayer = tileLayer(PROVIDER.url, {
-    attribution: 'Независимое Велосообщество',
-    maxNativeZoom: 18,
-    maxZoom: 18,
-  });
-
-  setProvider = (provider: string): void => {
-    const { url } = (provider && PROVIDERS[provider] && PROVIDERS[provider]) || PROVIDERS[DEFAULT_PROVIDER];
-
-    this.tileLayer.setUrl(url);
-  }
-}
diff --git a/src/_modules/Poly.ts b/src/_modules/Poly.ts
deleted file mode 100644
index fa10316..0000000
--- a/src/_modules/Poly.ts
+++ /dev/null
@@ -1,119 +0,0 @@
-import { Map, LatLng } from 'leaflet';
-import { simplify } from '~/utils/simplify';
-import { editor, Editor } from "~/modules/Editor";
-import { ILatLng } from "~/modules/Stickers";
-import { InteractivePoly } from "~/modules/InteractivePoly";
-import { Arrows } from "~/modules/Arrows";
-import { KmMarks } from "~/modules/KmMarks";
-import { isMobile } from "~/utils/window";
-
-interface Props {
-  map: Map;
-  editor: Editor;
-  routerMoveStart: typeof editor.routerMoveStart,
-  lockMapClicks: typeof editor.lockMapClicks,
-  setDistance: typeof editor.setDistance,
-  triggerOnChange: typeof editor.triggerOnChange,
-}
-
-export class Poly {
-  constructor({
-    map, routerMoveStart, lockMapClicks, setDistance, triggerOnChange, editor,
-  }: Props) {
-    this.poly = new InteractivePoly([ ], {
-      color: 'url(#activePathGradient)',
-      weight: 6,
-      maxMarkers: isMobile() ? 20 : 100,
-      smoothFactor: 3,
-    })
-      .on('distancechange', this.onDistanceUpdate)
-      .on('allvertexhide', this.onVertexHide)
-      .on('allvertexshow', this.onVertexShow)
-      .on('latlngschange', this.updateMarks)
-
-    this.poly.addTo(map);
-    this.editor = editor;
-
-    this.map = map;
-
-    this.routerMoveStart = routerMoveStart;
-    this.setDistance = setDistance;
-    this.triggerOnChange = triggerOnChange;
-    this.lockMapClicks = lockMapClicks;
-
-    this.arrows = new Arrows({}).addTo(map);
-    this.kmMarks = new KmMarks().addTo(map);
-  }
-
-  onDistanceUpdate = (event) => {
-    const { distance } = event as { distance: number };
-    this.setDistance(parseFloat(distance.toFixed(2)));
-  };
-
-  onVertexHide = (): void => this.editor.setMarkersShown(false);
-  onVertexShow = (): void => this.editor.setMarkersShown(true);
-
-  updateMarks = event => {
-    // this.editor.setChanged(true);
-    this.editor.triggerOnChange();
-
-    const { latlngs } = event;
-    this.arrows.setLatLngs(latlngs);
-    this.kmMarks.setLatLngs(latlngs);
-  };
-
-  continue = (): void => {
-    this.poly.editor.continue();
-  };
-
-  stop = (): void => {
-    this.poly.stopDrawing();
-  };
-
-  enableEditor = (): void => {
-    this.poly.editor.enable();
-  };
-
-  setPoints = (latlngs: Array<ILatLng>): void => {
-    if (!latlngs || latlngs.length <= 1) return;
-    this.poly.setPoints(latlngs);
-  };
-
-  pushPoints = (latlngs: Array<ILatLng>): void => {
-    const { map } = this;
-    const simplified = simplify({ map, latlngs });
-    const summary = [
-      ...this.poly.getLatLngs(),
-      ...simplified,
-    ];
-
-    this.poly.setPoints(summary);
-  };
-
-  clearAll = (): void => {
-    this.poly.setPoints([]);
-  };
-
-  dumpData = (): Array<LatLng> => this.latlngs;
-
-  get latlngs(): Array<LatLng> {
-    return (
-      this.poly && this.poly.getLatLngs().length
-        && this.poly.getLatLngs().map(el => ({ ...el }))) || [];
-  }
-
-  get isEmpty(): boolean {
-    return (!this.latlngs || Object.values(this.latlngs).length <= 0);
-  }
-
-  arrows;
-  poly;
-  kmMarks;
-
-  editor: Props['editor'];
-  map: Props['map'];
-  routerMoveStart: Props['routerMoveStart'];
-  setDistance: Props['setDistance'];
-  triggerOnChange: Props['triggerOnChange'];
-  lockMapClicks: Props['lockMapClicks'];
-}
diff --git a/src/_modules/Router.ts b/src/_modules/Router.ts
deleted file mode 100644
index a8cf47a..0000000
--- a/src/_modules/Router.ts
+++ /dev/null
@@ -1,174 +0,0 @@
-import { Map, Marker } from 'leaflet';
-import * as Routing from 'leaflet-routing-machine/src/index';
-import { CLIENT } from '~/config/frontend';
-import { DomMarker } from '~/utils/DomMarker';
-import { editor } from "~/modules/Editor";
-
-interface ILatLng {
-  lat: number, lng: number
-}
-
-interface IWaypoint {
-  latLng: ILatLng
-}
-
-interface Props {
-  setIsRouting: typeof editor.setIsRouting,
-  map: Map,
-  setRouterPoints: typeof editor.setRouterPoints,
-  pushPolyPoints: typeof editor.pushPolyPoints,
-  lockMapClicks: typeof editor.lockMapClicks;
-}
-
-export class Router {
-  constructor({
-    map, lockMapClicks, setRouterPoints, pushPolyPoints, setIsRouting,
-  }: Props) {
-    this.waypoints = [];
-    this.lockMapClicks = lockMapClicks;
-    this.setRouterPoints = setRouterPoints;
-    this.pushPolyPoints = pushPolyPoints;
-    this.setIsRouting = setIsRouting;
-
-    const routeLine = r => Routing.line(r, {
-      styles: [
-        { color: 'white', opacity: 0.8, weight: 6 },
-        { color: '#4597d0', opacity: 1, weight: 4, dashArray: '15,10' }
-      ],
-      addWaypoints: true,
-    }).on('linetouched', this.lockPropagations);
-
-    this.router = Routing.control({
-      serviceUrl: CLIENT.OSRM_URL,
-      profile: CLIENT.OSRM_PROFILE,
-      fitSelectedRoutes: false,
-      showAlternatives: false,
-      routeLine,
-      altLineOptions: {
-        styles: [{ color: '#4597d0', opacity: 1, weight: 3 }]
-      },
-      show: false,
-      plan: Routing.plan([], {
-        createMarker: (i, wp) => new Marker(wp.latLng, {
-          draggable: true,
-          icon: this.createWaypointMarker(),
-        }),
-        routeWhileDragging: false,
-      }),
-      routeWhileDragging: false,
-      routingOptions: {
-        geometryOnly: false,
-      },
-      useHints: false,
-    })
-      .on('routingstart', this.showSpinner)
-      .on('routesfound routingerror routeselected routingzoomend', this.hideSpinner)
-      .on('waypointschanged', this.updateWaypointsCount);
-
-    this.router.addTo(map);
-  }
-
-  showSpinner = () => {
-    this.setIsRouting(true);
-  };
-
-  hideSpinner = () => {
-    this.setIsRouting(false);
-  };
-
-  pushWaypointOnClick = ({ latlng: { lat, lng } }: { latlng: ILatLng }): void => {
-    const waypoints = this.router.getWaypoints().filter(({ latLng }) => !!latLng);
-    this.router.setWaypoints([...waypoints, { lat, lng }]);
-  };
-
-  createWaypointMarker = (): DomMarker => {
-    const element = document.createElement('div');
-
-    element.addEventListener('mousedown', this.lockPropagations);
-    element.addEventListener('mouseup', this.unlockPropagations);
-
-    return new DomMarker({
-      element,
-      className: 'router-waypoint',
-    });
-  };
-
-  lockPropagations = (): void => {
-    window.addEventListener('mouseup', this.unlockPropagations);
-    this.lockMapClicks(true);
-  };
-
-  unlockPropagations = (e): void => {
-    if (e && e.preventPropagations) {
-      e.preventDefault();
-      e.preventPropagations();
-    }
-
-    window.removeEventListener('mouseup', this.unlockPropagations);
-    setTimeout(() => this.lockMapClicks(false), 0);
-  };
-
-  startFrom = (latlngs: ILatLng): void => {
-    const waypoints = this.router.getWaypoints();
-
-    if (waypoints && waypoints.length) {
-      waypoints[0] = { ...latlngs };
-      this.router.setWaypoints(waypoints);
-      return;
-    }
-
-    this.router.setWaypoints([{ ...latlngs }]);
-  };
-
-  moveStart = (latlng: ILatLng): void => {
-    const waypoints = this.router.getWaypoints();
-    const { latLng }: { latLng: ILatLng } = (waypoints[0] || {});
-
-    if (!latLng || !latlng) return;
-
-    if (
-      latlng.lat.toFixed(5) === latLng.lat.toFixed(5) &&
-      latlng.lng.toFixed(5) === latLng.lng.toFixed(5)
-    ) {
-      return;
-    }
-
-    waypoints[0] = { ...latlng };
-
-    this.router.setWaypoints(waypoints);
-  };
-
-  updateWaypointsCount = (): void => {
-    const waypoints = this.router.getWaypoints().filter(({ latLng }) => !!latLng);
-    this.setRouterPoints(waypoints.length);
-  };
-
-  cancelDrawing = (): void => {
-    this.router.setWaypoints([]);
-  };
-
-  submitDrawing = (): void => {
-    const [route] = this.router._routes;
-    if (!route) return;
-
-    const { coordinates } = route;
-    this.pushPolyPoints(coordinates);
-
-    this.router.setWaypoints([]);
-
-    // UNCOMMENT THIS TO CONTINUE DRAWING
-    // const waypoints = this.router.getWaypoints().filter(({ latLng }) => !!latLng);
-    // this.router.setWaypoints(waypoints[waypoints.length - 1]);
-  };
-
-  clearAll = (): void => {
-    this.router.setWaypoints([]);
-  };
-
-  waypoints: Array<IWaypoint> = [];
-  setIsRouting: Props['setIsRouting'];
-  lockMapClicks: Props['lockMapClicks'];
-  setRouterPoints: Props['setRouterPoints'];
-  pushPolyPoints: Props['pushPolyPoints'];
-  router: Routing;
-}
diff --git a/src/_modules/Sticker.tsx b/src/_modules/Sticker.tsx
deleted file mode 100644
index ceec2c1..0000000
--- a/src/_modules/Sticker.tsx
+++ /dev/null
@@ -1,236 +0,0 @@
-import { Map, Marker, marker } from 'leaflet';
-import React from 'react';
-import { DomMarker } from '~/utils/DomMarker';
-
-import { STICKERS } from '~/constants/stickers';
-import ReactDOM from 'react-dom';
-import { StickerDesc } from '~/components/StickerDesc';
-import classnames from 'classnames';
-import { getLabelDirection } from '~/utils/geom';
-import { ILatLng } from "~/modules/Stickers";
-import { IRootState } from "~/redux/user";
-import { Editor, editor } from "~/modules/Editor";
-
-const getX = e => (
-  e.touches && e.touches.length > 0
-    ? { pageX: e.touches[0].pageX, pageY: e.touches[0].pageY }
-    : { pageX: e.pageX, pageY: e.pageY }
-);
-
-
-export interface IStickerDump {
-  latlng: ILatLng,
-  set: IRootState['activeSticker']['set'],
-  sticker: IRootState['activeSticker']['sticker'],
-  angle?: number,
-  text?: string,
-}
-
-interface Props {
-  latlng: ILatLng;
-  map: Map;
-  sticker:  IRootState['activeSticker']['sticker'];
-  set: IRootState['activeSticker']['set'];
-  angle?: number;
-  text?: string;
-  editor: Editor,
-
-  deleteSticker: (sticker: this) => void;
-
-  triggerOnChange: typeof editor.triggerOnChange;
-  lockMapClicks: typeof editor.lockMapClicks;
-}
-
-export class Sticker {
-  constructor({
-    latlng, deleteSticker, map, lockMapClicks, sticker, set, triggerOnChange, angle = 2.2, text = '', editor,
-  }: Props) {
-    this.text = text;
-    this.latlng = latlng;
-    this.angle = parseFloat(((angle && (angle % Math.PI)) || 2.2).toFixed(2));
-    this.map = map;
-    this.sticker = sticker;
-    this.set = set;
-    this.triggerOnChange = triggerOnChange;
-    this.direction = getLabelDirection(this.angle);
-    this.deleteSticker = deleteSticker;
-    this.lockMapClicks = lockMapClicks;
-    this.editor = editor;
-    this.element = document.createElement('div');
-
-    ReactDOM.render(
-      <React.Fragment>
-        <div
-          className="sticker-arrow"
-          ref={el => { this.stickerArrow = el; }}
-        />
-        <div
-          className={classnames(`sticker-label ${this.direction}`, {})}
-          ref={el => { this.stickerImage = el; }}
-        >
-          <StickerDesc value={this.text} onChange={this.setText} />
-          <div
-            className="sticker-image"
-            style={{
-              backgroundImage: `url('${STICKERS[set].url}`,
-              backgroundPosition: `${-STICKERS[set].layers[sticker].off * 72} 50%`,
-            }}
-            onMouseDown={this.onDragStart}
-            onMouseUp={this.onDragStop}
-            onTouchStart={this.onDragStart}
-            onTouchEnd={this.onDragStop}
-          />
-          <div
-            className="sticker-delete"
-            onMouseDown={this.onDelete}
-            onTouchStart={this.onDelete}
-          />
-        </div>
-      </React.Fragment>,
-      this.element
-    );
-
-    const mark = new DomMarker({
-      element: this.element,
-      className: 'sticker-container',
-    });
-
-    this.marker = marker(latlng, { icon: mark, draggable: true });
-
-    this.marker.on('add', this.updateModeOnAdd);
-
-    this.element.addEventListener('mouseup', this.onDragStop);
-    this.element.addEventListener('mouseup', this.preventPropagations);
-
-    this.element.addEventListener('touchend', this.onDragStop);
-    this.element.addEventListener('touchend', this.preventPropagations);
-
-    this.marker.on('dragend', this.triggerOnChange);
-
-    this.setAngle(this.angle);
-  }
-
-  updateModeOnAdd = () => {
-    if (this.editor.getEditing()) {
-      this.startEditing();
-    } else {
-      this.stopEditing();
-    }
-  };
-
-  setText = (text: Props['text']): void => {
-    this.text = text;
-  };
-
-  onDelete = (): void => {
-    if (!this.isDragging) this.deleteSticker(this);
-  };
-
-  onDragStart = (e): void => {
-    this.preventPropagations(e);
-    this.marker.dragging.disable();
-
-    this.isDragging = true;
-
-    this.lockMapClicks(true);
-
-    window.addEventListener('mousemove', this.onDrag);
-    window.addEventListener('touchmove', this.onDrag);
-
-    window.addEventListener('mouseup', this.onDragStop);
-    window.addEventListener('touchend', this.onDragStop);
-  };
-
-  preventPropagations = (e): void => {
-    if (!e || !e.stopPropagation) return;
-
-    e.stopPropagation();
-    e.preventDefault();
-  };
-
-  onDragStop = (e): void => {
-    this.preventPropagations(e);
-    this.marker.dragging.enable();
-
-    this.triggerOnChange();
-    this.isDragging = false;
-
-    window.removeEventListener('mousemove', this.onDrag);
-    window.removeEventListener('touchmove', this.onDrag);
-
-    window.removeEventListener('mouseup', this.onDragStop);
-    window.removeEventListener('touchend', this.onDragStop);
-
-    this.lockMapClicks(false);
-  };
-
-  onDrag = (e: DragEvent): void => {
-    this.preventPropagations(e);
-    this.estimateAngle(e);
-  };
-
-  estimateAngle = (e): void => {
-    const { x, y } = this.element.getBoundingClientRect() as DOMRect;
-    const { pageX, pageY } = getX(e);
-
-    this.angle = parseFloat(Math.atan2((y - pageY), (x - pageX)).toFixed(2));
-
-    this.setAngle(this.angle);
-  };
-
-  setAngle = (angle: Props['angle']): void => {
-    if (!this.stickerImage) return;
-
-    const direction = getLabelDirection(angle);
-
-    if (direction !== this.direction) {
-      this.direction = direction;
-      this.stickerImage.className = `sticker-label ${direction}`;
-    }
-
-    const rad = 56;
-
-    const x = ((Math.cos(angle + Math.PI) * rad) - 30);
-    const y = ((Math.sin(angle + Math.PI) * rad) - 30);
-
-    this.stickerImage.style.left = String(6 + x);
-    this.stickerImage.style.top = String(6 + y);
-
-    this.stickerArrow.style.transform = `rotate(${angle + Math.PI}rad)`;
-  };
-
-  dumpData = (): IStickerDump => ({
-    angle: this.angle,
-    latlng: { ...this.marker.getLatLng() },
-    sticker: this.sticker,
-    set: this.set,
-    text: this.text,
-  });
-
-  stopEditing = (): void => {
-    this.element.className = 'sticker-container inactive';
-  };
-
-  startEditing = (): void => {
-    this.element.className = 'sticker-container';
-  };
-
-  element: HTMLDivElement = document.createElement('div');
-  stickerImage: HTMLDivElement;
-  stickerArrow: HTMLDivElement;
-  marker: Marker;
-  isDragging: boolean = false;
-  direction: string;
-  editor: Editor;
-
-  text: Props['text'];
-  latlng:  Props['latlng'];
-  angle: Props['angle'];
-  map: Props['map'];
-  sticker:  Props['sticker'];
-  set: Props['set'];
-  triggerOnChange: Props['triggerOnChange'];
-
-  deleteSticker: Props['deleteSticker'];
-  lockMapClicks: Props['lockMapClicks'];
-}
diff --git a/src/_modules/Stickers.ts b/src/_modules/Stickers.ts
deleted file mode 100644
index 4b42d74..0000000
--- a/src/_modules/Stickers.ts
+++ /dev/null
@@ -1,117 +0,0 @@
-import {FeatureGroup, LayerGroup, layerGroup, Map} from 'leaflet';
-import { IStickerDump, Sticker } from '~/modules/Sticker';
-import { MarkerClusterGroup } from 'leaflet.markercluster/dist/leaflet.markercluster-src.js';
-import { clusterIcon } from '~/utils/clusterIcon';
-import { editor, Editor } from "~/modules/Editor";
-import { STICKERS } from "~/constants/stickers";
-
-export interface ILatLng {
-  lat: number,
-  lng: number,
-}
-
-interface Props {
-  editor: Editor;
-  map: Map;
-
-  triggerOnChange: typeof editor.triggerOnChange;
-  lockMapClicks: typeof editor.lockMapClicks;
-}
-
-export class Stickers {
-  constructor({ map, lockMapClicks, triggerOnChange, editor }: Props) {
-    this.map = map;
-    this.triggerOnChange = triggerOnChange;
-    this.editor = editor;
-
-    // this.clusterLayer.addTo(map);
-    // this.clusterLayer.on('animationend', this.onSpiderify);
-
-    this.lockMapClicks = lockMapClicks;
-    this.stickers = [];
-
-    this.layer.addTo(this.map);
-  }
-
-  createSticker = ({
-    latlng, sticker, angle = 2.2, text = '', set
-  }: IStickerDump): void => {
-
-    if (!STICKERS[set] || !STICKERS[set].layers || !STICKERS[set].layers[sticker]) return;
-
-    const marker = new Sticker({
-      latlng,
-      angle,
-      deleteSticker: this.deleteStickerByReference,
-      map: this.map,
-      lockMapClicks: this.lockMapClicks,
-      sticker,
-      set,
-      triggerOnChange: this.triggerOnChange,
-      text,
-      editor: this.editor,
-    });
-
-    this.stickers.push(marker);
-
-    marker.marker.addTo(this.layer);
-
-    this.triggerOnChange();
-  };
-
-  deleteStickerByReference = (ref: Sticker): void => {
-    const index = this.stickers.indexOf(ref);
-
-    if (index < 0) return;
-
-    // this.clusterLayer.removeLayer(ref.marker);
-    this.layer.removeLayer(ref.marker);
-    this.stickers.splice(index, 1);
-
-    this.triggerOnChange();
-  };
-
-  clearAll = (): void => {
-    const target = [...this.stickers];
-    target.map(sticker => {
-      this.deleteStickerByReference(sticker);
-      return true;
-    });
-  };
-
-  dumpData = (): Array<IStickerDump> => this.stickers.map(sticker => sticker.dumpData());
-
-  startEditing = (): void => {
-    this.stickers.map(sticker => sticker.startEditing());
-  };
-
-  stopEditing = (): void => {
-    this.stickers.map(sticker => sticker.stopEditing());
-  };
-
-  get isEmpty(): boolean {
-    return !this.stickers || this.stickers.length === 0
-  };
-  // clusterLayer: LayerGroup = new LayerGroup();
-
-  // uncomment to enable clustering
-
-  // clusterLayer: MarkerClusterGroup = new MarkerClusterGroup({
-  //   spiderfyOnMaxZoom: false,
-  //   showCoverageOnHover: false,
-  //   zoomToBoundsOnClick: true,
-  //   animate: false,
-  //   maxClusterRadius: 8,
-  //   // disableClusteringAtZoom: 13,
-  //   iconCreateFunction: clusterIcon,
-  // });
-
-  editor: Props['editor'];
-  map: Props['map'];
-
-  stickers: Array<Sticker> = [];
-  layer: FeatureGroup = new FeatureGroup();
-
-  triggerOnChange: Props['triggerOnChange'];
-  lockMapClicks: Props['lockMapClicks'];
-}
diff --git a/src/components/dialogs/MapListDialog.tsx b/src/components/dialogs/MapListDialog.tsx
index 66d296d..a351192 100644
--- a/src/components/dialogs/MapListDialog.tsx
+++ b/src/components/dialogs/MapListDialog.tsx
@@ -32,20 +32,11 @@ const mapStateToProps = ({
     user: { role },
   },
 }: IState) => {
-  if (routes.filter.max >= 9999) {
-    return {
-      routes,
-      editing,
-      ready: false,
-      role,
-    };
-  }
-
   return {
     role,
     routes,
     editing,
-    ready: true,
+    ready: routes.filter.max < 9999,
   };
 };
 
diff --git a/src/components/dialogs/RouterDialog.tsx b/src/components/dialogs/RouterDialog.tsx
index 467f91a..d75c166 100644
--- a/src/components/dialogs/RouterDialog.tsx
+++ b/src/components/dialogs/RouterDialog.tsx
@@ -89,18 +89,17 @@ const mapDispatchToProps = {
   editorRouterSubmit: EDITOR_ACTIONS.editorRouterSubmit,
 };
 
-type Props = ReturnType<typeof mapStateToProps> & typeof mapDispatchToProps & { };
+type Props = ReturnType<typeof mapStateToProps> & typeof mapDispatchToProps & {};
 
 const RouterDialogUnconnected: FC<Props> = ({
   editor: {
     router: { waypoints },
-    is_routing,
   },
   editorRouterCancel,
   editorRouterSubmit,
 }) => (
   <div className="control-dialog bottom right">
-    <div className={classnames('save-loader', { active: is_routing })} />
+    <div className={classnames('save-loader')} />
 
     {!waypoints.length && noPoints({ editorRouterCancel })}
     {waypoints.length === 1 && firstPoint({ editorRouterCancel })}
diff --git a/src/components/maps/RouteRowDrop.tsx b/src/components/maps/RouteRowDrop.tsx
index e85d756..5b03e4f 100644
--- a/src/components/maps/RouteRowDrop.tsx
+++ b/src/components/maps/RouteRowDrop.tsx
@@ -1,29 +1,25 @@
 // @flow
-import React from 'react';
-import { Icon } from '~/components/panels/Icon';
-import { MapListDialog } from "~/components/dialogs/MapListDialog";
-import { Tooltip } from "~/components/panels/Tooltip";
-import { ReactElement } from "react";
+import React, { FC, memo } from 'react';
+import { MapListDialog } from '~/components/dialogs/MapListDialog';
+import { ReactElement } from 'react';
 
 interface Props {
-  address: string,
-  stopEditing: typeof MapListDialog.stopEditing,
-  dropRoute: typeof MapListDialog.dropRoute,
+  address: string;
+  stopEditing: typeof MapListDialog.stopEditing;
+  dropRoute: typeof MapListDialog.dropRoute;
 }
 
-export const RouteRowDrop = ({
-  address, stopEditing, dropRoute,
-}: Props): ReactElement<Props, null> => (
-  <div
-    className="route-row-drop"
-  >
-    <div
-      className="route-row"
-    >
+export const RouteRowDrop: FC<Props> = memo(({ address, stopEditing, dropRoute }) => (
+  <div className="route-row-drop">
+    <div className="route-row">
       <div className="button-group">
-        <div className="button" onClick={dropRoute.bind(null, address)}>Удалить</div>
-        <div className="button primary" onClick={stopEditing}>Отмена</div>
+        <div className="button" onClick={dropRoute.bind(null, address)}>
+          Удалить
+        </div>
+        <div className="button primary" onClick={stopEditing}>
+          Отмена
+        </div>
       </div>
     </div>
   </div>
-);
+));
diff --git a/src/components/panels/DistanceBar.tsx b/src/components/panels/DistanceBar.tsx
index 866537d..5c33ead 100644
--- a/src/components/panels/DistanceBar.tsx
+++ b/src/components/panels/DistanceBar.tsx
@@ -4,27 +4,24 @@ import { Icon } from '~/components/panels/Icon';
 import { connect } from 'react-redux';
 import Slider from 'rc-slider/lib/Slider';
 import { editorSetSpeed } from '~/redux/editor/actions';
-import { Tooltip } from "~/components/panels/Tooltip";
-import { isMobile } from "~/utils/window";
+import { Tooltip } from '~/components/panels/Tooltip';
+import { isMobile } from '~/utils/window';
 import { IState } from '~/redux/store';
+import pick from 'ramda/es/pick';
+import { selectEditor } from '~/redux/editor/selectors';
 
-function mapStateToProps(state) {
-  const {
-    editor: { distance, estimated, speed },
-  }: IState = state;
-
-  return { distance, estimated, speed };
-}
+const mapStateToProps = (state: IState) =>
+  pick(['distance', 'estimated', 'speed'], selectEditor(state));
 
 const mapDispatchToProps = { editorSetSpeed };
 
 type Props = ReturnType<typeof mapStateToProps> & typeof mapDispatchToProps & {};
 
 interface State {
-  dialogOpened: boolean,
+  dialogOpened: boolean;
 }
 
-class Component extends React.PureComponent<Props, State> {
+class DistanceBarUnconnected extends React.PureComponent<Props, State> {
   constructor(props) {
     super(props);
     this.state = {
@@ -36,10 +33,15 @@ class Component extends React.PureComponent<Props, State> {
   min: number = 5;
   max: number = 30;
 
-  marks: { [x: number]: string } = [...Array((Math.floor(this.max - this.min) / this.step) + 1)].reduce((obj, el, index) => ({
-    ...obj,
-    [this.min + (index * this.step)]: String(this.min + (index * this.step)),
-  }), { });
+  marks: { [x: number]: string } = [
+    ...Array(Math.floor(this.max - this.min) / this.step + 1),
+  ].reduce(
+    (obj, el, index) => ({
+      ...obj,
+      [this.min + index * this.step]: String(this.min + index * this.step),
+    }),
+    {}
+  );
 
   toggleDialog = () => {
     if (isMobile()) return;
@@ -51,10 +53,12 @@ class Component extends React.PureComponent<Props, State> {
     const {
       props: { distance, estimated, speed },
       state: { dialogOpened },
-      min, max, step, marks,
+      min,
+      max,
+      step,
+      marks,
     } = this;
 
-
     return (
       <React.Fragment>
         <div className="status-bar padded pointer tooltip-container" onClick={this.toggleDialog}>
@@ -65,8 +69,7 @@ class Component extends React.PureComponent<Props, State> {
           </span>
           <div className="desktop-only">{toHours(estimated)}</div>
         </div>
-        {
-          dialogOpened &&
+        {dialogOpened && (
           <div className="control-dialog top left" style={{ left: 0, top: 42 }}>
             <div className="helper speed-helper">
               <Slider
@@ -80,13 +83,10 @@ class Component extends React.PureComponent<Props, State> {
               />
             </div>
           </div>
-        }
+        )}
       </React.Fragment>
     );
   }
 }
 
-export const DistanceBar = connect(
-  mapStateToProps,
-  mapDispatchToProps
-)(Component);
+export const DistanceBar = connect(mapStateToProps, mapDispatchToProps)(DistanceBarUnconnected);
diff --git a/src/components/panels/EditorPanel.tsx b/src/components/panels/EditorPanel.tsx
index ef4bf4d..54382b0 100644
--- a/src/components/panels/EditorPanel.tsx
+++ b/src/components/panels/EditorPanel.tsx
@@ -4,7 +4,6 @@ import classnames from 'classnames';
 
 import { Icon } from '~/components/panels/Icon';
 import { EditorDialog } from '~/components/panels/EditorDialog';
-import { bindActionCreators } from 'redux';
 import { connect } from 'react-redux';
 import {
   editorSetMode,
@@ -16,10 +15,10 @@ import {
 import { Tooltip } from '~/components/panels/Tooltip';
 import { IState } from '~/redux/store';
 import { selectEditor } from '~/redux/editor/selectors';
+import pick from 'ramda/es/pick';
 
-const mapStateToProps = (state: IState) => ({
-  editor: selectEditor(state),
-});
+const mapStateToProps = (state: IState) =>
+  pick(['mode', 'changed', 'editing', 'features'], selectEditor(state));
 
 const mapDispatchToProps = {
   editorSetMode,
@@ -54,18 +53,15 @@ class EditorPanelUnconnected extends PureComponent<Props, void> {
   startRouterMode = () => this.props.editorSetMode(MODES.ROUTER);
   startTrashMode = () => this.props.editorSetMode(MODES.TRASH);
   startSaveMode = () => {
-    // if (!this.props.changed) return;
     this.props.editorSetMode(MODES.SAVE);
   };
 
   render() {
     const {
-      editor: {
-        mode,
-        changed,
-        editing,
-        features: { routing },
-      },
+      mode,
+      changed,
+      editing,
+      features: { routing },
     } = this.props;
 
     return (
diff --git a/src/components/panels/Icon.tsx b/src/components/panels/Icon.tsx
index 62354f1..a381018 100644
--- a/src/components/panels/Icon.tsx
+++ b/src/components/panels/Icon.tsx
@@ -1,6 +1,6 @@
-import React from 'react';
+import React, { memo } from 'react';
 
-export const Icon = ({ icon, size = 32 }: { icon: string, size?: number }) => (
+export const Icon = memo(({ icon, size = 32 }: { icon: string; size?: number }) => (
   <svg width={size} height={size} viewBox="0 0 32 32">
     <defs>
       <mask id={`icon-mask-${icon}`}>
@@ -9,5 +9,4 @@ export const Icon = ({ icon, size = 32 }: { icon: string, size?: number }) => (
     </defs>
     <rect x="0" y="0" width="32" height="32" stroke="none" mask={`url(#icon-mask-${icon})`} />
   </svg>
-);
-
+));
diff --git a/src/components/panels/TopLeftPanel.tsx b/src/components/panels/TopLeftPanel.tsx
index 44acce7..aa7b902 100644
--- a/src/components/panels/TopLeftPanel.tsx
+++ b/src/components/panels/TopLeftPanel.tsx
@@ -1,10 +1,10 @@
-import React from 'react';
+import React, { memo } from 'react';
 import { UserLocation } from '~/components/UserLocation';
 import { DistanceBar } from '~/components/panels/DistanceBar';
 
-export const TopLeftPanel = () => (
+export const TopLeftPanel = memo(() => (
   <div className="status-panel top left">
     <UserLocation />
     <DistanceBar />
   </div>
-);
+));
diff --git a/src/components/panels/TopRightPanel.tsx b/src/components/panels/TopRightPanel.tsx
index 9394433..5b6278c 100644
--- a/src/components/panels/TopRightPanel.tsx
+++ b/src/components/panels/TopRightPanel.tsx
@@ -9,11 +9,14 @@ import { MODES } from '~/constants/modes';
 import { Tooltip } from '~/components/panels/Tooltip';
 import { selectMap } from '~/redux/map/selectors';
 import { selectEditor } from '~/redux/editor/selectors';
+import { IState } from '~/redux/store';
 
-const mapStateToProps = state => ({
-  map: selectMap(state),
-  editor: selectEditor(state),
-});
+const mapStateToProps = (state: IState) => {
+  const { provider, logo } = selectMap(state);
+  const { markers_shown, editing } = selectEditor(state);
+
+  return { provider, logo, markers_shown, editing };
+};
 
 const mapDispatchToProps = {
   editorSetMode: EDITOR_ACTIONS.editorSetMode,
@@ -22,8 +25,10 @@ const mapDispatchToProps = {
 type Props = ReturnType<typeof mapStateToProps> & typeof mapDispatchToProps & {};
 
 const TopRightPanelUnconnected = ({
-  map: { provider, logo },
-  editor: { markers_shown, editing },
+  provider,
+  logo,
+  markers_shown,
+  editing,
   editorSetMode,
 }: Props) => {
   const startProviderMode = useCallback(() => editorSetMode(MODES.PROVIDER), [editorSetMode]);
diff --git a/src/components/panels/UserPanel.tsx b/src/components/panels/UserPanel.tsx
index d4049fc..112e0f4 100644
--- a/src/components/panels/UserPanel.tsx
+++ b/src/components/panels/UserPanel.tsx
@@ -19,21 +19,18 @@ import { CLIENT } from '~/config/frontend';
 import { DIALOGS, TABS } from '~/constants/dialogs';
 import { Tooltip } from '~/components/panels/Tooltip';
 import { TitleDialog } from '~/components/dialogs/TitleDialog';
+import { IState } from '~/redux/store';
 
 const mapStateToProps = ({
   user: { user },
   editor: { dialog, dialog_active },
   map: { route, stickers },
-}) => ({
-  editor: {
-    dialog,
-    dialog_active,
-  },
-  user: { user },
-  map: {
-    route,
-    stickers,
-  }
+}: IState) => ({
+  dialog,
+  dialog_active,
+  user,
+  route,
+  stickers,
 });
 
 const mapDispatchToProps = {
@@ -100,7 +97,7 @@ export class UserPanelUnconnected extends PureComponent<Props, State> {
   openAppInfoDialog = () => {
     this.setMenuOpened();
     this.props.editorSetDialog(DIALOGS.APP_INFO);
-    this.props.editorSetDialogActive(this.props.editor.dialog !== DIALOGS.APP_INFO);
+    this.props.editorSetDialogActive(this.props.dialog !== DIALOGS.APP_INFO);
   };
 
   openOauthFrame = () => {
@@ -118,7 +115,7 @@ export class UserPanelUnconnected extends PureComponent<Props, State> {
 
   render() {
     const {
-      props: { user: { user }, editor: { dialog, dialog_active }, map: { route, stickers } },
+      props: { user, dialog, dialog_active, route, stickers },
       state: { menuOpened },
     } = this;
 
diff --git a/src/components/user/UserButton.tsx b/src/components/user/UserButton.tsx
index 2ab1bf2..d013294 100644
--- a/src/components/user/UserButton.tsx
+++ b/src/components/user/UserButton.tsx
@@ -1,25 +1,22 @@
 // @flow
-import React from "react";
-import { UserPicture } from "~/components/user/UserPicture";
-import { IUser } from "~/constants/auth";
+import React, { FC, memo } from 'react';
+import { UserPicture } from '~/components/user/UserPicture';
+import { IUser } from '~/constants/auth';
 
 interface Props {
   user: IUser;
   setMenuOpened: () => void;
 }
 
-export const UserButton = ({
-  setMenuOpened,
-  user: { uid, photo, name }
-}: Props) => (
+export const UserButton: FC<Props> = memo(({ setMenuOpened, user: { uid, photo, name } }) => (
   <div className="control-bar user-bar">
     <div className="user-button" onClick={setMenuOpened}>
       <UserPicture photo={photo} />
 
       <div className="user-button-fields">
-        <div className="user-button-name">{name || uid || "..."}</div>
-        <div className="user-button-text">{uid || "пользователь"}</div>
+        <div className="user-button-name">{name || uid || '...'}</div>
+        <div className="user-button-text">{uid || 'пользователь'}</div>
       </div>
     </div>
   </div>
-);
+));
diff --git a/src/containers/App.tsx b/src/containers/App.tsx
index e7c7c85..3be17a4 100644
--- a/src/containers/App.tsx
+++ b/src/containers/App.tsx
@@ -32,7 +32,7 @@ type Props = {
   editorSetDialogActive: typeof editorSetDialogActive;
 };
 
-const Component = (props: Props) => (
+const AppUnconnected = (props: Props) => (
   <div>
     <Fills />
     <UserPanel />
@@ -72,6 +72,8 @@ const mapStateToProps = ({
   set,
 });
 
-const mapDispatchToProps = dispatch =>
-  bindActionCreators({ editorHideRenderer, editorSetDialogActive }, dispatch);
-export const App = connect(mapStateToProps, mapDispatchToProps)(hot(module)(Component));
+const mapDispatchToProps = { editorHideRenderer, editorSetDialogActive };
+
+const App = connect(mapStateToProps, mapDispatchToProps)(hot(module)(AppUnconnected));
+
+export { App };
diff --git a/src/containers/LeftDialog.tsx b/src/containers/LeftDialog.tsx
index f22a993..4455738 100644
--- a/src/containers/LeftDialog.tsx
+++ b/src/containers/LeftDialog.tsx
@@ -1,4 +1,4 @@
-import React, { createElement } from 'react';
+import React, { createElement, FC, memo } from 'react';
 import { DIALOGS, IDialogs } from '~/constants/dialogs';
 import classnames from 'classnames';
 import { AppInfoDialog } from '~/components/dialogs/AppInfoDialog';
@@ -17,7 +17,7 @@ const LEFT_DIALOGS = {
   [DIALOGS.APP_INFO]: AppInfoDialog,
 };
 
-const LeftDialog = ({ dialog, dialog_active, editorSetDialogActive }: Props) => (
+const LeftDialog: FC<Props> = memo(({ dialog, dialog_active, editorSetDialogActive }) => (
   <React.Fragment>
     {Object.keys(LEFT_DIALOGS).map(item => (
       <div
@@ -26,16 +26,22 @@ const LeftDialog = ({ dialog, dialog_active, editorSetDialogActive }: Props) =>
       >
         {dialog && LEFT_DIALOGS[item] && createElement(LEFT_DIALOGS[item], {})}
 
-        <div className="dialog-close-button desktop-only" onClick={() => editorSetDialogActive(false)}>
+        <div
+          className="dialog-close-button desktop-only"
+          onClick={() => editorSetDialogActive(false)}
+        >
           <Icon icon="icon-cancel-1" />
         </div>
 
-        <div className="dialog-close-button mobile-only" onClick={() => editorSetDialogActive(false)}>
+        <div
+          className="dialog-close-button mobile-only"
+          onClick={() => editorSetDialogActive(false)}
+        >
           <Icon icon="icon-chevron-down" />
         </div>
       </div>
     ))}
   </React.Fragment>
-);
+));
 
 export { LeftDialog };
diff --git a/src/containers/map/Route/index.tsx b/src/containers/map/Route/index.tsx
index 2fb157e..f7c0a54 100644
--- a/src/containers/map/Route/index.tsx
+++ b/src/containers/map/Route/index.tsx
@@ -1,6 +1,6 @@
 import React, { FC, useEffect, memo, useState, useCallback } from 'react';
 import { IMapRoute } from '../../../redux/map/types';
-import { InteractivePoly } from '~/utils/polyline';
+import { InteractivePoly } from '~/utils/map/InteractivePoly';
 import { isMobile } from '~/utils/window';
 import { LatLng, Map, LeafletEvent } from 'leaflet';
 import { selectEditor } from '~/redux/editor/selectors';
diff --git a/src/containers/map/Router/index.tsx b/src/containers/map/Router/index.tsx
index ad25411..a2dd2dd 100644
--- a/src/containers/map/Router/index.tsx
+++ b/src/containers/map/Router/index.tsx
@@ -1,6 +1,6 @@
-import React, { FC, useEffect, useMemo, useCallback, memo } from 'react';
+import { FC, useEffect, useCallback, memo } from 'react';
 import pick from 'ramda/es/pick';
-import { OsrmRouter } from '~/utils/osrm';
+import { OsrmRouter } from '~/utils/map/OsrmRouter';
 import { connect } from 'react-redux';
 import { selectMap } from '~/redux/map/selectors';
 import { selectEditorRouter, selectEditorMode } from '~/redux/editor/selectors';
diff --git a/src/containers/map/Sticker/index.tsx b/src/containers/map/Sticker/index.tsx
index c8567fb..a128a26 100644
--- a/src/containers/map/Sticker/index.tsx
+++ b/src/containers/map/Sticker/index.tsx
@@ -4,7 +4,7 @@ import { IStickerDump } from '~/redux/map/types';
 import { STICKERS } from '~/constants/stickers';
 import { StickerDesc } from '~/components/StickerDesc';
 import classNames from 'classnames';
-import { DomMarker } from '~/utils/DomMarker';
+import { DomMarker } from '~/utils/map/DomMarker';
 import { createPortal } from 'react-dom';
 import { MapContainer, MainMap } from '~/constants/map';
 
diff --git a/src/redux/editor/sagas.ts b/src/redux/editor/sagas.ts
index 24ddb3a..e0c1ba7 100644
--- a/src/redux/editor/sagas.ts
+++ b/src/redux/editor/sagas.ts
@@ -42,7 +42,7 @@ import { LOGOS } from '~/constants/logos';
 import { loadMapFromPath } from '../map/sagas';
 import { mapClicked, mapSetRoute } from '../map/actions';
 import { MAP_ACTIONS } from '../map/constants';
-import { OsrmRouter } from '~/utils/osrm';
+import { OsrmRouter } from '~/utils/map/OsrmRouter';
 import path from 'ramda/es/path';
 import { MainMap } from '~/constants/map';
 import { EDITOR_INITIAL_STATE } from '.';
@@ -236,7 +236,7 @@ function* mapClick({ latlng }: ReturnType<typeof mapClicked>) {
 
 function* routerSubmit() {
   const route: ReturnType<typeof selectMapRoute> = yield select(selectMapRoute);
-  const latlngs = path(['_routes', 0, 'coordinates'], OsrmRouter);
+  const latlngs: LatLng[] = path(['_routes', 0, 'coordinates'], OsrmRouter);
 
   const coordinates = simplify({ map: MainMap, latlngs });
 
diff --git a/src/utils/arrow.ts b/src/utils/arrow.ts
index ae52e2a..bfbc909 100644
--- a/src/utils/arrow.ts
+++ b/src/utils/arrow.ts
@@ -23,11 +23,6 @@ export const createArrow = (latlng: LatLngLiteral, angle: number): Marker => mar
 export const arrowClusterIcon = (cluster): DivIcon => {
   const markers = cluster.getAllChildMarkers();
 
-  // search for nearest marker to cluster (slow)
-  // const nearest = markers.sort((a, b) => (
-  //   dist2(a.getLatLng(), cluster.getLatLng()) - dist2(b.getLatLng(), cluster.getLatLng())
-  // ));
-
   // faster way
   cluster.setLatLng(markers[markers.length - 1].getLatLng());
   return markers[markers.length - 1].options.icon;
diff --git a/src/_modules/Arrows.ts b/src/utils/map/Arrows.ts
similarity index 100%
rename from src/_modules/Arrows.ts
rename to src/utils/map/Arrows.ts
diff --git a/src/utils/DomMarker.js b/src/utils/map/DomMarker.js
similarity index 94%
rename from src/utils/DomMarker.js
rename to src/utils/map/DomMarker.js
index a2f2d1e..a8ca9d0 100644
--- a/src/utils/DomMarker.js
+++ b/src/utils/map/DomMarker.js
@@ -15,7 +15,7 @@ export const DomMarker = DivIcon.extend({
 
     this._setIconStyles(element, 'icon');
 
-    return element;
+    return element; 
   }
 });
 
diff --git a/src/utils/polyline.ts b/src/utils/map/InteractivePoly.ts
similarity index 99%
rename from src/utils/polyline.ts
rename to src/utils/map/InteractivePoly.ts
index f209dd2..c2037ba 100644
--- a/src/utils/polyline.ts
+++ b/src/utils/map/InteractivePoly.ts
@@ -576,7 +576,6 @@ InteractivePoly.addInitHook(function() {
   });
 });
 
-// export const InteractivePoly = Component;
 /*
   events:
   vertexdragstart,
diff --git a/src/utils/osrm.ts b/src/utils/map/OsrmRouter.ts
similarity index 96%
rename from src/utils/osrm.ts
rename to src/utils/map/OsrmRouter.ts
index 9f12dc8..3e30da2 100644
--- a/src/utils/osrm.ts
+++ b/src/utils/map/OsrmRouter.ts
@@ -1,7 +1,7 @@
 import { Marker } from 'leaflet';
 import * as Routing from 'leaflet-routing-machine/src/index';
 import { CLIENT } from '~/config/frontend';
-import { DomMarker } from '~/utils/DomMarker';
+import { DomMarker } from '~/utils/map/DomMarker';
 import { MainMap } from '~/constants/map';
 
 const createWaypointMarker = (): DomMarker => {
diff --git a/src/utils/clusterIcon.ts b/src/utils/map/clusterIcon.ts
similarity index 100%
rename from src/utils/clusterIcon.ts
rename to src/utils/map/clusterIcon.ts