Skip to content
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

Allow user to get current location #25990

Merged
merged 29 commits into from
Aug 31, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
fc04db7
Allow user to get current location
huzaifa-99 Aug 26, 2023
5fec130
Added default props and JSDoc
huzaifa-99 Aug 26, 2023
7465ef1
Added comment explaining locationErrorCode default value
huzaifa-99 Aug 26, 2023
5f11209
Fix import
huzaifa-99 Aug 26, 2023
8ab688f
Updated react-native-x-geolocation to 1.0.10
huzaifa-99 Aug 26, 2023
ffb3138
Fix onyx key type
huzaifa-99 Aug 26, 2023
31658da
Use undefined instead of 0 as deafult value for location error
huzaifa-99 Aug 26, 2023
d73335a
Added variable for conditional rendering
huzaifa-99 Aug 26, 2023
312cff2
Added address hint with updated message
huzaifa-99 Aug 26, 2023
d7bd1ea
Merge branch 'main' into 25855-get-user-location
huzaifa-99 Aug 27, 2023
a12968e
Removed accessibilityLabel as prop from UserCurrentLocationButton
huzaifa-99 Aug 27, 2023
9bf0236
Added platform specific files for 'allow location permission' link
huzaifa-99 Aug 27, 2023
f371f95
Fixed type
huzaifa-99 Aug 27, 2023
14494b9
Removed unnecessary prop objects
huzaifa-99 Aug 28, 2023
6403a08
Fix unnecessary prop objects
huzaifa-99 Aug 28, 2023
348fc5c
Removed unnecessary default prop
huzaifa-99 Aug 28, 2023
0fd62fc
Added default prop back to location error message
huzaifa-99 Aug 28, 2023
c582e75
Added geolocation util from react-native-x-geolocation
huzaifa-99 Aug 30, 2023
24dfaf7
Removed react-native-x-geolocation
huzaifa-99 Aug 30, 2023
90e5866
Added react-native-geolocation^1.0.0
huzaifa-99 Aug 30, 2023
2633ae3
Merge branch 'main' into 25855-get-user-location
huzaifa-99 Aug 30, 2023
3b9fe10
Fix lint
huzaifa-99 Aug 30, 2023
ca04daf
Added GOOGLE_GEOLOCATION_API_KEY tokens
huzaifa-99 Aug 30, 2023
3bd4faf
Merge branch 'main' into 25855-get-user-location
huzaifa-99 Aug 30, 2023
ee60ec3
Use null instead of undefined when clearing location error
huzaifa-99 Aug 30, 2023
72fc797
Added geolocation api key as default value in config
huzaifa-99 Aug 30, 2023
86b61b7
Show close icon with location error message
huzaifa-99 Aug 30, 2023
3856f7f
Use set instead of merge for location error code onyx
huzaifa-99 Aug 31, 2023
4dea2d6
Removed unnecessary desktop config
huzaifa-99 Aug 31, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ USE_WEB_PROXY=false
USE_WDYR=false
CAPTURE_METRICS=false
ONYX_METRICS=false
GOOGLE_GEOLOCATION_API_KEY=AIzaSyBqg6bMvQU7cPWDKhhzpYqJrTEnSorpiLI

