import React, { Component } from 'react'; import PropTypes from 'prop-types'; import { compose, withStateHandlers } from 'recompose'; import { withScriptjs, withGoogleMap, GoogleMap, Marker, InfoWindow } from 'react-google-maps'; import { MarkerClusterer } from 'react-google-maps/lib/components/addons/MarkerClusterer'; // Helpers import Client from '../utils/feathers'; import { stripProtocol } from '../utils/url'; import Countries from 'country-list'; // Custom Components import LayerMapSwitches from './LayerMapSwitches'; // Helpers import { GOOGLE_MAPS_API } from '../utils/constants'; // Images import MerchantPin from '../assets/img/map/merchant_pin.png'; import AmbassadorPin from '../assets/img/map/ambassador_pin.png'; import TellerPin from '../assets/img/map/teller_pin.png'; import ambs_m1 from '../assets/img/map/cluster/ambassadors/m1.png'; import ambs_m2 from '../assets/img/map/cluster/ambassadors/m2.png'; import ambs_m3 from '../assets/img/map/cluster/ambassadors/m3.png'; import ambs_m4 from '../assets/img/map/cluster/ambassadors/m4.png'; import ambs_m5 from '../assets/img/map/cluster/ambassadors/m5.png'; import mer_m1 from '../assets/img/map/cluster/merchants/m1.png'; import mer_m2 from '../assets/img/map/cluster/merchants/m2.png'; import mer_m3 from '../assets/img/map/cluster/merchants/m3.png'; import mer_m4 from '../assets/img/map/cluster/merchants/m4.png'; import mer_m5 from '../assets/img/map/cluster/merchants/m5.png'; import tel_m1 from '../assets/img/map/cluster/tellers/m1.png'; import tel_m2 from '../assets/img/map/cluster/tellers/m2.png'; import tel_m3 from '../assets/img/map/cluster/tellers/m3.png'; import tel_m4 from '../assets/img/map/cluster/tellers/m4.png'; import tel_m5 from '../assets/img/map/cluster/tellers/m5.png'; import { addProtocol, getProtocol } from '../utils/url'; import { WHICH_OPTIONS } from '../utils/constants'; // List of countries const countries = Countries(); /** * This object is used for type checking the props of the component. */ const propTypes = { ambassadors: PropTypes.array, merchants: PropTypes.array, mapCenter: PropTypes.object, mapZoom: PropTypes.number, // Fix google maps modal problem showControls: PropTypes.bool }; /** * This object sets default values to the optional props. */ const defaultProps = { mapCenter: { lat: -22.9068, lng: -43.1729 }, mapZoom: 12, googleMapURL: `https://maps.googleapis.com/maps/api/js?key=${GOOGLE_MAPS_API}&v=3.exp&libraries=geometry,drawing,places`, loadingElement:
, containerElement:
, mapElement:
, // Fix google maps modal problem showControls: true, ambassadors: [], merchants: [], tellers: [] }; defaultProps['markers'] = [ defaultProps.mapCenter ]; /** * Map that support Merchant Layer Markers and Ambassadors Layer Markers. */ const CustomLayerMap = compose( withStateHandlers(() => ({ isOpenObj: {}, isOpenAmbassadorObj:{} }), { onToggleOpen: ({ isOpenObj }) => (index) => { const openObj = isOpenObj; openObj[index] = !openObj[index]; return openObj; }, onToggleAmbassadorOpen: ({ isOpenAmbassadorObj }) => (index) => { const openObj = isOpenAmbassadorObj; openObj[index] = !openObj[index]; return openObj; } }), withScriptjs, withGoogleMap )(props => {props.ambassadors.map( (marker, index) => ( marker.withInfo ? ( props.onToggleOpen(index)} > {props.isOpenObj[index] && props.onToggleOpen(index)}>
{marker.infoTitle}

