From 0809781d84318c0fb3cd2c4d89a15b1fa8e9f2ef Mon Sep 17 00:00:00 2001 From: Clinton Lunn Date: Sun, 10 Nov 2024 09:46:53 -0700 Subject: [PATCH 01/14] feat: updating url when user clicks a point --- src/app/(maps)/components/FullScreenMap.tsx | 126 +++++++++++++------- src/components/maps/GlobalMap.tsx | 70 ++++++----- 2 files changed, 120 insertions(+), 76 deletions(-) diff --git a/src/app/(maps)/components/FullScreenMap.tsx b/src/app/(maps)/components/FullScreenMap.tsx index 12407e899..753e41f06 100644 --- a/src/app/(maps)/components/FullScreenMap.tsx +++ b/src/app/(maps)/components/FullScreenMap.tsx @@ -2,44 +2,71 @@ import { useCallback, useEffect, useState } from 'react' import { CameraInfo, GlobalMap } from '@/components/maps/GlobalMap' import { usePathname, useRouter, useSearchParams } from 'next/navigation' +import { MapLayerMouseEvent } from 'maplibre-gl' export const FullScreenMap: React.FC = () => { - const [initialCenter, setInitialCenter] = useState<[number, number] | undefined>(undefined) - const [initialZoom, setInitialZoom] = useState(undefined) - const router = useRouter() + const [center, setCenter] = useState<[number, number] | undefined>(undefined) + const [zoom, setZoom] = useState(undefined) + const [isInitialized, setIsInitialized] = useState(false) - const cameraParams = useCameraParams() + const router = useRouter() + const urlParams = useUrlParams() + // Handle initial state setup only once useEffect(() => { - const initialStateFromUrl = cameraParams.fromUrl() + if (isInitialized) return - if (initialStateFromUrl != null) { - setInitialCenter([initialStateFromUrl.center.lng, initialStateFromUrl.center.lat]) - setInitialZoom(initialStateFromUrl.zoom) + const { camera } = urlParams.fromUrl() + + if (camera !== null) { + setCenter([camera.center.lng, camera.center.lat]) + setZoom(camera.zoom) + setIsInitialized(true) return } - getVisitorLocation().then((visitorLocation) => { - if (visitorLocation != null) { - setInitialCenter([visitorLocation.longitude, visitorLocation.latitude]) - } - }).catch(() => { - console.log('Unable to determine user\'s location') - }) - }, []) + getVisitorLocation() + .then((visitorLocation) => { + if (visitorLocation !== null && visitorLocation !== undefined) { + setCenter([visitorLocation.longitude, visitorLocation.latitude]) + setIsInitialized(true) + } + }) + .catch(() => { + console.log('Unable to determine user\'s location') + setIsInitialized(true) + }) + }, [urlParams, isInitialized]) + + const handleCameraMovement = useCallback( + (camera: CameraInfo) => { + const { areaId } = urlParams.fromUrl() + const url = urlParams.toUrl({ camera, areaId }) + router.replace(url, { scroll: false }) + }, + [urlParams, router] + ) - const handleCamerMovement = useCallback((camera: CameraInfo) => { - const url = cameraParams.toUrl(camera) + const handleMapClick = useCallback( + (e: MapLayerMouseEvent) => { + const areaId = e.features?.[0]?.properties?.id + if (areaId == null) { + return + } - router.replace(url, { scroll: false }) - }, []) + const { camera } = urlParams.fromUrl() + const url = urlParams.toUrl({ camera: camera ?? null, areaId }) + router.replace(url, { scroll: false }) + }, + [urlParams, router] + ) return ( ) } @@ -54,29 +81,48 @@ const getVisitorLocation = async (): Promise<{ longitude: number, latitude: numb } } -function useCameraParams (): { toUrl: (camera: CameraInfo) => string, fromUrl: () => CameraInfo | null } { +interface UrlProps { camera: CameraInfo | null, areaId: string | null } + +interface UseUrlParamsReturn { + toUrl: (props: UrlProps) => string + fromUrl: () => UrlProps +} + +const useUrlParams = (): UseUrlParamsReturn => { const pathname = usePathname() - const initialSearchParams = useSearchParams() + const searchParams = useSearchParams() - function toUrl (camera: CameraInfo): string { - const params = new URLSearchParams(initialSearchParams) - params.delete('camera') + const toUrl = ({ camera, areaId }: UrlProps): string => { + const params = new URLSearchParams() - const queryParams = [ - params.toString(), - `camera=${cameraInfoToQuery(camera)}` - ] + if (areaId !== null && areaId !== undefined) { + params.set('areaId', areaId) + } - return `${pathname}?${queryParams.filter(Boolean).join('&')}` - } + const baseUrl = `${pathname}?` + const cameraParam = (camera !== null && camera !== undefined) ? `camera=${cameraInfoToQuery(camera)}` : '' + const otherParams = params.toString() - function fromUrl (): CameraInfo | null { - const cameraParams = initialSearchParams.get('camera') - if (cameraParams == null) { - return null + if (cameraParam !== null && otherParams !== null) { + return `${baseUrl}${cameraParam}&${otherParams}` + } else if (cameraParam !== null) { + return `${baseUrl}${cameraParam}` + } else if (otherParams !== null) { + return `${baseUrl}${otherParams}` } - return queryToCameraInfo(cameraParams) + return pathname + } + + const fromUrl = (): UrlProps => { + const rawUrl = window.location.search + const cameraMatch = rawUrl.match(/[?&]camera=([^&]+)/) + const cameraParam = (cameraMatch !== null) ? cameraMatch[1] : null + + return { + camera: cameraParam !== null ? queryToCameraInfo(cameraParam) : null, + areaId: searchParams.get('areaId') + } } return { toUrl, fromUrl } diff --git a/src/components/maps/GlobalMap.tsx b/src/components/maps/GlobalMap.tsx index 2076aaa69..3472c8658 100644 --- a/src/components/maps/GlobalMap.tsx +++ b/src/components/maps/GlobalMap.tsx @@ -11,7 +11,6 @@ import { OBCustomLayers } from './OBCustomLayers' import { tileToFeature } from './utils' import { ActiveFeature, TileProps } from './TileTypes' import MapLayersSelector from './MapLayersSelector' -import { debounce } from 'underscore' import { MapToolbar } from './MapToolbar' import { SelectedFeature } from './AreaActiveMarker' @@ -27,12 +26,14 @@ interface FeatureState { selected?: boolean hover?: boolean } + export interface DataLayersDisplayState { areaBoundaries: boolean organizations: boolean heatmap: boolean crags: boolean } + interface GlobalMapProps { showFullscreenControl?: boolean initialCenter?: [number, number] @@ -43,13 +44,14 @@ interface GlobalMapProps { } onCameraMovement?: (camera: CameraInfo) => void children?: React.ReactNode + handleOnClick?: (e: MapLayerMouseEvent) => void } /** * Global map */ export const GlobalMap: React.FC = ({ - showFullscreenControl = true, initialCenter, initialZoom, initialViewState, onCameraMovement, children + showFullscreenControl = true, initialCenter, initialZoom, initialViewState, onCameraMovement, children, handleOnClick }) => { const [clickInfo, setClickInfo] = useState(null) const [hoverInfo, setHoverInfo] = useState(null) @@ -72,30 +74,31 @@ export const GlobalMap: React.FC = ({ }, fState) } - const onMove = useCallback(debounce((e: ViewStateChangeEvent) => { - if (onCameraMovement != null) { - onCameraMovement({ - center: { - lat: e.viewState.latitude, - lng: e.viewState.longitude - }, - zoom: e.viewState.zoom - }) - } - }, 300), []) + const onMove = useCallback((e: ViewStateChangeEvent) => { + if ((mapInstance == null) || e.viewState == null || (onCameraMovement == null)) return + onCameraMovement({ + center: { + lat: e.viewState.latitude, + lng: e.viewState.longitude + }, + zoom: e.viewState.zoom + }) + }, [mapInstance, onCameraMovement]) const onLoad = useCallback((e: MapLibreEvent) => { if (e.target == null) return setMapInstance(e.target) - if (initialCenter != null) { + + // Only apply jumpTo if initial values are defined + if (initialCenter != null && initialZoom != null) { e.target.jumpTo({ center: initialCenter, zoom: initialZoom ?? 6 }) } else if (initialViewState != null) { e.target.fitBounds(initialViewState.bounds, initialViewState.fitBoundsOptions) } - }, [initialCenter, initialZoom]) + }, [initialCenter, initialZoom, initialViewState]) /** - * Handle click event on the map. Place a market on the map and activate the side drawer. + * Handle click event on the map. Place a marker on the map and activate the side drawer. */ const onClick = (event: MapLayerMouseEvent): void => { if (mapInstance == null) return @@ -104,7 +107,7 @@ export const GlobalMap: React.FC = ({ setClickInfo(null) } else { const { layer, geometry, properties } = feature - + handleOnClick?.(event) setClickInfo(prev => { setActiveFeatureVisual(prev, { selected: false, hover: false }) const activeFeature = tileToFeature(layer.id, event.point, geometry, properties as TileProps, mapInstance) @@ -115,7 +118,7 @@ export const GlobalMap: React.FC = ({ } /** - * Handle click event on the popover. Behave as if the user clicked on a feature on the map. + * Handle click event on the popover. Behave as if the user clicked on a feature on the map. */ const onHoverCardClick = (feature: ActiveFeature): void => { setClickInfo(prevFeature => { @@ -129,10 +132,15 @@ export const GlobalMap: React.FC = ({ } /** - * Handle mouseover event on the map. Show the popover with the area info. + * Handle mouseover event on the map. Show the popover with the area info. */ const onHover = (event: MapLayerMouseEvent): void => { - const obLayerId = event.features?.findIndex((f) => f.layer.id === 'crag-markers' || f.layer.id === 'crag-name-labels' || f.layer.id === 'area-boundaries' || f.layer.id === 'area-background') ?? -1 + const obLayerId = event.features?.findIndex((f) => + f.layer.id === 'crag-markers' || + f.layer.id === 'crag-name-labels' || + f.layer.id === 'area-boundaries' || + f.layer.id === 'area-background' + ) ?? -1 if (obLayerId !== -1) { setCursor('pointer') @@ -163,13 +171,9 @@ export const GlobalMap: React.FC = ({ { - setCursor('move') - }} + onDragStart={() => setCursor('move')} onMove={onMove} - onDragEnd={() => { - setCursor('default') - }} + onDragEnd={() => setCursor('default')} onMouseEnter={onHover} onMouseLeave={() => { setHoverInfo(prev => { @@ -188,25 +192,19 @@ export const GlobalMap: React.FC = ({ - {showFullscreenControl && } - {clickInfo != null && - } + {clickInfo != null && } {hoverInfo != null && ( - )} + + )} {children} From 7723635e6290e714d56438ee1bc71fa8a600f787 Mon Sep 17 00:00:00 2001 From: Clinton Lunn Date: Sun, 10 Nov 2024 18:36:01 -0700 Subject: [PATCH 02/14] feat: open drawer on page load when areaId provided --- src/app/(maps)/components/FullScreenMap.tsx | 24 ++++++++----- src/components/maps/GlobalMap.tsx | 39 +++++++++++++++++++-- 2 files changed, 52 insertions(+), 11 deletions(-) diff --git a/src/app/(maps)/components/FullScreenMap.tsx b/src/app/(maps)/components/FullScreenMap.tsx index 753e41f06..857ecb250 100644 --- a/src/app/(maps)/components/FullScreenMap.tsx +++ b/src/app/(maps)/components/FullScreenMap.tsx @@ -7,6 +7,7 @@ import { MapLayerMouseEvent } from 'maplibre-gl' export const FullScreenMap: React.FC = () => { const [center, setCenter] = useState<[number, number] | undefined>(undefined) const [zoom, setZoom] = useState(undefined) + const [initialAreaId, setInitialAreaId] = useState(undefined) const [isInitialized, setIsInitialized] = useState(false) const router = useRouter() @@ -17,6 +18,11 @@ export const FullScreenMap: React.FC = () => { if (isInitialized) return const { camera } = urlParams.fromUrl() + const { areaId } = urlParams.fromUrl() + + if (areaId !== null) { + setInitialAreaId(areaId) + } if (camera !== null) { setCenter([camera.center.lng, camera.center.lat]) @@ -25,17 +31,17 @@ export const FullScreenMap: React.FC = () => { return } - getVisitorLocation() - .then((visitorLocation) => { + getVisitorLocation().then( + (visitorLocation) => { if (visitorLocation !== null && visitorLocation !== undefined) { setCenter([visitorLocation.longitude, visitorLocation.latitude]) setIsInitialized(true) } - }) - .catch(() => { - console.log('Unable to determine user\'s location') - setIsInitialized(true) - }) + } + ).catch(() => { + console.log('Unable to determine user\'s location') + setIsInitialized(true) + }) }, [urlParams, isInitialized]) const handleCameraMovement = useCallback( @@ -57,12 +63,12 @@ export const FullScreenMap: React.FC = () => { const { camera } = urlParams.fromUrl() const url = urlParams.toUrl({ camera: camera ?? null, areaId }) router.replace(url, { scroll: false }) - }, - [urlParams, router] + }, [urlParams, router] ) return ( void children?: React.ReactNode handleOnClick?: (e: MapLayerMouseEvent) => void + initialAreaId?: string } /** * Global map */ export const GlobalMap: React.FC = ({ - showFullscreenControl = true, initialCenter, initialZoom, initialViewState, onCameraMovement, children, handleOnClick + showFullscreenControl = true, initialCenter, initialZoom, initialViewState, onCameraMovement, children, handleOnClick, initialAreaId }) => { const [clickInfo, setClickInfo] = useState(null) const [hoverInfo, setHoverInfo] = useState(null) const [mapInstance, setMapInstance] = useState(null) const [cursor, setCursor] = useState('default') const [mapStyle, setMapStyle] = useState(MAP_STYLES.light.style) + const [isSourceLoaded, setIsSourceLoaded] = useState(false) const [dataLayersDisplayState, setDataLayersDisplayState] = useState({ areaBoundaries: false, organizations: false, @@ -166,6 +168,39 @@ export const GlobalMap: React.FC = ({ setMapStyle(style.style) } + const findAreaById = useCallback((map: maplibregl.Map, areaId: string) => { + const features = map.querySourceFeatures('crags', { + sourceLayer: 'crags', + filter: ['==', ['get', 'id'], areaId] + }) + return features[0] // return first feature because it could be duplicated by the tileset + }, []) + + useEffect(() => { + if (mapInstance == null) return + + if (!isSourceLoaded) { + mapInstance.on('sourcedata', (e) => { + if (e.sourceId === 'crags' && e.isSourceLoaded) { + setIsSourceLoaded(true) + } + }) + } + + if (isSourceLoaded && initialAreaId !== undefined) { + const feature = findAreaById(mapInstance, initialAreaId) + if (feature != null) { + setClickInfo(prev => { + setActiveFeatureVisual(prev, { selected: false, hover: false }) + + const activeFeature = tileToFeature('crag-name-labels', { x: 0, y: 0 }, feature.geometry, feature.properties as TileProps, mapInstance) + setActiveFeatureVisual(activeFeature, { selected: true, hover: false }) + return activeFeature + }) + } + } + }, [mapInstance, isSourceLoaded, initialAreaId, findAreaById]) + return (
Date: Sun, 10 Nov 2024 18:40:30 -0700 Subject: [PATCH 03/14] refactor: rename areaId state variable --- src/app/(maps)/components/FullScreenMap.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/app/(maps)/components/FullScreenMap.tsx b/src/app/(maps)/components/FullScreenMap.tsx index 857ecb250..419cf541f 100644 --- a/src/app/(maps)/components/FullScreenMap.tsx +++ b/src/app/(maps)/components/FullScreenMap.tsx @@ -7,7 +7,7 @@ import { MapLayerMouseEvent } from 'maplibre-gl' export const FullScreenMap: React.FC = () => { const [center, setCenter] = useState<[number, number] | undefined>(undefined) const [zoom, setZoom] = useState(undefined) - const [initialAreaId, setInitialAreaId] = useState(undefined) + const [areaId, setAreaId] = useState(undefined) const [isInitialized, setIsInitialized] = useState(false) const router = useRouter() @@ -21,7 +21,7 @@ export const FullScreenMap: React.FC = () => { const { areaId } = urlParams.fromUrl() if (areaId !== null) { - setInitialAreaId(areaId) + setAreaId(areaId) } if (camera !== null) { @@ -68,7 +68,7 @@ export const FullScreenMap: React.FC = () => { return ( Date: Mon, 11 Nov 2024 08:55:45 -0700 Subject: [PATCH 04/14] refactor: add share button, add default camera position if one not provided --- src/app/(maps)/components/FullScreenMap.tsx | 68 +++++++++++++++---- .../maps/TileHandlers/CragContent.tsx | 1 + src/components/maps/TileHandlers/Drawer.tsx | 8 ++- 3 files changed, 59 insertions(+), 18 deletions(-) diff --git a/src/app/(maps)/components/FullScreenMap.tsx b/src/app/(maps)/components/FullScreenMap.tsx index 419cf541f..d7b85b6b6 100644 --- a/src/app/(maps)/components/FullScreenMap.tsx +++ b/src/app/(maps)/components/FullScreenMap.tsx @@ -17,13 +17,15 @@ export const FullScreenMap: React.FC = () => { useEffect(() => { if (isInitialized) return - const { camera } = urlParams.fromUrl() - const { areaId } = urlParams.fromUrl() + const { camera, areaId: urlAreaId } = urlParams.fromUrl() + const DEFAULT_CENTER: [number, number] = [-98.5795, 39.8283] // Center of US + const DEFAULT_ZOOM = 3 - if (areaId !== null) { - setAreaId(areaId) + if (urlAreaId !== null) { + setAreaId(urlAreaId) } + // If camera params exist in URL, use them if (camera !== null) { setCenter([camera.center.lng, camera.center.lat]) setZoom(camera.zoom) @@ -31,18 +33,54 @@ export const FullScreenMap: React.FC = () => { return } - getVisitorLocation().then( - (visitorLocation) => { - if (visitorLocation !== null && visitorLocation !== undefined) { - setCenter([visitorLocation.longitude, visitorLocation.latitude]) - setIsInitialized(true) + // If no camera params, get visitor location and set URL + setZoom(DEFAULT_ZOOM) + getVisitorLocation() + .then((visitorLocation) => { + const newCenter: [number, number] = (visitorLocation != null) + ? [visitorLocation.longitude, visitorLocation.latitude] + : DEFAULT_CENTER + + setCenter(newCenter) + + // Always update URL with camera position + const newCamera: CameraInfo = { + center: { + lng: newCenter[0], + lat: newCenter[1] + }, + zoom: DEFAULT_ZOOM } - } - ).catch(() => { - console.log('Unable to determine user\'s location') - setIsInitialized(true) - }) - }, [urlParams, isInitialized]) + + const url = urlParams.toUrl({ + camera: newCamera, + areaId: urlAreaId + }) + router.replace(url, { scroll: false }) + }) + .catch(() => { + console.log('Unable to determine user\'s location') + setCenter(DEFAULT_CENTER) + + // Set URL with default camera position on error + const defaultCamera: CameraInfo = { + center: { + lng: DEFAULT_CENTER[0], + lat: DEFAULT_CENTER[1] + }, + zoom: DEFAULT_ZOOM + } + + const url = urlParams.toUrl({ + camera: defaultCamera, + areaId: urlAreaId + }) + router.replace(url, { scroll: false }) + }) + .finally(() => { + setIsInitialized(true) + }) + }, [urlParams, isInitialized, router]) const handleCameraMovement = useCallback( (camera: CameraInfo) => { diff --git a/src/components/maps/TileHandlers/CragContent.tsx b/src/components/maps/TileHandlers/CragContent.tsx index 99cf012bf..a2061940a 100644 --- a/src/components/maps/TileHandlers/CragContent.tsx +++ b/src/components/maps/TileHandlers/CragContent.tsx @@ -15,6 +15,7 @@ export const CragDrawerContent: React.FC = ({ id, areaNam heading={{areaName}} subheading={} cta={Edit area} + share={} >
{description == null || description.trim() === '' diff --git a/src/components/maps/TileHandlers/Drawer.tsx b/src/components/maps/TileHandlers/Drawer.tsx index fe23843af..a4e38ad24 100644 --- a/src/components/maps/TileHandlers/Drawer.tsx +++ b/src/components/maps/TileHandlers/Drawer.tsx @@ -33,7 +33,7 @@ export const Drawer: React.FC<{ feature: ActiveFeature | null, onClose?: () => v ) } -export const BaseDrawerContent: React.FC<{ media: ReactNode, heading: ReactNode, subheading: ReactNode, cta: ReactNode, children: ReactNode }> = ({ media, heading, subheading, cta, children }) => { +export const BaseDrawerContent: React.FC<{ media: ReactNode, heading: ReactNode, subheading: ReactNode, cta: ReactNode, children: ReactNode, share?: ReactNode }> = ({ media, heading, subheading, cta, children, share }) => { return (
@@ -46,8 +46,10 @@ export const BaseDrawerContent: React.FC<{ media: ReactNode, heading: ReactNode,
{subheading}
- - {cta} +
+ {cta} + {share} +

From 92b33bc8083407ef780171782d73a6598c910b9b Mon Sep 17 00:00:00 2001 From: Clinton Lunn Date: Mon, 11 Nov 2024 12:28:12 -0700 Subject: [PATCH 05/14] refactor: grab camera params directly, disable cooperativegestures, add defauilt camera position --- src/app/(maps)/components/FullScreenMap.tsx | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/app/(maps)/components/FullScreenMap.tsx b/src/app/(maps)/components/FullScreenMap.tsx index d7b85b6b6..8559b7ed4 100644 --- a/src/app/(maps)/components/FullScreenMap.tsx +++ b/src/app/(maps)/components/FullScreenMap.tsx @@ -9,6 +9,8 @@ export const FullScreenMap: React.FC = () => { const [zoom, setZoom] = useState(undefined) const [areaId, setAreaId] = useState(undefined) const [isInitialized, setIsInitialized] = useState(false) + const DEFAULT_CENTER: [number, number] = [0, 0] + const DEFAULT_ZOOM = 2 const router = useRouter() const urlParams = useUrlParams() @@ -18,8 +20,6 @@ export const FullScreenMap: React.FC = () => { if (isInitialized) return const { camera, areaId: urlAreaId } = urlParams.fromUrl() - const DEFAULT_CENTER: [number, number] = [-98.5795, 39.8283] // Center of US - const DEFAULT_ZOOM = 3 if (urlAreaId !== null) { setAreaId(urlAreaId) @@ -106,6 +106,7 @@ export const FullScreenMap: React.FC = () => { return ( { } const fromUrl = (): UrlProps => { - const rawUrl = window.location.search - const cameraMatch = rawUrl.match(/[?&]camera=([^&]+)/) - const cameraParam = (cameraMatch !== null) ? cameraMatch[1] : null - + const cameraParam = searchParams.get('camera') return { camera: cameraParam !== null ? queryToCameraInfo(cameraParam) : null, areaId: searchParams.get('areaId') From 16d6d1d01c4326570deca7802fe580fc5a6247cc Mon Sep 17 00:00:00 2001 From: Clinton Lunn Date: Fri, 15 Nov 2024 20:15:38 -0700 Subject: [PATCH 06/14] refactor: populate areaid on hover card click --- src/app/(maps)/components/FullScreenMap.tsx | 67 +------------------- src/components/maps/GlobalMap.tsx | 13 +++- src/js/hooks/useUrlParams.tsx | 68 +++++++++++++++++++++ 3 files changed, 82 insertions(+), 66 deletions(-) create mode 100644 src/js/hooks/useUrlParams.tsx diff --git a/src/app/(maps)/components/FullScreenMap.tsx b/src/app/(maps)/components/FullScreenMap.tsx index 8559b7ed4..1605acf2b 100644 --- a/src/app/(maps)/components/FullScreenMap.tsx +++ b/src/app/(maps)/components/FullScreenMap.tsx @@ -1,8 +1,9 @@ 'use client' import { useCallback, useEffect, useState } from 'react' import { CameraInfo, GlobalMap } from '@/components/maps/GlobalMap' -import { usePathname, useRouter, useSearchParams } from 'next/navigation' +import { useRouter } from 'next/navigation' import { MapLayerMouseEvent } from 'maplibre-gl' +import { useUrlParams } from '@/js/hooks/useUrlParams' export const FullScreenMap: React.FC = () => { const [center, setCenter] = useState<[number, number] | undefined>(undefined) @@ -125,67 +126,3 @@ const getVisitorLocation = async (): Promise<{ longitude: number, latitude: numb return undefined } } - -interface UrlProps { camera: CameraInfo | null, areaId: string | null } - -interface UseUrlParamsReturn { - toUrl: (props: UrlProps) => string - fromUrl: () => UrlProps -} - -const useUrlParams = (): UseUrlParamsReturn => { - const pathname = usePathname() - const searchParams = useSearchParams() - - const toUrl = ({ camera, areaId }: UrlProps): string => { - const params = new URLSearchParams() - - if (areaId !== null && areaId !== undefined) { - params.set('areaId', areaId) - } - - const baseUrl = `${pathname}?` - const cameraParam = (camera !== null && camera !== undefined) ? `camera=${cameraInfoToQuery(camera)}` : '' - const otherParams = params.toString() - - if (cameraParam !== null && otherParams !== null) { - return `${baseUrl}${cameraParam}&${otherParams}` - } else if (cameraParam !== null) { - return `${baseUrl}${cameraParam}` - } else if (otherParams !== null) { - return `${baseUrl}${otherParams}` - } - - return pathname - } - - const fromUrl = (): UrlProps => { - const cameraParam = searchParams.get('camera') - return { - camera: cameraParam !== null ? queryToCameraInfo(cameraParam) : null, - areaId: searchParams.get('areaId') - } - } - - return { toUrl, fromUrl } -} - -const cameraInfoToQuery = ({ zoom, center }: CameraInfo): string => { - return `${Math.ceil(zoom)}/${center.lat.toFixed(5)}/${center.lng.toFixed(5)}` -} - -const queryToCameraInfo = (cameraParam: string): CameraInfo | null => { - const [zoomRaw, latitude, longitude] = cameraParam.split('/') - const lat = parseFloat(latitude) - const lng = parseFloat(longitude) - const zoom = parseInt(zoomRaw, 10) - - if ([lat, lng, zoom].some(isNaN)) { - return null - } - - return { - center: { lat, lng }, - zoom - } -} diff --git a/src/components/maps/GlobalMap.tsx b/src/components/maps/GlobalMap.tsx index fef5716d6..5c4935fc9 100644 --- a/src/components/maps/GlobalMap.tsx +++ b/src/components/maps/GlobalMap.tsx @@ -3,7 +3,6 @@ import { useCallback, useEffect, useState } from 'react' import { Map, FullscreenControl, ScaleControl, NavigationControl, MapLayerMouseEvent, ViewStateChangeEvent, GeolocateControl } from 'react-map-gl/maplibre' import maplibregl, { MapLibreEvent } from 'maplibre-gl' import dynamic from 'next/dynamic' - import { MAP_STYLES, type MapStyles } from './MapSelector' import { Drawer } from './TileHandlers/Drawer' import { HoverCard } from './TileHandlers/HoverCard' @@ -13,6 +12,8 @@ import { ActiveFeature, TileProps } from './TileTypes' import MapLayersSelector from './MapLayersSelector' import { MapToolbar } from './MapToolbar' import { SelectedFeature } from './AreaActiveMarker' +import { useRouter } from 'next/navigation' +import { useUrlParams } from '@/js/hooks/useUrlParams' export interface CameraInfo { center: { @@ -66,6 +67,8 @@ export const GlobalMap: React.FC = ({ heatmap: false, crags: true }) + const router = useRouter() + const urlParams = useUrlParams() const setActiveFeatureVisual = (feature: ActiveFeature | null, fState: FeatureState): void => { if (feature == null || mapInstance == null) return @@ -123,6 +126,14 @@ export const GlobalMap: React.FC = ({ * Handle click event on the popover. Behave as if the user clicked on a feature on the map. */ const onHoverCardClick = (feature: ActiveFeature): void => { + const areaId = feature.data?.id + if (areaId == null) { + return + } + + const { camera } = urlParams.fromUrl() + const url = urlParams.toUrl({ camera: camera ?? null, areaId }) + router.replace(url, { scroll: false }) setClickInfo(prevFeature => { setHoverInfo(null) setActiveFeatureVisual(prevFeature, { selected: false, hover: false }) diff --git a/src/js/hooks/useUrlParams.tsx b/src/js/hooks/useUrlParams.tsx new file mode 100644 index 000000000..27425b308 --- /dev/null +++ b/src/js/hooks/useUrlParams.tsx @@ -0,0 +1,68 @@ +import { CameraInfo } from '@/components/maps/GlobalMap' +import { usePathname, useSearchParams } from 'next/navigation' + +interface UrlProps { camera: CameraInfo | null, areaId: string | null } + +interface UseUrlParamsReturn { + toUrl: (props: UrlProps) => string + fromUrl: () => UrlProps +} + +const useUrlParams = (): UseUrlParamsReturn => { + const pathname = usePathname() + const searchParams = useSearchParams() + + const toUrl = ({ camera, areaId }: UrlProps): string => { + const params = new URLSearchParams() + + if (areaId !== null && areaId !== undefined) { + params.set('areaId', areaId) + } + + const baseUrl = `${pathname}?` + const cameraParam = (camera !== null && camera !== undefined) ? `camera=${cameraInfoToQuery(camera)}` : '' + const otherParams = params.toString() + + if (cameraParam !== null && otherParams !== null) { + return `${baseUrl}${cameraParam}&${otherParams}` + } else if (cameraParam !== null) { + return `${baseUrl}${cameraParam}` + } else if (otherParams !== null) { + return `${baseUrl}${otherParams}` + } + + return pathname + } + + const fromUrl = (): UrlProps => { + const cameraParam = searchParams.get('camera') + return { + camera: cameraParam !== null ? queryToCameraInfo(cameraParam) : null, + areaId: searchParams.get('areaId') + } + } + + return { toUrl, fromUrl } +} + +const cameraInfoToQuery = ({ zoom, center }: CameraInfo): string => { + return `${Math.ceil(zoom)}/${center.lat.toFixed(5)}/${center.lng.toFixed(5)}` +} + +const queryToCameraInfo = (cameraParam: string): CameraInfo | null => { + const [zoomRaw, latitude, longitude] = cameraParam.split('/') + const lat = parseFloat(latitude) + const lng = parseFloat(longitude) + const zoom = parseInt(zoomRaw, 10) + + if ([lat, lng, zoom].some(isNaN)) { + return null + } + + return { + center: { lat, lng }, + zoom + } +} + +export { useUrlParams } From 510c6e952ed2701d61a32688f227bc6d51b3c862 Mon Sep 17 00:00:00 2001 From: Clinton Lunn Date: Sat, 16 Nov 2024 12:05:48 -0700 Subject: [PATCH 07/14] feat: add share button, move toastifyprovider to a common area --- src/app/(default)/layout.tsx | 2 +- src/app/(maps)/layout.tsx | 2 + .../maps/TileHandlers/CragContent.tsx | 3 +- .../toast}/ReactToastifyProvider.tsx | 0 src/components/ui/ShareButton.tsx | 42 +++++++++++++++++++ 5 files changed, 47 insertions(+), 2 deletions(-) rename src/{app/(default)/components => components/toast}/ReactToastifyProvider.tsx (100%) create mode 100644 src/components/ui/ShareButton.tsx diff --git a/src/app/(default)/layout.tsx b/src/app/(default)/layout.tsx index 6a8f7e8c0..d3cfad4dc 100644 --- a/src/app/(default)/layout.tsx +++ b/src/app/(default)/layout.tsx @@ -6,7 +6,7 @@ import '../global.css' import Header from './header' import { PageFooter } from './components/PageFooter' import { NextAuthProvider } from '@/components/auth/NextAuthProvider' -import { ReactToastifyProvider } from './components/ReactToastifyProvider' +import { ReactToastifyProvider } from '@/components/toast/ReactToastifyProvider' import { BlockingAlertUploadingInProgress } from './components/ui/GlobalAlerts' import { OnboardingCheck } from '@/components/auth/OnboardingCheck' diff --git a/src/app/(maps)/layout.tsx b/src/app/(maps)/layout.tsx index c4d83e6f3..bddc39cc7 100644 --- a/src/app/(maps)/layout.tsx +++ b/src/app/(maps)/layout.tsx @@ -2,6 +2,7 @@ import 'maplibre-gl/dist/maplibre-gl.css' import { Metadata } from 'next' import '@/public/fonts/fonts.css' import './../global.css' +import { ReactToastifyProvider } from '@/components/toast/ReactToastifyProvider' /** * Root layout for `/maps` route @@ -15,6 +16,7 @@ export default function MapsRootLayout ({ {children} + ) diff --git a/src/components/maps/TileHandlers/CragContent.tsx b/src/components/maps/TileHandlers/CragContent.tsx index a2061940a..4ff46a907 100644 --- a/src/components/maps/TileHandlers/CragContent.tsx +++ b/src/components/maps/TileHandlers/CragContent.tsx @@ -4,6 +4,7 @@ import { getAreaPageFriendlyUrl } from '@/js/utils' import { EntityIcon } from '@/app/(default)/editArea/[slug]/general/components/AreaItem' import { BaseDrawerContent } from './Drawer' import { MiniCarousel } from '../CardGallery' +import { ShareButton } from '../../ui/ShareButton' export const CragDrawerContent: React.FC = ({ id, areaName, climbs, content: { description }, media }) => { const friendlyUrl = getAreaPageFriendlyUrl(id, areaName) @@ -15,7 +16,7 @@ export const CragDrawerContent: React.FC = ({ id, areaNam heading={{areaName}} subheading={} cta={Edit area} - share={} + share={} >
{description == null || description.trim() === '' diff --git a/src/app/(default)/components/ReactToastifyProvider.tsx b/src/components/toast/ReactToastifyProvider.tsx similarity index 100% rename from src/app/(default)/components/ReactToastifyProvider.tsx rename to src/components/toast/ReactToastifyProvider.tsx diff --git a/src/components/ui/ShareButton.tsx b/src/components/ui/ShareButton.tsx new file mode 100644 index 000000000..b43bf0010 --- /dev/null +++ b/src/components/ui/ShareButton.tsx @@ -0,0 +1,42 @@ +'use client' + +import { Share } from '@phosphor-icons/react' +import { usePathname, useSearchParams } from 'next/navigation' +import React from 'react' +import { toast } from 'react-toastify' + +const ShareButton: React.FC = () => { + const pathname = usePathname() + const searchParams = useSearchParams() + + const handleShareClick = (): void => { + const fullUrl = `${window.location.origin}${pathname}${searchParams.toString() !== '' ? '?' + searchParams.toString() : ''}` + + if (typeof navigator.clipboard === 'undefined') { + toast.error('Clipboard API is not supported in your browser') + return + } + + navigator.clipboard.writeText(fullUrl) + .then(() => { + toast.success('URL copied to clipboard!') + }) + .catch((error) => { + console.error('Error copying to clipboard:', error) + toast.error('Failed to copy URL to clipboard') + }) + } + + return ( + + ) +} + +export { ShareButton } From ef6181a4b3e4d437db94209103fc49f160f3e30a Mon Sep 17 00:00:00 2001 From: Clinton Lunn Date: Sat, 16 Nov 2024 12:29:03 -0700 Subject: [PATCH 08/14] refactor: use === --- src/app/(maps)/components/FullScreenMap.tsx | 2 +- src/components/maps/GlobalMap.tsx | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/app/(maps)/components/FullScreenMap.tsx b/src/app/(maps)/components/FullScreenMap.tsx index 1605acf2b..8d0e59ea7 100644 --- a/src/app/(maps)/components/FullScreenMap.tsx +++ b/src/app/(maps)/components/FullScreenMap.tsx @@ -95,7 +95,7 @@ export const FullScreenMap: React.FC = () => { const handleMapClick = useCallback( (e: MapLayerMouseEvent) => { const areaId = e.features?.[0]?.properties?.id - if (areaId == null) { + if (areaId === '') { return } diff --git a/src/components/maps/GlobalMap.tsx b/src/components/maps/GlobalMap.tsx index 5c4935fc9..09725f0e3 100644 --- a/src/components/maps/GlobalMap.tsx +++ b/src/components/maps/GlobalMap.tsx @@ -71,7 +71,7 @@ export const GlobalMap: React.FC = ({ const urlParams = useUrlParams() const setActiveFeatureVisual = (feature: ActiveFeature | null, fState: FeatureState): void => { - if (feature == null || mapInstance == null) return + if (feature === null || mapInstance === null) return mapInstance.setFeatureState({ source: 'areas', sourceLayer: 'areas', @@ -80,7 +80,7 @@ export const GlobalMap: React.FC = ({ } const onMove = useCallback((e: ViewStateChangeEvent) => { - if ((mapInstance == null) || e.viewState == null || (onCameraMovement == null)) return + if ((mapInstance === null) || e.viewState === null || (onCameraMovement === undefined)) return onCameraMovement({ center: { lat: e.viewState.latitude, @@ -91,7 +91,7 @@ export const GlobalMap: React.FC = ({ }, [mapInstance, onCameraMovement]) const onLoad = useCallback((e: MapLibreEvent) => { - if (e.target == null) return + if (e.target === null) return setMapInstance(e.target) // Only apply jumpTo if initial values are defined @@ -106,9 +106,9 @@ export const GlobalMap: React.FC = ({ * Handle click event on the map. Place a marker on the map and activate the side drawer. */ const onClick = (event: MapLayerMouseEvent): void => { - if (mapInstance == null) return + if (mapInstance === null) return const feature = event?.features?.[0] - if (feature == null) { + if (feature === undefined) { setClickInfo(null) } else { const { layer, geometry, properties } = feature @@ -127,7 +127,7 @@ export const GlobalMap: React.FC = ({ */ const onHoverCardClick = (feature: ActiveFeature): void => { const areaId = feature.data?.id - if (areaId == null) { + if (areaId === '') { return } @@ -188,7 +188,7 @@ export const GlobalMap: React.FC = ({ }, []) useEffect(() => { - if (mapInstance == null) return + if (mapInstance === null) return if (!isSourceLoaded) { mapInstance.on('sourcedata', (e) => { From a8a995d72e9daa6310f79df2a6f54e109fbbe0ee Mon Sep 17 00:00:00 2001 From: Clinton Lunn Date: Sat, 16 Nov 2024 15:33:38 -0700 Subject: [PATCH 09/14] refactor: use onmoveend instead of onmove to handle camera in url --- src/components/maps/GlobalMap.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/maps/GlobalMap.tsx b/src/components/maps/GlobalMap.tsx index 09725f0e3..908e695df 100644 --- a/src/components/maps/GlobalMap.tsx +++ b/src/components/maps/GlobalMap.tsx @@ -79,7 +79,7 @@ export const GlobalMap: React.FC = ({ }, fState) } - const onMove = useCallback((e: ViewStateChangeEvent) => { + const onMoveEnd = useCallback((e: ViewStateChangeEvent) => { if ((mapInstance === null) || e.viewState === null || (onCameraMovement === undefined)) return onCameraMovement({ center: { @@ -218,7 +218,7 @@ export const GlobalMap: React.FC = ({ id='global-map' onLoad={onLoad} onDragStart={() => setCursor('move')} - onMove={onMove} + onMoveEnd={onMoveEnd} onDragEnd={() => setCursor('default')} onMouseEnter={onHover} onMouseLeave={() => { From 47e4bd2d3109c08ad8dc5edd77d5ec95ff36549c Mon Sep 17 00:00:00 2001 From: Clinton Lunn Date: Mon, 25 Nov 2024 11:44:57 -0700 Subject: [PATCH 10/14] refactor: use != instead of !== --- src/app/(maps)/components/FullScreenMap.tsx | 4 ++-- src/js/hooks/useUrlParams.tsx | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/app/(maps)/components/FullScreenMap.tsx b/src/app/(maps)/components/FullScreenMap.tsx index 8d0e59ea7..d1f3eb32d 100644 --- a/src/app/(maps)/components/FullScreenMap.tsx +++ b/src/app/(maps)/components/FullScreenMap.tsx @@ -22,12 +22,12 @@ export const FullScreenMap: React.FC = () => { const { camera, areaId: urlAreaId } = urlParams.fromUrl() - if (urlAreaId !== null) { + if (urlAreaId != null) { setAreaId(urlAreaId) } // If camera params exist in URL, use them - if (camera !== null) { + if (camera != null) { setCenter([camera.center.lng, camera.center.lat]) setZoom(camera.zoom) setIsInitialized(true) diff --git a/src/js/hooks/useUrlParams.tsx b/src/js/hooks/useUrlParams.tsx index 27425b308..15850b601 100644 --- a/src/js/hooks/useUrlParams.tsx +++ b/src/js/hooks/useUrlParams.tsx @@ -15,19 +15,19 @@ const useUrlParams = (): UseUrlParamsReturn => { const toUrl = ({ camera, areaId }: UrlProps): string => { const params = new URLSearchParams() - if (areaId !== null && areaId !== undefined) { + if (areaId != null) { params.set('areaId', areaId) } const baseUrl = `${pathname}?` - const cameraParam = (camera !== null && camera !== undefined) ? `camera=${cameraInfoToQuery(camera)}` : '' + const cameraParam = (camera != null) ? `camera=${cameraInfoToQuery(camera)}` : '' const otherParams = params.toString() - if (cameraParam !== null && otherParams !== null) { + if (cameraParam != null && otherParams != null) { return `${baseUrl}${cameraParam}&${otherParams}` - } else if (cameraParam !== null) { + } else if (cameraParam != null) { return `${baseUrl}${cameraParam}` - } else if (otherParams !== null) { + } else if (otherParams != null) { return `${baseUrl}${otherParams}` } @@ -37,7 +37,7 @@ const useUrlParams = (): UseUrlParamsReturn => { const fromUrl = (): UrlProps => { const cameraParam = searchParams.get('camera') return { - camera: cameraParam !== null ? queryToCameraInfo(cameraParam) : null, + camera: cameraParam != null ? queryToCameraInfo(cameraParam) : null, areaId: searchParams.get('areaId') } } From 2c994cd1a7f8a9892b57aa3e5e2cab1ffb013c0b Mon Sep 17 00:00:00 2001 From: Clinton Lunn Date: Tue, 26 Nov 2024 20:00:09 -0500 Subject: [PATCH 11/14] refactor: reuse SharePageUrlButton --- .../components/SharePageURLButton.tsx | 4 +- .../maps/TileHandlers/CragContent.tsx | 7 +++- src/components/ui/ShareButton.tsx | 42 ------------------- 3 files changed, 8 insertions(+), 45 deletions(-) delete mode 100644 src/components/ui/ShareButton.tsx diff --git a/src/app/(default)/components/SharePageURLButton.tsx b/src/app/(default)/components/SharePageURLButton.tsx index 1233ef03d..43500a25b 100644 --- a/src/app/(default)/components/SharePageURLButton.tsx +++ b/src/app/(default)/components/SharePageURLButton.tsx @@ -10,7 +10,9 @@ import { ControlledTooltip } from '@/components/ui/Tooltip' */ export const SharePageURLButton: React.FC<{ path: string, name: string }> = ({ path, name }) => { const slug = getFriendlySlug(name) - const url = `https://openbeta.io/${path}/${slug}` + const baseUrl = process.env.NEXT_PUBLIC_BASE_URL != null ? process.env.NEXT_PUBLIC_BASE_URL : 'http://localhost:3000' + const optionalSlug = slug !== '' ? `/${slug}` : '' + const url = `${baseUrl}${path}${optionalSlug}` const [clicked, setClicked] = useState(false) diff --git a/src/components/maps/TileHandlers/CragContent.tsx b/src/components/maps/TileHandlers/CragContent.tsx index 4ff46a907..4363d6323 100644 --- a/src/components/maps/TileHandlers/CragContent.tsx +++ b/src/components/maps/TileHandlers/CragContent.tsx @@ -4,11 +4,14 @@ import { getAreaPageFriendlyUrl } from '@/js/utils' import { EntityIcon } from '@/app/(default)/editArea/[slug]/general/components/AreaItem' import { BaseDrawerContent } from './Drawer' import { MiniCarousel } from '../CardGallery' -import { ShareButton } from '../../ui/ShareButton' +import { SharePageURLButton } from '@/app/(default)/components/SharePageURLButton' +import { usePathname } from 'next/navigation' export const CragDrawerContent: React.FC = ({ id, areaName, climbs, content: { description }, media }) => { const friendlyUrl = getAreaPageFriendlyUrl(id, areaName) const editUrl = `/editArea/${id}/general` + const pathname = `${usePathname()}${window.location.search}` + return ( <> = ({ id, areaNam heading={{areaName}} subheading={} cta={Edit area} - share={} + share={} >
{description == null || description.trim() === '' diff --git a/src/components/ui/ShareButton.tsx b/src/components/ui/ShareButton.tsx deleted file mode 100644 index b43bf0010..000000000 --- a/src/components/ui/ShareButton.tsx +++ /dev/null @@ -1,42 +0,0 @@ -'use client' - -import { Share } from '@phosphor-icons/react' -import { usePathname, useSearchParams } from 'next/navigation' -import React from 'react' -import { toast } from 'react-toastify' - -const ShareButton: React.FC = () => { - const pathname = usePathname() - const searchParams = useSearchParams() - - const handleShareClick = (): void => { - const fullUrl = `${window.location.origin}${pathname}${searchParams.toString() !== '' ? '?' + searchParams.toString() : ''}` - - if (typeof navigator.clipboard === 'undefined') { - toast.error('Clipboard API is not supported in your browser') - return - } - - navigator.clipboard.writeText(fullUrl) - .then(() => { - toast.success('URL copied to clipboard!') - }) - .catch((error) => { - console.error('Error copying to clipboard:', error) - toast.error('Failed to copy URL to clipboard') - }) - } - - return ( - - ) -} - -export { ShareButton } From 09ff2b9b059eaf2e03a5355ac4df7ee76ffad6c8 Mon Sep 17 00:00:00 2001 From: Clinton Lunn Date: Tue, 26 Nov 2024 21:20:20 -0500 Subject: [PATCH 12/14] refactor: clear areaId if nothing under click --- src/app/(maps)/components/FullScreenMap.tsx | 6 +----- src/components/maps/GlobalMap.tsx | 2 +- src/js/hooks/useUrlParams.tsx | 14 +++++--------- 3 files changed, 7 insertions(+), 15 deletions(-) diff --git a/src/app/(maps)/components/FullScreenMap.tsx b/src/app/(maps)/components/FullScreenMap.tsx index d1f3eb32d..c2b37dba6 100644 --- a/src/app/(maps)/components/FullScreenMap.tsx +++ b/src/app/(maps)/components/FullScreenMap.tsx @@ -94,11 +94,7 @@ export const FullScreenMap: React.FC = () => { const handleMapClick = useCallback( (e: MapLayerMouseEvent) => { - const areaId = e.features?.[0]?.properties?.id - if (areaId === '') { - return - } - + const areaId = e.features?.[0]?.properties?.id ?? null const { camera } = urlParams.fromUrl() const url = urlParams.toUrl({ camera: camera ?? null, areaId }) router.replace(url, { scroll: false }) diff --git a/src/components/maps/GlobalMap.tsx b/src/components/maps/GlobalMap.tsx index 908e695df..b8e19442c 100644 --- a/src/components/maps/GlobalMap.tsx +++ b/src/components/maps/GlobalMap.tsx @@ -108,11 +108,11 @@ export const GlobalMap: React.FC = ({ const onClick = (event: MapLayerMouseEvent): void => { if (mapInstance === null) return const feature = event?.features?.[0] + handleOnClick?.(event) if (feature === undefined) { setClickInfo(null) } else { const { layer, geometry, properties } = feature - handleOnClick?.(event) setClickInfo(prev => { setActiveFeatureVisual(prev, { selected: false, hover: false }) const activeFeature = tileToFeature(layer.id, event.point, geometry, properties as TileProps, mapInstance) diff --git a/src/js/hooks/useUrlParams.tsx b/src/js/hooks/useUrlParams.tsx index 15850b601..a18437801 100644 --- a/src/js/hooks/useUrlParams.tsx +++ b/src/js/hooks/useUrlParams.tsx @@ -20,18 +20,14 @@ const useUrlParams = (): UseUrlParamsReturn => { } const baseUrl = `${pathname}?` - const cameraParam = (camera != null) ? `camera=${cameraInfoToQuery(camera)}` : '' + const cameraParam = camera != null ? `camera=${cameraInfoToQuery(camera)}` : '' const otherParams = params.toString() - if (cameraParam != null && otherParams != null) { - return `${baseUrl}${cameraParam}&${otherParams}` - } else if (cameraParam != null) { - return `${baseUrl}${cameraParam}` - } else if (otherParams != null) { - return `${baseUrl}${otherParams}` - } + const query = [cameraParam, otherParams] + .filter(param => param !== '') // Remove empty params + .join('&') // Join non-empty params with `&` - return pathname + return query !== '' ? `${baseUrl}${query}` : pathname // Return base URL if query is empty } const fromUrl = (): UrlProps => { From 2d3d3c83bb35b7e8513318ffc31516acaabf5789 Mon Sep 17 00:00:00 2001 From: Clinton Lunn Date: Tue, 26 Nov 2024 21:52:16 -0500 Subject: [PATCH 13/14] refactor: remove toastify provider --- src/app/(maps)/layout.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/app/(maps)/layout.tsx b/src/app/(maps)/layout.tsx index bddc39cc7..c4d83e6f3 100644 --- a/src/app/(maps)/layout.tsx +++ b/src/app/(maps)/layout.tsx @@ -2,7 +2,6 @@ import 'maplibre-gl/dist/maplibre-gl.css' import { Metadata } from 'next' import '@/public/fonts/fonts.css' import './../global.css' -import { ReactToastifyProvider } from '@/components/toast/ReactToastifyProvider' /** * Root layout for `/maps` route @@ -16,7 +15,6 @@ export default function MapsRootLayout ({ {children} - ) From a83f9373a9e7d7f69adce9967441cfe07b277f0e Mon Sep 17 00:00:00 2001 From: Clinton Lunn Date: Tue, 3 Dec 2024 00:01:41 -0500 Subject: [PATCH 14/14] refactor: remove more === --- src/components/maps/GlobalMap.tsx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/components/maps/GlobalMap.tsx b/src/components/maps/GlobalMap.tsx index b8e19442c..0bacd3cc9 100644 --- a/src/components/maps/GlobalMap.tsx +++ b/src/components/maps/GlobalMap.tsx @@ -71,7 +71,7 @@ export const GlobalMap: React.FC = ({ const urlParams = useUrlParams() const setActiveFeatureVisual = (feature: ActiveFeature | null, fState: FeatureState): void => { - if (feature === null || mapInstance === null) return + if (feature == null || mapInstance == null) return mapInstance.setFeatureState({ source: 'areas', sourceLayer: 'areas', @@ -80,7 +80,7 @@ export const GlobalMap: React.FC = ({ } const onMoveEnd = useCallback((e: ViewStateChangeEvent) => { - if ((mapInstance === null) || e.viewState === null || (onCameraMovement === undefined)) return + if ((mapInstance == null) || e.viewState == null || (onCameraMovement === undefined)) return onCameraMovement({ center: { lat: e.viewState.latitude, @@ -91,7 +91,7 @@ export const GlobalMap: React.FC = ({ }, [mapInstance, onCameraMovement]) const onLoad = useCallback((e: MapLibreEvent) => { - if (e.target === null) return + if (e.target == null) return setMapInstance(e.target) // Only apply jumpTo if initial values are defined @@ -106,7 +106,7 @@ export const GlobalMap: React.FC = ({ * Handle click event on the map. Place a marker on the map and activate the side drawer. */ const onClick = (event: MapLayerMouseEvent): void => { - if (mapInstance === null) return + if (mapInstance == null) return const feature = event?.features?.[0] handleOnClick?.(event) if (feature === undefined) { @@ -188,7 +188,7 @@ export const GlobalMap: React.FC = ({ }, []) useEffect(() => { - if (mapInstance === null) return + if (mapInstance == null) return if (!isSourceLoaded) { mapInstance.on('sourcedata', (e) => {