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

Added option to get user's current location #26546

Merged
merged 59 commits into from
Oct 5, 2023
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
59 commits
Select commit Hold shift + click to select a range
cd8cb9b
Added getCurrentPosition util
huzaifa-99 Sep 1, 2023
27f52d2
Added option to use current location in waypoint select
huzaifa-99 Sep 1, 2023
6d55057
Keep suggestion open on empty text with predefined suggestions
huzaifa-99 Sep 1, 2023
4f3c0ff
Disable current location button when offline
huzaifa-99 Sep 1, 2023
8203e03
Updated translation keys for address search hint
huzaifa-99 Sep 1, 2023
a55358c
Updated config for getting current location
huzaifa-99 Sep 1, 2023
f718ed6
Disable text select on current location button when offline
huzaifa-99 Sep 2, 2023
98f7570
Added missing comma in spanish translation
huzaifa-99 Sep 2, 2023
c2d67b8
Updated current location timeout to 5 seconds
huzaifa-99 Sep 2, 2023
1221d12
Updated function name
huzaifa-99 Sep 2, 2023
1be54a1
Use sentense case for comments
huzaifa-99 Sep 2, 2023
c097915
Move success/error callbacks inline
huzaifa-99 Sep 2, 2023
fa5aaef
Used index.platform file format for getCurrentPosition lib
huzaifa-99 Sep 2, 2023
befbb1f
Updated locationErrorCode prop type
huzaifa-99 Sep 2, 2023
fab2259
Moved navigator callback outside component body
huzaifa-99 Sep 2, 2023
19d0742
Updated desktop geolocation implementation
huzaifa-99 Sep 2, 2023
f7654d6
Prevent saving current location waypoint as recent wauypoint
huzaifa-99 Sep 2, 2023
cc97747
Updated location error text style
huzaifa-99 Sep 3, 2023
67c7c8a
Added location permission in android manifest
huzaifa-99 Sep 3, 2023
45947ce
Show system location enable prompt for geolocation on android
huzaifa-99 Sep 3, 2023
a7f6e18
Fix action argument type doc
huzaifa-99 Sep 3, 2023
7656511
Fix lint
huzaifa-99 Sep 4, 2023
f4ee7d7
Added loader when fetching location
huzaifa-99 Sep 4, 2023
fb37344
Prevent modal close on location fetch
huzaifa-99 Sep 4, 2023
40792cc
Moved location callback branch case to UserCurrentLocationButton
huzaifa-99 Sep 4, 2023
e0be146
Merge branch 'main' into 25855-get-user-location
huzaifa-99 Sep 6, 2023
79c187d
Hide keyboard immediately after clicking 'current location' btn
huzaifa-99 Sep 6, 2023
cb65a73
Merge branch 'main' into 25855-get-user-location
huzaifa-99 Sep 15, 2023
49c619b
Added null as default for locationErrorCode prop
huzaifa-99 Sep 18, 2023
08debbc
Inlined User.clearLocationError function call
huzaifa-99 Sep 18, 2023
809a449
Added comment for onyx props
huzaifa-99 Sep 18, 2023
1d31921
Updated location not found translation
huzaifa-99 Sep 18, 2023
1122848
Updated JSDoc for function definition
huzaifa-99 Sep 18, 2023
7d7f05f
Remove react-native-geolocation lib
huzaifa-99 Sep 18, 2023
07d3da3
Updated locationErrorCode proptype
huzaifa-99 Sep 18, 2023
393d582
Updated location error link style
huzaifa-99 Sep 18, 2023
b1a7da2
Filter 'Your Location' from recent waypoints without flag param
huzaifa-99 Sep 20, 2023
774fcd1
Updated navigator branch condition
huzaifa-99 Sep 20, 2023
50680b4
Merge branch 'main' into 25855-get-user-location
huzaifa-99 Sep 20, 2023
caa536d
Merge branch 'main' into 25855-get-user-location
huzaifa-99 Sep 21, 2023
7f309ad
Reverted undefined check
huzaifa-99 Sep 21, 2023
63b4379
Fix lint
huzaifa-99 Sep 21, 2023
7c8109e
Fix package-lock.json
huzaifa-99 Sep 25, 2023
2ba6260
Merge branch 'main' into 25855-get-user-location
huzaifa-99 Sep 25, 2023
74b16e2
Fix lint
huzaifa-99 Sep 25, 2023
2f0c0f0
Updated comment about ms delay for getCurrentPosition call
huzaifa-99 Sep 25, 2023
8d16400
Fix lint
huzaifa-99 Sep 25, 2023
c0d32e2
Merge branch 'main' into 25855-get-user-location
huzaifa-99 Sep 26, 2023
2f04195
Merge branch 'main' into 25855-get-user-location
huzaifa-99 Sep 28, 2023
3e8a077
Use state for location error message
huzaifa-99 Sep 28, 2023
572cd7a
Updated import
huzaifa-99 Sep 28, 2023
853f7e4
Pass default props to location error message
huzaifa-99 Sep 28, 2023
2b34ccd
Added comment about using withLocalize
huzaifa-99 Sep 28, 2023
6c505e4
Merge branch 'main' into 25855-get-user-location
huzaifa-99 Sep 29, 2023
ae8ec7a
Updated comments
huzaifa-99 Oct 2, 2023
37a47c2
Added line break before condition
huzaifa-99 Oct 2, 2023
98fcdc5
Merge branch 'main' into 25855-get-user-location
huzaifa-99 Oct 2, 2023
8a5cc0b
Merge branch 'main' into 25855-get-user-location
huzaifa-99 Oct 3, 2023
c226384
Merge branch 'main' into 25855-get-user-location
huzaifa-99 Oct 5, 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
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
10 changes: 8 additions & 2 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 All @@ -591,7 +593,7 @@ PODS:
- React-Core
- react-native-pager-view (6.2.0):
- React-Core
- react-native-pdf (6.6.2):
- react-native-pdf (6.7.1):
- React-Core
- react-native-performance (4.0.0):
- React-Core
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,12 +1254,13 @@ 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
react-native-netinfo: ccbe1085dffd16592791d550189772e13bf479e2
react-native-pager-view: 0ccb8bf60e2ebd38b1f3669fa3650ecce81db2df
react-native-pdf: 33c622cbdf776a649929e8b9d1ce2d313347c4fa
react-native-pdf: 7c0e91ada997bac8bac3bb5bea5b6b81f5a3caae
react-native-performance: 224bd53e6a835fda4353302cf891d088a0af7406
react-native-plaid-link-sdk: 9eb0f71dad94b3bdde649c7a384cba93024af46c
react-native-quick-sqlite: bcc7a7a250a40222f18913a97cd356bf82d0a6c4
Expand Down
9,658 changes: 9,259 additions & 399 deletions package-lock.json
hayata-suenaga marked this conversation as resolved.
Show resolved Hide resolved

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",
huzaifa-99 marked this conversation as resolved.
Show resolved Hide resolved
"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
4 changes: 2 additions & 2 deletions src/components/AddressSearch/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -333,8 +333,8 @@ function AddressSearch(props) {
props.onInputChange({street: text});
}

