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) && (
)}
{(ambassador.keybase) && (
Keybase: {ambassador.keybase}
)}
{(ambassador.email) && (
Email: {ambassador.email}
)}
{(ambassador.phone) && (
Phone: {ambassador.phone}
)}
{(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) && (
)}
{(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) && (
)
}
{(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) && (
)}
);
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;