mirror of
https://github.com/inaturalist/iNaturalistReactNative.git
synced 2025-12-23 22:18:36 -05:00
fix: don't ask for PHOTO LIBRARY permission if user can't change their mind (#2020)
* stop asking for PHOTO LIBRARY permission before import, b/c it's not necessary for importing * use a forked version of react-native-cameraroll that allows us to write location metadata to newly-created photos, which was the really important thing we were using the PHOTO LIBRARY permission for Closes #1612
This commit is contained in:
@@ -899,7 +899,7 @@ PODS:
|
||||
- React-Mapbuffer (0.73.7):
|
||||
- glog
|
||||
- React-debug
|
||||
- react-native-cameraroll (7.5.2):
|
||||
- react-native-cameraroll (7.8.3):
|
||||
- glog
|
||||
- RCT-Folly (= 2022.05.16.00)
|
||||
- React-Core
|
||||
@@ -1505,7 +1505,7 @@ SPEC CHECKSUMS:
|
||||
React-jsinspector: f356e49aa086380d3a4892708ca173ad31ac69c1
|
||||
React-logger: 7b19bdfb254772a0332d6cd4d66eceb0678b6730
|
||||
React-Mapbuffer: 6f392912435adb8fbf4c3eee0e79a0a0b4e4b717
|
||||
react-native-cameraroll: af8eec1e585d053ff485d98ec837f9a8a11b5745
|
||||
react-native-cameraroll: 0fe31282894817a82b5991dd0d78dc04c5a6ed35
|
||||
react-native-config: 86038147314e2e6d10ea9972022aa171e6b1d4d8
|
||||
react-native-exif-reader: fe4678df00e36e1aba6ade9013fd1d35c78c8381
|
||||
react-native-geocoder-reborn: c31cbc630d9307ebbceea1dea2746d0054be35c4
|
||||
|
||||
8
package-lock.json
generated
8
package-lock.json
generated
@@ -13,7 +13,7 @@
|
||||
"@candlefinance/faster-image": "^1.4.3",
|
||||
"@formidable-webview/webshell": "^2.6.0",
|
||||
"@gorhom/bottom-sheet": "^4.6.1",
|
||||
"@react-native-camera-roll/camera-roll": "^7.5.2",
|
||||
"@react-native-camera-roll/camera-roll": "github:inaturalist/react-native-cameraroll",
|
||||
"@react-native-clipboard/clipboard": "^1.13.2",
|
||||
"@react-native-community/datetimepicker": "^7.6.3",
|
||||
"@react-native-community/geolocation": "^3.2.1",
|
||||
@@ -3834,9 +3834,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@react-native-camera-roll/camera-roll": {
|
||||
"version": "7.5.2",
|
||||
"resolved": "https://registry.npmjs.org/@react-native-camera-roll/camera-roll/-/camera-roll-7.5.2.tgz",
|
||||
"integrity": "sha512-XiVIrW17EFXrFzqB48q6cQOaYeVnw0iC3tH+Jhl+MAHDYGLJp+ulzxCNNwngaMvnVAA5Q2mUMzRocUiJPy8q0g==",
|
||||
"version": "7.8.3",
|
||||
"resolved": "git+ssh://git@github.com/inaturalist/react-native-cameraroll.git#c49afe441e1465f2eb1c25c3a4654f3c4ee5457c",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 18.17.0"
|
||||
},
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
"@candlefinance/faster-image": "^1.4.3",
|
||||
"@formidable-webview/webshell": "^2.6.0",
|
||||
"@gorhom/bottom-sheet": "^4.6.1",
|
||||
"@react-native-camera-roll/camera-roll": "^7.5.2",
|
||||
"@react-native-camera-roll/camera-roll": "github:inaturalist/react-native-cameraroll",
|
||||
"@react-native-clipboard/clipboard": "^1.13.2",
|
||||
"@react-native-community/datetimepicker": "^7.6.3",
|
||||
"@react-native-community/geolocation": "^3.2.1",
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
diff --git a/node_modules/@react-native-camera-roll/camera-roll/ios/RNCCameraRoll.mm b/node_modules/@react-native-camera-roll/camera-roll/ios/RNCCameraRoll.mm
|
||||
index b8f2aa2..aa0df68 100644
|
||||
--- a/node_modules/@react-native-camera-roll/camera-roll/ios/RNCCameraRoll.mm
|
||||
+++ b/node_modules/@react-native-camera-roll/camera-roll/ios/RNCCameraRoll.mm
|
||||
@@ -207,6 +207,26 @@ RCT_EXPORT_METHOD(saveToCameraRoll:(NSURLRequest *)request
|
||||
}
|
||||
} completionHandler:^(BOOL success, NSError *error) {
|
||||
if (success) {
|
||||
+ // If the write succeeded but we don't have readwrite permission, that
|
||||
+ // means we have addonly permission and we cannot read the file we
|
||||
+ // just created to construct a response
|
||||
+ if (@available(iOS 14, *)) {
|
||||
+ PHAuthorizationStatus readWriteAuthStatus = [PHPhotoLibrary authorizationStatusForAccessLevel:PHAccessLevelReadWrite];
|
||||
+ if (readWriteAuthStatus != PHAuthorizationStatusAuthorized) {
|
||||
+ NSDictionary *addOnlyResponse = @{
|
||||
+ @"node": @{
|
||||
+ @"id": placeholder.localIdentifier,
|
||||
+ @"type": options[@"type"],
|
||||
+ @"image": @{
|
||||
+ @"uri": @"placeholder/readWritePermissionNotGranted"
|
||||
+ }
|
||||
+ }
|
||||
+ };
|
||||
+ resolve(addOnlyResponse);
|
||||
+ return;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
PHFetchOptions *options = [PHFetchOptions new];
|
||||
options.includeHiddenAssets = YES;
|
||||
options.includeAllBurstAssets = YES;
|
||||
@@ -35,7 +35,8 @@ type Options = {
|
||||
// $FlowIgnore
|
||||
export async function savePhotosToCameraGallery(
|
||||
uris: [string],
|
||||
onEachSuccess: Function
|
||||
onEachSuccess: Function,
|
||||
location: Object
|
||||
) {
|
||||
const readWritePermissionResult = permissionResultFromMultiple(
|
||||
await checkMultiple( READ_WRITE_MEDIA_PERMISSIONS )
|
||||
@@ -43,20 +44,22 @@ export async function savePhotosToCameraGallery(
|
||||
uris.reduce(
|
||||
async ( memo, uri ) => {
|
||||
try {
|
||||
let savedPhotoUri;
|
||||
const saveOptions = {};
|
||||
// One quirk of CameraRoll is that if you want to write to an album, you
|
||||
// need readwrite permission, but we don't want to ask for that here
|
||||
// b/c it might come immediately after asking for *add only*
|
||||
// permission, so we're checking to see if we have that permission
|
||||
// and skipping the album if we don't
|
||||
if ( readWritePermissionResult === RESULTS.GRANTED ) {
|
||||
savedPhotoUri = await CameraRoll.save( uri, {
|
||||
type: "photo",
|
||||
album: "iNaturalist Next"
|
||||
} );
|
||||
} else {
|
||||
savedPhotoUri = await CameraRoll.save( uri );
|
||||
saveOptions.type = "photo";
|
||||
saveOptions.album = "iNaturalist Next";
|
||||
}
|
||||
if ( location ) {
|
||||
saveOptions.latitude = location.latitude;
|
||||
saveOptions.longitude = location.longitude;
|
||||
saveOptions.horizontalAccuracy = location.positional_accuracy;
|
||||
}
|
||||
const savedPhotoUri = await CameraRoll.save( uri, saveOptions );
|
||||
|
||||
// Save these camera roll URIs, so later on observation editor can update
|
||||
// the EXIF metadata of these photos, once we retrieve a location.
|
||||
@@ -122,7 +125,7 @@ const usePrepareStoreAndNavigate = ( options: Options ): Function => {
|
||||
} );
|
||||
setObservations( [newObservation] );
|
||||
if ( addPhotoPermissionResult !== RESULTS.GRANTED ) return Promise.resolve( );
|
||||
return savePhotosToCameraGallery( cameraUris, addCameraRollUri );
|
||||
return savePhotosToCameraGallery( cameraUris, addCameraRollUri, userLocation );
|
||||
}, [
|
||||
addCameraRollUri,
|
||||
addPhotoPermissionResult,
|
||||
|
||||
@@ -9,8 +9,7 @@ import { Heading4 } from "components/SharedComponents";
|
||||
import Mortal from "components/SharedComponents/Mortal";
|
||||
import PermissionGateContainer, {
|
||||
AUDIO_PERMISSIONS,
|
||||
CAMERA_PERMISSIONS,
|
||||
READ_WRITE_MEDIA_PERMISSIONS
|
||||
CAMERA_PERMISSIONS
|
||||
} from "components/SharedComponents/PermissionGateContainer.tsx";
|
||||
import SoundRecorder from "components/SoundRecorder/SoundRecorder";
|
||||
import { t } from "i18next";
|
||||
@@ -70,19 +69,30 @@ const CameraContainerWithPermission = ( ) => (
|
||||
</Mortal>
|
||||
);
|
||||
|
||||
// const GalleryContainerWithPermission = ( ) => (
|
||||
// <PermissionGateContainer
|
||||
// permissions={READ_WRITE_MEDIA_PERMISSIONS}
|
||||
// title={t( "Observe-and-identify-organisms-from-your-gallery" )}
|
||||
// titleDenied={t( "Please-Allow-Gallery-Access" )}
|
||||
// body={t( "Upload-photos-from-your-gallery-and-create-observations" )}
|
||||
// blockedPrompt={t( "Youve-previously-denied-gallery-permissions" )}
|
||||
// buttonText={t( "CHOOSE-PHOTOS" )}
|
||||
// icon="gallery"
|
||||
// image={require( "images/background/viviana-rishe-j2330n6bg3I-unsplash.jpg" )}
|
||||
// >
|
||||
// <PhotoGallery />
|
||||
// </PermissionGateContainer>
|
||||
// );
|
||||
|
||||
// On iOS we don't actually need PHOTO LIBRARY permission to import photos,
|
||||
// and in fact, if we ask for it and the user denies it after already
|
||||
// granting add-only permission, the user can never grant it again until they
|
||||
// uninstall the app. We *may* want to bring this back to handle writing to
|
||||
// albums, but for now this works. ~~~~kueda20240829
|
||||
|
||||
// TODO verify this is true for Android
|
||||
const GalleryContainerWithPermission = ( ) => (
|
||||
<PermissionGateContainer
|
||||
permissions={READ_WRITE_MEDIA_PERMISSIONS}
|
||||
title={t( "Observe-and-identify-organisms-from-your-gallery" )}
|
||||
titleDenied={t( "Please-Allow-Gallery-Access" )}
|
||||
body={t( "Upload-photos-from-your-gallery-and-create-observations" )}
|
||||
blockedPrompt={t( "Youve-previously-denied-gallery-permissions" )}
|
||||
buttonText={t( "CHOOSE-PHOTOS" )}
|
||||
icon="gallery"
|
||||
image={require( "images/background/viviana-rishe-j2330n6bg3I-unsplash.jpg" )}
|
||||
>
|
||||
<PhotoGallery />
|
||||
</PermissionGateContainer>
|
||||
<PhotoGallery />
|
||||
);
|
||||
|
||||
const SoundRecorderWithPermission = ( ) => (
|
||||
|
||||
Reference in New Issue
Block a user