import React from 'react'; import { Platform, requireNativeComponent, NativeModules, UIManager, findNodeHandle, ViewProps, NativeSyntheticEvent, ListRenderItemInfo, ImageSourcePropType, } from 'react-native'; // @ts-ignore import resolveAssetSource from 'react-native/Libraries/Image/resolveAssetSource'; import CallbacksManager from '../utils/CallbacksManager'; import { MapType, Animation, Point, DrivingInfo, MasstransitInfo, RoutesFoundEvent, Vehicles, CameraPosition, VisibleRegion, ScreenPoint, MapLoaded, InitialRegion, YandexLogoPosition, YandexLogoPadding, YamapNativeIcon, } from '../interfaces'; import {processColorProps} from '../utils'; const {yamap: NativeYamapModule} = NativeModules; export interface ClusteredYaMapProps extends ViewProps { userLocationIcon?: ImageSourcePropType; userLocationNativeIcon?: YamapNativeIcon; userLocationIconScale?: number; clusteredMarkers: ReadonlyArray<{point: Point; data: T}>; renderMarker: ( info: {point: Point; data: ListRenderItemInfo}, index: number, ) => React.ReactElement; clusterColor?: string; showUserPosition?: boolean; nightMode?: boolean; mapStyle?: string; mapType?: MapType; onCameraPositionChange?: ( event: NativeSyntheticEvent, ) => void; onCameraPositionChangeEnd?: ( event: NativeSyntheticEvent, ) => void; onMapPress?: (event: NativeSyntheticEvent) => void; onMapLongPress?: (event: NativeSyntheticEvent) => void; onMapLoaded?: (event: NativeSyntheticEvent) => void; userLocationAccuracyFillColor?: string; userLocationAccuracyStrokeColor?: string; userLocationAccuracyStrokeWidth?: number; scrollGesturesEnabled?: boolean; zoomGesturesEnabled?: boolean; tiltGesturesEnabled?: boolean; rotateGesturesEnabled?: boolean; fastTapEnabled?: boolean; initialRegion?: InitialRegion; maxFps?: number; followUser?: boolean; logoPosition?: YandexLogoPosition; logoPadding?: YandexLogoPadding; } const YaMapNativeComponent = requireNativeComponent< Omit & {clusteredMarkers: Point[]} >('ClusteredYamapView'); export class ClusteredYamap extends React.Component { static defaultProps = { showUserPosition: true, clusterColor: 'red', maxFps: 60, }; // @ts-ignore map = React.createRef(); public static init(apiKey: string): Promise { return NativeYamapModule.init(apiKey); } public static setLocale(locale: string): Promise { return new Promise((resolve, reject) => { NativeYamapModule.setLocale( locale, () => resolve(), (err: string) => reject(new Error(err)), ); }); } public static getLocale(): Promise { return new Promise((resolve, reject) => { NativeYamapModule.getLocale( (locale: string) => resolve(locale), (err: string) => reject(new Error(err)), ); }); } public static resetLocale(): Promise { return new Promise((resolve, reject) => { NativeYamapModule.resetLocale( () => resolve(), (err: string) => reject(new Error(err)), ); }); } public findRoutes( points: Point[], vehicles: Vehicles[], callback: (event: RoutesFoundEvent) => void, ) { this._findRoutes(points, vehicles, callback); } public findPedestrianRoutes( points: Point[], callback: (event: RoutesFoundEvent) => void, ) { this._findRoutes(points, [], callback); } public findDrivingRoutes( points: Point[], callback: (event: RoutesFoundEvent) => void, ) { this._findRoutes(points, ['car'], callback); } public fitAllMarkers() { UIManager.dispatchViewManagerCommand( findNodeHandle(this), this.getCommand('fitAllMarkers'), [], ); } public setTrafficVisible(isVisible: boolean) { UIManager.dispatchViewManagerCommand( findNodeHandle(this), this.getCommand('setTrafficVisible'), [isVisible], ); } public fitMarkers(points: Point[]) { UIManager.dispatchViewManagerCommand( findNodeHandle(this), this.getCommand('fitMarkers'), [points], ); } public setCenter( center: {lon: number; lat: number; zoom?: number}, zoom: number = center.zoom || 10, azimuth: number = 0, tilt: number = 0, duration: number = 0, animation: Animation = Animation.SMOOTH, ) { UIManager.dispatchViewManagerCommand( findNodeHandle(this), this.getCommand('setCenter'), [center, zoom, azimuth, tilt, duration, animation], ); } public setZoom( zoom: number, duration: number = 0, animation: Animation = Animation.SMOOTH, ) { UIManager.dispatchViewManagerCommand( findNodeHandle(this), this.getCommand('setZoom'), [zoom, duration, animation], ); } public getCameraPosition(callback: (position: CameraPosition) => void) { const cbId = CallbacksManager.addCallback(callback); UIManager.dispatchViewManagerCommand( findNodeHandle(this), this.getCommand('getCameraPosition'), [cbId], ); } public getVisibleRegion(callback: (VisibleRegion: VisibleRegion) => void) { const cbId = CallbacksManager.addCallback(callback); UIManager.dispatchViewManagerCommand( findNodeHandle(this), this.getCommand('getVisibleRegion'), [cbId], ); } public getScreenPoints( points: Point[], callback: (screenPoint: ScreenPoint) => void, ) { const cbId = CallbacksManager.addCallback(callback); UIManager.dispatchViewManagerCommand( findNodeHandle(this), this.getCommand('getScreenPoints'), [points, cbId], ); } public getWorldPoints( points: ScreenPoint[], callback: (point: Point) => void, ) { const cbId = CallbacksManager.addCallback(callback); UIManager.dispatchViewManagerCommand( findNodeHandle(this), this.getCommand('getWorldPoints'), [points, cbId], ); } private _findRoutes( points: Point[], vehicles: Vehicles[], callback: | ((event: RoutesFoundEvent) => void) | ((event: RoutesFoundEvent) => void) | ((event: RoutesFoundEvent) => void), ) { const cbId = CallbacksManager.addCallback(callback); const args = Platform.OS === 'ios' ? [{points, vehicles, id: cbId}] : [points, vehicles, cbId]; UIManager.dispatchViewManagerCommand( findNodeHandle(this), this.getCommand('findRoutes'), args, ); } private getCommand(cmd: string): any { return Platform.OS === 'ios' ? UIManager.getViewManagerConfig('ClusteredYamapView').Commands[cmd] : cmd; } private processRoute( event: NativeSyntheticEvent< {id: string} & RoutesFoundEvent >, ) { const {id, ...routes} = event.nativeEvent; CallbacksManager.call(id, routes); } private processCameraPosition( event: NativeSyntheticEvent<{id: string} & CameraPosition>, ) { const {id, ...point} = event.nativeEvent; CallbacksManager.call(id, point); } private processVisibleRegion( event: NativeSyntheticEvent<{id: string} & VisibleRegion>, ) { const {id, ...visibleRegion} = event.nativeEvent; CallbacksManager.call(id, visibleRegion); } private processWorldToScreenPointsReceived( event: NativeSyntheticEvent<{id: string} & ScreenPoint[]>, ) { const {id, ...screenPoints} = event.nativeEvent; CallbacksManager.call(id, screenPoints); } private processScreenToWorldPointsReceived( event: NativeSyntheticEvent<{id: string} & Point[]>, ) { const {id, ...worldPoints} = event.nativeEvent; CallbacksManager.call(id, worldPoints); } private resolveImageUri(img: ImageSourcePropType) { return img ? resolveAssetSource(img).uri : ''; } private getProps() { const props = { ...this.props, clusteredMarkers: this.props.clusteredMarkers.map(mark => mark.point), children: this.props.clusteredMarkers.map(this.props.renderMarker), onRouteFound: this.processRoute, onCameraPositionReceived: this.processCameraPosition, onVisibleRegionReceived: this.processVisibleRegion, onWorldToScreenPointsReceived: this.processWorldToScreenPointsReceived, onScreenToWorldPointsReceived: this.processScreenToWorldPointsReceived, userLocationIcon: this.props.userLocationIcon ? this.resolveImageUri(this.props.userLocationIcon) : undefined, }; processColorProps(props, 'clusterColor' as keyof ClusteredYaMapProps); processColorProps( props, 'userLocationAccuracyFillColor' as keyof ClusteredYaMapProps, ); processColorProps( props, 'userLocationAccuracyStrokeColor' as keyof ClusteredYaMapProps, ); return props; } render() { return ; } }