From d5fe0f71750ac90de765057fe5d91cd4b0df5911 Mon Sep 17 00:00:00 2001 From: Trevor D Date: Fri, 19 Apr 2024 15:57:08 -0600 Subject: [PATCH 01/16] v3.1.0 - Merging the main changes into the forked branch. --- README.md | 30 ++++- index.d.ts | 131 +++++++++++++++++++ package.json | 2 +- src/index.js | 363 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/utils.js | 262 +++++++++++++++++++++++++++++++++++++ 5 files changed, 784 insertions(+), 4 deletions(-) create mode 100644 index.d.ts create mode 100644 src/index.js create mode 100644 src/utils.js diff --git a/README.md b/README.md index 79fe5b7..acd7388 100644 --- a/README.md +++ b/README.md @@ -249,8 +249,9 @@ showLocation({ latitude: 38.8976763, longitude: -77.0387185, sourceLatitude: -8.0870631, // optionally specify starting location for directions - sourceLongitude: -34.8941619, // not optional if sourceLatitude is specified - title: 'The White House', // optional + sourceLongitude: -34.8941619, // required if sourceLatitude is specified + address: '', // optional but can replace latitude and longitude if needed + title: 'The White House', // optional googleForceLatLon: false, // optionally force GoogleMaps to use the latlon for the query instead of the title googlePlaceId: 'ChIJGVtI4by3t4kRr51d_Qm_x58', // optionally specify the google-place-id alwaysIncludeGoogle: true, // optional, true will always add Google Maps to iOS and open in Safari, even if app is not installed (default: false) @@ -265,12 +266,34 @@ showLocation({ }); ``` +Alternatively you can specify the `address` field and leave the latitude and longitude properties as empty strings + +```js +import {showLocation} from 'react-native-map-link'; + +showLocation({ + latitude: '', + longitude: '', + address: '1600 Pennsylvania Avenue NW, Washington, DC 20500', // Required if replacing latitude and longitude + title: 'The White House', // optional + googleForceLatLon: false, // optionally force GoogleMaps to use the latlon for the query instead of the title + alwaysIncludeGoogle: true, // optional, true will always add Google Maps to iOS and open in Safari, even if app is not installed (default: false) + dialogTitle: 'This is the dialog Title', // optional (default: 'Open in Maps') + dialogMessage: 'This is the amazing dialog Message', // optional (default: 'What app would you like to use?') + cancelText: 'This is the cancel button text', // optional (default: 'Cancel') + appsWhiteList: ['google-maps'], // optionally you can set which apps to show (default: will show all supported apps installed on device) + naverCallerName: 'com.example.myapp', // to link into Naver Map You should provide your appname which is the bundle ID in iOS and applicationId in android. + appTitles: { 'google-maps': 'My custom Google Maps title' }, // optionally you can override default app titles + app: 'uber', // optionally specify specific app to use +}); +``` + Notes: - The `sourceLatitude` / `sourceLongitude` options only work if you specify both. Currently supports all apps except Waze. - `directionsMode` works on google-maps, apple-maps and sygic (on apple-maps, `bike` mode will not work, while on sygic, only `walk` and `car` will work). Without setting it, the app will decide based on its own settings. - If you set `directionsMode` but do not set `sourceLatitude` and `sourceLongitude`, google-maps and apple-maps will still enter directions mode, and use the current location as starting point. -- +- If you want to query an address instead of passing the `latitude` and `longitude` fields, you can do this by leaving both of the required fields as empty strings and provide a full address to be queried. ### Or @@ -296,6 +319,7 @@ const Demo = () => { const result = await getApps({ latitude: 38.8976763, longitude: -77.0387185, + address: '1600 Pennsylvania Avenue NW, Washington, DC 20500', // optional title: 'The White House', // optional googleForceLatLon: false, // optionally force GoogleMaps to use the latlon for the query instead of the title alwaysIncludeGoogle: true, // optional, true will always add Google Maps to iOS and open in Safari, even if app is not installed (default: false) diff --git a/index.d.ts b/index.d.ts new file mode 100644 index 0000000..236893b --- /dev/null +++ b/index.d.ts @@ -0,0 +1,131 @@ +import * as React from 'react'; +import { + ViewStyle, + StyleProp, + ImageStyle, + TextStyle, + ImageSourcePropType, +} from 'react-native'; + +/** id for map application. this is the id that is passed to the `app` option */ +export type MapId = + | 'apple-maps' + | 'google-maps' + | 'citymapper' + | 'uber' + | 'lyft' + | 'transit' + | 'truckmap' + | 'waze' + | 'yandex' + | 'moovit' + | 'yandex-maps' + | 'yandex-taxi' + | 'kakaomap' + | 'tmap' + | 'mapycz' + | 'maps-me' + | 'osmand' + | 'gett' + | 'navermap' + | 'dgis' + | 'liftago' + | 'petalmaps'; + +/** options shared across different types */ +interface SharedOptions { + /** optionally you can set which apps to show (default: will show all supported apps installed on device) */ + appsWhiteList?: MapId[]; + /** custom titles to display for each app instead of using default titles. */ + appTitles?: Partial>; +} + +interface Options extends SharedOptions { + latitude: number | string; + longitude: number | string; + /** optionally you can enter a full address that will be queried against the map app's API and return the initial results if not the actual matched result. */ + /** latitude and longitude will be ignored if the address field is set */ + address?: string | undefined; + /** optionally specify starting location for directions */ + sourceLatitude?: number; + /** required if `sourceLatitude` is specified */ + sourceLongitude?: number; + /** optional, true will always add Google Maps to iOS and open in Safari, even if app is not installed (default: false) */ + alwaysIncludeGoogle?: boolean; + googleForceLatLon?: boolean; + appleIgnoreLatLon?: boolean; + googlePlaceId?: string; + title?: string; + /** optionally specify specific app to use */ + app?: MapId; + /** optional (default: 'Open in Maps') */ + dialogTitle?: string; + /** optional (default: 'What app would you like to use?') */ + dialogMessage?: string; + cancelText?: string; + /** to link into Naver Map You should provide your appname which is the bundle ID in iOS and applicationId in android. */ + naverCallerName?: string; + directionsMode?: 'car' | 'walk' | 'public-transport' | 'bike'; +} + +interface PopupStyleProp { + container?: StyleProp; + itemContainer?: StyleProp; + image?: StyleProp; + itemText?: StyleProp; + headerContainer?: StyleProp; + titleText?: StyleProp; + subtitleText?: StyleProp; + cancelButtonContainer?: StyleProp; + cancelButtonText?: StyleProp; + separatorStyle?: StyleProp; + activityIndicatorContainer?: StyleProp; +} + +interface PopupProps extends SharedOptions { + isVisible: boolean; + showHeader?: boolean; + customHeader?: React.ReactNode; + customFooter?: React.ReactNode; + onCancelPressed: () => void; + onBackButtonPressed: () => void; + onAppPressed: (appName: MapId) => void; + style?: PopupStyleProp; + modalProps?: object; + options: Options; +} + +/** + * Link users to their desired map app. + * + * If an `app` option is passed, it will directly link to that map app, + * else it will prompt user to select map app. + * + * Prompts via `ActionSheetIOS` on iOS & `Alert.alert` on Android. + * + * If these prompts don't meet your UI use case, checkout the `Popup` component, + * or use the `getApps` function to build a custom UI. + */ +export function showLocation( + options: Options, +): Promise; + +export type GetAppResult = { + id: MapId; + name: string; + icon: ImageSourcePropType; + /** function to link user to map app */ + open: () => Promise; +}; + +/** + * Get array of map apps on users device. + * + * Useful for building custom UIs. + */ +export function getApps(options: Options): Promise; + +/** + * A styled popup component that displays icons in the app list + */ +export class Popup extends React.Component {} diff --git a/package.json b/package.json index ee91db2..4a95e05 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-native-map-link", - "version": "3.0.1", + "version": "3.1.0", "description": "Open the map app of the user's choice with a specific location.", "source": "src/index", "main": "lib/index.js", diff --git a/src/index.js b/src/index.js new file mode 100644 index 0000000..2a78774 --- /dev/null +++ b/src/index.js @@ -0,0 +1,363 @@ +/** + * React Native Map Link + */ + +import {Linking} from 'react-native'; + +import {generatePrefixes, generateTitles, isIOS, icons} from './constants'; +import { + askAppChoice, + checkOptions, + getAvailableApps, + checkNotSupportedApps, +} from './utils'; + +/** + * Open a maps app, or let the user choose what app to open, with the given location. + * + * @param {{ + * latitude: number | string, + * longitude: number | string, + * sourceLatitude: number | undefined | null, + * sourceLongitude: number | undefined | null, + * address: string | undefined | null, + * alwaysIncludeGoogle: boolean | undefined | null, + * googleForceLatLon: boolean | undefined | null, + * googlePlaceId: number | string | undefined | null, + * title: string | undefined | null, + * app: string | undefined | null + * dialogTitle: string | undefined | null + * dialogMessage: string | undefined | null + * cancelText: string | undefined | null + * appsWhiteList: array | undefined | null + * appTitles: object | undefined | null + * naverCallerName: string | undefined + * directionsMode: 'car' | 'walk' | 'public-transport' | 'bike' | undefined + * }} options + */ +export async function showLocation(options) { + const prefixes = generatePrefixes(options); + checkOptions(options, prefixes); + + let useSourceDestiny = false; + let sourceLat; + let sourceLng; + let sourceLatLng; + let fullAddress; + + if (options.sourceLatitude != null && options.sourceLongitude != null) { + useSourceDestiny = true; + sourceLat = parseFloat(options.sourceLatitude); + sourceLng = parseFloat(options.sourceLongitude); + sourceLatLng = `${sourceLat},${sourceLng}`; + } + + if (options.address) { + fullAddress = encodeURIComponent(options.address) + } + + const lat = parseFloat(options.latitude); + const lng = parseFloat(options.longitude); + const latlng = `${lat},${lng}`; + const title = options.title && options.title.length ? options.title : null; + const encodedTitle = encodeURIComponent(title); + let app = options.app && options.app.length ? options.app : null; + const dialogTitle = + options.dialogTitle && options.dialogTitle.length + ? options.dialogTitle + : 'Open in Maps'; + const dialogMessage = + options.dialogMessage && options.dialogMessage.length + ? options.dialogMessage + : 'What app would you like to use?'; + const cancelText = + options.cancelText && options.cancelText.length + ? options.cancelText + : 'Cancel'; + const appsWhiteList = + options.appsWhiteList && options.appsWhiteList.length + ? options.appsWhiteList + : null; + + if (!app) { + app = await askAppChoice({ + dialogTitle, + dialogMessage, + cancelText, + appsWhiteList, + prefixes, + appTitles: generateTitles(options.appTitles), + }); + } + + let url = null; + + const getDirectionsModeAppleMaps = () => { + switch (options.directionsMode) { + case 'car': + return 'd'; + + case 'walk': + return 'w'; + + case 'public-transport': + return 'r'; + + default: + return undefined; + } + }; + + const getDirectionsModeGoogleMaps = () => { + switch (options.directionsMode) { + case 'car': + return 'driving'; + + case 'walk': + return 'walking'; + + case 'public-transport': + return 'transit'; + + case 'bike': + return 'bicycling'; + + default: + return undefined; + } + }; + + switch (app) { + case 'apple-maps': + const appleDirectionMode = getDirectionsModeAppleMaps(); + url = prefixes['apple-maps']; + if (fullAddress) { + url = `${url}?address=${fullAddress}`; + } else { + if (useSourceDestiny || options.directionsMode) { + url = `${url}?daddr=${latlng}`; + url += sourceLatLng ? `&saddr=${sourceLatLng}` : ''; + } else if (!options.appleIgnoreLatLon) { + url = `${url}?ll=${latlng}`; + } + } + url += + useSourceDestiny || options.directionsMode || !options.appleIgnoreLatLon || fullAddress + ? '&' + : '?'; + url += `q=${title ? encodedTitle : 'Location'}`; + url += appleDirectionMode ? `&dirflg=${appleDirectionMode}` : ''; + break; + case 'google-maps': + const googleDirectionMode = getDirectionsModeGoogleMaps(); + // Always using universal URL instead of URI scheme since the latter doesn't support all parameters (#155) + if (useSourceDestiny || options.directionsMode) { + // Use "dir" as this will open up directions + url = 'https://www.google.com/maps/dir/?api=1'; + url += sourceLatLng ? `&origin=${sourceLatLng}` : ''; + if (!options.googleForceLatLon && title) { + url += `&destination=${encodedTitle}`; + } else { + url += `&destination=${latlng}`; + } + + url += options.googlePlaceId + ? `&destination_place_id=${options.googlePlaceId}` + : ''; + + url += googleDirectionMode ? `&travelmode=${googleDirectionMode}` : ''; + } else { + // Use "search" as this will open up a single marker + if (fullAddress) { + url = `https://www.google.com/maps/search/?q=${fullAddress}`; + } else { + url = 'https://www.google.com/maps/search/?api=1'; + + if (!options.googleForceLatLon && title) { + url += `&query=${encodedTitle}`; + } else { + url += `&query=${latlng}`; + } + + url += options.googlePlaceId + ? `&query_place_id=${options.googlePlaceId}` + : ''; + } + } + break; + case 'citymapper': + if (fullAddress) { + throw new Error('Cannot use fullAddress on citymapper. This is not supported.') + } else { + url = `${prefixes.citymapper}directions?endcoord=${latlng}`; + + if (title) { + url += `&endname=${encodedTitle}`; + } + + if (useSourceDestiny) { + url += `&startcoord=${sourceLatLng}`; + } + } + break; + case 'uber': + 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': + url = `${prefixes.lyft}ridetype?id=lyft&destination[latitude]=${lat}&destination[longitude]=${lng}`; + + if (useSourceDestiny) { + url += `&pickup[latitude]=${sourceLat}&pickup[longitude]=${sourceLng}`; + } + + break; + case 'transit': + url = `${prefixes.transit}directions?to=${latlng}`; + + if (useSourceDestiny) { + url += `&from=${sourceLatLng}`; + } + break; + case 'truckmap': + url = `https://truckmap.com/place/${lat},${lng}`; + + if (useSourceDestiny) { + url = `https://truckmap.com/route/${sourceLat},${sourceLng}/${lat},${lng}`; + } + break; + case 'waze': + url = `${prefixes.waze}?ll=${latlng}&navigate=yes`; + if (title) { + url += `&q=${encodedTitle}`; + } + break; + case 'yandex': + 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': + 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': + url = `${prefixes['yandex-taxi']}route?end-lat=${lat}&end-lon=${lng}&appmetrica_tracking_id=1178268795219780156`; + + break; + case 'yandex-maps': + url = `${prefixes['yandex-maps']}?pt=${lng},${lat}`; + + break; + case 'kakaomap': + url = `${prefixes.kakaomap}look?p=${latlng}`; + + if (useSourceDestiny) { + url = `${prefixes.kakaomap}route?sp=${sourceLat},${sourceLng}&ep=${latlng}&by=CAR`; + } + break; + case 'tmap': + 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': + url = `${prefixes.mapycz}www.mapy.cz/zakladni?x=${lng}&y=${lat}&source=coor&id=${lng},${lat}`; + + break; + case 'maps-me': + url = `${prefixes['maps-me']}route?sll=${sourceLat},${sourceLng}&saddr= &dll=${lat},${lng}&daddr=${title}&type=vehicle`; + + break; + case 'osmand': + url = isIOS + ? `${prefixes.osmand}?lat=${lat}&lon=${lng}` + : `${prefixes.osmand}?q=${lat},${lng}`; + + break; + case 'gett': + url = `${prefixes.gett}order?pickup=my_location&dropoff_latitude=${lat}&dropoff_longitude=${lng}`; + + break; + case 'navermap': + url = `${prefixes.navermap}map?lat=${lat}&lng=${lng}&appname=${options.naverCallerName}`; + + if (useSourceDestiny) { + url = `${prefixes.navermap}route?slat=${sourceLat}&slng=${sourceLng}&dlat=${lat}&dlng=${lng}&appname=${options.naverCallerName}`; + } + break; + case 'dgis': + url = `${prefixes.dgis}routeSearch/to/${lng},${lat}/go`; + + if (useSourceDestiny) { + url = `${prefixes.dgis}routeSearch/to/${lng},${lat}/from/${sourceLng},${sourceLat}/go`; + } + break; + case 'liftago': + url = `${prefixes.liftago}order?destinationLat=${lat}&destinationLon=${lng}`; + + if (title) { + url += `&destinationName=${encodedTitle}`; + } + + if (useSourceDestiny) { + url += `&pickupLat=${sourceLat}&pickupLon=${sourceLng}`; + } + break; + case 'petalmaps': + url = `${prefixes.petalmaps}navigation?daddr=${lat},${lng}`; + + if (useSourceDestiny) { + url += `&saddr=${sourceLat},${sourceLng}`; + } + break; + } + + if (url) { + return Linking.openURL(url).then(() => Promise.resolve(app)); + } +} + +export async function getApps(options) { + let apps = await getAvailableApps(generatePrefixes(options)); + if ('appsWhiteList' in options && options.appsWhiteList.length) { + checkNotSupportedApps(options.appsWhiteList); + apps = apps.filter((appName) => options.appsWhiteList.includes(appName)); + } + + const titles = generateTitles(options.appTitles); + async function open(app) { + return showLocation({...options, app}); + } + + let list = []; + for (const app of apps) { + list.push({ + id: app, + name: titles[app], + icon: icons[app], + open: open.bind(this, app), + }); + } + + return list; +} diff --git a/src/utils.js b/src/utils.js new file mode 100644 index 0000000..c7a227f --- /dev/null +++ b/src/utils.js @@ -0,0 +1,262 @@ +/** + * React Native Map Link + */ + +import {Linking, ActionSheetIOS, Alert} from 'react-native'; + +import {appKeys, isIOS} from './constants'; + +/** + * Get available navigation apps. + */ +export const getAvailableApps = async (prefixes) => { + const availableApps = []; + const promises = []; + + for (const app in prefixes) { + if (prefixes.hasOwnProperty(app)) { + promises.push( + new Promise(async (resolve) => { + try { + const isInstalled = await isAppInstalled(app, prefixes); + + resolve({ + app, + isInstalled, + }); + } catch (error) { + resolve({ + app, + isInstalled: false, + }); + } + }), + ); + } + } + + const results = await Promise.all(promises); + results.forEach( + ({isInstalled, app}) => isInstalled && availableApps.push(app), + ); + + return availableApps; +}; + +/** + * Check if a given map app is installed. + * + * @param {string} app + * @param {object} prefixes + * @returns {Promise} + */ +export function isAppInstalled(app, prefixes) { + return new Promise((resolve) => { + if (!(app in prefixes)) { + return resolve(false); + } + + Linking.canOpenURL(prefixes[app]) + .then((result) => { + resolve(!!result); + }) + .catch(() => resolve(false)); + }); +} + +/** + * Check if a given app is supported by this library + * + * @param {string} app + * @returns {boolean} + */ +function isSupportedApp(app) { + return appKeys.includes(app); +} + +/** + * Get a list of not supported apps from a given array of apps + * + * @param {array} apps + * @returns {array} + */ +function getNotSupportedApps(apps) { + return apps.filter((app) => !isSupportedApp(app)); +} + +/** + * Throws an exception if some of the given apps is not supported + * + * @param {array} apps + */ +export function checkNotSupportedApps(apps) { + const notSupportedApps = getNotSupportedApps(apps); + if (notSupportedApps.length) { + throw new MapsException( + `appsWhiteList [${notSupportedApps}] are not supported apps, please provide some of the supported apps [${appKeys}]`, + ); + } +} + +/** + * Ask the user to choose one of the available map apps. + * @param {{ + * title: string | undefined | null + * message: string | undefined | null + * cancelText: string | undefined | null + * appsWhiteList: string[] | null + * prefixes: string[], + * appTitles: object | undefined | null + * }} options + * @returns {Promise} + */ +export function askAppChoice({ + dialogTitle, + dialogMessage, + cancelText, + appsWhiteList, + prefixes, + appTitles, +}) { + return new Promise(async (resolve) => { + let availableApps = await getAvailableApps(prefixes); + + if (appsWhiteList && appsWhiteList.length) { + availableApps = availableApps.filter((appName) => + appsWhiteList.includes(appName), + ); + } + + if (availableApps.length < 2) { + return resolve(availableApps[0] || null); + } + + if (isIOS) { + const options = availableApps.map((app) => appTitles[app]); + options.push(cancelText); + + ActionSheetIOS.showActionSheetWithOptions( + { + title: dialogTitle, + message: dialogMessage, + options: options, + cancelButtonIndex: options.length - 1, + }, + (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), + style: 'cancel', + }); + + return Alert.alert(dialogTitle, dialogMessage, options, { + cancelable: true, + onDismiss: () => resolve(null), + }); + }); +} + +/** + * Check if options are valid and well passed + * + * @param {{ + * latitude: number | string, + * longitude: number | string, + * address: string | undefined | null, + * sourceLatitude: number | undefined | null, + * sourceLongitude: number | undefined | null, + * googleForceLatLon: boolean | undefined | null, + * googlePlaceId: number | undefined | null, + * title: string | undefined | null, + * app: string | undefined | null + * dialogTitle: string | undefined | null + * dialogMessage: string | undefined | null + * cancelText: string | undefined | null + * naverCallerName: string | undefined + * }} options + * @param {object} prefixes + */ +export function checkOptions(options, prefixes) { + if (!options || typeof options !== 'object') { + throw new MapsException( + 'First parameter of `showLocation` should contain object with options.', + ); + } + if (!('latitude' in options) || !('longitude' in options)) { + throw new MapsException( + 'First parameter of `showLocation` should contain object with at least keys `latitude` and `longitude`.', + ); + } + if ( + 'address' in options && + options.address && + typeof options.address !== 'string' + ) { + throw new MapsException( + 'Option `address` should be of type `string`.', + ); + } + if ( + 'title' in options && + options.title && + typeof options.title !== 'string' + ) { + throw new MapsException('Option `title` should be of type `string`.'); + } + if ( + 'googleForceLatLon' in options && + options.googleForceLatLon && + typeof options.googleForceLatLon !== 'boolean' + ) { + throw new MapsException( + 'Option `googleForceLatLon` should be of type `boolean`.', + ); + } + if ( + 'googlePlaceId' in options && + options.googlePlaceId && + typeof options.googlePlaceId !== 'string' + ) { + throw new MapsException( + 'Option `googlePlaceId` should be of type `string`.', + ); + } + if ('app' in options && options.app && !(options.app in prefixes)) { + throw new MapsException( + 'Option `app` should be undefined, null, or one of the following: "' + + Object.keys(prefixes).join('", "') + + '".', + ); + } + if ('appsWhiteList' in options && options.appsWhiteList) { + checkNotSupportedApps(options.appsWhiteList); + } + if ( + 'appTitles' in options && + options.appTitles && + typeof options.appTitles !== 'object' + ) { + throw new MapsException('Option `appTitles` should be of type `object`.'); + } +} + +class MapsException { + constructor(message) { + this.message = message; + this.name = 'MapsException'; + } +} From 32d008316c442bbf3189a5d572fef3557e324fbf Mon Sep 17 00:00:00 2001 From: Trevor D Date: Fri, 1 Mar 2024 11:51:26 -0700 Subject: [PATCH 02/16] issue210-address-as-parameter: Merged the changes from master back into this fork. Exported a MapException class, and made more changes to the maps to include passing an address as a query string to those individual maps. Still have a number to update until it's fully done. --- src/index.js | 203 +++++++++++++++++++++++++++++++++++---------------- src/utils.js | 2 +- 2 files changed, 142 insertions(+), 63 deletions(-) diff --git a/src/index.js b/src/index.js index 2a78774..0772829 100644 --- a/src/index.js +++ b/src/index.js @@ -10,6 +10,7 @@ import { checkOptions, getAvailableApps, checkNotSupportedApps, + MapsException } from './utils'; /** @@ -187,7 +188,7 @@ export async function showLocation(options) { break; case 'citymapper': if (fullAddress) { - throw new Error('Cannot use fullAddress on citymapper. This is not supported.') + url = `${prefixes.citymapper}directions?endname=${fullAddress}`; } else { url = `${prefixes.citymapper}directions?endcoord=${latlng}`; @@ -201,77 +202,117 @@ export async function showLocation(options) { } break; case 'uber': - url = `${prefixes.uber}?action=setPickup&dropoff[latitude]=${lat}&dropoff[longitude]=${lng}`; - - if (title) { - url += `&dropoff[nickname]=${encodedTitle}`; - } + if (fullAddress) { + url = `${prefixes.uber}?action=setPickup&pickup=my_location&dropoff=${fullAddress}` + } else { + url = `${prefixes.uber}?action=setPickup&dropoff[latitude]=${lat}&dropoff[longitude]=${lng}`; - url += useSourceDestiny - ? `&pickup[latitude]=${sourceLat}&pickup[longitude]=${sourceLng}` - : '&pickup=my_location'; + if (title) { + url += `&dropoff[nickname]=${encodedTitle}`; + } + url += useSourceDestiny + ? `&pickup[latitude]=${sourceLat}&pickup[longitude]=${sourceLng}` + : '&pickup=my_location'; + } break; case 'lyft': - url = `${prefixes.lyft}ridetype?id=lyft&destination[latitude]=${lat}&destination[longitude]=${lng}`; + if (fullAddress) { + url = `${prefixes.lyft}ridetype?id=lyft&destination[address]=${fullAddress}`; + } else { + url = `${prefixes.lyft}ridetype?id=lyft&destination[latitude]=${lat}&destination[longitude]=${lng}`; - if (useSourceDestiny) { - url += `&pickup[latitude]=${sourceLat}&pickup[longitude]=${sourceLng}`; + if (useSourceDestiny) { + url += `&pickup[latitude]=${sourceLat}&pickup[longitude]=${sourceLng}`; + } } - break; case 'transit': - url = `${prefixes.transit}directions?to=${latlng}`; + if (fullAddress) { + url = `${prefixes.transit}directions?destination=${fullAddress}`; + } else { + url = `${prefixes.transit}directions?to=${latlng}`; + } if (useSourceDestiny) { url += `&from=${sourceLatLng}`; } break; case 'truckmap': - url = `https://truckmap.com/place/${lat},${lng}`; - - if (useSourceDestiny) { - url = `https://truckmap.com/route/${sourceLat},${sourceLng}/${lat},${lng}`; + if (fullAddress) { + // Constructed from documentation from https://truckmap.com/solutions/developer + url = `https://truckmap.com/place/${fullAddress}`; + } else { + url = `https://truckmap.com/place/${lat},${lng}`; + + if (useSourceDestiny) { + url = `https://truckmap.com/route/${sourceLat},${sourceLng}/${lat},${lng}`; + } } break; case 'waze': - url = `${prefixes.waze}?ll=${latlng}&navigate=yes`; - if (title) { - url += `&q=${encodedTitle}`; + if (fullAddress) { + url = `${prefixes.waze}?q=${fullAddress}` + } else { + url = `${prefixes.waze}?ll=${latlng}&navigate=yes`; + if (title) { + url += `&q=${encodedTitle}`; + } } break; case 'yandex': - url = `${prefixes.yandex}build_route_on_map?lat_to=${lat}&lon_to=${lng}`; - - if (useSourceDestiny) { - url += `&lat_from=${sourceLat}&lon_from=${sourceLng}`; + if (fullAddress) { + url = `${prefixes.yandex}?text=${fullAddress}`; + } 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': - url = `${prefixes.moovit}?dest_lat=${lat}&dest_lon=${lng}`; - - if (title) { - url += `&dest_name=${encodedTitle}`; - } - - if (useSourceDestiny) { - url += `&orig_lat=${sourceLat}&orig_lon=${sourceLng}`; + if (fullAddress) { + url = `${prefixes.moovit}?dest_name=${fullAddress}`; + } 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': - url = `${prefixes['yandex-taxi']}route?end-lat=${lat}&end-lon=${lng}&appmetrica_tracking_id=1178268795219780156`; + if (fullAddress) { + throw new MapsException(`yandex-taxi does not support passing the fullAddress.`); + } else { + url = `${prefixes['yandex-taxi']}route?end-lat=${lat}&end-lon=${lng}&appmetrica_tracking_id=1178268795219780156`; + } break; case 'yandex-maps': - url = `${prefixes['yandex-maps']}?pt=${lng},${lat}`; + if (fullAddress) { + url = `${prefixes['yandex-maps']}?text=${fullAddress}`; + } else { + url = `${prefixes['yandex-maps']}?pt=${lng},${lat}`; + } break; case 'kakaomap': - url = `${prefixes.kakaomap}look?p=${latlng}`; - - if (useSourceDestiny) { - url = `${prefixes.kakaomap}route?sp=${sourceLat},${sourceLng}&ep=${latlng}&by=CAR`; + if (fullAddress) { + url = `${prefixes.kakaomap}`; + } else { + url = `${prefixes.kakaomap}look?p=${latlng}`; + + if (useSourceDestiny) { + url = `${prefixes.kakaomap}route?sp=${sourceLat},${sourceLng}&ep=${latlng}&by=CAR`; + } } + break; case 'tmap': url = `${prefixes.tmap}viewmap?x=${lng}&y=${lat}`; @@ -279,56 +320,94 @@ export async function showLocation(options) { if (useSourceDestiny) { url = `${prefixes.tmap}route?startx=${sourceLng}&starty=${sourceLat}&goalx=${lng}&goaly=${lat}`; } + break; case 'mapycz': - url = `${prefixes.mapycz}www.mapy.cz/zakladni?x=${lng}&y=${lat}&source=coor&id=${lng},${lat}`; + if (fullAddress) { + url = `${prefixes.mapycz}`; + } else { + url = `${prefixes.mapycz}www.mapy.cz/zakladni?x=${lng}&y=${lat}&source=coor&id=${lng},${lat}`; + } break; case 'maps-me': - url = `${prefixes['maps-me']}route?sll=${sourceLat},${sourceLng}&saddr= &dll=${lat},${lng}&daddr=${title}&type=vehicle`; + if (fullAddress) { + url = `${prefixes['maps-me']}`; + } else { + url = `${prefixes['maps-me']}route?sll=${sourceLat},${sourceLng}&saddr= &dll=${lat},${lng}&daddr=${title}&type=vehicle`; + } break; case 'osmand': - url = isIOS - ? `${prefixes.osmand}?lat=${lat}&lon=${lng}` - : `${prefixes.osmand}?q=${lat},${lng}`; + if (fullAddress) { + url = `${prefixes.osmand}?` + } else { + url = isIOS + ? `${prefixes.osmand}?lat=${lat}&lon=${lng}` + : `${prefixes.osmand}?q=${lat},${lng}`; + } break; case 'gett': - url = `${prefixes.gett}order?pickup=my_location&dropoff_latitude=${lat}&dropoff_longitude=${lng}`; + if (fullAddress) { + url = `${prefixes.gett}`; + } else { + url = `${prefixes.gett}order?pickup=my_location&dropoff_latitude=${lat}&dropoff_longitude=${lng}`; + } break; case 'navermap': - url = `${prefixes.navermap}map?lat=${lat}&lng=${lng}&appname=${options.naverCallerName}`; - - if (useSourceDestiny) { - url = `${prefixes.navermap}route?slat=${sourceLat}&slng=${sourceLng}&dlat=${lat}&dlng=${lng}&appname=${options.naverCallerName}`; + if (fullAddress) { + url = `${prefixes.navermap}`; + } else { + url = `${prefixes.navermap}map?lat=${lat}&lng=${lng}&appname=${options.naverCallerName}`; + + if (useSourceDestiny) { + url = `${prefixes.navermap}route?slat=${sourceLat}&slng=${sourceLng}&dlat=${lat}&dlng=${lng}&appname=${options.naverCallerName}`; + } } + break; case 'dgis': - url = `${prefixes.dgis}routeSearch/to/${lng},${lat}/go`; + if (fullAddress) { + url = `${prefixes.dgis}`; + } else { + url = `${prefixes.dgis}routeSearch/to/${lng},${lat}/go`; - if (useSourceDestiny) { - url = `${prefixes.dgis}routeSearch/to/${lng},${lat}/from/${sourceLng},${sourceLat}/go`; + if (useSourceDestiny) { + url = `${prefixes.dgis}routeSearch/to/${lng},${lat}/from/${sourceLng},${sourceLat}/go`; + } } + break; case 'liftago': - url = `${prefixes.liftago}order?destinationLat=${lat}&destinationLon=${lng}`; + if (fullAddress) { + url = `${prefixes.liftago}`; + } else { + url = `${prefixes.liftago}order?destinationLat=${lat}&destinationLon=${lng}`; - if (title) { - url += `&destinationName=${encodedTitle}`; - } + if (title) { + url += `&destinationName=${encodedTitle}`; + } - if (useSourceDestiny) { - url += `&pickupLat=${sourceLat}&pickupLon=${sourceLng}`; + if (useSourceDestiny) { + url += `&pickupLat=${sourceLat}&pickupLon=${sourceLng}`; + } } + break; case 'petalmaps': - url = `${prefixes.petalmaps}navigation?daddr=${lat},${lng}`; - - if (useSourceDestiny) { - url += `&saddr=${sourceLat},${sourceLng}`; + if (fullAddress) { + // Got this from this documentation https://developer.huawei.com/consumer/en/doc/HMSCore-Guides/petal-maps-introduction-0000001059189679 + url = `${prefixes.petalmaps}textSearch?text=${fullAddress}`; + } else { + url = `${prefixes.petalmaps}navigation?daddr=${lat},${lng}`; + + if (useSourceDestiny) { + url += `&saddr=${sourceLat},${sourceLng}`; + } } + break; } diff --git a/src/utils.js b/src/utils.js index c7a227f..e5f6ffb 100644 --- a/src/utils.js +++ b/src/utils.js @@ -254,7 +254,7 @@ export function checkOptions(options, prefixes) { } } -class MapsException { +export class MapsException { constructor(message) { this.message = message; this.name = 'MapsException'; From 3c969e021ddb1f85f5085f0d139809700c4ec093 Mon Sep 17 00:00:00 2001 From: Trevor D Date: Fri, 1 Mar 2024 14:50:17 -0700 Subject: [PATCH 03/16] issue210-address-as-parameter: Finished all of the logic to pass a full address to each of the map apps. A few either did NOT support passing the destination address or it wasn't documented. Ready to open a PR. --- src/index.js | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/src/index.js b/src/index.js index 0772829..913b84f 100644 --- a/src/index.js +++ b/src/index.js @@ -288,7 +288,7 @@ export async function showLocation(options) { break; case 'yandex-taxi': if (fullAddress) { - throw new MapsException(`yandex-taxi does not support passing the fullAddress.`); + throw new MapsException(`yandex-taxi does not support passing the fullAddress or has not been implemented yet.`); } else { url = `${prefixes['yandex-taxi']}route?end-lat=${lat}&end-lon=${lng}&appmetrica_tracking_id=1178268795219780156`; } @@ -304,7 +304,7 @@ export async function showLocation(options) { break; case 'kakaomap': if (fullAddress) { - url = `${prefixes.kakaomap}`; + url = `${prefixes.kakaomap}route?ep=${fullAddress}`; } else { url = `${prefixes.kakaomap}look?p=${latlng}`; @@ -315,16 +315,20 @@ export async function showLocation(options) { break; case 'tmap': - url = `${prefixes.tmap}viewmap?x=${lng}&y=${lat}`; - - if (useSourceDestiny) { - url = `${prefixes.tmap}route?startx=${sourceLng}&starty=${sourceLat}&goalx=${lng}&goaly=${lat}`; + if (fullAddress) { + url = `${prefixes.tmap}search?name=${fullAddress}`; + } 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 (fullAddress) { - url = `${prefixes.mapycz}`; + url = `${prefixes.mapycz}www.mapy.cz/zakladni?q=${fullAddress}`; } else { url = `${prefixes.mapycz}www.mapy.cz/zakladni?x=${lng}&y=${lat}&source=coor&id=${lng},${lat}`; } @@ -332,7 +336,7 @@ export async function showLocation(options) { break; case 'maps-me': if (fullAddress) { - url = `${prefixes['maps-me']}`; + url = `${prefixes['maps-me']}?q=${fullAddress}`; } else { url = `${prefixes['maps-me']}route?sll=${sourceLat},${sourceLng}&saddr= &dll=${lat},${lng}&daddr=${title}&type=vehicle`; } @@ -340,7 +344,7 @@ export async function showLocation(options) { break; case 'osmand': if (fullAddress) { - url = `${prefixes.osmand}?` + url = `${prefixes.osmand}show_map?addr=${fullAddress}` } else { url = isIOS ? `${prefixes.osmand}?lat=${lat}&lon=${lng}` @@ -350,7 +354,7 @@ export async function showLocation(options) { break; case 'gett': if (fullAddress) { - url = `${prefixes.gett}`; + throw new MapsException(`gett does not support passing the fullAddress or has not been implemented yet.`); } else { url = `${prefixes.gett}order?pickup=my_location&dropoff_latitude=${lat}&dropoff_longitude=${lng}`; } @@ -358,7 +362,7 @@ export async function showLocation(options) { break; case 'navermap': if (fullAddress) { - url = `${prefixes.navermap}`; + url = `${prefixes.navermap}search?query=${fullAddress}`; } else { url = `${prefixes.navermap}map?lat=${lat}&lng=${lng}&appname=${options.naverCallerName}`; @@ -370,7 +374,7 @@ export async function showLocation(options) { break; case 'dgis': if (fullAddress) { - url = `${prefixes.dgis}`; + url = `${prefixes.dgis}?q=${fullAddress}`; } else { url = `${prefixes.dgis}routeSearch/to/${lng},${lat}/go`; @@ -382,7 +386,7 @@ export async function showLocation(options) { break; case 'liftago': if (fullAddress) { - url = `${prefixes.liftago}`; + throw new MapsException(`liftago does not support passing the fullAddress or has not been implemented yet.`); } else { url = `${prefixes.liftago}order?destinationLat=${lat}&destinationLon=${lng}`; From 7936c7d3fc248cbbd4ae8dcc05efc07734930c78 Mon Sep 17 00:00:00 2001 From: Trevor D Date: Fri, 19 Apr 2024 17:04:54 -0600 Subject: [PATCH 04/16] v3.1.0 - Merged the address feature code to the new version 3 code. Need to double check the compare files then a PR may be ready. --- README.md | 2 +- index.d.ts | 131 --------------- src/index.js | 446 --------------------------------------------------- src/index.ts | 8 + src/type.ts | 3 + src/utils.js | 262 ------------------------------ src/utils.ts | 260 +++++++++++++++++++++--------- 7 files changed, 196 insertions(+), 916 deletions(-) delete mode 100644 index.d.ts delete mode 100644 src/index.js delete mode 100644 src/utils.js diff --git a/README.md b/README.md index acd7388..14dbabb 100644 --- a/README.md +++ b/README.md @@ -293,7 +293,7 @@ Notes: - The `sourceLatitude` / `sourceLongitude` options only work if you specify both. Currently supports all apps except Waze. - `directionsMode` works on google-maps, apple-maps and sygic (on apple-maps, `bike` mode will not work, while on sygic, only `walk` and `car` will work). Without setting it, the app will decide based on its own settings. - If you set `directionsMode` but do not set `sourceLatitude` and `sourceLongitude`, google-maps and apple-maps will still enter directions mode, and use the current location as starting point. -- If you want to query an address instead of passing the `latitude` and `longitude` fields, you can do this by leaving both of the required fields as empty strings and provide a full address to be queried. +- If you want to query an address instead of passing the `latitude` and `longitude` fields, you can do this by leaving both of the required fields as empty strings and provide a full address to be queried. Just be aware that not all applications support this. ### Or diff --git a/index.d.ts b/index.d.ts deleted file mode 100644 index 236893b..0000000 --- a/index.d.ts +++ /dev/null @@ -1,131 +0,0 @@ -import * as React from 'react'; -import { - ViewStyle, - StyleProp, - ImageStyle, - TextStyle, - ImageSourcePropType, -} from 'react-native'; - -/** id for map application. this is the id that is passed to the `app` option */ -export type MapId = - | 'apple-maps' - | 'google-maps' - | 'citymapper' - | 'uber' - | 'lyft' - | 'transit' - | 'truckmap' - | 'waze' - | 'yandex' - | 'moovit' - | 'yandex-maps' - | 'yandex-taxi' - | 'kakaomap' - | 'tmap' - | 'mapycz' - | 'maps-me' - | 'osmand' - | 'gett' - | 'navermap' - | 'dgis' - | 'liftago' - | 'petalmaps'; - -/** options shared across different types */ -interface SharedOptions { - /** optionally you can set which apps to show (default: will show all supported apps installed on device) */ - appsWhiteList?: MapId[]; - /** custom titles to display for each app instead of using default titles. */ - appTitles?: Partial>; -} - -interface Options extends SharedOptions { - latitude: number | string; - longitude: number | string; - /** optionally you can enter a full address that will be queried against the map app's API and return the initial results if not the actual matched result. */ - /** latitude and longitude will be ignored if the address field is set */ - address?: string | undefined; - /** optionally specify starting location for directions */ - sourceLatitude?: number; - /** required if `sourceLatitude` is specified */ - sourceLongitude?: number; - /** optional, true will always add Google Maps to iOS and open in Safari, even if app is not installed (default: false) */ - alwaysIncludeGoogle?: boolean; - googleForceLatLon?: boolean; - appleIgnoreLatLon?: boolean; - googlePlaceId?: string; - title?: string; - /** optionally specify specific app to use */ - app?: MapId; - /** optional (default: 'Open in Maps') */ - dialogTitle?: string; - /** optional (default: 'What app would you like to use?') */ - dialogMessage?: string; - cancelText?: string; - /** to link into Naver Map You should provide your appname which is the bundle ID in iOS and applicationId in android. */ - naverCallerName?: string; - directionsMode?: 'car' | 'walk' | 'public-transport' | 'bike'; -} - -interface PopupStyleProp { - container?: StyleProp; - itemContainer?: StyleProp; - image?: StyleProp; - itemText?: StyleProp; - headerContainer?: StyleProp; - titleText?: StyleProp; - subtitleText?: StyleProp; - cancelButtonContainer?: StyleProp; - cancelButtonText?: StyleProp; - separatorStyle?: StyleProp; - activityIndicatorContainer?: StyleProp; -} - -interface PopupProps extends SharedOptions { - isVisible: boolean; - showHeader?: boolean; - customHeader?: React.ReactNode; - customFooter?: React.ReactNode; - onCancelPressed: () => void; - onBackButtonPressed: () => void; - onAppPressed: (appName: MapId) => void; - style?: PopupStyleProp; - modalProps?: object; - options: Options; -} - -/** - * Link users to their desired map app. - * - * If an `app` option is passed, it will directly link to that map app, - * else it will prompt user to select map app. - * - * Prompts via `ActionSheetIOS` on iOS & `Alert.alert` on Android. - * - * If these prompts don't meet your UI use case, checkout the `Popup` component, - * or use the `getApps` function to build a custom UI. - */ -export function showLocation( - options: Options, -): Promise; - -export type GetAppResult = { - id: MapId; - name: string; - icon: ImageSourcePropType; - /** function to link user to map app */ - open: () => Promise; -}; - -/** - * Get array of map apps on users device. - * - * Useful for building custom UIs. - */ -export function getApps(options: Options): Promise; - -/** - * A styled popup component that displays icons in the app list - */ -export class Popup extends React.Component {} diff --git a/src/index.js b/src/index.js deleted file mode 100644 index 913b84f..0000000 --- a/src/index.js +++ /dev/null @@ -1,446 +0,0 @@ -/** - * React Native Map Link - */ - -import {Linking} from 'react-native'; - -import {generatePrefixes, generateTitles, isIOS, icons} from './constants'; -import { - askAppChoice, - checkOptions, - getAvailableApps, - checkNotSupportedApps, - MapsException -} from './utils'; - -/** - * Open a maps app, or let the user choose what app to open, with the given location. - * - * @param {{ - * latitude: number | string, - * longitude: number | string, - * sourceLatitude: number | undefined | null, - * sourceLongitude: number | undefined | null, - * address: string | undefined | null, - * alwaysIncludeGoogle: boolean | undefined | null, - * googleForceLatLon: boolean | undefined | null, - * googlePlaceId: number | string | undefined | null, - * title: string | undefined | null, - * app: string | undefined | null - * dialogTitle: string | undefined | null - * dialogMessage: string | undefined | null - * cancelText: string | undefined | null - * appsWhiteList: array | undefined | null - * appTitles: object | undefined | null - * naverCallerName: string | undefined - * directionsMode: 'car' | 'walk' | 'public-transport' | 'bike' | undefined - * }} options - */ -export async function showLocation(options) { - const prefixes = generatePrefixes(options); - checkOptions(options, prefixes); - - let useSourceDestiny = false; - let sourceLat; - let sourceLng; - let sourceLatLng; - let fullAddress; - - if (options.sourceLatitude != null && options.sourceLongitude != null) { - useSourceDestiny = true; - sourceLat = parseFloat(options.sourceLatitude); - sourceLng = parseFloat(options.sourceLongitude); - sourceLatLng = `${sourceLat},${sourceLng}`; - } - - if (options.address) { - fullAddress = encodeURIComponent(options.address) - } - - const lat = parseFloat(options.latitude); - const lng = parseFloat(options.longitude); - const latlng = `${lat},${lng}`; - const title = options.title && options.title.length ? options.title : null; - const encodedTitle = encodeURIComponent(title); - let app = options.app && options.app.length ? options.app : null; - const dialogTitle = - options.dialogTitle && options.dialogTitle.length - ? options.dialogTitle - : 'Open in Maps'; - const dialogMessage = - options.dialogMessage && options.dialogMessage.length - ? options.dialogMessage - : 'What app would you like to use?'; - const cancelText = - options.cancelText && options.cancelText.length - ? options.cancelText - : 'Cancel'; - const appsWhiteList = - options.appsWhiteList && options.appsWhiteList.length - ? options.appsWhiteList - : null; - - if (!app) { - app = await askAppChoice({ - dialogTitle, - dialogMessage, - cancelText, - appsWhiteList, - prefixes, - appTitles: generateTitles(options.appTitles), - }); - } - - let url = null; - - const getDirectionsModeAppleMaps = () => { - switch (options.directionsMode) { - case 'car': - return 'd'; - - case 'walk': - return 'w'; - - case 'public-transport': - return 'r'; - - default: - return undefined; - } - }; - - const getDirectionsModeGoogleMaps = () => { - switch (options.directionsMode) { - case 'car': - return 'driving'; - - case 'walk': - return 'walking'; - - case 'public-transport': - return 'transit'; - - case 'bike': - return 'bicycling'; - - default: - return undefined; - } - }; - - switch (app) { - case 'apple-maps': - const appleDirectionMode = getDirectionsModeAppleMaps(); - url = prefixes['apple-maps']; - if (fullAddress) { - url = `${url}?address=${fullAddress}`; - } else { - if (useSourceDestiny || options.directionsMode) { - url = `${url}?daddr=${latlng}`; - url += sourceLatLng ? `&saddr=${sourceLatLng}` : ''; - } else if (!options.appleIgnoreLatLon) { - url = `${url}?ll=${latlng}`; - } - } - url += - useSourceDestiny || options.directionsMode || !options.appleIgnoreLatLon || fullAddress - ? '&' - : '?'; - url += `q=${title ? encodedTitle : 'Location'}`; - url += appleDirectionMode ? `&dirflg=${appleDirectionMode}` : ''; - break; - case 'google-maps': - const googleDirectionMode = getDirectionsModeGoogleMaps(); - // Always using universal URL instead of URI scheme since the latter doesn't support all parameters (#155) - if (useSourceDestiny || options.directionsMode) { - // Use "dir" as this will open up directions - url = 'https://www.google.com/maps/dir/?api=1'; - url += sourceLatLng ? `&origin=${sourceLatLng}` : ''; - if (!options.googleForceLatLon && title) { - url += `&destination=${encodedTitle}`; - } else { - url += `&destination=${latlng}`; - } - - url += options.googlePlaceId - ? `&destination_place_id=${options.googlePlaceId}` - : ''; - - url += googleDirectionMode ? `&travelmode=${googleDirectionMode}` : ''; - } else { - // Use "search" as this will open up a single marker - if (fullAddress) { - url = `https://www.google.com/maps/search/?q=${fullAddress}`; - } else { - url = 'https://www.google.com/maps/search/?api=1'; - - if (!options.googleForceLatLon && title) { - url += `&query=${encodedTitle}`; - } else { - url += `&query=${latlng}`; - } - - url += options.googlePlaceId - ? `&query_place_id=${options.googlePlaceId}` - : ''; - } - } - break; - case 'citymapper': - if (fullAddress) { - url = `${prefixes.citymapper}directions?endname=${fullAddress}`; - } else { - url = `${prefixes.citymapper}directions?endcoord=${latlng}`; - - if (title) { - url += `&endname=${encodedTitle}`; - } - - if (useSourceDestiny) { - url += `&startcoord=${sourceLatLng}`; - } - } - break; - case 'uber': - if (fullAddress) { - url = `${prefixes.uber}?action=setPickup&pickup=my_location&dropoff=${fullAddress}` - } 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 (fullAddress) { - url = `${prefixes.lyft}ridetype?id=lyft&destination[address]=${fullAddress}`; - } 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 (fullAddress) { - url = `${prefixes.transit}directions?destination=${fullAddress}`; - } else { - url = `${prefixes.transit}directions?to=${latlng}`; - } - - if (useSourceDestiny) { - url += `&from=${sourceLatLng}`; - } - break; - case 'truckmap': - if (fullAddress) { - // Constructed from documentation from https://truckmap.com/solutions/developer - url = `https://truckmap.com/place/${fullAddress}`; - } else { - url = `https://truckmap.com/place/${lat},${lng}`; - - if (useSourceDestiny) { - url = `https://truckmap.com/route/${sourceLat},${sourceLng}/${lat},${lng}`; - } - } - break; - case 'waze': - if (fullAddress) { - url = `${prefixes.waze}?q=${fullAddress}` - } else { - url = `${prefixes.waze}?ll=${latlng}&navigate=yes`; - if (title) { - url += `&q=${encodedTitle}`; - } - } - break; - case 'yandex': - if (fullAddress) { - url = `${prefixes.yandex}?text=${fullAddress}`; - } 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 (fullAddress) { - url = `${prefixes.moovit}?dest_name=${fullAddress}`; - } 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 (fullAddress) { - throw new MapsException(`yandex-taxi does not support passing the fullAddress 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 (fullAddress) { - url = `${prefixes['yandex-maps']}?text=${fullAddress}`; - } else { - url = `${prefixes['yandex-maps']}?pt=${lng},${lat}`; - } - - break; - case 'kakaomap': - if (fullAddress) { - url = `${prefixes.kakaomap}route?ep=${fullAddress}`; - } else { - url = `${prefixes.kakaomap}look?p=${latlng}`; - - if (useSourceDestiny) { - url = `${prefixes.kakaomap}route?sp=${sourceLat},${sourceLng}&ep=${latlng}&by=CAR`; - } - } - - break; - case 'tmap': - if (fullAddress) { - url = `${prefixes.tmap}search?name=${fullAddress}`; - } 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 (fullAddress) { - url = `${prefixes.mapycz}www.mapy.cz/zakladni?q=${fullAddress}`; - } else { - url = `${prefixes.mapycz}www.mapy.cz/zakladni?x=${lng}&y=${lat}&source=coor&id=${lng},${lat}`; - } - - break; - case 'maps-me': - if (fullAddress) { - url = `${prefixes['maps-me']}?q=${fullAddress}`; - } else { - url = `${prefixes['maps-me']}route?sll=${sourceLat},${sourceLng}&saddr= &dll=${lat},${lng}&daddr=${title}&type=vehicle`; - } - - break; - case 'osmand': - if (fullAddress) { - url = `${prefixes.osmand}show_map?addr=${fullAddress}` - } else { - url = isIOS - ? `${prefixes.osmand}?lat=${lat}&lon=${lng}` - : `${prefixes.osmand}?q=${lat},${lng}`; - } - - break; - case 'gett': - if (fullAddress) { - throw new MapsException(`gett does not support passing the fullAddress or has not been implemented yet.`); - } else { - url = `${prefixes.gett}order?pickup=my_location&dropoff_latitude=${lat}&dropoff_longitude=${lng}`; - } - - break; - case 'navermap': - if (fullAddress) { - url = `${prefixes.navermap}search?query=${fullAddress}`; - } else { - url = `${prefixes.navermap}map?lat=${lat}&lng=${lng}&appname=${options.naverCallerName}`; - - if (useSourceDestiny) { - url = `${prefixes.navermap}route?slat=${sourceLat}&slng=${sourceLng}&dlat=${lat}&dlng=${lng}&appname=${options.naverCallerName}`; - } - } - - break; - case 'dgis': - if (fullAddress) { - url = `${prefixes.dgis}?q=${fullAddress}`; - } 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 (fullAddress) { - throw new MapsException(`liftago does not support passing the fullAddress 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 (fullAddress) { - // Got this from this documentation https://developer.huawei.com/consumer/en/doc/HMSCore-Guides/petal-maps-introduction-0000001059189679 - url = `${prefixes.petalmaps}textSearch?text=${fullAddress}`; - } else { - url = `${prefixes.petalmaps}navigation?daddr=${lat},${lng}`; - - if (useSourceDestiny) { - url += `&saddr=${sourceLat},${sourceLng}`; - } - } - - break; - } - - if (url) { - return Linking.openURL(url).then(() => Promise.resolve(app)); - } -} - -export async function getApps(options) { - let apps = await getAvailableApps(generatePrefixes(options)); - if ('appsWhiteList' in options && options.appsWhiteList.length) { - checkNotSupportedApps(options.appsWhiteList); - apps = apps.filter((appName) => options.appsWhiteList.includes(appName)); - } - - const titles = generateTitles(options.appTitles); - async function open(app) { - return showLocation({...options, app}); - } - - let list = []; - for (const app of apps) { - list.push({ - id: app, - name: titles[app], - icon: icons[app], - open: open.bind(this, app), - }); - } - - return list; -} diff --git a/src/index.ts b/src/index.ts index 1494f10..ac14f69 100644 --- a/src/index.ts +++ b/src/index.ts @@ -25,6 +25,7 @@ export type {PopupProps} from './components/popup/Popup'; export const showLocation = async ({ latitude, longitude, + address, sourceLatitude, sourceLongitude, appleIgnoreLatLon, @@ -49,6 +50,7 @@ export const showLocation = async ({ checkOptions({ latitude, longitude, + address, googleForceLatLon, googlePlaceId, title: customTitle, @@ -62,6 +64,7 @@ export const showLocation = async ({ let sourceLat; let sourceLng; let sourceLatLng; + let fullAddress; if (sourceLatitude != null && sourceLongitude != null) { useSourceDestiny = true; @@ -76,6 +79,10 @@ export const showLocation = async ({ sourceLatLng = `${sourceLat},${sourceLng}`; } + if (address) { + fullAddress = encodeURIComponent(address) + } + const lat = typeof latitude === 'string' ? parseFloat(latitude) : latitude; const lng = typeof longitude === 'string' ? parseFloat(longitude) : longitude; const latlng = `${lat},${lng}`; @@ -121,6 +128,7 @@ export const showLocation = async ({ sourceLat, sourceLng, sourceLatLng, + address : fullAddress, title, encodedTitle, prefixes, diff --git a/src/type.ts b/src/type.ts index 9a1eb65..805ef58 100644 --- a/src/type.ts +++ b/src/type.ts @@ -47,6 +47,9 @@ export type GetAppsResponse = { export interface ShowLocationProps { latitude: number | string; longitude: number | string; + /** optionally you can enter a full address that will be queried against the map app's API and return the initial results if not the actual matched result. */ + /** latitude and longitude will be ignored if the address field is set */ + address?: string | null; sourceLatitude?: number | null; sourceLongitude?: number | null; appleIgnoreLatLon?: boolean; diff --git a/src/utils.js b/src/utils.js deleted file mode 100644 index e5f6ffb..0000000 --- a/src/utils.js +++ /dev/null @@ -1,262 +0,0 @@ -/** - * React Native Map Link - */ - -import {Linking, ActionSheetIOS, Alert} from 'react-native'; - -import {appKeys, isIOS} from './constants'; - -/** - * Get available navigation apps. - */ -export const getAvailableApps = async (prefixes) => { - const availableApps = []; - const promises = []; - - for (const app in prefixes) { - if (prefixes.hasOwnProperty(app)) { - promises.push( - new Promise(async (resolve) => { - try { - const isInstalled = await isAppInstalled(app, prefixes); - - resolve({ - app, - isInstalled, - }); - } catch (error) { - resolve({ - app, - isInstalled: false, - }); - } - }), - ); - } - } - - const results = await Promise.all(promises); - results.forEach( - ({isInstalled, app}) => isInstalled && availableApps.push(app), - ); - - return availableApps; -}; - -/** - * Check if a given map app is installed. - * - * @param {string} app - * @param {object} prefixes - * @returns {Promise} - */ -export function isAppInstalled(app, prefixes) { - return new Promise((resolve) => { - if (!(app in prefixes)) { - return resolve(false); - } - - Linking.canOpenURL(prefixes[app]) - .then((result) => { - resolve(!!result); - }) - .catch(() => resolve(false)); - }); -} - -/** - * Check if a given app is supported by this library - * - * @param {string} app - * @returns {boolean} - */ -function isSupportedApp(app) { - return appKeys.includes(app); -} - -/** - * Get a list of not supported apps from a given array of apps - * - * @param {array} apps - * @returns {array} - */ -function getNotSupportedApps(apps) { - return apps.filter((app) => !isSupportedApp(app)); -} - -/** - * Throws an exception if some of the given apps is not supported - * - * @param {array} apps - */ -export function checkNotSupportedApps(apps) { - const notSupportedApps = getNotSupportedApps(apps); - if (notSupportedApps.length) { - throw new MapsException( - `appsWhiteList [${notSupportedApps}] are not supported apps, please provide some of the supported apps [${appKeys}]`, - ); - } -} - -/** - * Ask the user to choose one of the available map apps. - * @param {{ - * title: string | undefined | null - * message: string | undefined | null - * cancelText: string | undefined | null - * appsWhiteList: string[] | null - * prefixes: string[], - * appTitles: object | undefined | null - * }} options - * @returns {Promise} - */ -export function askAppChoice({ - dialogTitle, - dialogMessage, - cancelText, - appsWhiteList, - prefixes, - appTitles, -}) { - return new Promise(async (resolve) => { - let availableApps = await getAvailableApps(prefixes); - - if (appsWhiteList && appsWhiteList.length) { - availableApps = availableApps.filter((appName) => - appsWhiteList.includes(appName), - ); - } - - if (availableApps.length < 2) { - return resolve(availableApps[0] || null); - } - - if (isIOS) { - const options = availableApps.map((app) => appTitles[app]); - options.push(cancelText); - - ActionSheetIOS.showActionSheetWithOptions( - { - title: dialogTitle, - message: dialogMessage, - options: options, - cancelButtonIndex: options.length - 1, - }, - (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), - style: 'cancel', - }); - - return Alert.alert(dialogTitle, dialogMessage, options, { - cancelable: true, - onDismiss: () => resolve(null), - }); - }); -} - -/** - * Check if options are valid and well passed - * - * @param {{ - * latitude: number | string, - * longitude: number | string, - * address: string | undefined | null, - * sourceLatitude: number | undefined | null, - * sourceLongitude: number | undefined | null, - * googleForceLatLon: boolean | undefined | null, - * googlePlaceId: number | undefined | null, - * title: string | undefined | null, - * app: string | undefined | null - * dialogTitle: string | undefined | null - * dialogMessage: string | undefined | null - * cancelText: string | undefined | null - * naverCallerName: string | undefined - * }} options - * @param {object} prefixes - */ -export function checkOptions(options, prefixes) { - if (!options || typeof options !== 'object') { - throw new MapsException( - 'First parameter of `showLocation` should contain object with options.', - ); - } - if (!('latitude' in options) || !('longitude' in options)) { - throw new MapsException( - 'First parameter of `showLocation` should contain object with at least keys `latitude` and `longitude`.', - ); - } - if ( - 'address' in options && - options.address && - typeof options.address !== 'string' - ) { - throw new MapsException( - 'Option `address` should be of type `string`.', - ); - } - if ( - 'title' in options && - options.title && - typeof options.title !== 'string' - ) { - throw new MapsException('Option `title` should be of type `string`.'); - } - if ( - 'googleForceLatLon' in options && - options.googleForceLatLon && - typeof options.googleForceLatLon !== 'boolean' - ) { - throw new MapsException( - 'Option `googleForceLatLon` should be of type `boolean`.', - ); - } - if ( - 'googlePlaceId' in options && - options.googlePlaceId && - typeof options.googlePlaceId !== 'string' - ) { - throw new MapsException( - 'Option `googlePlaceId` should be of type `string`.', - ); - } - if ('app' in options && options.app && !(options.app in prefixes)) { - throw new MapsException( - 'Option `app` should be undefined, null, or one of the following: "' + - Object.keys(prefixes).join('", "') + - '".', - ); - } - if ('appsWhiteList' in options && options.appsWhiteList) { - checkNotSupportedApps(options.appsWhiteList); - } - if ( - 'appTitles' in options && - options.appTitles && - typeof options.appTitles !== 'object' - ) { - throw new MapsException('Option `appTitles` should be of type `object`.'); - } -} - -export class MapsException { - constructor(message) { - this.message = message; - this.name = 'MapsException'; - } -} diff --git a/src/utils.ts b/src/utils.ts index 1c9c068..76816f3 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -166,6 +166,7 @@ export const getDirectionsModeSygic = ( export const checkOptions = ({ latitude, longitude, + address, googleForceLatLon, googlePlaceId, title, @@ -176,6 +177,7 @@ export const checkOptions = ({ }: { latitude: number | string; longitude: number | string; + address?: string | null; googleForceLatLon?: boolean | null | undefined; googlePlaceId?: number | string | null | undefined; title?: string | null | undefined; @@ -189,6 +191,11 @@ export const checkOptions = ({ '`showLocation` should contain keys `latitude` and `longitude`.', ); } + 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`.'); } @@ -230,6 +237,7 @@ export const generateMapUrl = ({ sourceLat, sourceLng, sourceLatLng, + address, title, encodedTitle, prefixes, @@ -247,6 +255,7 @@ export const generateMapUrl = ({ sourceLat?: number; sourceLng?: number; sourceLatLng?: string; + address?: string | null; title?: string | null; encodedTitle?: string; prefixes: Record; @@ -258,14 +267,18 @@ export const generateMapUrl = ({ case 'apple-maps': const appleDirectionMode = getDirectionsModeAppleMaps(directionsMode); url = prefixes['apple-maps']; - if (useSourceDestiny || directionsMode) { - url = `${url}?daddr=${latlng}`; - url += sourceLatLng ? `&saddr=${sourceLatLng}` : ''; - } else if (!appleIgnoreLatLon) { - url = `${url}?ll=${latlng}`; + 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 || !appleIgnoreLatLon ? '&' : '?'; + useSourceDestiny || directionsMode || address || !appleIgnoreLatLon ? '&' : '?'; url += `q=${title ? encodedTitle : 'Location'}`; url += appleDirectionMode ? `&dirflg=${appleDirectionMode}` : ''; break; @@ -286,162 +299,257 @@ export const generateMapUrl = ({ url += googleDirectionMode ? `&travelmode=${googleDirectionMode}` : ''; } 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}`; + if (address) { + url = `https://www.google.com/maps/search/?q=${address}`; } else { - url += `&query=${latlng}`; - } + // 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}` : ''; + url += googlePlaceId ? `&query_place_id=${googlePlaceId}` : ''; + } } break; case 'citymapper': - url = `${prefixes.citymapper}directions?endcoord=${latlng}`; + if (address) { + url = `${prefixes.citymapper}directions?endname=${address}`; + } else { + url = `${prefixes.citymapper}directions?endcoord=${latlng}`; - if (title) { - url += `&endname=${encodedTitle}`; - } + if (title) { + url += `&endname=${encodedTitle}`; + } - if (useSourceDestiny) { - url += `&startcoord=${sourceLatLng}`; + if (useSourceDestiny) { + url += `&startcoord=${sourceLatLng}`; + } } break; case 'uber': - url = `${prefixes.uber}?action=setPickup&dropoff[latitude]=${lat}&dropoff[longitude]=${lng}`; + 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}`; - } + if (title) { + url += `&dropoff[nickname]=${encodedTitle}`; + } - url += useSourceDestiny - ? `&pickup[latitude]=${sourceLat}&pickup[longitude]=${sourceLng}` - : '&pickup=my_location'; + url += useSourceDestiny + ? `&pickup[latitude]=${sourceLat}&pickup[longitude]=${sourceLng}` + : '&pickup=my_location'; + } break; case 'lyft': - url = `${prefixes.lyft}ridetype?id=lyft&destination[latitude]=${lat}&destination[longitude]=${lng}`; + 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}`; - } + if (useSourceDestiny) { + url += `&pickup[latitude]=${sourceLat}&pickup[longitude]=${sourceLng}`; + } + } break; case 'transit': - url = `${prefixes.transit}directions?to=${latlng}`; + if (address) { + url = `${prefixes.transit}directions?destination=${address}`; + } else { + url = `${prefixes.transit}directions?to=${latlng}`; + } if (useSourceDestiny) { url += `&from=${sourceLatLng}`; } break; case 'truckmap': - url = `https://truckmap.com/place/${lat},${lng}`; + 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}`; + if (useSourceDestiny) { + url = `https://truckmap.com/route/${sourceLat},${sourceLng}/${lat},${lng}`; + } } break; case 'waze': - url = `${prefixes.waze}?ll=${latlng}&navigate=yes`; - if (title) { - url += `&q=${encodedTitle}`; + if (address) { + url = `${prefixes.waze}?q=${address}` + } else { + url = `${prefixes.waze}?ll=${latlng}&navigate=yes`; + if (title) { + url += `&q=${encodedTitle}`; + } } break; case 'yandex': - url = `${prefixes.yandex}build_route_on_map?lat_to=${lat}&lon_to=${lng}`; + 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}`; + if (useSourceDestiny) { + url += `&lat_from=${sourceLat}&lon_from=${sourceLng}`; + } } break; case 'moovit': - url = `${prefixes.moovit}?dest_lat=${lat}&dest_lon=${lng}`; + 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 (title) { + url += `&dest_name=${encodedTitle}`; + } - if (useSourceDestiny) { - url += `&orig_lat=${sourceLat}&orig_lon=${sourceLng}`; + if (useSourceDestiny) { + url += `&orig_lat=${sourceLat}&orig_lon=${sourceLng}`; + } } break; case 'yandex-taxi': - url = `${prefixes['yandex-taxi']}route?end-lat=${lat}&end-lon=${lng}&appmetrica_tracking_id=1178268795219780156`; + 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': - url = `${prefixes['yandex-maps']}?pt=${lng},${lat}`; + if (address) { + url = `${prefixes['yandex-maps']}?text=${address}`; + } else { + url = `${prefixes['yandex-maps']}?pt=${lng},${lat}`; + } break; case 'kakaomap': - url = `${prefixes.kakaomap}look?p=${latlng}`; + 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`; + if (useSourceDestiny) { + url = `${prefixes.kakaomap}route?sp=${sourceLat},${sourceLng}&ep=${latlng}&by=CAR`; + } } + break; case 'tmap': - url = `${prefixes.tmap}viewmap?x=${lng}&y=${lat}`; + 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}`; + 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': - url = `${prefixes['maps-me']}route?sll=${sourceLat},${sourceLng}&saddr= &dll=${lat},${lng}&daddr=${title}&type=vehicle`; + 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': - url = isIOS - ? `${prefixes.osmand}?lat=${lat}&lon=${lng}` - : `${prefixes.osmand}?q=${lat},${lng}`; + if (address) { + url = `${prefixes.osmand}show_map?addr=${address}` + } else { + url = isIOS + ? `${prefixes.osmand}?lat=${lat}&lon=${lng}` + : `${prefixes.osmand}?q=${lat},${lng}`; + } break; case 'gett': - url = `${prefixes.gett}order?pickup=my_location&dropoff_latitude=${lat}&dropoff_longitude=${lng}`; + 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': - url = `${prefixes.navermap}map?lat=${lat}&lng=${lng}&appname=${naverCallerName}`; + 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}`; + if (useSourceDestiny) { + url = `${prefixes.navermap}route?slat=${sourceLat}&slng=${sourceLng}&dlat=${lat}&dlng=${lng}&appname=${naverCallerName}`; + } } + break; case 'dgis': - url = `${prefixes.dgis}routeSearch/to/${lng},${lat}/go`; + 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`; + if (useSourceDestiny) { + url = `${prefixes.dgis}routeSearch/to/${lng},${lat}/from/${sourceLng},${sourceLat}/go`; + } } + break; case 'liftago': - url = `${prefixes.liftago}order?destinationLat=${lat}&destinationLon=${lng}`; + 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 (title) { + url += `&destinationName=${encodedTitle}`; + } - if (useSourceDestiny) { - url += `&pickupLat=${sourceLat}&pickupLon=${sourceLng}`; + if (useSourceDestiny) { + url += `&pickupLat=${sourceLat}&pickupLon=${sourceLng}`; + } } + break; case 'petalmaps': - url = `${prefixes.petalmaps}navigation?daddr=${lat},${lng}`; + 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}`; + if (useSourceDestiny) { + url += `&saddr=${sourceLat},${sourceLng}`; + } } break; case 'sygic': const sygicDirectionsMode = getDirectionsModeSygic(directionsMode); - url = `${prefixes.sygic}coordinate|${lng}|${lat}|`; + 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; } From 9ceb08cc72366cdfb8dffdd83b80c0affdfbd920 Mon Sep 17 00:00:00 2001 From: Trevor D Date: Fri, 19 Apr 2024 17:08:51 -0600 Subject: [PATCH 05/16] v3.1.0 - Made one more adjustment to the utils file. Is ready for a PR. --- src/utils.ts | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/src/utils.ts b/src/utils.ts index 76816f3..9acd672 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -192,9 +192,7 @@ export const checkOptions = ({ ); } if (address && typeof address !== 'string') { - throw new MapsException( - 'Option `address` should be of type `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`.'); @@ -423,7 +421,6 @@ export const generateMapUrl = ({ } else { url = `${prefixes['yandex-taxi']}route?end-lat=${lat}&end-lon=${lng}&appmetrica_tracking_id=1178268795219780156`; } - break; case 'yandex-maps': if (address) { @@ -431,7 +428,6 @@ export const generateMapUrl = ({ } else { url = `${prefixes['yandex-maps']}?pt=${lng},${lat}`; } - break; case 'kakaomap': if (address) { @@ -443,7 +439,6 @@ export const generateMapUrl = ({ url = `${prefixes.kakaomap}route?sp=${sourceLat},${sourceLng}&ep=${latlng}&by=CAR`; } } - break; case 'tmap': if (address) { @@ -455,7 +450,6 @@ export const generateMapUrl = ({ url = `${prefixes.tmap}route?startx=${sourceLng}&starty=${sourceLat}&goalx=${lng}&goaly=${lat}`; } } - break; case 'mapycz': if (address) { @@ -463,7 +457,6 @@ export const generateMapUrl = ({ } else { url = `${prefixes.mapycz}www.mapy.cz/zakladni?x=${lng}&y=${lat}&source=coor&id=${lng},${lat}`; } - break; case 'maps-me': if (address) { @@ -471,7 +464,6 @@ export const generateMapUrl = ({ } else { url = `${prefixes['maps-me']}route?sll=${sourceLat},${sourceLng}&saddr= &dll=${lat},${lng}&daddr=${title}&type=vehicle`; } - break; case 'osmand': if (address) { @@ -481,7 +473,6 @@ export const generateMapUrl = ({ ? `${prefixes.osmand}?lat=${lat}&lon=${lng}` : `${prefixes.osmand}?q=${lat},${lng}`; } - break; case 'gett': if (address) { @@ -489,7 +480,6 @@ export const generateMapUrl = ({ } else { url = `${prefixes.gett}order?pickup=my_location&dropoff_latitude=${lat}&dropoff_longitude=${lng}`; } - break; case 'navermap': if (address) { @@ -501,7 +491,6 @@ export const generateMapUrl = ({ url = `${prefixes.navermap}route?slat=${sourceLat}&slng=${sourceLng}&dlat=${lat}&dlng=${lng}&appname=${naverCallerName}`; } } - break; case 'dgis': if (address) { @@ -513,7 +502,6 @@ export const generateMapUrl = ({ url = `${prefixes.dgis}routeSearch/to/${lng},${lat}/from/${sourceLng},${sourceLat}/go`; } } - break; case 'liftago': if (address) { @@ -529,7 +517,6 @@ export const generateMapUrl = ({ url += `&pickupLat=${sourceLat}&pickupLon=${sourceLng}`; } } - break; case 'petalmaps': if (address) { From 07dbbed71525b7fb52ba3231d9c793cef8ae0169 Mon Sep 17 00:00:00 2001 From: Trevor D Date: Fri, 19 Apr 2024 17:14:42 -0600 Subject: [PATCH 06/16] v3.1.0 - Missing extra semicolon. --- src/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/index.ts b/src/index.ts index ac14f69..75b2ba7 100644 --- a/src/index.ts +++ b/src/index.ts @@ -80,7 +80,7 @@ export const showLocation = async ({ } if (address) { - fullAddress = encodeURIComponent(address) + fullAddress = encodeURIComponent(address); } const lat = typeof latitude === 'string' ? parseFloat(latitude) : latitude; From 83d9eadc42151cd087549d5e96d911be46f26e90 Mon Sep 17 00:00:00 2001 From: Trevor D Date: Mon, 22 Apr 2024 09:34:55 -0600 Subject: [PATCH 07/16] v3.1.0 - Changed latitude and longitude to optional variables if using an address --- src/type.ts | 4 ++-- src/utils.ts | 11 +++++++---- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/type.ts b/src/type.ts index 805ef58..370f44b 100644 --- a/src/type.ts +++ b/src/type.ts @@ -45,8 +45,8 @@ export type GetAppsResponse = { }; export interface ShowLocationProps { - latitude: number | string; - longitude: number | string; + latitude?: number | string; + longitude?: number | string; /** optionally you can enter a full address that will be queried against the map app's API and return the initial results if not the actual matched result. */ /** latitude and longitude will be ignored if the address field is set */ address?: string | null; diff --git a/src/utils.ts b/src/utils.ts index 9acd672..29fb8aa 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -175,8 +175,8 @@ export const checkOptions = ({ appTitles, appsWhiteList, }: { - latitude: number | string; - longitude: number | string; + latitude?: number | string; + longitude?: number | string; address?: string | null; googleForceLatLon?: boolean | null | undefined; googlePlaceId?: number | string | null | undefined; @@ -194,6 +194,9 @@ export const checkOptions = ({ if (address && typeof address !== 'string') { throw new MapsException('Option `address` should be of type `string`.'); } + if(!latitude && !longitude && !address) { + throw new MapsException('Latitude & Longitude or Address is required. Both cannot be undefined.') + } if (title && typeof title !== 'string') { throw new MapsException('`title` should be of type `string`.'); } @@ -247,8 +250,8 @@ export const generateMapUrl = ({ googleForceLatLon?: boolean; googlePlaceId: string | number | null | undefined; naverCallerName: string | null | undefined; - lat: number; - lng: number; + lat?: number; + lng?: number; latlng: string; sourceLat?: number; sourceLng?: number; From f7a3ff4ce34371bbfde823bde3bffbbf37f2d271 Mon Sep 17 00:00:00 2001 From: Trevor D Date: Mon, 22 Apr 2024 09:55:22 -0600 Subject: [PATCH 08/16] v3.1.0 - Updated README to not include empty string latitude and longitude parameters --- README.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/README.md b/README.md index 14dbabb..4ca4c53 100644 --- a/README.md +++ b/README.md @@ -250,7 +250,6 @@ showLocation({ longitude: -77.0387185, sourceLatitude: -8.0870631, // optionally specify starting location for directions sourceLongitude: -34.8941619, // required if sourceLatitude is specified - address: '', // optional but can replace latitude and longitude if needed title: 'The White House', // optional googleForceLatLon: false, // optionally force GoogleMaps to use the latlon for the query instead of the title googlePlaceId: 'ChIJGVtI4by3t4kRr51d_Qm_x58', // optionally specify the google-place-id @@ -272,8 +271,6 @@ Alternatively you can specify the `address` field and leave the latitude and lon import {showLocation} from 'react-native-map-link'; showLocation({ - latitude: '', - longitude: '', address: '1600 Pennsylvania Avenue NW, Washington, DC 20500', // Required if replacing latitude and longitude title: 'The White House', // optional googleForceLatLon: false, // optionally force GoogleMaps to use the latlon for the query instead of the title From ecadbd65fa7fce499c47aa13e1770b31ddfcca37 Mon Sep 17 00:00:00 2001 From: Trevor D Date: Mon, 22 Apr 2024 09:59:07 -0600 Subject: [PATCH 09/16] v3.1.0 - Fixing some linting issues. --- README.md | 1 - src/utils.ts | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index 4ca4c53..6e181b0 100644 --- a/README.md +++ b/README.md @@ -273,7 +273,6 @@ import {showLocation} from 'react-native-map-link'; showLocation({ address: '1600 Pennsylvania Avenue NW, Washington, DC 20500', // Required if replacing latitude and longitude title: 'The White House', // optional - googleForceLatLon: false, // optionally force GoogleMaps to use the latlon for the query instead of the title alwaysIncludeGoogle: true, // optional, true will always add Google Maps to iOS and open in Safari, even if app is not installed (default: false) dialogTitle: 'This is the dialog Title', // optional (default: 'Open in Maps') dialogMessage: 'This is the amazing dialog Message', // optional (default: 'What app would you like to use?') diff --git a/src/utils.ts b/src/utils.ts index 29fb8aa..e97ac1f 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -195,7 +195,7 @@ export const checkOptions = ({ throw new MapsException('Option `address` should be of type `string`.'); } if(!latitude && !longitude && !address) { - throw new MapsException('Latitude & Longitude or Address is required. Both cannot be undefined.') + throw new MapsException('`latitude` & `longitude` or `address` is required. Both cannot be undefined.'); } if (title && typeof title !== 'string') { throw new MapsException('`title` should be of type `string`.'); From 859c0741cedad8a1af8f328b798d2cf022bd22eb Mon Sep 17 00:00:00 2001 From: Trevor D Date: Mon, 22 Apr 2024 10:01:12 -0600 Subject: [PATCH 10/16] v3.1.0 - Fixing some linting issues. --- src/index.ts | 2 +- src/utils.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/index.ts b/src/index.ts index 75b2ba7..d4d30cb 100644 --- a/src/index.ts +++ b/src/index.ts @@ -128,7 +128,7 @@ export const showLocation = async ({ sourceLat, sourceLng, sourceLatLng, - address : fullAddress, + address:fullAddress, title, encodedTitle, prefixes, diff --git a/src/utils.ts b/src/utils.ts index e97ac1f..9c0ac8e 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -194,7 +194,7 @@ export const checkOptions = ({ if (address && typeof address !== 'string') { throw new MapsException('Option `address` should be of type `string`.'); } - if(!latitude && !longitude && !address) { + if (!latitude && !longitude && !address) { throw new MapsException('`latitude` & `longitude` or `address` is required. Both cannot be undefined.'); } if (title && typeof title !== 'string') { From 34aa1f30e1b6273410d555ccc1dacd6551d10ea5 Mon Sep 17 00:00:00 2001 From: Trevor D Date: Mon, 22 Apr 2024 10:05:42 -0600 Subject: [PATCH 11/16] v3.1.0 - More lint fixes. --- src/utils.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/utils.ts b/src/utils.ts index 9c0ac8e..1925035 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -195,7 +195,9 @@ export const checkOptions = ({ throw new MapsException('Option `address` should be of type `string`.'); } if (!latitude && !longitude && !address) { - throw new MapsException('`latitude` & `longitude` or `address` is required. Both cannot be undefined.'); + throw new MapsException( + '`latitude` & `longitude` or `address` is required. Both cannot be undefined.', + ); } if (title && typeof title !== 'string') { throw new MapsException('`title` should be of type `string`.'); From d7acc5ede1898ed16b5001f1dc6169339968ffdd Mon Sep 17 00:00:00 2001 From: Trevor D Date: Mon, 22 Apr 2024 10:14:26 -0600 Subject: [PATCH 12/16] v3.1.0 - Fixing warnings and other issues with lint. Trying again. --- src/index.ts | 2 +- src/utils.ts | 26 +++++++++++++++----------- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/src/index.ts b/src/index.ts index d4d30cb..f08bb1e 100644 --- a/src/index.ts +++ b/src/index.ts @@ -128,7 +128,7 @@ export const showLocation = async ({ sourceLat, sourceLng, sourceLatLng, - address:fullAddress, + address: fullAddress, title, encodedTitle, prefixes, diff --git a/src/utils.ts b/src/utils.ts index 1925035..605dda7 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -281,7 +281,9 @@ export const generateMapUrl = ({ } } url += - useSourceDestiny || directionsMode || address || !appleIgnoreLatLon ? '&' : '?'; + useSourceDestiny || directionsMode || address || !appleIgnoreLatLon + ? '&' + : '?'; url += `q=${title ? encodedTitle : 'Location'}`; url += appleDirectionMode ? `&dirflg=${appleDirectionMode}` : ''; break; @@ -335,7 +337,7 @@ export const generateMapUrl = ({ break; case 'uber': if (address) { - url = `${prefixes.uber}?action=setPickup&pickup=my_location&dropoff=${address}` + url = `${prefixes.uber}?action=setPickup&pickup=my_location&dropoff=${address}`; } else { url = `${prefixes.uber}?action=setPickup&dropoff[latitude]=${lat}&dropoff[longitude]=${lng}`; @@ -346,7 +348,6 @@ export const generateMapUrl = ({ url += useSourceDestiny ? `&pickup[latitude]=${sourceLat}&pickup[longitude]=${sourceLng}` : '&pickup=my_location'; - } break; case 'lyft': @@ -358,7 +359,6 @@ export const generateMapUrl = ({ if (useSourceDestiny) { url += `&pickup[latitude]=${sourceLat}&pickup[longitude]=${sourceLng}`; } - } break; case 'transit': @@ -386,7 +386,7 @@ export const generateMapUrl = ({ break; case 'waze': if (address) { - url = `${prefixes.waze}?q=${address}` + url = `${prefixes.waze}?q=${address}`; } else { url = `${prefixes.waze}?ll=${latlng}&navigate=yes`; if (title) { @@ -422,7 +422,9 @@ export const generateMapUrl = ({ break; case 'yandex-taxi': if (address) { - throw new MapsException(`yandex-taxi does not support passing the address or has not been implemented yet.`); + 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`; } @@ -460,7 +462,7 @@ export const generateMapUrl = ({ 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}`; + url = `${prefixes.mapycz}www.mapy.cz/zakladni?x=${lng}&y=${lat}&source=coor&id=${lng},${lat}`; } break; case 'maps-me': @@ -472,7 +474,7 @@ export const generateMapUrl = ({ break; case 'osmand': if (address) { - url = `${prefixes.osmand}show_map?addr=${address}` + url = `${prefixes.osmand}show_map?addr=${address}`; } else { url = isIOS ? `${prefixes.osmand}?lat=${lat}&lon=${lng}` @@ -481,7 +483,9 @@ export const generateMapUrl = ({ break; case 'gett': if (address) { - throw new MapsException(`gett does not support passing the address or has not been implemented yet.`); + 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}`; } @@ -510,7 +514,7 @@ export const generateMapUrl = ({ break; case 'liftago': if (address) { - throw new MapsException(`liftago does not support passing the address or has not been implemented yet.`); + 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}`; @@ -538,7 +542,7 @@ export const generateMapUrl = ({ case 'sygic': const sygicDirectionsMode = getDirectionsModeSygic(directionsMode); if (address) { - throw new MapsException(`sygic does not support passing the address or has not been implemented yet.`); + throw new MapsException('sygic does not support passing the address or has not been implemented yet.'); } else { url = `${prefixes.sygic}coordinate|${lng}|${lat}|`; } From cb7a8390f6b70bf2edf99367b32ccb1524197319 Mon Sep 17 00:00:00 2001 From: Trevor D Date: Mon, 22 Apr 2024 10:16:01 -0600 Subject: [PATCH 13/16] v3.1.0 - Spaces and tabs. --- src/utils.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/utils.ts b/src/utils.ts index 605dda7..8ee687d 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -281,9 +281,9 @@ export const generateMapUrl = ({ } } url += - useSourceDestiny || directionsMode || address || !appleIgnoreLatLon - ? '&' - : '?'; + useSourceDestiny || directionsMode || address || !appleIgnoreLatLon + ? '&' + : '?'; url += `q=${title ? encodedTitle : 'Location'}`; url += appleDirectionMode ? `&dirflg=${appleDirectionMode}` : ''; break; From 2bbe583c3747e7fa453db0161ba17b7ad8de7ef1 Mon Sep 17 00:00:00 2001 From: Trevor D Date: Mon, 22 Apr 2024 10:18:19 -0600 Subject: [PATCH 14/16] v3.1.0 - More linting consistencies. --- src/utils.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/utils.ts b/src/utils.ts index 8ee687d..e5a2387 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -514,7 +514,9 @@ export const generateMapUrl = ({ break; case 'liftago': if (address) { - throw new MapsException('liftago does not support passing the address or has not been implemented yet.'); + 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}`; @@ -542,7 +544,9 @@ export const generateMapUrl = ({ case 'sygic': const sygicDirectionsMode = getDirectionsModeSygic(directionsMode); if (address) { - throw new MapsException('sygic does not support passing the address or has not been implemented yet.'); + throw new MapsException( + 'sygic does not support passing the address or has not been implemented yet.', + ); } else { url = `${prefixes.sygic}coordinate|${lng}|${lat}|`; } From 32fc068326da4e3d9c21939485886d498337f83f Mon Sep 17 00:00:00 2001 From: Trevor D Date: Mon, 13 May 2024 08:40:46 -0600 Subject: [PATCH 15/16] v3.1.0 - Updated README and simplified it for easier example. --- README.md | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/README.md b/README.md index 6e181b0..cdea8d5 100644 --- a/README.md +++ b/README.md @@ -272,15 +272,7 @@ import {showLocation} from 'react-native-map-link'; showLocation({ address: '1600 Pennsylvania Avenue NW, Washington, DC 20500', // Required if replacing latitude and longitude - title: 'The White House', // optional - alwaysIncludeGoogle: true, // optional, true will always add Google Maps to iOS and open in Safari, even if app is not installed (default: false) - dialogTitle: 'This is the dialog Title', // optional (default: 'Open in Maps') - dialogMessage: 'This is the amazing dialog Message', // optional (default: 'What app would you like to use?') - cancelText: 'This is the cancel button text', // optional (default: 'Cancel') - appsWhiteList: ['google-maps'], // optionally you can set which apps to show (default: will show all supported apps installed on device) - naverCallerName: 'com.example.myapp', // to link into Naver Map You should provide your appname which is the bundle ID in iOS and applicationId in android. - appTitles: { 'google-maps': 'My custom Google Maps title' }, // optionally you can override default app titles - app: 'uber', // optionally specify specific app to use + app: 'comgooglemaps', // optionally specify specific app to use }); ``` From c2c8378ff3138291aa092eee9174f254f4c11644 Mon Sep 17 00:00:00 2001 From: Trevor D Date: Mon, 13 May 2024 08:42:29 -0600 Subject: [PATCH 16/16] v3.1.0 - Fixed other verbiage within the README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index cdea8d5..906da97 100644 --- a/README.md +++ b/README.md @@ -281,7 +281,7 @@ Notes: - The `sourceLatitude` / `sourceLongitude` options only work if you specify both. Currently supports all apps except Waze. - `directionsMode` works on google-maps, apple-maps and sygic (on apple-maps, `bike` mode will not work, while on sygic, only `walk` and `car` will work). Without setting it, the app will decide based on its own settings. - If you set `directionsMode` but do not set `sourceLatitude` and `sourceLongitude`, google-maps and apple-maps will still enter directions mode, and use the current location as starting point. -- If you want to query an address instead of passing the `latitude` and `longitude` fields, you can do this by leaving both of the required fields as empty strings and provide a full address to be queried. Just be aware that not all applications support this. +- If you want to query an address instead of passing the `latitude` and `longitude` fields, you can do this by leaving those fields off and provide a full address to be queried with the `address` field. Just be aware that not all applications support this. ### Or