Repository: Couchers-org/couchers (language: python)

Issue:
Update maplibre-gl to current version
We're way behind in versions of maplibre. Since we're already improving the map, let's deal with this.


Discussion / hints:

Fix patch:
diff --git a/app/web/.env.development b/app/web/.env.development
index fb396ddfa8..b6ea4f0821 100644
--- a/app/web/.env.development
+++ b/app/web/.env.development
@@ -2,7 +2,6 @@ NEXT_PUBLIC_COUCHERS_ENV=preview
 NEXT_PUBLIC_API_BASE_URL="https://next.couchershq.org/api"
 NEXT_PUBLIC_MEDIA_BASE_URL="https://dev-user-media.couchershq.org"
 NEXT_PUBLIC_CONSOLE_BASE_URL="https://next-console.couchershq.org"
-NEXT_PUBLIC_MAPBOX_KEY="pk.eyJ1IjoiY291Y2hlcnMiLCJhIjoiY2tpYzY4eHd2MGVpcTJ0bGdhdGhvbHhlbiJ9.u5zvDeFE9H8itTK_dNp7Pg"
 NEXT_PUBLIC_NOMINATIM_URL="https://nominatim.openstreetmap.org/"
 NEXT_PUBLIC_STRIPE_KEY="pk_test_51KEzByIfR5z29g5khFE5samD8XKOGLcCrM1lhCkfOomGPUFAEYOw8uAqI2Nkv33wYdPM2FgTQNTC07IiNfHY1kLJ00Jqm8Ppai"
 NEXT_PUBLIC_GLOBAL_MESSAGE_URL="https://gm.couchershq.org/next.json"
diff --git a/app/web/.env.localdev b/app/web/.env.localdev
index 895169ee5a..2e0c1596fc 100644
--- a/app/web/.env.localdev
+++ b/app/web/.env.localdev
@@ -3,7 +3,6 @@ NEXT_PUBLIC_API_BASE_URL=http://localhost:8888
 NEXT_PUBLIC_MEDIA_BASE_URL="http://localhost:5001"
 NEXT_PUBLIC_CONSOLE_BASE_URL="http://localhost:10027"
 NEXT_PUBLIC_IS_POST_BETA_ENABLED=true
-NEXT_PUBLIC_MAPBOX_KEY="pk.eyJ1IjoiY291Y2hlcnMiLCJhIjoiY2tpYzY4eHd2MGVpcTJ0bGdhdGhvbHhlbiJ9.u5zvDeFE9H8itTK_dNp7Pg"
 NEXT_PUBLIC_NOMINATIM_URL="https://nominatim.openstreetmap.org/"
 NEXT_PUBLIC_IS_VERIFICATION_ENABLED=true
 NEXT_PUBLIC_IS_COMMUNITIES_PART2_ENABLED=true
diff --git a/app/web/.env.next b/app/web/.env.next
index 2baa3b1a90..42d4e1c89d 100644
--- a/app/web/.env.next
+++ b/app/web/.env.next
@@ -3,7 +3,6 @@ NEXT_PUBLIC_COUCHERS_ENV=preview
 NEXT_PUBLIC_API_BASE_URL="/api"
 NEXT_PUBLIC_MEDIA_BASE_URL="https://next-usercontent.couchershq.org"
 NEXT_PUBLIC_CONSOLE_BASE_URL="https://next-console.couchershq.org"
-NEXT_PUBLIC_MAPBOX_KEY="pk.eyJ1IjoiY291Y2hlcnMiLCJhIjoiY2tpYzVnaXA0MGdmejJ4bGFrdTdrNjA0NCJ9.1VAPRbE6a_kwGw1kucppJw"
 NEXT_PUBLIC_NOMINATIM_URL="https://nominatim.openstreetmap.org/"
 NEXT_PUBLIC_STRIPE_KEY="pk_test_51KEzByIfR5z29g5khFE5samD8XKOGLcCrM1lhCkfOomGPUFAEYOw8uAqI2Nkv33wYdPM2FgTQNTC07IiNfHY1kLJ00Jqm8Ppai"
 NEXT_PUBLIC_GLOBAL_MESSAGE_URL="https://gm.couchershq.org/next.json"
diff --git a/app/web/.env.production b/app/web/.env.production
index 1342d2fb90..111fc9fefd 100644
--- a/app/web/.env.production
+++ b/app/web/.env.production
@@ -3,7 +3,6 @@ NEXT_PUBLIC_COUCHERS_ENV=prod
 NEXT_PUBLIC_API_BASE_URL=https://couchers.org/api
 NEXT_PUBLIC_MEDIA_BASE_URL="https://usercontent.couchershq.org"
 NEXT_PUBLIC_CONSOLE_BASE_URL="https://console.couchers.org"
-NEXT_PUBLIC_MAPBOX_KEY="pk.eyJ1IjoiY291Y2hlcnMiLCJhIjoiY2tpYzVnaXA0MGdmejJ4bGFrdTdrNjA0NCJ9.1VAPRbE6a_kwGw1kucppJw"
 NEXT_PUBLIC_NOMINATIM_URL="https://nominatim.openstreetmap.org/"
 NEXT_PUBLIC_STRIPE_KEY="pk_live_51KEzByIfR5z29g5kbIZgVZrZtuQIQW0vYcwKxvkOCjG2jn6KRoRQAuOPd9JkmsVoKnpwpkowI2J1Qm8tQ2umEnQ400DNiV7Vw5"
 NEXT_PUBLIC_GLOBAL_MESSAGE_URL="https://gm.couchershq.org/prod.json"
