-
Notifications
You must be signed in to change notification settings - Fork 3.9k
Allow user to get current location #25990
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
fc04db7
5fec130
7465ef1
5f11209
8ab688f
ffb3138
31658da
d73335a
312cff2
d7bd1ea
a12968e
9bf0236
f371f95
14494b9
6403a08
348fc5c
0fd62fc
c582e75
24dfaf7
90e5866
2633ae3
3b9fe10
ca04daf
3bd4faf
ee60ec3
72fc797
86b61b7
3856f7f
4dea2d6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,99 @@ | ||
| import PropTypes from 'prop-types'; | ||
| import React from 'react'; | ||
| import {View} from 'react-native'; | ||
| import {withOnyx} from 'react-native-onyx'; | ||
| import ONYXKEYS from '../../ONYXKEYS'; | ||
| import compose from '../../libs/compose'; | ||
| import colors from '../../styles/colors'; | ||
| import styles from '../../styles/styles'; | ||
| import Icon from '../Icon'; | ||
| import * as Expensicons from '../Icon/Expensicons'; | ||
| import Text from '../Text'; | ||
| import TextLink from '../TextLink'; | ||
| import withLocalize, {withLocalizePropTypes} from '../withLocalize'; | ||
| import Tooltip from '../Tooltip'; | ||
| import PressableWithoutFeedback from '../Pressable/PressableWithoutFeedback'; | ||
| import * as User from '../../libs/actions/User'; | ||
| import CONST from '../../CONST'; | ||
|
|
||
| const propTypes = { | ||
| /** The location error code from onyx */ | ||
| locationErrorCode: PropTypes.oneOfType([PropTypes.number, PropTypes.oneOf([null])]), | ||
|
|
||
| /** A callback that runs when 'allow location permission' link is pressed */ | ||
| onAllowLocationLinkPress: PropTypes.func.isRequired, | ||
|
|
||
| ...withLocalizePropTypes, | ||
| }; | ||
|
|
||
| const defaultProps = { | ||
| locationErrorCode: undefined, | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. not sure that you need this - it will be undefined by default itself
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. hm, I think it's not needed. Removed here
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So I added it back, the lint was failing (we need to add to satisfy the linter) |
||
| }; | ||
|
|
||
| function BaseLocationErrorMessage({locationErrorCode, onAllowLocationLinkPress, translate}) { | ||
| if (!locationErrorCode) { | ||
| return null; | ||
| } | ||
|
|
||
| const isPermissionDenied = locationErrorCode === 1; | ||
|
|
||
| /** | ||
| * Clears the location error on press of close icon | ||
| */ | ||
| const dismissError = () => { | ||
| User.clearLocationError(); | ||
| }; | ||
|
|
||
| return ( | ||
| <View style={[styles.dotIndicatorMessage, styles.mt4]}> | ||
| <View style={styles.offlineFeedback.errorDot}> | ||
| <Icon | ||
|
stitesExpensify marked this conversation as resolved.
|
||
| src={Expensicons.DotIndicator} | ||
| fill={colors.red} | ||
| /> | ||
| </View> | ||
| <View style={styles.offlineFeedback.textContainer}> | ||
| {/* | ||
| Show appropriate error msg on location issues | ||
| - errorCode = -1 -> location not supported (web only) | ||
| - errorCode = 1 -> location permission is not enabled | ||
| - errorCode = 2 -> location is unavailable or there is some connection issue | ||
| - errorCode = 3 -> location fetch timeout | ||
| */} | ||
| {isPermissionDenied ? ( | ||
| <Text style={styles.offlineFeedback.text}> | ||
| <Text>{`${translate('location.permissionDenied')} ${translate('common.please')}`}</Text> | ||
| <TextLink onPress={onAllowLocationLinkPress}>{` ${translate('location.allowPermission')} `}</TextLink> | ||
| <Text>{translate('location.tryAgain')}</Text> | ||
| </Text> | ||
| ) : ( | ||
| <Text style={styles.offlineFeedback.text}>{translate('location.notFound')}</Text> | ||
| )} | ||
| </View> | ||
| <View> | ||
| <Tooltip text={translate('common.close')}> | ||
| <PressableWithoutFeedback | ||
| onPress={dismissError} | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Coming from #29433. If the address input is focused, pressing the mouse on this button will trigger We add |
||
| 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 = defaultProps; | ||
| export default compose( | ||
| withOnyx({ | ||
| locationErrorCode: { | ||
| key: ONYXKEYS.LOCATION_ERROR_CODE, | ||
| }, | ||
| }), | ||
| withLocalize, | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. you can start using hooks instead of HOCs - just add in function const {translate} = useLocalize();
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Replied here |
||
| )(BaseLocationErrorMessage); | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| import React from 'react'; | ||
| import {Linking} from 'react-native'; | ||
| import CONST from '../../CONST'; | ||
| import BaseLocationErrorMessage from './BaseLocationErrorMessage'; | ||
|
|
||
| function LocationErrorMessage() { | ||
| /** opens expensify help site in a new browser tab */ | ||
| const navigateToExpensifyHelpSite = () => { | ||
| Linking.openURL(CONST.NEWHELP_URL); | ||
| }; | ||
|
|
||
| return <BaseLocationErrorMessage onAllowLocationLinkPress={navigateToExpensifyHelpSite} />; | ||
| } | ||
|
|
||
| LocationErrorMessage.displayName = 'LocationErrorMessage'; | ||
| export default LocationErrorMessage; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| import React from 'react'; | ||
| import {Linking} from 'react-native'; | ||
| import BaseLocationErrorMessage from './BaseLocationErrorMessage'; | ||
|
|
||
| function LocationErrorMessage() { | ||
| /** opens app level settings from the system settings */ | ||
| const openAppSettings = () => { | ||
| Linking.openSettings(); | ||
| }; | ||
|
|
||
| return <BaseLocationErrorMessage onAllowLocationLinkPress={openAppSettings} />; | ||
| } | ||
|
|
||
| LocationErrorMessage.displayName = 'LocationErrorMessage'; | ||
| export default LocationErrorMessage; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,88 @@ | ||
| import PropTypes from 'prop-types'; | ||
| import React, {useEffect, useRef} from 'react'; | ||
| import {Text} from 'react-native'; | ||
| import getCurrentPosition from '../libs/getCurrentPosition'; | ||
| import * as User from '../libs/actions/User'; | ||
| 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, | ||
|
|
||
| ...withLocalizePropTypes, | ||
| }; | ||
|
|
||
| const defaultProps = {}; | ||
|
|
||
| function UserCurrentLocationButton({onLocationFetched, translate}) { | ||
| const isFetchingLocation = useRef(false); | ||
|
|
||
| /** | ||
| * handles error when failed to get user's current location | ||
| * @param {Object} errorData | ||
| * @param {Number} errorData.code | ||
| */ | ||
| const onError = (errorData) => { | ||
| isFetchingLocation.current = false; | ||
|
|
||
| User.setLocationError(errorData.code); | ||
| }; | ||
|
|
||
| /** | ||
| * handles success after getting user's current location | ||
| * @param {Object} successData | ||
| * @param {Object} successData.coords | ||
| * @param {Number} successData.coords.longitude | ||
| * @param {Number} successData.coords.latitude | ||
| * @param {Number} successData.timestamp | ||
| */ | ||
| const onSuccess = (successData) => { | ||
| isFetchingLocation.current = false; | ||
|
|
||
| User.clearLocationError(); | ||
|
|
||
| onLocationFetched(successData); | ||
| }; | ||
|
|
||
| /** Gets the user's current location and registers success/error callbacks */ | ||
| const useCurrentLocation = () => { | ||
| if (isFetchingLocation.current) return; | ||
|
|
||
| isFetchingLocation.current = true; | ||
|
|
||
| getCurrentPosition(onSuccess, onError); | ||
| }; | ||
|
|
||
| useEffect(() => { | ||
| // clear location errors on mount | ||
| User.clearLocationError(); | ||
| }, []); | ||
|
|
||
| return ( | ||
| <> | ||
| <PressableWithFeedback | ||
| style={[styles.flexRow, styles.mt4]} | ||
| onPress={useCurrentLocation} | ||
| accessibilityLabel={translate('location.useCurrent')} | ||
| > | ||
| <Icon | ||
| src={Expensicons.Location} | ||
| fill={colors.green} | ||
| /> | ||
| <Text style={[styles.textLabel, styles.mh2]}>{translate('location.useCurrent')}</Text> | ||
| </PressableWithFeedback> | ||
| <LocationErrorMessage /> | ||
| </> | ||
| ); | ||
| } | ||
|
|
||
| UserCurrentLocationButton.displayName = 'UserCurrentLocationButton'; | ||
| UserCurrentLocationButton.propTypes = propTypes; | ||
| UserCurrentLocationButton.defaultProps = defaultProps; | ||
| export default withLocalize(UserCurrentLocationButton); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. you can use useLocalize hook
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Actually since |
||
Uh oh!
There was an error while loading. Please reload this page.