// If the text is empty, we set displayListViewBorder to false to prevent UI flickering
if (_.isEmpty(text)) {
// If the text is empty and we have no predefined places, we set displayListViewBorder to false to prevent UI flickering
if (_.isEmpty(text) && _.isEmpty(props.predefinedPlaces)) {
hayata-suenaga marked this conversation as resolved.
Show resolved Hide resolved
setDisplayListViewBorder(false);
}
},
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])]),
hayata-suenaga marked this conversation as resolved.
Show resolved Hide resolved

/** A callback that runs when 'allow location permission' link is pressed */
onAllowLocationLinkPress: PropTypes.func.isRequired,

...withLocalizePropTypes,
huzaifa-99 marked this conversation as resolved.
Show resolved Hide resolved
};

const defaultProps = {
locationErrorCode: undefined,
huzaifa-99 marked this conversation as resolved.
Show resolved Hide resolved
};

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();
};
huzaifa-99 marked this conversation as resolved.
Show resolved Hide resolved

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}>
{/*
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
*/}
huzaifa-99 marked this conversation as resolved.
Show resolved Hide resolved
{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}
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,
},
huzaifa-99 marked this conversation as resolved.
Show resolved Hide resolved
}),
withLocalize,
)(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);
};
hayata-suenaga marked this conversation as resolved.
Show resolved Hide resolved

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 = () => {
hayata-suenaga marked this conversation as resolved.
Show resolved Hide resolved
Linking.openSettings();
};

return <BaseLocationErrorMessage onAllowLocationLinkPress={openAppSettings} />;
}

LocationErrorMessage.displayName = 'LocationErrorMessage';
export default LocationErrorMessage;
97 changes: 97 additions & 0 deletions src/components/UserCurrentLocationButton.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
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,

/** Boolean to indicate if the button is clickable */
isDisabled: PropTypes.bool,

...withLocalizePropTypes,
huzaifa-99 marked this conversation as resolved.
Show resolved Hide resolved
};

const defaultProps = {
isDisabled: false,
};

function UserCurrentLocationButton({onLocationFetched, isDisabled, 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);
};
hayata-suenaga marked this conversation as resolved.
Show resolved Hide resolved

/** Gets the user's current location and registers success/error callbacks */
const useCurrentLocation = () => {
hayata-suenaga marked this conversation as resolved.
Show resolved Hide resolved
if (isFetchingLocation.current) return;

isFetchingLocation.current = true;

getCurrentPosition(onSuccess, onError, {
maximumAge: 0, // no cache, always get fresh location info
timeout: 3000,
hayata-suenaga marked this conversation as resolved.
Show resolved Hide resolved
});
};

useEffect(() => {
// clear location errors on mount
hayata-suenaga marked this conversation as resolved.
Show resolved Hide resolved
User.clearLocationError();
}, []);

return (
<>
<PressableWithFeedback
style={[styles.flexRow, styles.mt4, styles.alignSelfStart, isDisabled && styles.buttonOpacityDisabled]}
onPress={useCurrentLocation}
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 />
</>
);
}

UserCurrentLocationButton.displayName = 'UserCurrentLocationButton';
UserCurrentLocationButton.propTypes = propTypes;
UserCurrentLocationButton.defaultProps = defaultProps;
export default withLocalize(UserCurrentLocationButton);
huzaifa-99 marked this conversation as resolved.
Show resolved Hide resolved
9 changes: 8 additions & 1 deletion src/languages/en.js
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,13 @@ export default {
recent: 'Recent',
all: 'All',
},
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 @@ -1664,7 +1671,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',
},
},
demos: {
Expand Down
Loading