diff --git a/app/web/components/EditLocationMap.tsx b/app/web/components/EditLocationMap.tsx
index cf86621c4f..f57e0c4b88 100644
--- a/app/web/components/EditLocationMap.tsx
+++ b/app/web/components/EditLocationMap.tsx
@@ -4,9 +4,12 @@ import classNames from "classnames";
 import Map from "components/Map";
 import MapSearch from "components/MapSearch";
 import TextField from "components/TextField";
-import maplibregl, {
+import { Feature, GeoJsonProperties, Geometry } from "geojson";
+import {
+  Event as MaplibreEvent,
   GeoJSONSource,
   LngLat,
+  Map as MaplibreMap,
   MapMouseEvent,
   MapTouchEvent,
 } from "maplibre-gl";
@@ -73,7 +76,7 @@ export default function EditLocationMap({
   const theme = useTheme();
   const [error, setError] = useState("");
 
-  const map = useRef<maplibregl.Map | null>(null);
+  const map = useRef<MaplibreMap | null>(null);
 
   // map is imperative so these don't need to cause re-render
   const location = useRef<ApproximateLocation>({
@@ -197,7 +200,7 @@ export default function EditLocationMap({
     }
   };
 
-  const initializeMap = (mapRef: maplibregl.Map) => {
+  const initializeMap = (mapRef: MaplibreMap) => {
     map.current = mapRef;
     map.current.once("load", () => {
       if (!map.current) return;
@@ -254,7 +257,7 @@ export default function EditLocationMap({
       }
     });
 
-    const onDblClick = (e: MapMouseEvent & maplibregl.EventData) => {
+    const onDblClick = (e: MapMouseEvent & MaplibreEvent) => {
       e.preventDefault();
       handleCoordinateMoved(e);
     };
@@ -262,8 +265,8 @@ export default function EditLocationMap({
 
     const onCircleTouch = (
       e: MapTouchEvent & {
-        features?: maplibregl.MapboxGeoJSONFeature[] | undefined;
-      } & maplibregl.EventData
+        features?: Feature<Geometry, GeoJsonProperties>[] | undefined;
+      } & MaplibreEvent
     ) => {
       if (e.points.length !== 1) return;
       onCircleMouseDown(e);
diff --git a/app/web/components/Map.tsx b/app/web/components/Map.tsx
index acff4fd109..1421f08259 100644
--- a/app/web/components/Map.tsx
+++ b/app/web/components/Map.tsx
@@ -3,14 +3,17 @@ import "maplibre-gl/dist/maplibre-gl.css";
 import { Typography } from "@material-ui/core";
 import classNames from "classnames";
 import { NO_MAP_SUPPORT } from "components/constants";
-import maplibregl, { LngLat, RequestParameters } from "maplibre-gl";
+import {
+  LngLat,
+  Map as MaplibreMap,
+  NavigationControl,
+  RequestParameters,
+} from "maplibre-gl";
 import { useEffect, useRef, useState } from "react";
 import makeStyles from "utils/makeStyles";
 
 const URL = process.env.NEXT_PUBLIC_API_BASE_URL;
 
-maplibregl.accessToken = process.env.NEXT_PUBLIC_MAPBOX_KEY!;
-
 const useStyles = makeStyles({
   root: {
     position: "relative",
@@ -39,7 +42,7 @@ const useStyles = makeStyles({
 export interface MapProps {
   initialCenter: LngLat | undefined;
   initialZoom: number;
-  postMapInitialize?: (map: maplibregl.Map) => void;
+  postMapInitialize?: (map: MaplibreMap) => void;
   className?: string;
   onUpdate?: (center: LngLat, zoom: number) => void;
   grow?: boolean;
@@ -76,7 +79,7 @@ export default function Map({
     return { url };
   };
 
-  const mapRef = useRef<maplibregl.Map>();
+  const mapRef = useRef<MaplibreMap>();
 
   useEffect(() => {
     if (!containerRef.current) return;
@@ -85,7 +88,7 @@ export default function Map({
     if (mapRef.current) return;
 
     try {
-      const map = new maplibregl.Map({
+      const map = new MaplibreMap({
         center: initialCenter,
         container: containerRef.current,
         hash: hash ? "loc" : false,
@@ -98,9 +101,7 @@ export default function Map({
       mapRef.current = map;
 
       if (interactive) {
-        map.addControl(
-          new maplibregl.NavigationControl({ showCompass: false })
-        );
+        map.addControl(new NavigationControl({ showCompass: false }));
       }
 
       if (onUpdate) {
diff --git a/app/web/features/search/MapWrapper.tsx b/app/web/features/search/MapWrapper.tsx
index 466dac6c59..e7c55ec93a 100644
--- a/app/web/features/search/MapWrapper.tsx
+++ b/app/web/features/search/MapWrapper.tsx
@@ -8,10 +8,15 @@ import {
   layers,
   reRenderUsersOnMap,
 } from "features/search/users";
-import { Point } from "geojson";
+import { Feature, GeoJsonProperties, Geometry, Point } from "geojson";
 import { useTranslation } from "i18n";
 import { SEARCH } from "i18n/namespaces";
-import maplibregl, { EventData, LngLat, Map as MaplibreMap } from "maplibre-gl";
+import {
+  Event as MaplibreEvent,
+  LngLat,
+  Map as MaplibreMap,
+  MapMouseEvent,
+} from "maplibre-gl";
 import { User } from "proto/api_pb";
 import { UserSearchRes } from "proto/search_pb";
 import {
@@ -101,9 +106,9 @@ export default function MapWrapper({
    */
   const handleMapUserClick = useCallback(
     (
-      ev: maplibregl.MapMouseEvent & {
-        features?: maplibregl.MapboxGeoJSONFeature[] | undefined;
-      } & EventData
+      ev: MapMouseEvent & {
+        features?: Feature<Geometry, GeoJsonProperties>[] | undefined;
+      } & MaplibreEvent
     ) => {
       ev.preventDefault();
 
@@ -182,7 +187,7 @@ export default function MapWrapper({
 
   useEffect(() => {
     if (!map.current) return;
-    const handleMapClickAway = (e: EventData) => {
+    const handleMapClickAway = (e: MapMouseEvent) => {
       // DefaultPrevented is true when a map feature has been clicked
       if (!e.defaultPrevented) {
         setSelectedResult(undefined);
diff --git a/app/web/features/search/communities.ts b/app/web/features/search/communities.ts
index 0cf7a5b51a..42a7d6e6ac 100644
--- a/app/web/features/search/communities.ts
+++ b/app/web/features/search/communities.ts
@@ -1,15 +1,19 @@
-import { AnyLayer, AnySourceData, Map as MaplibreMap } from "maplibre-gl";
+import {
+  LayerSpecification,
+  Map as MaplibreMap,
+  SourceSpecification,
+} from "maplibre-gl";
 
 const URL = process.env.NEXT_PUBLIC_API_BASE_URL;
 
-export const sources: Record<string, AnySourceData> = {
+export const sources: Record<string, SourceSpecification> = {
   communities: {
     data: URL + "/geojson/communities",
     type: "geojson",
   },
 };
 
-export const layers: Record<string, AnyLayer> = {
+export const layers: Record<string, LayerSpecification> = {
   communitiesLayer: {
     id: "communities-layer",
     paint: {
diff --git a/app/web/features/search/constants.ts b/app/web/features/search/constants.ts
index e0b6426acf..1636aa930e 100644
--- a/app/web/features/search/constants.ts
+++ b/app/web/features/search/constants.ts
@@ -1,5 +1,6 @@
+import { Feature, GeoJsonProperties, Geometry } from "geojson";
 import { TFunction } from "i18n";
-import maplibregl from "maplibre-gl";
+import { Event as MaplibreEvent, MapMouseEvent } from "maplibre-gl";
 import { User } from "proto/api_pb";
 import { UserSearchFilters } from "service/search";
 import { firstName } from "utils/names";
@@ -29,9 +30,9 @@ export const selectedUserZoom = 10;
 export type Coordinates = [number, number, number, number];
 
 export type MapClickedCallback = (
-  ev: maplibregl.MapMouseEvent & {
-    features?: maplibregl.MapboxGeoJSONFeature[] | undefined;
-  } & maplibregl.EventData
+  ev: MapMouseEvent & {
+    features?: Feature<Geometry, GeoJsonProperties>[] | undefined;
+  } & MaplibreEvent
 ) => void;
 
 export interface SearchParams extends UserSearchFilters {
diff --git a/app/web/features/search/guides.ts b/app/web/features/search/guides.ts
index d121ddde82..38bd9abb58 100644
--- a/app/web/features/search/guides.ts
+++ b/app/web/features/search/guides.ts
@@ -1,21 +1,21 @@
 import {
-  AnyLayer,
-  AnySourceData,
-  EventData,
+  Event as MaplibreEvent,
+  LayerSpecification,
   Map as MaplibreMap,
   MapLayerEventType,
+  SourceSpecification,
 } from "maplibre-gl";
 
 const URL = process.env.NEXT_PUBLIC_API_BASE_URL;
 
-export const sources: Record<string, AnySourceData> = {
+export const sources: Record<string, SourceSpecification> = {
   guides: {
     data: URL + "/geojson/guides",
     type: "geojson",
   },
 };
 
-export const layers: Record<string, AnyLayer> = {
+export const layers: Record<string, LayerSpecification> = {
   guideLayer: {
     id: "guide-points",
     paint: {
@@ -31,7 +31,9 @@ export const layers: Record<string, AnyLayer> = {
 
 export const addGuidesToMap = (
   map: MaplibreMap,
-  guideClickedCallback?: (ev: MapLayerEventType["click"] & EventData) => void
+  guideClickedCallback?: (
+    ev: MapLayerEventType["click"] & MaplibreEvent
+  ) => void
 ) => {
   map.addSource("guides", sources["guides"]);
   map.addLayer(layers["guideLayer"]);
diff --git a/app/web/features/search/places.ts b/app/web/features/search/places.ts
index 524803c392..ddc300cd06 100644
--- a/app/web/features/search/places.ts
+++ b/app/web/features/search/places.ts
@@ -1,21 +1,21 @@
 import {
-  AnyLayer,
-  AnySourceData,
-  EventData,
+  Event as MaplibreEvent,
+  LayerSpecification,
   Map as MaplibreMap,
   MapLayerEventType,
+  SourceSpecification,
 } from "maplibre-gl";
 
 const URL = process.env.NEXT_PUBLIC_API_BASE_URL;
 
-export const sources: Record<string, AnySourceData> = {
+export const sources: Record<string, SourceSpecification> = {
   places: {
     data: URL + "/geojson/places",
     type: "geojson",
   },
 };
 
-export const layers: Record<string, AnyLayer> = {
+export const layers: Record<string, LayerSpecification> = {
   placeLayer: {
     id: "place-points",
     paint: {
@@ -31,7 +31,9 @@ export const layers: Record<string, AnyLayer> = {
 
 export const addPlacesToMap = (
   map: MaplibreMap,
-  placeClickedCallback?: (ev: MapLayerEventType["click"] & EventData) => void
+  placeClickedCallback?: (
+    ev: MapLayerEventType["click"] & MaplibreEvent
+  ) => void
 ) => {
   map.addSource("places", sources["places"]);
   map.addLayer(layers["placeLayer"]);
diff --git a/app/web/features/search/users.ts b/app/web/features/search/users.ts
index 3fa2b5e61f..3380a3564e 100644
--- a/app/web/features/search/users.ts
+++ b/app/web/features/search/users.ts
@@ -1,10 +1,12 @@
 import { MapClickedCallback } from "features/search/constants";
-import { Point } from "geojson";
-import maplibregl, {
-  AnyLayer,
-  AnySourceData,
+import { Feature, GeoJsonProperties, Geometry, Point } from "geojson";
+import {
+  Event as MaplibreEvent,
   GeoJSONSource,
+  LayerSpecification,
   Map as MaplibreMap,
+  MapMouseEvent,
+  SourceSpecification,
 } from "maplibre-gl";
 import { User } from "proto/api_pb";
 import { UserSearchRes } from "proto/search_pb";
@@ -16,7 +18,7 @@ import userPin from "./resources/userPin.png";
 const URL = process.env.NEXT_PUBLIC_API_BASE_URL;
 
 type SourceKeys = "clustered-users";
-export const sources: Record<SourceKeys, AnySourceData> = {
+export const sources: Record<SourceKeys, SourceSpecification> = {
   "clustered-users": {
     cluster: true,
     clusterMaxZoom: 14,
@@ -28,7 +30,7 @@ export const sources: Record<SourceKeys, AnySourceData> = {
 };
 
 type LayerKeys = "clusterCountLayer" | "clusterLayer" | "unclusteredPointLayer";
-export const layers: Record<LayerKeys, AnyLayer> = {
+export const layers: Record<LayerKeys, LayerSpecification> = {
   clusterCountLayer: {
     filter: ["has", "point_count"],
     id: "clusters-count",
@@ -106,20 +108,29 @@ export const layers: Record<LayerKeys, AnyLayer> = {
 const addPinImages = (map: MaplibreMap) => {
   if (map.hasImage("user-pin")) return;
 
-  map.loadImage(userPin.src, (error: Error, image: HTMLImageElement) => {
-    if (error) {
-      throw error;
+  map.loadImage(
+    userPin.src,
+    (
+      error: Error | null | undefined,
+      image?: HTMLImageElement | ImageBitmap | null
+    ) => {
+      if (error) {
+        throw error;
+      }
+      //this is twice because of loading race condition
+      if (map.hasImage("user-pin")) return;
+
+      if (image) {
+        map.addImage("user-pin", image, { sdf: true });
+      }
     }
-    //this is twice because of loading race condition
-    if (map.hasImage("user-pin")) return;
-    map.addImage("user-pin", image, { sdf: true });
-  });
+  );
 };
 
 const zoomCluster = (
-  ev: maplibregl.MapMouseEvent & {
-    features?: maplibregl.MapboxGeoJSONFeature[] | undefined;
-  } & maplibregl.EventData
+  ev: MapMouseEvent & {
+    features?: Feature<Geometry, GeoJsonProperties>[] | undefined;
+  } & MaplibreEvent
 ) => {
   const map = ev.target;
   const cluster = ev.features?.[0];
@@ -128,10 +139,12 @@ const zoomCluster = (
   (map.getSource("clustered-users") as GeoJSONSource).getClusterExpansionZoom(
     cluster.properties.cluster_id,
     (_error, zoom) => {
-      map.flyTo({
-        center: (cluster.geometry as Point).coordinates as [number, number],
-        zoom,
-      });
+      if (zoom !== null && zoom !== undefined) {
+        map.flyTo({
+          center: (cluster.geometry as Point).coordinates as [number, number],
+          zoom,
+        });
+      }
     }
   );
 };
diff --git a/app/web/package.json b/app/web/package.json
index 794a9fcc8a..c0a0838601 100644
--- a/app/web/package.json
+++ b/app/web/package.json
@@ -42,7 +42,7 @@
     "grpc-web": "1.5.0",
     "intersection-observer": "^0.12.2",
     "luhn": "^2.4.1",
-    "maplibre-gl": "^1.15.3",
+    "maplibre-gl": "^2.4.0",
     "markdown-it": "^14.1.0",
     "next": "12.1.6",
     "next-i18next": "git+https://github.com/Couchers-org/next-i18next.git#39a50ec9",
diff --git a/app/web/yarn.lock b/app/web/yarn.lock
index 194c6abe8c..a2962cdf54 100644
--- a/app/web/yarn.lock
+++ b/app/web/yarn.lock
@@ -2712,43 +2712,38 @@
     "@jridgewell/resolve-uri" "^3.1.0"
     "@jridgewell/sourcemap-codec" "^1.4.14"
 
-"@mapbox/geojson-rewind@^0.5.0":
-  version "0.5.1"
-  resolved "https://registry.yarnpkg.com/@mapbox/geojson-rewind/-/geojson-rewind-0.5.1.tgz#adbe16dc683eb40e90934c51a5e28c7bbf44f4e1"
-  integrity sha512-eL7fMmfTBKjrb+VFHXCGv9Ot0zc3C0U+CwXo1IrP+EPwDczLoXv34Tgq3y+2mPSFNVUXgU42ILWJTC7145KPTA==
+"@mapbox/geojson-rewind@^0.5.2":
+  version "0.5.2"
+  resolved "https://registry.yarnpkg.com/@mapbox/geojson-rewind/-/geojson-rewind-0.5.2.tgz#591a5d71a9cd1da1a0bf3420b3bea31b0fc7946a"
+  integrity sha512-tJaT+RbYGJYStt7wI3cq4Nl4SXxG8W7JDG5DMJu97V25RnbNg3QtQtf+KD+VLjNpWKYsRvXDNmNrBgEETr1ifA==
   dependencies:
     get-stream "^6.0.1"
-    minimist "^1.2.5"
-
-"@mapbox/geojson-types@^1.0.2":
-  version "1.0.2"
-  resolved "https://registry.yarnpkg.com/@mapbox/geojson-types/-/geojson-types-1.0.2.tgz#9aecf642cb00eab1080a57c4f949a65b4a5846d6"
-  integrity sha512-e9EBqHHv3EORHrSfbR9DqecPNn+AmuAoQxV6aL8Xu30bJMJR1o8PZLZzpk1Wq7/NfCbuhmakHTPYRhoqLsXRnw==
+    minimist "^1.2.6"
 
 "@mapbox/jsonlint-lines-primitives@^2.0.2":
   version "2.0.2"
   resolved "https://registry.yarnpkg.com/@mapbox/jsonlint-lines-primitives/-/jsonlint-lines-primitives-2.0.2.tgz#ce56e539f83552b58d10d672ea4d6fc9adc7b234"
   integrity sha1-zlblOfg1UrWNENZy6k1vya3HsjQ=
 
-"@mapbox/mapbox-gl-supported@^1.5.0":
-  version "1.5.0"
-  resolved "https://registry.yarnpkg.com/@mapbox/mapbox-gl-supported/-/mapbox-gl-supported-1.5.0.tgz#f60b6a55a5d8e5ee908347d2ce4250b15103dc8e"
-  integrity sha512-/PT1P6DNf7vjEEiPkVIRJkvibbqWtqnyGaBz3nfRdcxclNSnSdaLU5tfAgcD7I8Yt5i+L19s406YLl1koLnLbg==
+"@mapbox/mapbox-gl-supported@^2.0.1":
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/@mapbox/mapbox-gl-supported/-/mapbox-gl-supported-2.0.1.tgz#c15367178d8bfe4765e6b47b542fe821ce259c7b"
+  integrity sha512-HP6XvfNIzfoMVfyGjBckjiAOQK9WfX0ywdLubuPMPv+Vqf5fj0uCbgBQYpiqcWZT6cbyyRnTSXDheT1ugvF6UQ==
 
 "@mapbox/point-geometry@0.1.0", "@mapbox/point-geometry@^0.1.0", "@mapbox/point-geometry@~0.1.0":
   version "0.1.0"
   resolved "https://registry.yarnpkg.com/@mapbox/point-geometry/-/point-geometry-0.1.0.tgz#8a83f9335c7860effa2eeeca254332aa0aeed8f2"
   integrity sha1-ioP5M1x4YO/6Lu7KJUMyqgru2PI=
 
-"@mapbox/tiny-sdf@^1.1.1":
-  version "1.2.5"
-  resolved "https://registry.yarnpkg.com/@mapbox/tiny-sdf/-/tiny-sdf-1.2.5.tgz#424c620a96442b20402552be70a7f62a8407cc59"
-  integrity sha512-cD8A/zJlm6fdJOk6DqPUV8mcpyJkRz2x2R+/fYcWDYG3oWbG7/L7Yl/WqQ1VZCjnL9OTIMAn6c+BC5Eru4sQEw==
+"@mapbox/tiny-sdf@^2.0.5":
+  version "2.0.6"
+  resolved "https://registry.yarnpkg.com/@mapbox/tiny-sdf/-/tiny-sdf-2.0.6.tgz#9a1d33e5018093e88f6a4df2343e886056287282"
+  integrity sha512-qMqa27TLw+ZQz5Jk+RcwZGH7BQf5G/TrutJhspsca/3SHwmgKQ1iq+d3Jxz5oysPVYTGP6aXxCo5Lk9Er6YBAA==
 
-"@mapbox/unitbezier@^0.0.0":
-  version "0.0.0"
-  resolved "https://registry.yarnpkg.com/@mapbox/unitbezier/-/unitbezier-0.0.0.tgz#15651bd553a67b8581fb398810c98ad86a34524e"
-  integrity sha1-FWUb1VOme4WB+zmIEMmK2Go0Uk4=
+"@mapbox/unitbezier@^0.0.1":
+  version "0.0.1"
+  resolved "https://registry.yarnpkg.com/@mapbox/unitbezier/-/unitbezier-0.0.1.tgz#d32deb66c7177e9e9dfc3bbd697083e2e657ff01"
+  integrity sha512-nMkuDXFv60aBr9soUG5q+GvZYL+2KZHVvsqFCzqnkGEf46U2fvmytHaEVc1/YZbiLn8X+eR3QzX1+dwDO1lxlw==
 
 "@mapbox/vector-tile@^1.3.1":
   version "1.3.1"
@@ -4644,6 +4639,20 @@
   resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.182.tgz#05301a4d5e62963227eaafe0ce04dd77c54ea5c2"
   integrity sha512-/THyiqyQAP9AfARo4pF+aCGcyiQ94tX/Is2I7HofNRqoYLgN1PBoOWu2/zTA5zMxzP5EFutMtWtGAFRKUe961Q==
 
+"@types/mapbox__point-geometry@*", "@types/mapbox__point-geometry@^0.1.2":
+  version "0.1.4"
+  resolved "https://registry.yarnpkg.com/@types/mapbox__point-geometry/-/mapbox__point-geometry-0.1.4.tgz#0ef017b75eedce02ff6243b4189210e2e6d5e56d"
+  integrity sha512-mUWlSxAmYLfwnRBmgYV86tgYmMIICX4kza8YnE/eIlywGe2XoOxlpVnXWwir92xRLjwyarqwpu2EJKD2pk0IUA==
+
+"@types/mapbox__vector-tile@^1.3.0":
+  version "1.3.4"
+  resolved "https://registry.yarnpkg.com/@types/mapbox__vector-tile/-/mapbox__vector-tile-1.3.4.tgz#ad757441ef1d34628d9e098afd9c91423c1f8734"
+  integrity sha512-bpd8dRn9pr6xKvuEBQup8pwQfD4VUyqO/2deGjfpe6AwC8YRlyEipvefyRJUSiCJTZuCb8Pl1ciVV5ekqJ96Bg==
+  dependencies:
+    "@types/geojson" "*"
+    "@types/mapbox__point-geometry" "*"
+    "@types/pbf" "*"
+
 "@types/markdown-it@^14.1.2":
   version "14.1.2"
   resolved "https://registry.yarnpkg.com/@types/markdown-it/-/markdown-it-14.1.2.tgz#57f2532a0800067d9b934f3521429a2e8bfb4c61"
@@ -4714,6 +4723,11 @@
   resolved "https://registry.yarnpkg.com/@types/parse5/-/parse5-5.0.3.tgz#e7b5aebbac150f8b5fdd4a46e7f0bd8e65e19109"
   integrity sha512-kUNnecmtkunAoQ3CnjmMkzNU/gtxG8guhi+Fk2U/kOpIKjIMKnXGp4IJCgQJrXSgMsWYimYG4TGjz/UzbGEBTw==
 
+"@types/pbf@*", "@types/pbf@^3.0.2":
+  version "3.0.5"
+  resolved "https://registry.yarnpkg.com/@types/pbf/-/pbf-3.0.5.tgz#a9495a58d8c75be4ffe9a0bd749a307715c07404"
+  integrity sha512-j3pOPiEcWZ34R6a6mN07mUkM4o4Lwf6hPNt8eilOeZhTFbxFXmKhvXl9Y28jotFPaI1bpPDJsbCprUoNke6OrA==
+
 "@types/prettier@^2.1.5":
   version "2.4.2"
   resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.4.2.tgz#4c62fae93eb479660c3bd93f9d24d561597a8281"
@@ -7814,10 +7828,10 @@ duplexify@^3.4.2, duplexify@^3.6.0:
     readable-stream "^2.0.0"
     stream-shift "^1.0.0"
 
-earcut@^2.2.2:
-  version "2.2.3"
-  resolved "https://registry.yarnpkg.com/earcut/-/earcut-2.2.3.tgz#d44ced2ff5a18859568e327dd9c7d46b16f55cf4"
-  integrity sha512-iRDI1QeCQIhMCZk48DRDMVgQSSBDmbzzNhnxIo+pwx3swkfjMh6vh0nWLq1NdvGHLKH6wIrAM3vQWeTj6qeoug==
+earcut@^2.2.4:
+  version "2.2.4"
+  resolved "https://registry.yarnpkg.com/earcut/-/earcut-2.2.4.tgz#6d02fd4d68160c114825d06890a92ecaae60343a"
+  integrity sha512-/pjZsA1b4RPHbeWZQn66SWS8nZZWLQQ23oE3Eam7aroEFGEvwKAsJfZ9ytiEMycfzXWpca4FA9QIOehf7PocBQ==
 
 eastasianwidth@^0.2.0:
   version "0.2.0"
@@ -9250,7 +9264,7 @@ github-slugger@^1.0.0:
   resolved "https://registry.yarnpkg.com/github-slugger/-/github-slugger-1.4.0.tgz#206eb96cdb22ee56fdc53a28d5a302338463444e"
   integrity sha512-w0dzqw/nt51xMVmlaV1+JRzN+oCa1KfcgGEWhxUG16wbdA+Xnt/yoFO8Z8x/V82ZcZ0wy6ln9QDup5avbhiDhQ==
 
-gl-matrix@^3.2.1:
+gl-matrix@^3.4.3:
   version "3.4.3"
   resolved "https://registry.yarnpkg.com/gl-matrix/-/gl-matrix-3.4.3.tgz#fc1191e8320009fd4d20e9339595c6041ddc22c9"
   integrity sha512-wcCp8vu8FT22BnvKVPjXa/ICBWRq/zjFfdofZy1WSpQZpphblv12/bOQLBC1rMM7SGOFS9ltVmKOHil5+Ml7gA==
@@ -9341,6 +9355,15 @@ glob@^8.0.3:
     minimatch "^5.0.1"
     once "^1.3.0"
 
+global-prefix@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/global-prefix/-/global-prefix-3.0.0.tgz#fc85f73064df69f50421f47f883fe5b913ba9b97"
+  integrity sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==
+  dependencies:
+    ini "^1.3.5"
+    kind-of "^6.0.2"
+    which "^1.3.1"
+
 global@^4.4.0:
   version "4.4.0"
   resolved "https://registry.yarnpkg.com/global/-/global-4.4.0.tgz#3e7b105179006a323ed71aafca3e9c57a5cc6406"
@@ -9433,11 +9456,6 @@ graphql@^16.3.0:
   resolved "https://registry.yarnpkg.com/graphql/-/graphql-16.3.0.tgz#a91e24d10babf9e60c706919bb182b53ccdffc05"
   integrity sha512-xm+ANmA16BzCT5pLjuXySbQVFwH3oJctUVdy81w1sV0vBU0KgDdBGtxQOUd5zqOBk/JayAFeG8Dlmeq74rjm/A==
 
-grid-index@^1.1.0:
-  version "1.1.0"
-  resolved "https://registry.yarnpkg.com/grid-index/-/grid-index-1.1.0.tgz#97f8221edec1026c8377b86446a7c71e79522ea7"
-  integrity sha512-HZRwumpOGUrHyxO5bqKZL0B0GlUpwtCAzZ42sgxUPniu33R1LSFH5yrIcBCHjkctCAh3mtWKcKd9J4vDDdeVHA==
-
 grpc-web@1.5.0:
   version "1.5.0"
   resolved "https://registry.yarnpkg.com/grpc-web/-/grpc-web-1.5.0.tgz#154e4007ab59a94bf7726b87ef6c5bd8815ecf6e"
@@ -9990,6 +10008,11 @@ inherits@2.0.3:
   resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de"
   integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=
 
+ini@^1.3.5:
+  version "1.3.8"
+  resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c"
+  integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==
+
 inline-style-parser@0.1.1:
   version "0.1.1"
   resolved "https://registry.yarnpkg.com/inline-style-parser/-/inline-style-parser-0.1.1.tgz#ec8a3b429274e9c0a1f1c4ffa9453a7fef72cea1"
@@ -11663,34 +11686,35 @@ map-visit@^1.0.0:
   dependencies:
     object-visit "^1.0.0"
 
-maplibre-gl@^1.15.3:
-  version "1.15.3"
-  resolved "https://registry.yarnpkg.com/maplibre-gl/-/maplibre-gl-1.15.3.tgz#eebbdd6b4cba46c61a660d6fa1808fced126e24d"
-  integrity sha512-ZuOhLCNgp7Yl1L9uyKgZeuo7kKdewP0iWtmEXsZ/snp0JiVkR1Kl+m1rsfKT/wpm/O4zZ7mUGxF16cYbMIFDRA==
+maplibre-gl@^2.4.0:
+  version "2.4.0"
+  resolved "https://registry.yarnpkg.com/maplibre-gl/-/maplibre-gl-2.4.0.tgz#2b53dbf526626bf4ee92ad4f33f13ef09e5af182"
+  integrity sha512-csNFylzntPmHWidczfgCZpvbTSmhaWvLRj9e1ezUDBEPizGgshgm3ea1T5TCNEEBq0roauu7BPuRZjA3wO4KqA==
   dependencies:
-    "@mapbox/geojson-rewind" "^0.5.0"
-    "@mapbox/geojson-types" "^1.0.2"
+    "@mapbox/geojson-rewind" "^0.5.2"
     "@mapbox/jsonlint-lines-primitives" "^2.0.2"
-    "@mapbox/mapbox-gl-supported" "^1.5.0"
+    "@mapbox/mapbox-gl-supported" "^2.0.1"
     "@mapbox/point-geometry" "^0.1.0"
-    "@mapbox/tiny-sdf" "^1.1.1"
-    "@mapbox/unitbezier" "^0.0.0"
+    "@mapbox/tiny-sdf" "^2.0.5"
+    "@mapbox/unitbezier" "^0.0.1"
     "@mapbox/vector-tile" "^1.3.1"
     "@mapbox/whoots-js" "^3.1.0"
+    "@types/geojson" "^7946.0.10"
+    "@types/mapbox__point-geometry" "^0.1.2"
+    "@types/mapbox__vector-tile" "^1.3.0"
+    "@types/pbf" "^3.0.2"
     csscolorparser "~1.0.3"
-    earcut "^2.2.2"
+    earcut "^2.2.4"
     geojson-vt "^3.2.1"
-    gl-matrix "^3.2.1"
-    grid-index "^1.1.0"
-    minimist "^1.2.6"
+    gl-matrix "^3.4.3"
+    global-prefix "^3.0.0"
     murmurhash-js "^1.0.0"
     pbf "^3.2.1"
-    potpack "^1.0.1"
+    potpack "^1.0.2"
     quickselect "^2.0.0"
-    rw "^1.3.3"
-    supercluster "^7.1.0"
+    supercluster "^7.1.5"
     tinyqueue "^2.0.3"
-    vt-pbf "^3.1.1"
+    vt-pbf "^3.1.3"
 
 markdown-escapes@^1.0.0:
   version "1.0.4"
@@ -13296,7 +13320,7 @@ postcss@^7.0.14, postcss@^7.0.26, postcss@^7.0.32, postcss@^7.0.36, postcss@^7.0
     picocolors "^0.2.1"
     source-map "^0.6.1"
 
-potpack@^1.0.1:
+potpack@^1.0.2:
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/potpack/-/potpack-1.0.2.tgz#23b99e64eb74f5741ffe7656b5b5c4ddce8dfc14"
   integrity sha512-choctRBIV9EMT9WGAZHn3V7t0Z2pMQyl0EZE6pFc/6ml3ssw7Dlf/oAOvFwjm1HVsqfQN8GfeFyJ+d8tRzqueQ==
@@ -14352,11 +14376,6 @@ run-queue@^1.0.0, run-queue@^1.0.3:
   dependencies:
     aproba "^1.1.1"
 
-rw@^1.3.3:
-  version "1.3.3"
-  resolved "https://registry.yarnpkg.com/rw/-/rw-1.3.3.tgz#3f862dfa91ab766b14885ef4d01124bfda074fb4"
-  integrity sha1-P4Yt+pGrdmsUiF700BEkv9oHT7Q=
-
 rxjs@^7.2.0:
   version "7.5.1"
   resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.5.1.tgz#af73df343cbcab37628197f43ea0c8256f54b157"
@@ -15249,10 +15268,10 @@ styled-jsx@5.0.2:
   resolved "https://registry.yarnpkg.com/styled-jsx/-/styled-jsx-5.0.2.tgz#ff230fd593b737e9e68b630a694d460425478729"
   integrity sha512-LqPQrbBh3egD57NBcHET4qcgshPks+yblyhPlH2GY8oaDgKs8SK4C3dBh3oSJjgzJ3G5t1SYEZGHkP+QEpX9EQ==
 
-supercluster@^7.1.0:
-  version "7.1.4"
-  resolved "https://registry.yarnpkg.com/supercluster/-/supercluster-7.1.4.tgz#6762aabfd985d3390b49f13b815567d5116a828a"
-  integrity sha512-GhKkRM1jMR6WUwGPw05fs66pOFWhf59lXq+Q3J3SxPvhNcmgOtLRV6aVQPMRsmXdpaeFJGivt+t7QXUPL3ff4g==
+supercluster@^7.1.5:
+  version "7.1.5"
+  resolved "https://registry.yarnpkg.com/supercluster/-/supercluster-7.1.5.tgz#65a6ce4a037a972767740614c19051b64b8be5a3"
+  integrity sha512-EulshI3pGUM66o6ZdH3ReiFcvHpM3vAigyK+vcxdjpJyEbIIrtbmBdY23mGgnI24uXiGFvrGq9Gkum/8U7vJWg==
   dependencies:
     kdbush "^3.0.0"
 
@@ -16120,7 +16139,7 @@ void-elements@3.1.0:
   resolved "https://registry.yarnpkg.com/void-elements/-/void-elements-3.1.0.tgz#614f7fbf8d801f0bb5f0661f5b2f5785750e4f09"
   integrity sha1-YU9/v42AHwu18GYfWy9XhXUOTwk=
 
-vt-pbf@^3.1.1:
+vt-pbf@^3.1.3:
   version "3.1.3"
   resolved "https://registry.yarnpkg.com/vt-pbf/-/vt-pbf-3.1.3.tgz#68fd150756465e2edae1cc5c048e063916dcfaac"
   integrity sha512-2LzDFzt0mZKZ9IpVF2r69G9bXaP2Q2sArJCmcCgvfTdCCZzSyz4aCLoQyUilu37Ll56tCblIZrXFIjNUpGIlmA==
@@ -16421,7 +16440,7 @@ which-typed-array@^1.1.14, which-typed-array@^1.1.2:
     gopd "^1.0.1"
     has-tostringtag "^1.0.2"
 
-which@^1.2.9:
+which@^1.2.9, which@^1.3.1:
   version "1.3.1"
   resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a"
   integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==
diff --git a/docs/web-frontend.md b/docs/web-frontend.md
index 4c416db72a..21bfef9000 100644
--- a/docs/web-frontend.md
+++ b/docs/web-frontend.md
@@ -21,7 +21,6 @@ NEXT_PUBLIC_API_BASE_URL=http://localhost:8888
 NEXT_PUBLIC_MEDIA_BASE_URL="http://localhost:5001"
 NEXT_PUBLIC_CONSOLE_BASE_URL="http://localhost:10027"
 NEXT_PUBLIC_IS_POST_BETA_ENABLED=true
-NEXT_PUBLIC_MAPBOX_KEY="pk.eyJ1IjoiY291Y2hlcnMiLCJhIjoiY2tpYzY4eHd2MGVpcTJ0bGdhdGhvbHhlbiJ9.u5zvDeFE9H8itTK_dNp7Pg"
 NEXT_PUBLIC_NOMINATIM_URL="https://nominatim.openstreetmap.org/"
 NEXT_PUBLIC_IS_VERIFICATION_ENABLED=true
 NEXT_PUBLIC_IS_COMMUNITIES_PART2_ENABLED=true


Test patch:
diff --git a/app/web/components/EditLocationMap.test.tsx b/app/web/components/EditLocationMap.test.tsx
index 2a905a94b4..d42eb21921 100644
--- a/app/web/components/EditLocationMap.test.tsx
+++ b/app/web/components/EditLocationMap.test.tsx
@@ -1,7 +1,7 @@
 import { render, screen, waitFor } from "@testing-library/react";
 import userEvent from "@testing-library/user-event";
 import MapUI from "components/Map";
-import mapboxgl from "maplibre-gl";
+import { LngLat, Map as MaplibreMap } from "maplibre-gl";
 import { useEffect } from "react";
 import { server } from "test/restMock";
 import { t } from "test/utils";
@@ -11,10 +11,10 @@ import EditLocationMap from "./EditLocationMap";
 jest.mock("components/Map");
 jest.mock("maplibre-gl");
 
-const getCanvasMock = mapboxgl.Map.prototype.getCanvas as jest.Mock;
+const getCanvasMock = MaplibreMap.prototype.getCanvas as jest.Mock;
 const MapMock = MapUI as jest.Mock;
-const wrapMock = mapboxgl.LngLat.prototype.wrap as jest.Mock;
-const getSourceMock = mapboxgl.Map.prototype.getSource as jest.Mock;
+const wrapMock = LngLat.prototype.wrap as jest.Mock;
+const getSourceMock = MaplibreMap.prototype.getSource as jest.Mock;
 
 describe("Edit location map", () => {
   beforeEach(() => {
@@ -33,7 +33,12 @@ describe("Edit location map", () => {
 
     MapMock.mockImplementation(({ postMapInitialize }) => {
       useEffect(() => {
-        postMapInitialize?.(new mapboxgl.Map());
+        postMapInitialize?.(
+          new MaplibreMap({
+            container: document.createElement("div"),
+            style: "mapbox://styles/mapbox/streets-v11",
+          })
+        );
       });
       return <div>Map</div>;
     });