EXPENSIFY_ACCOUNT_ID_ACCOUNTING=-1
EXPENSIFY_ACCOUNT_ID_ADMIN=-1
Expand Down
1 change: 1 addition & 0 deletions .env.production
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ PUSHER_APP_KEY=268df511a204fbb60884
USE_WEB_PROXY=false
ENVIRONMENT=production
SEND_CRASH_REPORTS=true
GOOGLE_GEOLOCATION_API_KEY=AIzaSyBFKujMpzExz0_z2pAGfPUwkmlaUc-uw1Q
hayata-suenaga marked this conversation as resolved.
Show resolved Hide resolved
1 change: 1 addition & 0 deletions .env.staging
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ PUSHER_APP_KEY=268df511a204fbb60884
USE_WEB_PROXY=false
ENVIRONMENT=staging
SEND_CRASH_REPORTS=true
GOOGLE_GEOLOCATION_API_KEY=AIzaSyD2T1mlByThbUN88O8OPOD8vKuMMwLD4-M
6 changes: 6 additions & 0 deletions ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -581,6 +581,8 @@ PODS:
- React-Core
- react-native-flipper (0.159.0):
- React-Core
- react-native-geolocation (3.0.6):
- React-Core
- react-native-image-manipulator (1.0.5):
- React
- react-native-image-picker (5.1.0):
Expand Down Expand Up @@ -892,6 +894,7 @@ DEPENDENCIES:
- react-native-config (from `../node_modules/react-native-config`)
- react-native-document-picker (from `../node_modules/react-native-document-picker`)
- react-native-flipper (from `../node_modules/react-native-flipper`)
- "react-native-geolocation (from `../node_modules/@react-native-community/geolocation`)"
- "react-native-image-manipulator (from `../node_modules/@oguzhnatly/react-native-image-manipulator`)"
- react-native-image-picker (from `../node_modules/react-native-image-picker`)
- react-native-key-command (from `../node_modules/react-native-key-command`)
Expand Down Expand Up @@ -1065,6 +1068,8 @@ EXTERNAL SOURCES:
:path: "../node_modules/react-native-document-picker"
react-native-flipper:
:path: "../node_modules/react-native-flipper"
react-native-geolocation:
:path: "../node_modules/@react-native-community/geolocation"
react-native-image-manipulator:
:path: "../node_modules/@oguzhnatly/react-native-image-manipulator"
react-native-image-picker:
Expand Down Expand Up @@ -1249,6 +1254,7 @@ SPEC CHECKSUMS:
react-native-config: 7cd105e71d903104e8919261480858940a6b9c0e
react-native-document-picker: f68191637788994baed5f57d12994aa32cf8bf88
react-native-flipper: dc5290261fbeeb2faec1bdc57ae6dd8d562e1de4
react-native-geolocation: 0f7fe8a4c2de477e278b0365cce27d089a8c5903
react-native-image-manipulator: c48f64221cfcd46e9eec53619c4c0374f3328a56
react-native-image-picker: c33d4e79f0a14a2b66e5065e14946ae63749660b
react-native-key-command: c2645ec01eb1fa664606c09480c05cb4220ef67b
Expand Down
9,658 changes: 9,259 additions & 399 deletions package-lock.json

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@
"@react-native-camera-roll/camera-roll": "5.4.0",
"@react-native-community/clipboard": "^1.5.1",
"@react-native-community/datetimepicker": "^3.5.2",
"@react-native-community/geolocation": "^3.0.6",
"@react-native-community/netinfo": "^9.3.10",
"@react-native-firebase/analytics": "^12.3.0",
"@react-native-firebase/app": "^12.3.0",
Expand Down Expand Up @@ -115,6 +116,7 @@
"react-native-document-picker": "^8.0.0",
"react-native-fast-image": "^8.6.3",
"react-native-fs": "^2.20.0",
"react-native-geolocation": "^1.0.0",
"react-native-gesture-handler": "2.12.0",
"react-native-google-places-autocomplete": "git+https://github.com/Expensify/react-native-google-places-autocomplete.git#ee87343c3e827ff7818abc71b6bb04fcc1f120e0",
"react-native-haptic-feedback": "^1.13.0",
Expand Down
2 changes: 2 additions & 0 deletions src/CONFIG.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ const secureExpensifyUrl = Url.addTrailingForwardSlash(get(Config, 'SECURE_EXPEN
const useNgrok = get(Config, 'USE_NGROK', 'false') === 'true';
const useWebProxy = get(Config, 'USE_WEB_PROXY', 'true') === 'true';
const expensifyComWithProxy = getPlatform() === 'web' && useWebProxy ? '/' : expensifyURL;
const googleGeolocationAPIKey = get(Config, 'GOOGLE_GEOLOCATION_API_KEY', 'AIzaSyBqg6bMvQU7cPWDKhhzpYqJrTEnSorpiLI');

// Throw errors on dev if config variables are not set correctly
if (ENVIRONMENT === CONST.ENVIRONMENT.DEV) {
Expand Down Expand Up @@ -91,4 +92,5 @@ export default {
WEB_CLIENT_ID: '921154746561-gpsoaqgqfuqrfsjdf8l7vohfkfj7b9up.apps.googleusercontent.com',
IOS_CLIENT_ID: '921154746561-s3uqn2oe4m85tufi6mqflbfbuajrm2i3.apps.googleusercontent.com',
},
GOOGLE_GEOLOCATION_API_KEY: googleGeolocationAPIKey,
} as const;
4 changes: 4 additions & 0 deletions src/ONYXKEYS.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,9 @@ const ONYXKEYS = {
* It is expected to provide a two-letter country code such as US for United States, and so on. */
COUNTRY: 'country',

/** Represents current user's location error code, this error code comes from the geolocation api */
LOCATION_ERROR_CODE: 'locationErrorCode',
huzaifa-99 marked this conversation as resolved.
Show resolved Hide resolved

/** Contains all the users settings for the Settings page and sub pages */
USER: 'user',

Expand Down Expand Up @@ -320,6 +323,7 @@ type OnyxValues = {
[ONYXKEYS.SCREEN_SHARE_REQUEST]: OnyxTypes.ScreenShareRequest;
[ONYXKEYS.COUNTRY_CODE]: number;
[ONYXKEYS.COUNTRY]: string;
[ONYXKEYS.LOCATION_ERROR_CODE]: number;
[ONYXKEYS.USER]: OnyxTypes.User;
[ONYXKEYS.LOGIN_LIST]: OnyxTypes.Login;
[ONYXKEYS.SESSION]: OnyxTypes.Session;
Expand Down
99 changes: 99 additions & 0 deletions src/components/LocationErrorMessage/BaseLocationErrorMessage.js
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,
Copy link
Contributor

Choose a reason for hiding this comment

The 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

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hm, I think it's not needed. Removed here

Copy link
Contributor Author

Choose a reason for hiding this comment

The 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.
Show resolved Hide 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}
Copy link
Contributor

Choose a reason for hiding this comment

The 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 blur and hide the autocomplete component, causing this button to move up (layout shift), thus causing the click event to fail to be triggered on this button.

We add onMouseDown={e => e.preventDefault()} to prevent blur event.

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,
Copy link
Contributor

Choose a reason for hiding this comment

The 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();

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Replied here

)(BaseLocationErrorMessage);
16 changes: 16 additions & 0 deletions src/components/LocationErrorMessage/index.js
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;
15 changes: 15 additions & 0 deletions src/components/LocationErrorMessage/index.native.js
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;
88 changes: 88 additions & 0 deletions src/components/UserCurrentLocationButton.js
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);
Copy link
Contributor

@narefyev91 narefyev91 Aug 28, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you can use useLocalize hook

Copy link
Contributor Author

@huzaifa-99 huzaifa-99 Aug 28, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually since UserCurrentLocationButton is nested inside the Form component here, it was giving hooks error (react rules of hook realted). I made an assumption here that the UserCurrentLocationButton might be used in other places and maybe inside Form too, so didn't use the localize hook.

9 changes: 8 additions & 1 deletion src/languages/en.js
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,13 @@ export default {
replace: 'Replace',
distance: 'Distance',
},
location: {
useCurrent: 'Use current location',
notFound: 'We were unable to find your location, please try again or enter an address manually',
permissionDenied: 'It looks like you have denied permission to your location.',
allowPermission: 'allow location permission in settings',
tryAgain: 'and then try again.',
},
anonymousReportFooter: {
logoTagline: 'Join the discussion.',
},
Expand Down Expand Up @@ -1655,7 +1662,7 @@ export default {
onlineSubtitle: 'One moment while we set up the map',
},
errors: {
selectSuggestedAddress: 'Please select a suggested address',
selectSuggestedAddress: 'Please select a suggested address or use current location',
},
},
countrySelectorModal: {
Expand Down
9 changes: 8 additions & 1 deletion src/languages/es.js
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,13 @@ export default {
replace: 'Sustituir',
distance: 'Distancia',
},
location: {
useCurrent: 'Usar ubicación actual',
notFound: 'No pudimos encontrar su ubicación, inténtelo nuevamente o ingrese una dirección manualmente',
permissionDenied: 'Parece que has denegado el permiso a tu ubicación.',
allowPermission: 'permitir permiso de ubicación en la configuración',
tryAgain: 'y luego inténtalo de nuevo.',
},
hayata-suenaga marked this conversation as resolved.
Show resolved Hide resolved
anonymousReportFooter: {
logoTagline: 'Únete a la discusión.',
},
Expand Down Expand Up @@ -2143,7 +2150,7 @@ export default {
onlineSubtitle: 'Un momento mientras configuramos el mapa',
},
errors: {
selectSuggestedAddress: 'Por favor, selecciona una dirección sugerida',
selectSuggestedAddress: 'Por favor, selecciona una dirección sugerida o usa la ubicación actual.',
},
},
countrySelectorModal: {
Expand Down
Loading
Loading