-
Notifications
You must be signed in to change notification settings - Fork 2.9k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #26546 from huzaifa-99/25855-get-user-location
Added option to get user's current location
- Loading branch information
Showing
25 changed files
with
9,789 additions
and
514 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
77 changes: 77 additions & 0 deletions
77
src/components/LocationErrorMessage/BaseLocationErrorMessage.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
import PropTypes from 'prop-types'; | ||
import React from 'react'; | ||
import {View} from 'react-native'; | ||
import CONST from '../../CONST'; | ||
import colors from '../../styles/colors'; | ||
import styles from '../../styles/styles'; | ||
import Icon from '../Icon'; | ||
import * as Expensicons from '../Icon/Expensicons'; | ||
import PressableWithoutFeedback from '../Pressable/PressableWithoutFeedback'; | ||
import Text from '../Text'; | ||
import TextLink from '../TextLink'; | ||
import Tooltip from '../Tooltip'; | ||
import withLocalize, {withLocalizePropTypes} from '../withLocalize'; | ||
import * as locationErrorMessagePropTypes from './locationErrorMessagePropTypes'; | ||
|
||
const propTypes = { | ||
/** A callback that runs when 'allow location permission' link is pressed */ | ||
onAllowLocationLinkPress: PropTypes.func.isRequired, | ||
|
||
// eslint-disable-next-line react/forbid-foreign-prop-types | ||
...locationErrorMessagePropTypes.propTypes, | ||
|
||
/* Onyx Props */ | ||
...withLocalizePropTypes, | ||
}; | ||
|
||
function BaseLocationErrorMessage({onClose, onAllowLocationLinkPress, locationErrorCode, translate}) { | ||
if (!locationErrorCode) { | ||
return null; | ||
} | ||
|
||
const isPermissionDenied = locationErrorCode === 1; | ||
|
||
return ( | ||
<View style={[styles.dotIndicatorMessage, styles.mt4]}> | ||
<View style={styles.offlineFeedback.errorDot}> | ||
<Icon | ||
src={Expensicons.DotIndicator} | ||
fill={colors.red} | ||
/> | ||
</View> | ||
<View style={styles.offlineFeedback.textContainer}> | ||
{isPermissionDenied ? ( | ||
<Text> | ||
<Text style={[styles.offlineFeedback.text]}>{`${translate('location.permissionDenied')} ${translate('location.please')}`}</Text> | ||
<TextLink | ||
onPress={onAllowLocationLinkPress} | ||
style={styles.locationErrorLinkText} | ||
> | ||
{` ${translate('location.allowPermission')} `} | ||
</TextLink> | ||
<Text style={[styles.offlineFeedback.text]}>{translate('location.tryAgain')}</Text> | ||
</Text> | ||
) : ( | ||
<Text style={styles.offlineFeedback.text}>{translate('location.notFound')}</Text> | ||
)} | ||
</View> | ||
<View> | ||
<Tooltip text={translate('common.close')}> | ||
<PressableWithoutFeedback | ||
onPress={onClose} | ||
style={[styles.touchableButtonImage]} | ||
accessibilityRole={CONST.ACCESSIBILITY_ROLE.BUTTON} | ||
accessibilityLabel={translate('common.close')} | ||
> | ||
<Icon src={Expensicons.Close} /> | ||
</PressableWithoutFeedback> | ||
</Tooltip> | ||
</View> | ||
</View> | ||
); | ||
} | ||
|
||
BaseLocationErrorMessage.displayName = 'BaseLocationErrorMessage'; | ||
BaseLocationErrorMessage.propTypes = propTypes; | ||
BaseLocationErrorMessage.defaultProps = locationErrorMessagePropTypes.defaultProps; | ||
export default withLocalize(BaseLocationErrorMessage); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
import React from 'react'; | ||
import {Linking} from 'react-native'; | ||
import CONST from '../../CONST'; | ||
import BaseLocationErrorMessage from './BaseLocationErrorMessage'; | ||
import * as locationErrorMessagePropTypes from './locationErrorMessagePropTypes'; | ||
|
||
/** Opens expensify help site in a new browser tab */ | ||
const navigateToExpensifyHelpSite = () => { | ||
Linking.openURL(CONST.NEWHELP_URL); | ||
}; | ||
|
||
function LocationErrorMessage(props) { | ||
return ( | ||
<BaseLocationErrorMessage | ||
// eslint-disable-next-line react/jsx-props-no-spreading | ||
{...props} | ||
onAllowLocationLinkPress={navigateToExpensifyHelpSite} | ||
/> | ||
); | ||
} | ||
|
||
LocationErrorMessage.displayName = 'LocationErrorMessage'; | ||
LocationErrorMessage.propTypes = locationErrorMessagePropTypes.propTypes; | ||
LocationErrorMessage.defaultProps = locationErrorMessagePropTypes.defaultProps; | ||
export default LocationErrorMessage; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
import React from 'react'; | ||
import {Linking} from 'react-native'; | ||
import BaseLocationErrorMessage from './BaseLocationErrorMessage'; | ||
import * as locationErrorMessagePropTypes from './locationErrorMessagePropTypes'; | ||
|
||
/** Opens app level settings from the native system settings */ | ||
const openAppSettings = () => { | ||
Linking.openSettings(); | ||
}; | ||
|
||
function LocationErrorMessage(props) { | ||
return ( | ||
<BaseLocationErrorMessage | ||
// eslint-disable-next-line react/jsx-props-no-spreading | ||
{...props} | ||
onAllowLocationLinkPress={openAppSettings} | ||
/> | ||
); | ||
} | ||
|
||
LocationErrorMessage.displayName = 'LocationErrorMessage'; | ||
LocationErrorMessage.propTypes = locationErrorMessagePropTypes.propTypes; | ||
LocationErrorMessage.defaultProps = locationErrorMessagePropTypes.defaultProps; | ||
export default LocationErrorMessage; |
21 changes: 21 additions & 0 deletions
21
src/components/LocationErrorMessage/locationErrorMessagePropTypes.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
import PropTypes from 'prop-types'; | ||
|
||
const propTypes = { | ||
/** A callback that runs when close icon is pressed */ | ||
onClose: PropTypes.func.isRequired, | ||
|
||
/** | ||
* The location error code from onyx | ||
* - code -1 = location not supported (web only) | ||
* - code 1 = location permission is not enabled | ||
* - code 2 = location is unavailable or there is some connection issue | ||
* - code 3 = location fetch timeout | ||
*/ | ||
locationErrorCode: PropTypes.oneOf([-1, 1, 2, 3]), | ||
}; | ||
|
||
const defaultProps = { | ||
locationErrorCode: null, | ||
}; | ||
|
||
export {propTypes, defaultProps}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,112 @@ | ||
import PropTypes from 'prop-types'; | ||
import React, {useEffect, useRef, useState} from 'react'; | ||
import {Text} from 'react-native'; | ||
import getCurrentPosition from '../libs/getCurrentPosition'; | ||
import styles from '../styles/styles'; | ||
import Icon from './Icon'; | ||
import * as Expensicons from './Icon/Expensicons'; | ||
import LocationErrorMessage from './LocationErrorMessage'; | ||
import withLocalize, {withLocalizePropTypes} from './withLocalize'; | ||
import colors from '../styles/colors'; | ||
import PressableWithFeedback from './Pressable/PressableWithFeedback'; | ||
|
||
const propTypes = { | ||
/** Callback that runs when location data is fetched */ | ||
onLocationFetched: PropTypes.func.isRequired, | ||
|
||
/** Callback that runs when fetching location has errors */ | ||
onLocationError: PropTypes.func, | ||
|
||
/** Callback that runs when location button is clicked */ | ||
onClick: PropTypes.func, | ||
|
||
/** Boolean to indicate if the button is clickable */ | ||
isDisabled: PropTypes.bool, | ||
|
||
...withLocalizePropTypes, | ||
}; | ||
|
||
const defaultProps = { | ||
isDisabled: false, | ||
onLocationError: () => {}, | ||
onClick: () => {}, | ||
}; | ||
|
||
function UserCurrentLocationButton({onLocationFetched, onLocationError, onClick, isDisabled, translate}) { | ||
const isFetchingLocation = useRef(false); | ||
const shouldTriggerCallbacks = useRef(true); | ||
const [locationErrorCode, setLocationErrorCode] = useState(null); | ||
|
||
/** Gets the user's current location and registers success/error callbacks */ | ||
const getUserLocation = () => { | ||
if (isFetchingLocation.current) { | ||
return; | ||
} | ||
|
||
isFetchingLocation.current = true; | ||
|
||
onClick(); | ||
|
||
getCurrentPosition( | ||
(successData) => { | ||
isFetchingLocation.current = false; | ||
if (!shouldTriggerCallbacks.current) { | ||
return; | ||
} | ||
|
||
setLocationErrorCode(null); | ||
onLocationFetched(successData); | ||
}, | ||
(errorData) => { | ||
isFetchingLocation.current = false; | ||
if (!shouldTriggerCallbacks.current) { | ||
return; | ||
} | ||
|
||
setLocationErrorCode(errorData.code); | ||
onLocationError(errorData); | ||
}, | ||
{ | ||
maximumAge: 0, // No cache, always get fresh location info | ||
timeout: 5000, | ||
}, | ||
); | ||
}; | ||
|
||
// eslint-disable-next-line arrow-body-style | ||
useEffect(() => { | ||
return () => { | ||
// If the component unmounts we don't want any of the callback for geolocation to run. | ||
shouldTriggerCallbacks.current = false; | ||
}; | ||
}, []); | ||
|
||
return ( | ||
<> | ||
<PressableWithFeedback | ||
style={[styles.flexRow, styles.mt4, styles.alignSelfStart, isDisabled && styles.buttonOpacityDisabled]} | ||
onPress={getUserLocation} | ||
accessibilityLabel={translate('location.useCurrent')} | ||
disabled={isDisabled} | ||
> | ||
<Icon | ||
src={Expensicons.Location} | ||
fill={colors.green} | ||
/> | ||
<Text style={[styles.textLabel, styles.mh2, isDisabled && styles.userSelectNone]}>{translate('location.useCurrent')}</Text> | ||
</PressableWithFeedback> | ||
<LocationErrorMessage | ||
onClose={() => setLocationErrorCode(null)} | ||
locationErrorCode={locationErrorCode} | ||
/> | ||
</> | ||
); | ||
} | ||
|
||
UserCurrentLocationButton.displayName = 'UserCurrentLocationButton'; | ||
UserCurrentLocationButton.propTypes = propTypes; | ||
UserCurrentLocationButton.defaultProps = defaultProps; | ||
|
||
// This components gets used inside <Form/>, we are using an HOC (withLocalize) as function components with | ||
// hooks give hook errors when nested inside <Form/>. | ||
export default withLocalize(UserCurrentLocationButton); |
Oops, something went wrong.