diff --git a/ios/Podfile b/ios/Podfile index b30510572448..6aee4b94df04 100644 --- a/ios/Podfile +++ b/ios/Podfile @@ -2,17 +2,29 @@ # This value is used by $RNMapboxMaps $RNMapboxMapsImpl = 'mapbox' -# Resolve react_native_pods.rb with node to allow for hoisting -require Pod::Executable.execute_command('node', ['-p', - 'require.resolve( - "react-native/scripts/react_native_pods.rb", - {paths: [process.argv[1]]}, - )', __dir__]).strip +def node_require(script) + # Resolve script with node to allow for hoisting + require Pod::Executable.execute_command('node', ['-p', + "require.resolve( + '#{script}', + {paths: [process.argv[1]]}, + )", __dir__]).strip +end + +node_require('react-native/scripts/react_native_pods.rb') +node_require('react-native-permissions/scripts/setup.rb') # Our min supported iOS version is higher than the default (min_ios_version_supported) to support libraires such as Airship platform :ios, 13 prepare_react_native_project! +setup_permissions([ + 'Camera', + 'LocationAccuracy', + 'LocationAlways', + 'LocationWhenInUse' +]) + # If you are using a `react-native-flipper` your iOS build will fail when `NO_FLIPPER=1` is set. # because `react-native-flipper` depends on (FlipperKit,...) that will be excluded # @@ -51,8 +63,6 @@ pre_install do |installer| end target 'NewExpensify' do - permissions_path = '../node_modules/react-native-permissions/ios' - project 'NewExpensify', 'DebugDevelopment' => :debug, 'DebugAdHoc' => :debug, @@ -61,11 +71,6 @@ target 'NewExpensify' do 'ReleaseAdHoc' => :release, 'ReleaseProduction' => :release - pod 'Permission-LocationAccuracy', :path => "#{permissions_path}/LocationAccuracy" - pod 'Permission-LocationAlways', :path => "#{permissions_path}/LocationAlways" - pod 'Permission-LocationWhenInUse', :path => "#{permissions_path}/LocationWhenInUse" - pod 'Permission-Camera', :path => "#{permissions_path}/Camera" - config = use_native_modules! # Flags change depending on the env values. diff --git a/ios/Podfile.lock b/ios/Podfile.lock index fd019aeddb8b..1f08d51d24a8 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -256,14 +256,6 @@ PODS: - Onfido (= 27.4.0) - React - OpenSSL-Universal (1.1.1100) - - Permission-Camera (3.6.1): - - RNPermissions - - Permission-LocationAccuracy (3.6.1): - - RNPermissions - - Permission-LocationAlways (3.6.1): - - RNPermissions - - Permission-LocationWhenInUse (3.6.1): - - RNPermissions - Plaid (4.1.0) - PromisesObjC (2.2.0) - RCT-Folly (2021.07.22.00): @@ -781,7 +773,7 @@ PODS: - React - React-Core - Turf - - RNPermissions (3.6.1): + - RNPermissions (3.9.3): - React-Core - RNReactNativeHapticFeedback (1.14.0): - React-Core @@ -867,10 +859,6 @@ DEPENDENCIES: - lottie-react-native (from `../node_modules/lottie-react-native`) - "onfido-react-native-sdk (from `../node_modules/@onfido/react-native-sdk`)" - OpenSSL-Universal (= 1.1.1100) - - Permission-Camera (from `../node_modules/react-native-permissions/ios/Camera`) - - Permission-LocationAccuracy (from `../node_modules/react-native-permissions/ios/LocationAccuracy`) - - Permission-LocationAlways (from `../node_modules/react-native-permissions/ios/LocationAlways`) - - Permission-LocationWhenInUse (from `../node_modules/react-native-permissions/ios/LocationWhenInUse`) - RCT-Folly (from `../node_modules/react-native/third-party-podspecs/RCT-Folly.podspec`) - RCTRequired (from `../node_modules/react-native/Libraries/RCTRequired`) - RCTTypeSafety (from `../node_modules/react-native/Libraries/TypeSafety`) @@ -1018,14 +1006,6 @@ EXTERNAL SOURCES: :path: "../node_modules/lottie-react-native" onfido-react-native-sdk: :path: "../node_modules/@onfido/react-native-sdk" - Permission-Camera: - :path: "../node_modules/react-native-permissions/ios/Camera" - Permission-LocationAccuracy: - :path: "../node_modules/react-native-permissions/ios/LocationAccuracy" - Permission-LocationAlways: - :path: "../node_modules/react-native-permissions/ios/LocationAlways" - Permission-LocationWhenInUse: - :path: "../node_modules/react-native-permissions/ios/LocationWhenInUse" RCT-Folly: :podspec: "../node_modules/react-native/third-party-podspecs/RCT-Folly.podspec" RCTRequired: @@ -1227,10 +1207,6 @@ SPEC CHECKSUMS: Onfido: e36f284b865adcf99d9c905590a64ac09d4a576b onfido-react-native-sdk: 4ecde1a97435dcff9f00a878e3f8d1eb14fabbdc OpenSSL-Universal: ebc357f1e6bc71fa463ccb2fe676756aff50e88c - Permission-Camera: bf6791b17c7f614b6826019fcfdcc286d3a107f6 - Permission-LocationAccuracy: 76df17de5c6b8bc2eee34e61ee92cdd7a864c73d - Permission-LocationAlways: 8d99b025c9f73c696e0cdb367e42525f2e9a26f2 - Permission-LocationWhenInUse: 3ba99e45c852763f730eabecec2870c2382b7bd4 Plaid: 7d340abeadb46c7aa1a91f896c5b22395a31fcf2 PromisesObjC: 09985d6d70fbe7878040aa746d78236e6946d2ef RCT-Folly: 424b8c9a7a0b9ab2886ffe9c3b041ef628fd4fb1 @@ -1302,7 +1278,7 @@ SPEC CHECKSUMS: RNGoogleSignin: ccaa4a81582cf713eea562c5dd9dc1961a715fd0 RNLocalize: d4b8af4e442d4bcca54e68fc687a2129b4d71a81 rnmapbox-maps: 6f638ec002aa6e906a6f766d69cd45f968d98e64 - RNPermissions: dcdb7b99796bbeda6975a6e79ad519c41b251b1c + RNPermissions: 9b086c8f05b2e2faa587fdc31f4c5ab4509728aa RNReactNativeHapticFeedback: 1e3efeca9628ff9876ee7cdd9edec1b336913f8c RNReanimated: ab2e96c6d5591c3dfbb38a464f54c8d17fb34a87 RNScreens: d037903436160a4b039d32606668350d2a808806 @@ -1315,6 +1291,6 @@ SPEC CHECKSUMS: Yoga: 3efc43e0d48686ce2e8c60f99d4e6bd349aff981 YogaKit: f782866e155069a2cca2517aafea43200b01fd5a -PODFILE CHECKSUM: 2daf34c870819a933f3fefe426801d54b2ff2a14 +PODFILE CHECKSUM: ff769666b7221c15936ebc5576a8c8e467dc6879 COCOAPODS: 1.12.1 diff --git a/package-lock.json b/package-lock.json index 1681941f8aec..2bf2da4e4016 100644 --- a/package-lock.json +++ b/package-lock.json @@ -98,7 +98,7 @@ "react-native-pager-view": "^6.2.0", "react-native-pdf": "^6.7.1", "react-native-performance": "^5.1.0", - "react-native-permissions": "^3.0.1", + "react-native-permissions": "^3.9.3", "react-native-picker-select": "git+https://github.com/Expensify/react-native-picker-select.git#eae05855286dc699954415cc1d629bfd8e8e47e2", "react-native-plaid-link-sdk": "^10.0.0", "react-native-qrcode-svg": "^6.2.0", @@ -44799,8 +44799,9 @@ } }, "node_modules/react-native-permissions": { - "version": "3.6.1", - "license": "MIT", + "version": "3.9.3", + "resolved": "https://registry.npmjs.org/react-native-permissions/-/react-native-permissions-3.9.3.tgz", + "integrity": "sha512-2UqG2Em4xHxLq0E1XynXMdQ//XZltxVUjTn/i4fPIZuuZ0cQ+ydAQmLXqDPxOXvG0sICwc3oe0orJmQdqpa1sQ==", "peerDependencies": { "react": ">=16.13.1", "react-native": ">=0.63.3", @@ -85342,7 +85343,9 @@ "requires": {} }, "react-native-permissions": { - "version": "3.6.1", + "version": "3.9.3", + "resolved": "https://registry.npmjs.org/react-native-permissions/-/react-native-permissions-3.9.3.tgz", + "integrity": "sha512-2UqG2Em4xHxLq0E1XynXMdQ//XZltxVUjTn/i4fPIZuuZ0cQ+ydAQmLXqDPxOXvG0sICwc3oe0orJmQdqpa1sQ==", "requires": {} }, "react-native-picker-select": { diff --git a/package.json b/package.json index 5da7256344a9..c88b42b34b37 100644 --- a/package.json +++ b/package.json @@ -141,7 +141,7 @@ "react-native-pager-view": "^6.2.0", "react-native-pdf": "^6.7.1", "react-native-performance": "^5.1.0", - "react-native-permissions": "^3.0.1", + "react-native-permissions": "^3.9.3", "react-native-picker-select": "git+https://github.com/Expensify/react-native-picker-select.git#eae05855286dc699954415cc1d629bfd8e8e47e2", "react-native-plaid-link-sdk": "^10.0.0", "react-native-qrcode-svg": "^6.2.0", diff --git a/src/pages/iou/ReceiptSelector/index.native.js b/src/pages/iou/ReceiptSelector/index.native.js index 4887d0816c81..d509fbce176d 100644 --- a/src/pages/iou/ReceiptSelector/index.native.js +++ b/src/pages/iou/ReceiptSelector/index.native.js @@ -94,9 +94,7 @@ function ReceiptSelector({route, report, iou, transactionID, isInTabNavigator}) const camera = useRef(null); const [flash, setFlash] = useState(false); - const [permissions, setPermissions] = useState('granted'); - const isAndroidBlockedPermissionRef = useRef(false); - const appState = useRef(AppState.currentState); + const [cameraPermissionStatus, setCameraPermissionStatus] = useState(undefined); const iouType = lodashGet(route, 'params.iouType', ''); const pageIndex = lodashGet(route, 'params.pageIndex', 1); @@ -105,16 +103,23 @@ function ReceiptSelector({route, report, iou, transactionID, isInTabNavigator}) const CameraComponent = isInTabNavigator ? TabNavigationAwareCamera : NavigationAwareCamera; - // We want to listen to if the app has come back from background and refresh the permissions status to show camera when permissions were granted useEffect(() => { - const subscription = AppState.addEventListener('change', (nextAppState) => { - if (appState.current.match(/inactive|background/) && nextAppState === 'active') { - CameraPermission.getCameraPermissionStatus().then((permissionStatus) => { - setPermissions(permissionStatus); - }); + const refreshCameraPermissionStatus = () => { + CameraPermission.getCameraPermissionStatus() + .then(setCameraPermissionStatus) + .catch(() => setCameraPermissionStatus(RESULTS.UNAVAILABLE)); + }; + + // Check initial camera permission status + refreshCameraPermissionStatus(); + + // Refresh permission status when app gain focus + const subscription = AppState.addEventListener('change', (appState) => { + if (appState !== 'active') { + return; } - appState.current = nextAppState; + refreshCameraPermissionStatus(); }); return () => { @@ -154,14 +159,17 @@ function ReceiptSelector({route, report, iou, transactionID, isInTabNavigator}) const askForPermissions = () => { // There's no way we can check for the BLOCKED status without requesting the permission first // https://github.com/zoontek/react-native-permissions/blob/a836e114ce3a180b2b23916292c79841a267d828/README.md?plain=1#L670 - if (permissions === RESULTS.BLOCKED || isAndroidBlockedPermissionRef.current) { - Linking.openSettings(); - } else if (permissions === RESULTS.DENIED) { - CameraPermission.requestCameraPermission().then((permissionStatus) => { - setPermissions(permissionStatus); - isAndroidBlockedPermissionRef.current = permissionStatus === RESULTS.BLOCKED; + CameraPermission.requestCameraPermission() + .then((status) => { + setCameraPermissionStatus(status); + + if (status === RESULTS.BLOCKED) { + showPermissionsAlert(); + } + }) + .catch(() => { + setCameraPermissionStatus(RESULTS.UNAVAILABLE); }); - } }; /** @@ -230,13 +238,14 @@ function ReceiptSelector({route, report, iou, transactionID, isInTabNavigator}) }); }, [flash, iouType, iou, report, translate, transactionID, route.path]); - CameraPermission.getCameraPermissionStatus().then((permissionStatus) => { - setPermissions(permissionStatus); - }); + // Wait for camera permission status to render + if (cameraPermissionStatus == null) { + return null; + } return ( - {permissions !== RESULTS.GRANTED && ( + {cameraPermissionStatus !== RESULTS.GRANTED && ( )} - {permissions === RESULTS.GRANTED && device == null && ( + {cameraPermissionStatus === RESULTS.GRANTED && device == null && ( )} - {permissions === RESULTS.GRANTED && device != null && ( + {cameraPermissionStatus === RESULTS.GRANTED && device != null && ( setFlash((prevFlash) => !prevFlash)} >