react-native-map-link
Version:
Open the map app of the user's choice with a specific location.
469 lines (468 loc) • 19.2 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.generateMapUrl = exports.checkOptions = exports.getDirectionsModeSygic = exports.getDirectionsModeGoogleMaps = exports.getDirectionsModeAppleMaps = exports.askAppChoice = exports.checkNotSupportedApps = exports.getNotSupportedApps = exports.isSupportedApp = exports.isAppInstalled = exports.getAvailableApps = void 0;
const react_native_1 = require("react-native");
const constants_1 = require("./constants");
const getAvailableApps = async (prefixes) => {
const availableApps = [];
await Promise.all(Object.keys(prefixes).map(async (app) => {
try {
const isInstalled = await (0, exports.isAppInstalled)(app, prefixes);
if (isInstalled) {
availableApps.push(app);
}
}
catch (error) {
console.error(`Error checking if ${app} is installed:`, error);
}
}));
return availableApps;
};
exports.getAvailableApps = getAvailableApps;
const isAppInstalled = (app, prefixes) => {
return new Promise((resolve) => {
if (!(app in prefixes)) {
return resolve(false);
}
react_native_1.Linking.canOpenURL(prefixes[app])
.then((result) => {
resolve(!!result);
})
.catch(() => resolve(false));
});
};
exports.isAppInstalled = isAppInstalled;
const isSupportedApp = (app) => {
return constants_1.appKeys.includes(app);
};
exports.isSupportedApp = isSupportedApp;
const getNotSupportedApps = (apps) => {
return apps.filter((app) => !(0, exports.isSupportedApp)(app));
};
exports.getNotSupportedApps = getNotSupportedApps;
const checkNotSupportedApps = (apps) => {
const notSupportedApps = (0, exports.getNotSupportedApps)(apps);
if (notSupportedApps.length) {
throw new MapsException(`appsWhiteList [${notSupportedApps}] are not supported apps, please provide some of the supported apps [${constants_1.appKeys}]`);
}
};
exports.checkNotSupportedApps = checkNotSupportedApps;
const askAppChoice = ({ dialogTitle, dialogMessage, cancelText, appsWhiteList, appsBlackList, prefixes, appTitles, }) => {
return new Promise(async (resolve) => {
let availableApps = await (0, exports.getAvailableApps)(prefixes);
if (appsWhiteList && appsWhiteList.length) {
availableApps = availableApps.filter((appName) => appsWhiteList.includes(appName));
}
if (appsBlackList && appsBlackList.length) {
availableApps = availableApps.filter((appName) => !appsBlackList.includes(appName));
}
if (availableApps.length < 2) {
return resolve(availableApps[0] || null);
}
if (constants_1.isIOS) {
const options = availableApps.map((app) => appTitles?.[app]);
options?.push(cancelText || '');
react_native_1.ActionSheetIOS.showActionSheetWithOptions({
title: dialogTitle || '',
message: dialogMessage || '',
options: options || [],
cancelButtonIndex: options ? options.length - 1 : 0,
}, (buttonIndex) => {
if (buttonIndex === options.length - 1) {
return resolve(null);
}
return resolve(availableApps[buttonIndex]);
});
return;
}
const options = availableApps.map((app) => ({
text: appTitles?.[app] || '',
onPress: () => resolve(app),
}));
options.unshift({
text: cancelText || '',
onPress: () => resolve(null),
});
return react_native_1.Alert.alert(dialogTitle || '', dialogMessage || '', options, {
cancelable: true,
onDismiss: () => resolve(null),
});
});
};
exports.askAppChoice = askAppChoice;
const getDirectionsModeAppleMaps = (directionsMode) => {
const modeMap = {
car: 'd',
bike: 'b',
walk: 'w',
'public-transport': 'r',
};
return modeMap[directionsMode || ''] || undefined;
};
exports.getDirectionsModeAppleMaps = getDirectionsModeAppleMaps;
const getDirectionsModeGoogleMaps = (directionsMode) => {
const modeMap = {
car: 'driving',
walk: 'walking',
'public-transport': 'transit',
bike: 'bicycling',
};
return modeMap[directionsMode || ''] || undefined;
};
exports.getDirectionsModeGoogleMaps = getDirectionsModeGoogleMaps;
const getDirectionsModeSygic = (directionsMode) => {
const modeMap = {
car: 'drive',
walk: 'walk',
'public-transport': 'show',
bike: 'show',
};
return modeMap[directionsMode || ''] || undefined;
};
exports.getDirectionsModeSygic = getDirectionsModeSygic;
const checkOptions = ({ latitude, longitude, address, googleForceLatLon, googlePlaceId, title, app, prefixes, appTitles, appsWhiteList, }) => {
if (!(latitude && longitude) && !address) {
throw new MapsException('`latitude` & `longitude` or `address` is required. Both cannot be undefined.');
}
if (address && typeof address !== 'string') {
throw new MapsException('Option `address` should be of type `string`.');
}
if (title && typeof title !== 'string') {
throw new MapsException('`title` should be of type `string`.');
}
if (googleForceLatLon && typeof googleForceLatLon !== 'boolean') {
throw new MapsException('Option `googleForceLatLon` should be of type `boolean`.');
}
if (googlePlaceId && typeof googlePlaceId !== 'string') {
throw new MapsException('Option `googlePlaceId` should be of type `string`.');
}
if (app && !(app in prefixes)) {
throw new MapsException('Option `app` should be undefined, null, or one of the following: "' +
Object.keys(prefixes).join('", "') +
'".');
}
if (appsWhiteList && appsWhiteList.length) {
(0, exports.checkNotSupportedApps)(appsWhiteList);
}
if (appTitles && typeof appTitles !== 'object') {
throw new MapsException('Option `appTitles` should be of type `object`.');
}
};
exports.checkOptions = checkOptions;
const generateMapUrl = ({ app, directionsMode, appleIgnoreLatLon, googleForceLatLon, googlePlaceId, naverCallerName, lat, lng, latlng, sourceLat, sourceLng, sourceLatLng, address, title, encodedTitle, prefixes, useSourceDestiny, }) => {
let url = '';
switch (app) {
case 'apple-maps':
const appleDirectionMode = (0, exports.getDirectionsModeAppleMaps)(directionsMode);
url = prefixes['apple-maps'];
if (address) {
url = `${url}?address=${address}`;
}
else {
if (useSourceDestiny || directionsMode) {
url = `${url}?daddr=${latlng}`;
url += sourceLatLng ? `&saddr=${sourceLatLng}` : '';
}
else if (!appleIgnoreLatLon) {
url = `${url}?ll=${latlng}`;
}
}
url +=
useSourceDestiny || directionsMode || address || !appleIgnoreLatLon
? '&'
: '?';
url += `q=${title ? encodedTitle : 'Location'}`;
url += appleDirectionMode ? `&dirflg=${appleDirectionMode}` : '';
break;
case 'google-maps':
const googleDirectionMode = (0, exports.getDirectionsModeGoogleMaps)(directionsMode);
// Always using universal URL instead of URI scheme since the latter doesn't support all parameters (#155)
if (useSourceDestiny || directionsMode) {
// Use "dir" as this will open up directions
url = 'https://www.google.com/maps/dir/?api=1';
url += sourceLatLng ? `&origin=${sourceLatLng}` : '';
if (!googleForceLatLon && title) {
url += `&destination=${encodedTitle}`;
}
else if (!googleForceLatLon && address) {
url += `&destination=${address}`;
}
else {
url += `&destination=${latlng}`;
}
url += googlePlaceId ? `&destination_place_id=${googlePlaceId}` : '';
url += googleDirectionMode ? `&travelmode=${googleDirectionMode}` : '';
}
else {
if (address) {
url = `https://www.google.com/maps/search/?api=1&query=${address}`;
}
else {
// Use "search" as this will open up a single marker
url = 'https://www.google.com/maps/search/?api=1';
if (!googleForceLatLon && title) {
url += `&query=${encodedTitle}`;
}
else {
url += `&query=${latlng}`;
}
url += googlePlaceId ? `&query_place_id=${googlePlaceId}` : '';
}
}
break;
case 'citymapper':
if (address) {
url = `${prefixes.citymapper}directions?endname=${address}`;
}
else {
url = `${prefixes.citymapper}directions?endcoord=${latlng}`;
if (title) {
url += `&endname=${encodedTitle}`;
}
if (useSourceDestiny) {
url += `&startcoord=${sourceLatLng}`;
}
}
break;
case 'uber':
if (address) {
url = `${prefixes.uber}?action=setPickup&pickup=my_location&dropoff=${address}`;
}
else {
url = `${prefixes.uber}?action=setPickup&dropoff[latitude]=${lat}&dropoff[longitude]=${lng}`;
if (title) {
url += `&dropoff[nickname]=${encodedTitle}`;
}
url += useSourceDestiny
? `&pickup[latitude]=${sourceLat}&pickup[longitude]=${sourceLng}`
: '&pickup=my_location';
}
break;
case 'lyft':
if (address) {
url = `${prefixes.lyft}ridetype?id=lyft&destination[address]=${address}`;
}
else {
url = `${prefixes.lyft}ridetype?id=lyft&destination[latitude]=${lat}&destination[longitude]=${lng}`;
if (useSourceDestiny) {
url += `&pickup[latitude]=${sourceLat}&pickup[longitude]=${sourceLng}`;
}
}
break;
case 'transit':
if (address) {
url = `${prefixes.transit}directions?destination=${address}`;
}
else {
url = `${prefixes.transit}directions?to=${latlng}`;
}
if (useSourceDestiny) {
url += `&from=${sourceLatLng}`;
}
break;
case 'truckmap':
if (address) {
// Constructed from documentation from https://truckmap.com/solutions/developer
url = `https://truckmap.com/place/${address}`;
}
else {
url = `https://truckmap.com/place/${lat},${lng}`;
if (useSourceDestiny) {
url = `https://truckmap.com/route/${sourceLat},${sourceLng}/${lat},${lng}`;
}
}
break;
case 'waze':
if (address) {
url = `${prefixes.waze}?q=${address}`;
}
else {
url = `${prefixes.waze}?ll=${latlng}&navigate=yes`;
if (title) {
url += `&q=${encodedTitle}`;
}
}
break;
case 'yandex':
if (address) {
url = `${prefixes.yandex}?text=${address}`;
}
else {
url = `${prefixes.yandex}build_route_on_map?lat_to=${lat}&lon_to=${lng}`;
if (useSourceDestiny) {
url += `&lat_from=${sourceLat}&lon_from=${sourceLng}`;
}
}
break;
case 'moovit':
if (address) {
url = `${prefixes.moovit}?dest_name=${address}`;
}
else {
url = `${prefixes.moovit}?dest_lat=${lat}&dest_lon=${lng}`;
if (title) {
url += `&dest_name=${encodedTitle}`;
}
if (useSourceDestiny) {
url += `&orig_lat=${sourceLat}&orig_lon=${sourceLng}`;
}
}
break;
case 'yandex-taxi':
if (address) {
throw new MapsException('yandex-taxi does not support passing the address or has not been implemented yet.');
}
else {
url = `${prefixes['yandex-taxi']}route?end-lat=${lat}&end-lon=${lng}&appmetrica_tracking_id=1178268795219780156`;
}
break;
case 'yandex-maps':
if (address) {
url = `${prefixes['yandex-maps']}?text=${address}`;
}
else {
url = `${prefixes['yandex-maps']}?pt=${lng},${lat}`;
}
break;
case 'kakaomap':
if (address) {
url = `${prefixes.kakaomap}route?ep=${address}`;
}
else {
url = `${prefixes.kakaomap}look?p=${latlng}`;
if (useSourceDestiny) {
url = `${prefixes.kakaomap}route?sp=${sourceLat},${sourceLng}&ep=${latlng}&by=CAR`;
}
}
break;
case 'tmap':
if (address) {
url = `${prefixes.tmap}search?name=${address}`;
}
else {
url = `${prefixes.tmap}viewmap?x=${lng}&y=${lat}`;
if (useSourceDestiny) {
url = `${prefixes.tmap}route?startx=${sourceLng}&starty=${sourceLat}&goalx=${lng}&goaly=${lat}`;
}
}
break;
case 'mapycz':
if (address) {
url = `${prefixes.mapycz}www.mapy.cz/zakladni?q=${address}`;
}
else {
url = `${prefixes.mapycz}www.mapy.cz/zakladni?x=${lng}&y=${lat}&source=coor&id=${lng},${lat}`;
}
break;
case 'maps-me':
if (address) {
url = `${prefixes['maps-me']}?q=${address}`;
}
else {
url = `${prefixes['maps-me']}route?sll=${sourceLat},${sourceLng}&saddr= &dll=${lat},${lng}&daddr=${title}&type=vehicle`;
}
break;
case 'osmand':
if (address) {
url = `${prefixes.osmand}show_map?addr=${address}`;
}
else {
url = constants_1.isIOS
? `${prefixes.osmand}?lat=${lat}&lon=${lng}`
: `${prefixes.osmand}?q=${lat},${lng}`;
}
break;
case 'gett':
if (address) {
throw new MapsException('gett does not support passing the address or has not been implemented yet.');
}
else {
url = `${prefixes.gett}order?pickup=my_location&dropoff_latitude=${lat}&dropoff_longitude=${lng}`;
}
break;
case 'navermap':
if (address) {
url = `${prefixes.navermap}search?query=${address}`;
}
else {
url = `${prefixes.navermap}map?lat=${lat}&lng=${lng}&appname=${naverCallerName}`;
if (useSourceDestiny) {
url = `${prefixes.navermap}route?slat=${sourceLat}&slng=${sourceLng}&dlat=${lat}&dlng=${lng}&appname=${naverCallerName}`;
}
}
break;
case 'dgis':
if (address) {
url = `${prefixes.dgis}?q=${address}`;
}
else {
url = `${prefixes.dgis}routeSearch/to/${lng},${lat}/go`;
if (useSourceDestiny) {
url = `${prefixes.dgis}routeSearch/to/${lng},${lat}/from/${sourceLng},${sourceLat}/go`;
}
}
break;
case 'liftago':
if (address) {
throw new MapsException('liftago does not support passing the address or has not been implemented yet.');
}
else {
url = `${prefixes.liftago}order?destinationLat=${lat}&destinationLon=${lng}`;
if (title) {
url += `&destinationName=${encodedTitle}`;
}
if (useSourceDestiny) {
url += `&pickupLat=${sourceLat}&pickupLon=${sourceLng}`;
}
}
break;
case 'petalmaps':
if (address) {
// Got this from this documentation https://developer.huawei.com/consumer/en/doc/HMSCore-Guides/petal-maps-introduction-0000001059189679
url = `${prefixes.petalmaps}textSearch?text=${address}`;
}
else {
url = `${prefixes.petalmaps}navigation?daddr=${lat},${lng}`;
if (useSourceDestiny) {
url += `&saddr=${sourceLat},${sourceLng}`;
}
}
break;
case 'sygic':
const sygicDirectionsMode = (0, exports.getDirectionsModeSygic)(directionsMode);
if (address) {
throw new MapsException('sygic does not support passing the address or has not been implemented yet.');
}
else {
url = `${prefixes.sygic}coordinate|${lng}|${lat}|`;
}
url += sygicDirectionsMode ? `${sygicDirectionsMode}` : '';
break;
case 'here':
// the prefix "here-route://" is defined in the constants.ts file but Here recommends using the https-address for deep links
// so here-route:// is used only to detect if the app is installed and the https url is used for linking
if (address) {
url = `https://share.here.com/r/${sourceLat && sourceLng ? `${sourceLat},${sourceLng}/` : ''}${lat},${lng},${address}?m=d`;
}
else {
url = `https://share.here.com/r/${sourceLat && sourceLng ? `${sourceLat},${sourceLng}/` : ''}${lat},${lng}?m=d`;
}
break;
case 'tomtomgo':
if (address) {
throw new MapsException('tomtomgo does not support passing the address or has not been implemented yet.');
}
else {
url = `${prefixes.tomtomgo}x-callback-url/navigate?destination=${latlng}`;
}
break;
}
return url;
};
exports.generateMapUrl = generateMapUrl;
class MapsException extends Error {
constructor(message) {
super(message);
this.name = 'MapsException';
}
}