{marker.infoDescription}
}
) : ( ) ))}
{props.merchants.map( (marker, index) => ( marker.withInfo ? ( props.onToggleAmbassadorOpen(index)} > {props.isOpenAmbassadorObj[index] && props.onToggleAmbassadorOpen(index)}>
{marker.infoTitle}

{marker.infoDescription}
}
) : ( ) ))}
{props.tellers.map( (marker, index) => ( marker.withInfo ? ( props.onToggleAmbassadorOpen(index)} > {props.isOpenAmbassadorObj[index] && props.onToggleAmbassadorOpen(index)}>
{marker.infoTitle}

{marker.infoDescription}
}
) : ( ) ))}
); // Type checking the props of the component CustomLayerMap.propTypes = propTypes; // Assign default values to the optional props CustomLayerMap.defaultProps = defaultProps; /** * This object is used for type checking the props of the component. */ const propTypesLayerMap = { ambassadorsLayer: PropTypes.bool, merchantsLayer: PropTypes.bool, showControls: PropTypes.bool, mapHeight: PropTypes.string, ambassadors: PropTypes.array, merchants: PropTypes.array, tellers: PropTypes.array, }; /** * This object sets default values to the optional props. */ const defaultPropsLayerMap = { ambassadors: null, merchants: null, tellers: null }; class LayerMap extends Component { constructor(props) { super(props); this.state = { ambassadors: [], merchants: [], tellers: [], ambassadorLayer: this.props.ambassadorsLayer, merchantLayer: this.props.merchantsLayer, tellerLayer: this.props.tellersLayer, }; } /** * @description Lifecycle event handler called just after the App loads into the DOM. */ UNSAFE_componentWillMount() { this.updateMarkers(); } // Update markers based if props are informed updateMarkers() { if(!this.props.ambassadors) { this.getAmbassadors(); } if(!this.props.merchants) { this.getMerchants(); } if (!this.props.tellers) { this.getTellers(); } } fillResults(result) { const data = result; return (item) => data.data.push(item); } /** * @description Get ambassadors from the web service * @param {number} [limit=10] - Max items to be returned. * @param {number} [skip=0] - Start index search */ getAmbassadors = async (limit = 50, skip = 0) => { const app = this; // Initially we don't know how much the total value is, so to make sure we enter the loop // at least once we're just setting it to be 1 let total = 1; const ambassadors = Client.service('api/v2/ambassadors'); let result; while(skip < total){ let partialResponse = await ambassadors.find({ query: { $sort: { account: 1 }, $limit: limit, $skip: skip } }); total = partialResponse.total; result === undefined ? result = partialResponse : partialResponse.data.map(this.fillResults(result)); skip = skip + limit; } const markers = []; result.data.forEach(ambassador => { ambassador.cities.forEach(function(city) { const infoDescription =
Location: {(city.name).replace(/(^|\s)\S/g, l => l.toUpperCase())} - {countries.getName(city.country)}
{(ambassador.nickname) && (
Nickname: {ambassador.nickname}
)} {(ambassador.telegram) && (
Telegram: {ambassador.telegram}
)} {(ambassador.keybase) && (
Keybase: {ambassador.keybase}
)} {(ambassador.email) && (
Email: {ambassador.email}
)} {(ambassador.phone) && (
Phone: {ambassador.phone}
)} {(ambassador.url) && (
URL:: {stripProtocol(ambassador.url)}
)}
; const marker = { lat: city.lat, lng: city.lon, withInfo: true, infoTitle: ambassador.nickname, infoDescription: infoDescription, }; markers.push(marker); }); }); // Once both return, update the state app.setState({ ambassadors: markers }); }; /** * @description Get merchants from the web service * @param {number} [limit=10] - Max items to be returned. * @param {number} [skip=0] - Start index search */ getMerchants = async (limit = 50, skip = 0) => { const app = this; // Initially we don't know how much the total value is, so to make sure we enter the loop // at least once we're just setting it to be 1 let total = 1; const merchants = Client.service('api/v1/merchants'); let result; while(skip < total){ let partialResponse = await merchants.find({ query: { $sort: { account: 1 }, $limit: limit, $skip: skip } }); total = partialResponse.total; result === undefined ? result = partialResponse : partialResponse.data.map(this.fillResults(result)); skip = skip + limit; } result.data.forEach(function(merchants){ if(merchants.city !== undefined) merchants.city = (merchants.city).replace(/(^|\s)\S/g, l => l.toUpperCase()); if(merchants.country !== undefined) merchants.country = countries.getName(merchants.country); }); const markers = result.data.map(merchant => { const infoDescription =
Address: {merchant.address}
{(merchant.phone) && (
Phone: {merchant.phone}
)} {(merchant.telegram) && (
Telegram: {merchant.telegram}
)} {(merchant.website) && (
Website:: {stripProtocol(merchant.website)}
)}
; const marker = { lat: merchant.lat, lng: merchant.lon, withInfo: true, infoTitle: merchant.name, infoDescription: infoDescription, }; return marker; }); // Once both return, update the state app.setState({ merchants: markers, loading: false }); }; /** * @description Get tellers from the web service * @param {number} [limit=10] - Max items to be returned. * @param {number} [skip=0] - Start index search */ getTellers = async (limit = 50, skip = 0) => { const app = this; // Initially we don't know how much the total value is, so to make sure we enter the loop // at least once we're just setting it to be 1 let total = 1; const tellers = Client.service('api/v2/tellers'); this.setState({loading: true}); let result; while(skip < total){ let partialResponse = await tellers.find({ query: { //$sort: { account: 1 }, $limit: limit, $skip: skip } }); total = partialResponse.total; result === undefined ? result = partialResponse : partialResponse.data.map(this.fillResults(result)); skip = skip + limit; } result.data.forEach(function(tellers){ if(tellers.city !== undefined) tellers.city = (tellers.city).replace(/(^|\s)\S/g, l => l.toUpperCase()); if(tellers.country !== undefined) tellers.country = countries.getName(tellers.country); }); const markers = result.data.map(teller => { if(teller.telegram){ teller.telegram_original = teller.telegram; teller.telegram = { searchText: teller.telegram_original, value: ( {teller.telegram} ) }; } const which = WHICH_OPTIONS.filter(w => w.id.toLowerCase() === teller.which.toLowerCase()); teller.which_value = ( teller.which && which.length > 0) ? which[0].value : ''; const infoDescription = (
Address: {teller.address}
{(teller.which) && (
Which:: {teller.which}
)} {(teller.bitshares_address) && (
BTS Account:: {teller.bitshares_address}
)} {(teller.address) && (
Address:: {teller.address}
)} {(teller.telegram_original) && (
Telegram: {teller.telegram_original}
) } {(teller.keybase) && (
Keybase: {teller.keybase}
)} {(teller.whatsapp) && (
Whatsapp: {teller.whatsapp}
)} {(teller.viber) && (
Viber: {teller.viber}
)} {(teller.email) && (
Email: {teller.email}
)} {(teller.phone) && (
Phone: {teller.phone}
)} {(teller.url) && (
URL:: {stripProtocol(teller.url)}
)}
); const marker = { lat: teller.lat, lng: teller.lon, withInfo: true, infoTitle: teller.gt_name, infoDescription: infoDescription, }; return marker; }); // Once both return, update the state app.setState({ loading: false, tellers: markers }); }; handleLayerChange = name => event => { this.setState({ [name]: event.target.checked }); // Update any time changes this.updateMarkers(); }; render() { const { ambassadors: ambassadorsProps, merchants: merchantsProps, tellers: tellersProps } = this.props; let { ambassadors, merchants, tellers } = this.state; if(ambassadorsProps) { ambassadors = ambassadorsProps; } if(merchantsProps) { merchants = merchantsProps; } if(tellersProps) { tellers = tellersProps; } return (
{!this.props.showControls ? ( ) : (
)} } containerElement={
} mapElement={
} />
); } } // Type checking the props of the component LayerMap.propTypes = propTypesLayerMap; // Assign default values to the optional props LayerMap.defaultProps = defaultPropsLayerMap; export default LayerMap;