mattermost-mobile-upstream/patches/react-native-image-picker+8.2.1.patch
2026-09-10 17:32:35 +08:00

127 lines
5.3 KiB
Diff

diff --git a/node_modules/react-native-image-picker/ios/ImagePickerManager.mm b/node_modules/react-native-image-picker/ios/ImagePickerManager.mm
index 93e99be..94082ec 100644
--- a/node_modules/react-native-image-picker/ios/ImagePickerManager.mm
+++ b/node_modules/react-native-image-picker/ios/ImagePickerManager.mm
@@ -68,7 +68,7 @@ RCT_EXPORT_METHOD(launchImageLibrary:(NSDictionary *)options callback:(RCTRespon
self.callback = callback;
if (target == camera && [ImagePickerUtils isSimulator]) {
- self.callback(@[@{@"errorCode": errCameraUnavailable}]);
+ [self invokeCallback:@[@{@"errorCode": errCameraUnavailable}]];
return;
}
@@ -87,7 +87,7 @@ RCT_EXPORT_METHOD(launchImageLibrary:(NSDictionary *)options callback:(RCTRespon
[self checkPhotosPermissions:^(BOOL granted) {
if (!granted) {
- self.callback(@[@{@"errorCode": errPermission}]);
+ [self invokeCallback:@[@{@"errorCode": errPermission}]];
return;
}
[self showPickerViewController:picker];
@@ -108,7 +108,7 @@ RCT_EXPORT_METHOD(launchImageLibrary:(NSDictionary *)options callback:(RCTRespon
if([self.options[@"includeExtra"] boolValue]) {
[self checkPhotosPermissions:^(BOOL granted) {
if (!granted) {
- self.callback(@[@{@"errorCode": errPermission}]);
+ [self invokeCallback:@[@{@"errorCode": errPermission}]];
return;
}
[self showPickerViewController:picker];
@@ -126,6 +126,22 @@ RCT_EXPORT_METHOD(launchImageLibrary:(NSDictionary *)options callback:(RCTRespon
});
}
+// The bridge callback aborts the app if invoked more than once. Delegate
+// callbacks can double-fire (e.g. presentationControllerDidDismiss followed by
+// picker:didFinishPicking:), so consume the callback exactly once, serialized
+// on the main queue.
+- (void)invokeCallback:(NSArray *)response
+{
+ dispatch_async(dispatch_get_main_queue(), ^{
+ RCTResponseSenderBlock callback = self.callback;
+ if (callback == nil) {
+ return;
+ }
+ self.callback = nil;
+ callback(response);
+ });
+}
+
#pragma mark - Helpers
NSData* extractImageData(UIImage* image){
@@ -465,7 +481,7 @@ CGImagePropertyOrientation CGImagePropertyOrientationForUIImageOrientation(UIIma
if (videoAsset == nil) {
NSString *errorMessage = error.localizedFailureReason;
if (errorMessage == nil) errorMessage = @"Video asset not found";
- self.callback(@[@{@"errorCode": errOthers, @"errorMessage": errorMessage}]);
+ [self invokeCallback:@[@{@"errorCode": errOthers, @"errorMessage": errorMessage}]];
return;
}
[assets addObject:videoAsset];
@@ -473,7 +489,7 @@ CGImagePropertyOrientation CGImagePropertyOrientationForUIImageOrientation(UIIma
NSMutableDictionary *response = [[NSMutableDictionary alloc] init];
response[@"assets"] = assets;
- self.callback(@[response]);
+ [self invokeCallback:@[response]];
};
dispatch_async(dispatch_get_main_queue(), ^{
@@ -485,7 +501,7 @@ CGImagePropertyOrientation CGImagePropertyOrientationForUIImageOrientation(UIIma
{
dispatch_async(dispatch_get_main_queue(), ^{
[picker dismissViewControllerAnimated:YES completion:^{
- self.callback(@[@{@"didCancel": @YES}]);
+ [self invokeCallback:@[@{@"didCancel": @YES}]];
}];
});
}
@@ -496,7 +512,14 @@ CGImagePropertyOrientation CGImagePropertyOrientationForUIImageOrientation(UIIma
- (void)presentationControllerDidDismiss:(UIPresentationController *)presentationController
{
- self.callback(@[@{@"didCancel": @YES}]);
+ // A selection already being processed must win over the interactive
+ // dismissal, otherwise the picked assets would later hit a consumed
+ // callback (or be reported as a cancel).
+ if (photoSelected == YES) {
+ return;
+ }
+ photoSelected = YES;
+ [self invokeCallback:@[@{@"didCancel": @YES}]];
}
@end
@@ -514,9 +537,7 @@ CGImagePropertyOrientation CGImagePropertyOrientationForUIImageOrientation(UIIma
photoSelected = YES;
if (results.count == 0) {
- dispatch_async(dispatch_get_main_queue(), ^{
- self.callback(@[@{@"didCancel": @YES}]);
- });
+ [self invokeCallback:@[@{@"didCancel": @YES}]];
return;
}
@@ -571,7 +592,7 @@ CGImagePropertyOrientation CGImagePropertyOrientationForUIImageOrientation(UIIma
// mapVideoToAsset can fail and return nil, leaving asset NSNull.
for (NSDictionary *asset in assets) {
if ([asset isEqual:[NSNull null]]) {
- self.callback(@[@{@"errorCode": errOthers}]);
+ [self invokeCallback:@[@{@"errorCode": errOthers}]];
return;
}
}
@@ -579,7 +600,7 @@ CGImagePropertyOrientation CGImagePropertyOrientationForUIImageOrientation(UIIma
NSMutableDictionary *response = [[NSMutableDictionary alloc] init];
[response setObject:assets forKey:@"assets"];
- self.callback(@[response]);
+ [self invokeCallback:@[response]];
});
}