import { Camera, CameraRef, MapView, MapViewRef, RegionPayload, UserLocation, UserLocationRef } from "@maplibre/maplibre-react-native"; import { useQuery } from "@tanstack/react-query"; import { Text, StyleSheet, Dimensions } from "react-native"; import { useEffect } from "react"; import { PersistQueryClientProvider } from '@tanstack/react-query-persist-client' import {viewport} from '@placemarkio/geo-viewport'; import type { Feature } from "geojson"; import type { Location } from "@maplibre/maplibre-react-native"; import type { MapParameters, MapViewProps } from "./types"; import { queryClient, persister } from "./api"; import { getSnippet, mapKeys } from "./api/livemap-service"; import { applyFiltersToLayer, getStyle } from "./api/style-service"; import { styleKeys } from "./api/style-service"; import { deltaToBounds } from "./utils/map"; import { useCallbackRef } from "./hooks/useCallbackRef"; import POIManager from "./core/poi-manager"; import OfflineManager from "./core/offline-manager"; import { DEFAULT_TILES_STYLE } from "./core/constants"; import { useSettingsStore } from "./storages/settings"; import { usePOIsStore } from "./storages/pois"; import Polylines from "./components/polylines"; import OpenedPOIs from './components/opened-pois'; const screenDimensions = Dimensions.get('screen'); const WemapMapView = ({ emmid, ...props }: MapViewProps) => { const { isPending, error, data: mapParameter } = useQuery({ queryKey: mapKeys.snippet(emmid), queryFn: () => getSnippet(emmid) }); const {data: style} = useQuery({ queryKey: styleKeys.style(mapParameter ? mapParameter.snippet.tilesstyle || DEFAULT_TILES_STYLE : ''), queryFn: () => { if (!mapParameter) { return null; } return getStyle(mapParameter?.snippet.tilesstyle || DEFAULT_TILES_STYLE); }, }); const openedPOIs = usePOIsStore((state) => state.openedPOIs); const openPOI = usePOIsStore((state) => state.openPOI); const closeAll = usePOIsStore((state) => state.closeAll); const bounds = deltaToBounds(mapParameter); const [mapViewRef, mapViewCallback] = useCallbackRef(); const [cameraRef, cameraCallback] = useCallbackRef(); const [userLocationRef, userLocationCallback] = useCallbackRef(); const predictedCenterAndZoom = viewport(bounds.toArray(), [screenDimensions.width, screenDimensions.height], { minzoom: mapParameter?.snippet.minzoom, maxzoom: mapParameter?.snippet.maxzoom, allowFloat: true }); useEffect(() => { if (mapViewRef && cameraRef && userLocationRef) { props.onRefsReady?.({ mapView: mapViewRef, camera: cameraRef, userLocation: userLocationRef }); } }, [mapViewRef, cameraRef, userLocationRef]); useEffect(() => { if (mapParameter) { OfflineManager.createPack(mapParameter); } }, [mapParameter]); const onRegionDidChange = (region: Feature) => { props.onRegionDidChange?.(region); const pois = POIManager.getByBounds(region.properties.visibleBounds); props.onPOIsUpdate?.(pois); }; const onPress = async (feature: Feature) => { props.onPress?.(feature); if (openedPOIs.length > 0) { closeAll(); } if (mapViewRef && feature.properties) { const featuresCollection = await mapViewRef.queryRenderedFeaturesAtPoint([ feature.properties.screenPointX, feature.properties.screenPointY, ], undefined, []); const pinpointFeature = featuresCollection.features.find((feature) => feature.properties?.pinpoint); if (pinpointFeature) { const poi = POIManager.getByID(Number(pinpointFeature.properties?.pinpoint)); if (poi) { openPOI(poi); setTimeout(() => { cameraRef?.moveTo([poi.longitude, poi.latitude], 500); }, 100) props.onPOIOpen?.(poi); } } } } const onUserLocationUpdate = (location: Location) => { props.onUserLocationUpdate?.(location); } if (isPending || !style) return null; if (error && !mapParameter) { console.error(error); return An error occurred; } if (!mapParameter) return null; return ( poi.id) }) }}> {props.children} ); }; const styles = StyleSheet.create({ container: { flex: 1, } }); const Root = (props: MapViewProps) => { const setEmmid = useSettingsStore((state) => state.setEmmid); useEffect(() => { setEmmid(props.emmid); POIManager.fetchAll(); }, [props.emmid]); return ( { // resume mutations after initial restore from localStorage was successful queryClient.resumePausedMutations().then(() => { queryClient.invalidateQueries() }) }}> ); }; export default Root;